hass-core/tests/components/fronius/test_sensor.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

602 lines
27 KiB
Python
Raw Normal View History

"""Tests for the Fronius sensor platform."""
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant.components.fronius.const import DOMAIN
Rewrite Fronius integration (#59686) * Add unique_id and use DataUpdateCoordinator in Fronius (#57879) * initial refactoring commit - meters - config_flow (no strings, no tests yet) - import yaml config - FroniusSolarNet class for holding Fronius object , coordinators and some common data - meter descriptions - update coordinator - entities (including devices) * storage controllers * error handling on init; inverter unique_id * inverters * power_flow * fix VA, var, varh not valid for device_class power/energy and add custom icons * add SolarNet device for system wide values * cleanup * config_flow strings * test config_flow * use pyfronius 0.7.0 * enable strict typing * remove TODO comments * fix lint errors; move FroniusSensorEntity to sensor.py * power_flow as optional coordinator API V0 doesn't support power_flow endpoint * show error message in logs * prevent parallel requests to one host * logger_info coordinator * store FroniusSolarNet reference directly in coordinator * cleanup coordinators when unloading entry * round floats returned by Fronius API * default icons for grid im/export tariffs * small typing fix * Update homeassistant/components/fronius/sensor.py Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * DC icons * prepend names with "Fronius" and device type to get more reasonable default entity_ids (eg. have them next to each other when alphabetically sorted) * remove config_flow and devices * rename _FroniusUpdateCoordinator to FroniusCoordinatorBase and mark ABC * move SensorEntityDescriptions to sensor.py * Revert "move SensorEntityDescriptions to sensor.py" This reverts commit 2e5a726eb65854f236a0c72f3f67f04a6f8a2eff. * Don't raise ConfigEntryNotReady and use regular refresh method * move bridge initialization out of helper class * no coverage tests * power_flow update interval 10 seconds * move SensorEntityDescriptions to sensor.py without introducing a circular dependency * deprecation warning for CONF_MONITORED_CONDITIONS * remove extra_state_attributes form meter sensor entities * readd diagnostic entities * decouple default entity_id from default name * use key instead of name for entity_id and make deprecated config key optional * adjust tests * use old entity_ids these changes are now backwards compatible * check coverage * simplify entity description definitions * restore entity names of previous implementation Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * Add config_flow for Fronius integration (#59677) * Cleanup Fronius config_flow and tests (#60094) * Add devices to Fronius integration (#60104) * New entity names for Fronius entities (#60215) * Adaptive update interval for Fronius coordinators (#60192) Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
2021-11-24 02:04:36 +01:00
from homeassistant.components.fronius.coordinator import (
FroniusInverterUpdateCoordinator,
FroniusMeterUpdateCoordinator,
Rewrite Fronius integration (#59686) * Add unique_id and use DataUpdateCoordinator in Fronius (#57879) * initial refactoring commit - meters - config_flow (no strings, no tests yet) - import yaml config - FroniusSolarNet class for holding Fronius object , coordinators and some common data - meter descriptions - update coordinator - entities (including devices) * storage controllers * error handling on init; inverter unique_id * inverters * power_flow * fix VA, var, varh not valid for device_class power/energy and add custom icons * add SolarNet device for system wide values * cleanup * config_flow strings * test config_flow * use pyfronius 0.7.0 * enable strict typing * remove TODO comments * fix lint errors; move FroniusSensorEntity to sensor.py * power_flow as optional coordinator API V0 doesn't support power_flow endpoint * show error message in logs * prevent parallel requests to one host * logger_info coordinator * store FroniusSolarNet reference directly in coordinator * cleanup coordinators when unloading entry * round floats returned by Fronius API * default icons for grid im/export tariffs * small typing fix * Update homeassistant/components/fronius/sensor.py Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * DC icons * prepend names with "Fronius" and device type to get more reasonable default entity_ids (eg. have them next to each other when alphabetically sorted) * remove config_flow and devices * rename _FroniusUpdateCoordinator to FroniusCoordinatorBase and mark ABC * move SensorEntityDescriptions to sensor.py * Revert "move SensorEntityDescriptions to sensor.py" This reverts commit 2e5a726eb65854f236a0c72f3f67f04a6f8a2eff. * Don't raise ConfigEntryNotReady and use regular refresh method * move bridge initialization out of helper class * no coverage tests * power_flow update interval 10 seconds * move SensorEntityDescriptions to sensor.py without introducing a circular dependency * deprecation warning for CONF_MONITORED_CONDITIONS * remove extra_state_attributes form meter sensor entities * readd diagnostic entities * decouple default entity_id from default name * use key instead of name for entity_id and make deprecated config key optional * adjust tests * use old entity_ids these changes are now backwards compatible * check coverage * simplify entity description definitions * restore entity names of previous implementation Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * Add config_flow for Fronius integration (#59677) * Cleanup Fronius config_flow and tests (#60094) * Add devices to Fronius integration (#60104) * New entity names for Fronius entities (#60215) * Adaptive update interval for Fronius coordinators (#60192) Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
2021-11-24 02:04:36 +01:00
FroniusPowerFlowUpdateCoordinator,
)
Rewrite Fronius integration (#59686) * Add unique_id and use DataUpdateCoordinator in Fronius (#57879) * initial refactoring commit - meters - config_flow (no strings, no tests yet) - import yaml config - FroniusSolarNet class for holding Fronius object , coordinators and some common data - meter descriptions - update coordinator - entities (including devices) * storage controllers * error handling on init; inverter unique_id * inverters * power_flow * fix VA, var, varh not valid for device_class power/energy and add custom icons * add SolarNet device for system wide values * cleanup * config_flow strings * test config_flow * use pyfronius 0.7.0 * enable strict typing * remove TODO comments * fix lint errors; move FroniusSensorEntity to sensor.py * power_flow as optional coordinator API V0 doesn't support power_flow endpoint * show error message in logs * prevent parallel requests to one host * logger_info coordinator * store FroniusSolarNet reference directly in coordinator * cleanup coordinators when unloading entry * round floats returned by Fronius API * default icons for grid im/export tariffs * small typing fix * Update homeassistant/components/fronius/sensor.py Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * DC icons * prepend names with "Fronius" and device type to get more reasonable default entity_ids (eg. have them next to each other when alphabetically sorted) * remove config_flow and devices * rename _FroniusUpdateCoordinator to FroniusCoordinatorBase and mark ABC * move SensorEntityDescriptions to sensor.py * Revert "move SensorEntityDescriptions to sensor.py" This reverts commit 2e5a726eb65854f236a0c72f3f67f04a6f8a2eff. * Don't raise ConfigEntryNotReady and use regular refresh method * move bridge initialization out of helper class * no coverage tests * power_flow update interval 10 seconds * move SensorEntityDescriptions to sensor.py without introducing a circular dependency * deprecation warning for CONF_MONITORED_CONDITIONS * remove extra_state_attributes form meter sensor entities * readd diagnostic entities * decouple default entity_id from default name * use key instead of name for entity_id and make deprecated config key optional * adjust tests * use old entity_ids these changes are now backwards compatible * check coverage * simplify entity description definitions * restore entity names of previous implementation Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * Add config_flow for Fronius integration (#59677) * Cleanup Fronius config_flow and tests (#60094) * Add devices to Fronius integration (#60104) * New entity names for Fronius entities (#60215) * Adaptive update interval for Fronius coordinators (#60192) Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
2021-11-24 02:04:36 +01:00
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from . import enable_all_entities, mock_responses, setup_fronius_integration
Rewrite Fronius integration (#59686) * Add unique_id and use DataUpdateCoordinator in Fronius (#57879) * initial refactoring commit - meters - config_flow (no strings, no tests yet) - import yaml config - FroniusSolarNet class for holding Fronius object , coordinators and some common data - meter descriptions - update coordinator - entities (including devices) * storage controllers * error handling on init; inverter unique_id * inverters * power_flow * fix VA, var, varh not valid for device_class power/energy and add custom icons * add SolarNet device for system wide values * cleanup * config_flow strings * test config_flow * use pyfronius 0.7.0 * enable strict typing * remove TODO comments * fix lint errors; move FroniusSensorEntity to sensor.py * power_flow as optional coordinator API V0 doesn't support power_flow endpoint * show error message in logs * prevent parallel requests to one host * logger_info coordinator * store FroniusSolarNet reference directly in coordinator * cleanup coordinators when unloading entry * round floats returned by Fronius API * default icons for grid im/export tariffs * small typing fix * Update homeassistant/components/fronius/sensor.py Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * DC icons * prepend names with "Fronius" and device type to get more reasonable default entity_ids (eg. have them next to each other when alphabetically sorted) * remove config_flow and devices * rename _FroniusUpdateCoordinator to FroniusCoordinatorBase and mark ABC * move SensorEntityDescriptions to sensor.py * Revert "move SensorEntityDescriptions to sensor.py" This reverts commit 2e5a726eb65854f236a0c72f3f67f04a6f8a2eff. * Don't raise ConfigEntryNotReady and use regular refresh method * move bridge initialization out of helper class * no coverage tests * power_flow update interval 10 seconds * move SensorEntityDescriptions to sensor.py without introducing a circular dependency * deprecation warning for CONF_MONITORED_CONDITIONS * remove extra_state_attributes form meter sensor entities * readd diagnostic entities * decouple default entity_id from default name * use key instead of name for entity_id and make deprecated config key optional * adjust tests * use old entity_ids these changes are now backwards compatible * check coverage * simplify entity description definitions * restore entity names of previous implementation Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * Add config_flow for Fronius integration (#59677) * Cleanup Fronius config_flow and tests (#60094) * Add devices to Fronius integration (#60104) * New entity names for Fronius entities (#60215) * Adaptive update interval for Fronius coordinators (#60192) Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
2021-11-24 02:04:36 +01:00
from tests.common import async_fire_time_changed
from tests.test_util.aiohttp import AiohttpClientMocker
async def test_symo_inverter(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test Fronius Symo inverter entities."""
def assert_state(entity_id, expected_state):
state = hass.states.get(entity_id)
assert state.state == str(expected_state)
# Init at night
mock_responses(aioclient_mock, night=True)
config_entry = await setup_fronius_integration(hass)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 21
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusInverterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 54
assert_state("sensor.symo_20_dc_current", 0)
assert_state("sensor.symo_20_energy_day", 10828)
assert_state("sensor.symo_20_total_energy", 44186900)
assert_state("sensor.symo_20_energy_year", 25507686)
assert_state("sensor.symo_20_dc_voltage", 16)
assert_state("sensor.symo_20_status_message", "startup")
# Second test at daytime when inverter is producing
mock_responses(aioclient_mock, night=False)
freezer.tick(FroniusInverterUpdateCoordinator.default_interval)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 58
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusInverterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 60
# 4 additional AC entities
assert_state("sensor.symo_20_dc_current", 2.19)
assert_state("sensor.symo_20_energy_day", 1113)
assert_state("sensor.symo_20_total_energy", 44188000)
assert_state("sensor.symo_20_energy_year", 25508798)
assert_state("sensor.symo_20_dc_voltage", 518)
assert_state("sensor.symo_20_ac_current", 5.19)
assert_state("sensor.symo_20_frequency", 49.94)
assert_state("sensor.symo_20_ac_power", 1190)
assert_state("sensor.symo_20_ac_voltage", 227.90)
assert_state("sensor.symo_20_status_message", "running")
# Third test at nighttime - additional AC entities default to 0
mock_responses(aioclient_mock, night=True)
freezer.tick(FroniusInverterUpdateCoordinator.default_interval)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert_state("sensor.symo_20_ac_current", 0)
assert_state("sensor.symo_20_frequency", 0)
assert_state("sensor.symo_20_ac_power", 0)
assert_state("sensor.symo_20_ac_voltage", 0)
async def test_symo_logger(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test Fronius Symo logger entities."""
def assert_state(entity_id, expected_state):
state = hass.states.get(entity_id)
assert state
assert state.state == str(expected_state)
mock_responses(aioclient_mock)
Rewrite Fronius integration (#59686) * Add unique_id and use DataUpdateCoordinator in Fronius (#57879) * initial refactoring commit - meters - config_flow (no strings, no tests yet) - import yaml config - FroniusSolarNet class for holding Fronius object , coordinators and some common data - meter descriptions - update coordinator - entities (including devices) * storage controllers * error handling on init; inverter unique_id * inverters * power_flow * fix VA, var, varh not valid for device_class power/energy and add custom icons * add SolarNet device for system wide values * cleanup * config_flow strings * test config_flow * use pyfronius 0.7.0 * enable strict typing * remove TODO comments * fix lint errors; move FroniusSensorEntity to sensor.py * power_flow as optional coordinator API V0 doesn't support power_flow endpoint * show error message in logs * prevent parallel requests to one host * logger_info coordinator * store FroniusSolarNet reference directly in coordinator * cleanup coordinators when unloading entry * round floats returned by Fronius API * default icons for grid im/export tariffs * small typing fix * Update homeassistant/components/fronius/sensor.py Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * DC icons * prepend names with "Fronius" and device type to get more reasonable default entity_ids (eg. have them next to each other when alphabetically sorted) * remove config_flow and devices * rename _FroniusUpdateCoordinator to FroniusCoordinatorBase and mark ABC * move SensorEntityDescriptions to sensor.py * Revert "move SensorEntityDescriptions to sensor.py" This reverts commit 2e5a726eb65854f236a0c72f3f67f04a6f8a2eff. * Don't raise ConfigEntryNotReady and use regular refresh method * move bridge initialization out of helper class * no coverage tests * power_flow update interval 10 seconds * move SensorEntityDescriptions to sensor.py without introducing a circular dependency * deprecation warning for CONF_MONITORED_CONDITIONS * remove extra_state_attributes form meter sensor entities * readd diagnostic entities * decouple default entity_id from default name * use key instead of name for entity_id and make deprecated config key optional * adjust tests * use old entity_ids these changes are now backwards compatible * check coverage * simplify entity description definitions * restore entity names of previous implementation Co-authored-by: Brett Adams <Bre77@users.noreply.github.com> * Add config_flow for Fronius integration (#59677) * Cleanup Fronius config_flow and tests (#60094) * Add devices to Fronius integration (#60104) * New entity names for Fronius entities (#60215) * Adaptive update interval for Fronius coordinators (#60192) Co-authored-by: Brett Adams <Bre77@users.noreply.github.com>
2021-11-24 02:04:36 +01:00
await setup_fronius_integration(hass)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 25
# states are rounded to 4 decimals
assert_state("sensor.solarnet_grid_export_tariff", 0.078)
assert_state("sensor.solarnet_co2_factor", 0.53)
assert_state("sensor.solarnet_grid_import_tariff", 0.15)
async def test_symo_meter(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test Fronius Symo meter entities."""
def assert_state(entity_id, expected_state):
state = hass.states.get(entity_id)
assert state
assert state.state == str(expected_state)
mock_responses(aioclient_mock)
config_entry = await setup_fronius_integration(hass)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 25
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 60
# states are rounded to 4 decimals
assert_state("sensor.smart_meter_63a_current_phase_1", 7.755)
assert_state("sensor.smart_meter_63a_current_phase_2", 6.68)
assert_state("sensor.smart_meter_63a_current_phase_3", 10.102)
assert_state("sensor.smart_meter_63a_reactive_energy_consumed", 59960790)
assert_state("sensor.smart_meter_63a_reactive_energy_produced", 723160)
assert_state("sensor.smart_meter_63a_real_energy_minus", 35623065)
assert_state("sensor.smart_meter_63a_real_energy_plus", 15303334)
assert_state("sensor.smart_meter_63a_real_energy_consumed", 15303334)
assert_state("sensor.smart_meter_63a_real_energy_produced", 35623065)
assert_state("sensor.smart_meter_63a_frequency_phase_average", 50)
assert_state("sensor.smart_meter_63a_apparent_power_phase_1", 1772.793)
assert_state("sensor.smart_meter_63a_apparent_power_phase_2", 1527.048)
assert_state("sensor.smart_meter_63a_apparent_power_phase_3", 2333.562)
assert_state("sensor.smart_meter_63a_apparent_power", 5592.57)
assert_state("sensor.smart_meter_63a_power_factor_phase_1", -0.99)
assert_state("sensor.smart_meter_63a_power_factor_phase_2", -0.99)
assert_state("sensor.smart_meter_63a_power_factor_phase_3", 0.99)
assert_state("sensor.smart_meter_63a_power_factor", 1)
assert_state("sensor.smart_meter_63a_reactive_power_phase_1", 51.48)
assert_state("sensor.smart_meter_63a_reactive_power_phase_2", 115.63)
assert_state("sensor.smart_meter_63a_reactive_power_phase_3", -164.24)
assert_state("sensor.smart_meter_63a_reactive_power", 2.87)
assert_state("sensor.smart_meter_63a_real_power_phase_1", 1765.55)
assert_state("sensor.smart_meter_63a_real_power_phase_2", 1515.8)
assert_state("sensor.smart_meter_63a_real_power_phase_3", 2311.22)
assert_state("sensor.smart_meter_63a_real_power", 5592.57)
assert_state("sensor.smart_meter_63a_voltage_phase_1", 228.6)
assert_state("sensor.smart_meter_63a_voltage_phase_2", 228.6)
assert_state("sensor.smart_meter_63a_voltage_phase_3", 231)
assert_state("sensor.smart_meter_63a_voltage_phase_1_2", 395.9)
assert_state("sensor.smart_meter_63a_voltage_phase_2_3", 398)
assert_state("sensor.smart_meter_63a_voltage_phase_3_1", 398)
assert_state("sensor.smart_meter_63a_meter_location", 0)
assert_state("sensor.smart_meter_63a_meter_location_description", "feed_in")
@pytest.mark.parametrize(
("location_code", "expected_code", "expected_description"),
[
(-1, -1, "unknown"),
(3, 3, "external_generator"),
(4, 4, "external_battery"),
(7, 7, "unknown"),
(256, 256, "subload"),
(511, 511, "subload"),
(512, 512, "unknown"),
],
)
async def test_symo_meter_forged(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
location_code: int | None,
expected_code: int | str,
expected_description: str,
) -> None:
"""Tests for meter location codes we have no fixture for."""
def assert_state(entity_id, expected_state):
state = hass.states.get(entity_id)
assert state
assert state.state == str(expected_state)
mock_responses(
aioclient_mock,
fixture_set="symo",
override_data={
"symo/GetMeterRealtimeData.json": [
(["Body", "Data", "0", "Meter_Location_Current"], location_code),
],
},
)
await setup_fronius_integration(hass)
assert_state("sensor.smart_meter_63a_meter_location", expected_code)
assert_state(
"sensor.smart_meter_63a_meter_location_description", expected_description
)
async def test_symo_power_flow(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test Fronius Symo power flow entities."""
async_fire_time_changed(hass)
def assert_state(entity_id, expected_state):
state = hass.states.get(entity_id)
assert state.state == str(expected_state)
# First test at night
mock_responses(aioclient_mock, night=True)
config_entry = await setup_fronius_integration(hass)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 21
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusInverterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 54
# states are rounded to 4 decimals
assert_state("sensor.solarnet_energy_day", 10828)
assert_state("sensor.solarnet_total_energy", 44186900)
assert_state("sensor.solarnet_energy_year", 25507686)
assert_state("sensor.solarnet_power_grid", 975.31)
assert_state("sensor.solarnet_power_load", -975.31)
assert_state("sensor.solarnet_relative_autonomy", 0)
# Second test at daytime when inverter is producing
mock_responses(aioclient_mock, night=False)
freezer.tick(FroniusPowerFlowUpdateCoordinator.default_interval)
async_fire_time_changed(hass)
await hass.async_block_till_done()
# 54 because power_flow `rel_SelfConsumption` and `P_PV` is not `null` anymore
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 56
assert_state("sensor.solarnet_energy_day", 1101.7001)
assert_state("sensor.solarnet_total_energy", 44188000)
assert_state("sensor.solarnet_energy_year", 25508788)
assert_state("sensor.solarnet_power_grid", 1703.74)
assert_state("sensor.solarnet_power_load", -2814.74)
assert_state("sensor.solarnet_power_photovoltaics", 1111)
assert_state("sensor.solarnet_relative_autonomy", 39.4708)
assert_state("sensor.solarnet_relative_self_consumption", 100)
# Third test at nighttime - default values are used
mock_responses(aioclient_mock, night=True)
freezer.tick(FroniusPowerFlowUpdateCoordinator.default_interval)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 56
assert_state("sensor.solarnet_energy_day", 10828)
assert_state("sensor.solarnet_total_energy", 44186900)
assert_state("sensor.solarnet_energy_year", 25507686)
assert_state("sensor.solarnet_power_grid", 975.31)
assert_state("sensor.solarnet_power_load", -975.31)
assert_state("sensor.solarnet_power_photovoltaics", 0)
assert_state("sensor.solarnet_relative_autonomy", 0)
assert_state("sensor.solarnet_relative_self_consumption", 0)
async def test_gen24(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test Fronius Gen24 inverter entities."""
def assert_state(entity_id, expected_state):
state = hass.states.get(entity_id)
assert state
assert state.state == str(expected_state)
mock_responses(aioclient_mock, fixture_set="gen24")
config_entry = await setup_fronius_integration(hass, is_logger=False)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 23
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 54
# inverter 1
assert_state("sensor.inverter_name_ac_current", 0.1589)
assert_state("sensor.inverter_name_dc_current_2", 0.0754)
assert_state("sensor.inverter_name_status_code", 7)
assert_state("sensor.inverter_name_status_message", "running")
assert_state("sensor.inverter_name_dc_current", 0.0783)
assert_state("sensor.inverter_name_dc_voltage_2", 403.4312)
assert_state("sensor.inverter_name_ac_power", 37.3204)
assert_state("sensor.inverter_name_error_code", 0)
assert_state("sensor.inverter_name_dc_voltage", 411.3811)
assert_state("sensor.inverter_name_total_energy", 1530193.42)
assert_state("sensor.inverter_name_inverter_state", "Running")
assert_state("sensor.inverter_name_ac_voltage", 234.9168)
assert_state("sensor.inverter_name_frequency", 49.9917)
# meter
assert_state("sensor.smart_meter_ts_65a_3_real_energy_produced", 3863340.0)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_consumed", 2013105.0)
assert_state("sensor.smart_meter_ts_65a_3_real_power", 653.1)
assert_state("sensor.smart_meter_ts_65a_3_frequency_phase_average", 49.9)
assert_state("sensor.smart_meter_ts_65a_3_meter_location", 0)
assert_state("sensor.smart_meter_ts_65a_3_meter_location_description", "feed_in")
assert_state("sensor.smart_meter_ts_65a_3_power_factor", 0.828)
assert_state("sensor.smart_meter_ts_65a_3_reactive_energy_consumed", 88221.0)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_minus", 3863340.0)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_2", 2.33)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_1", 235.9)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_1_2", 408.7)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_2", 294.9)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_plus", 2013105.0)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_2", 236.1)
assert_state("sensor.smart_meter_ts_65a_3_reactive_energy_produced", 1989125.0)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_3", 236.9)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_1", 0.441)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_2_3", 409.6)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_3", 1.825)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_3", 0.832)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_1", 243.3)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_3_1", 409.4)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_2", 323.4)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_3", 301.2)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_1", 106.8)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_2", 0.934)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_3", 251.3)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_1", -218.6)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_2", -132.8)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_3", -166.0)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power", 868.0)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power", -517.4)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_1", 1.145)
# power_flow
assert_state("sensor.solarnet_power_grid", 658.4)
assert_state("sensor.solarnet_relative_self_consumption", 100.0)
assert_state("sensor.solarnet_power_photovoltaics", 62.9481)
assert_state("sensor.solarnet_power_load", -695.6827)
assert_state("sensor.solarnet_meter_mode", "meter")
assert_state("sensor.solarnet_relative_autonomy", 5.3592)
assert_state("sensor.solarnet_total_energy", 1530193.42)
# Gen24 devices may report 0 for total energy while doing firmware updates.
# This should yield "unknown" state instead of 0.
mock_responses(
aioclient_mock,
fixture_set="gen24",
override_data={
"gen24/GetInverterRealtimeData_Device_1.json": [
(["Body", "Data", "TOTAL_ENERGY", "Value"], 0),
],
},
)
freezer.tick(FroniusInverterUpdateCoordinator.default_interval)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert_state("sensor.inverter_name_total_energy", "unknown")
async def test_gen24_storage(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
device_registry: dr.DeviceRegistry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test Fronius Gen24 inverter with BYD battery and Ohmpilot entities."""
def assert_state(entity_id, expected_state):
state = hass.states.get(entity_id)
assert state
assert state.state == str(expected_state)
mock_responses(aioclient_mock, fixture_set="gen24_storage")
config_entry = await setup_fronius_integration(
hass, is_logger=False, unique_id="12345678"
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 35
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 66
# inverter 1
assert_state("sensor.gen24_storage_dc_current", 0.3952)
assert_state("sensor.gen24_storage_dc_voltage_2", 318.8103)
assert_state("sensor.gen24_storage_dc_current_2", 0.3564)
assert_state("sensor.gen24_storage_ac_current", 1.1087)
assert_state("sensor.gen24_storage_ac_power", 250.9093)
assert_state("sensor.gen24_storage_error_code", 0)
assert_state("sensor.gen24_storage_status_code", 7)
assert_state("sensor.gen24_storage_status_message", "running")
assert_state("sensor.gen24_storage_total_energy", 7512794.0117)
assert_state("sensor.gen24_storage_inverter_state", "Running")
assert_state("sensor.gen24_storage_dc_voltage", 419.1009)
assert_state("sensor.gen24_storage_ac_voltage", 227.354)
assert_state("sensor.gen24_storage_frequency", 49.9816)
# meter
assert_state("sensor.smart_meter_ts_65a_3_real_energy_produced", 1705128.0)
assert_state("sensor.smart_meter_ts_65a_3_real_power", 487.7)
assert_state("sensor.smart_meter_ts_65a_3_power_factor", 0.698)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_consumed", 1247204.0)
assert_state("sensor.smart_meter_ts_65a_3_frequency_phase_average", 49.9)
assert_state("sensor.smart_meter_ts_65a_3_meter_location", 0)
assert_state("sensor.smart_meter_ts_65a_3_meter_location_description", "feed_in")
assert_state("sensor.smart_meter_ts_65a_3_reactive_power", -501.5)
assert_state("sensor.smart_meter_ts_65a_3_reactive_energy_produced", 3266105.0)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_3", 19.6)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_3", 0.645)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_minus", 1705128.0)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_2", 383.9)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_1", 1.701)
assert_state("sensor.smart_meter_ts_65a_3_current_phase_2", 1.832)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_1", 319.5)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_1", 229.4)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_2", 150.0)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_3_1", 394.3)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_2", 225.6)
assert_state("sensor.smart_meter_ts_65a_3_reactive_energy_consumed", 5482.0)
assert_state("sensor.smart_meter_ts_65a_3_real_energy_plus", 1247204.0)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_1", 0.995)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_3", 0.163)
assert_state("sensor.smart_meter_ts_65a_3_power_factor_phase_2", 0.389)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_1", -31.3)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_3", -116.7)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_1_2", 396.0)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_2_3", 393.0)
assert_state("sensor.smart_meter_ts_65a_3_reactive_power_phase_2", -353.4)
assert_state("sensor.smart_meter_ts_65a_3_real_power_phase_1", 317.9)
assert_state("sensor.smart_meter_ts_65a_3_voltage_phase_3", 228.3)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power", 821.9)
assert_state("sensor.smart_meter_ts_65a_3_apparent_power_phase_3", 118.4)
# ohmpilot
assert_state("sensor.ohmpilot_energy_consumed", 1233295.0)
assert_state("sensor.ohmpilot_power", 0.0)
assert_state("sensor.ohmpilot_temperature", 38.9)
assert_state("sensor.ohmpilot_state_code", 0.0)
assert_state("sensor.ohmpilot_state_message", "up_and_running")
# power_flow
assert_state("sensor.solarnet_power_grid", 2274.9)
assert_state("sensor.solarnet_power_battery", 0.1591)
assert_state("sensor.solarnet_power_load", -2459.3092)
assert_state("sensor.solarnet_relative_self_consumption", 100.0)
assert_state("sensor.solarnet_power_photovoltaics", 216.4328)
assert_state("sensor.solarnet_relative_autonomy", 7.4984)
assert_state("sensor.solarnet_meter_mode", "bidirectional")
assert_state("sensor.solarnet_total_energy", 7512664.4042)
# storage
assert_state("sensor.byd_battery_box_premium_hv_dc_current", 0.0)
assert_state("sensor.byd_battery_box_premium_hv_state_of_charge", 4.6)
assert_state("sensor.byd_battery_box_premium_hv_maximum_capacity", 16588)
assert_state("sensor.byd_battery_box_premium_hv_temperature", 21.5)
assert_state("sensor.byd_battery_box_premium_hv_designed_capacity", 16588)
assert_state("sensor.byd_battery_box_premium_hv_dc_voltage", 0.0)
# Devices
solar_net = device_registry.async_get_device(
identifiers={(DOMAIN, "solar_net_12345678")}
)
assert solar_net.configuration_url == "http://fronius"
assert solar_net.manufacturer == "Fronius"
assert solar_net.name == "SolarNet"
inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "12345678")})
assert inverter_1.manufacturer == "Fronius"
assert inverter_1.model == "Gen24"
assert inverter_1.name == "Gen24 Storage"
meter = device_registry.async_get_device(identifiers={(DOMAIN, "1234567890")})
assert meter.manufacturer == "Fronius"
assert meter.model == "Smart Meter TS 65A-3"
assert meter.name == "Smart Meter TS 65A-3"
ohmpilot = device_registry.async_get_device(identifiers={(DOMAIN, "23456789")})
assert ohmpilot.manufacturer == "Fronius"
assert ohmpilot.model == "Ohmpilot 6"
assert ohmpilot.name == "Ohmpilot"
assert ohmpilot.sw_version == "1.0.25-3"
storage = device_registry.async_get_device(
identifiers={(DOMAIN, "P030T020Z2001234567 ")}
)
assert storage.manufacturer == "BYD"
assert storage.model == "BYD Battery-Box Premium HV"
assert storage.name == "BYD Battery-Box Premium HV"
async def test_primo_s0(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
device_registry: dr.DeviceRegistry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test Fronius Primo dual inverter with S0 meter entities."""
def assert_state(entity_id, expected_state):
state = hass.states.get(entity_id)
assert state
assert state.state == str(expected_state)
mock_responses(aioclient_mock, fixture_set="primo_s0", inverter_ids=[1, 2])
config_entry = await setup_fronius_integration(hass, is_logger=True)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 30
await enable_all_entities(
hass,
freezer,
config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval,
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 43
# logger
assert_state("sensor.solarnet_grid_export_tariff", 1)
assert_state("sensor.solarnet_co2_factor", 0.53)
assert_state("sensor.solarnet_grid_import_tariff", 1)
# inverter 1
assert_state("sensor.primo_5_0_1_total_energy", 17114940)
assert_state("sensor.primo_5_0_1_energy_day", 22504)
assert_state("sensor.primo_5_0_1_dc_voltage", 452.3)
assert_state("sensor.primo_5_0_1_ac_power", 862)
assert_state("sensor.primo_5_0_1_error_code", 0)
assert_state("sensor.primo_5_0_1_dc_current", 4.23)
assert_state("sensor.primo_5_0_1_status_code", 7)
assert_state("sensor.primo_5_0_1_status_message", "running")
assert_state("sensor.primo_5_0_1_energy_year", 7532755.5)
assert_state("sensor.primo_5_0_1_ac_current", 3.85)
assert_state("sensor.primo_5_0_1_ac_voltage", 223.9)
assert_state("sensor.primo_5_0_1_frequency", 60)
assert_state("sensor.primo_5_0_1_led_color", 2)
assert_state("sensor.primo_5_0_1_led_state", 0)
# inverter 2
assert_state("sensor.primo_3_0_1_total_energy", 5796010)
assert_state("sensor.primo_3_0_1_energy_day", 14237)
assert_state("sensor.primo_3_0_1_dc_voltage", 329.5)
assert_state("sensor.primo_3_0_1_ac_power", 296)
assert_state("sensor.primo_3_0_1_error_code", 0)
assert_state("sensor.primo_3_0_1_dc_current", 0.97)
assert_state("sensor.primo_3_0_1_status_code", 7)
assert_state("sensor.primo_3_0_1_status_message", "running")
assert_state("sensor.primo_3_0_1_energy_year", 3596193.25)
assert_state("sensor.primo_3_0_1_ac_current", 1.32)
assert_state("sensor.primo_3_0_1_ac_voltage", 223.6)
assert_state("sensor.primo_3_0_1_frequency", 60.01)
assert_state("sensor.primo_3_0_1_led_color", 2)
assert_state("sensor.primo_3_0_1_led_state", 0)
# meter
assert_state("sensor.s0_meter_at_inverter_1_meter_location", 1)
assert_state(
"sensor.s0_meter_at_inverter_1_meter_location_description", "consumption_path"
)
assert_state("sensor.s0_meter_at_inverter_1_real_power", -2216.7487)
# power_flow
assert_state("sensor.solarnet_power_load", -2218.9349)
assert_state("sensor.solarnet_meter_mode", "vague-meter")
assert_state("sensor.solarnet_power_photovoltaics", 1834)
assert_state("sensor.solarnet_power_grid", 384.9349)
assert_state("sensor.solarnet_relative_self_consumption", 100)
assert_state("sensor.solarnet_relative_autonomy", 82.6523)
assert_state("sensor.solarnet_total_energy", 22910919.5)
assert_state("sensor.solarnet_energy_day", 36724)
assert_state("sensor.solarnet_energy_year", 11128933.25)
# Devices
solar_net = device_registry.async_get_device(
identifiers={(DOMAIN, "solar_net_123.4567890")}
)
assert solar_net.configuration_url == "http://fronius"
assert solar_net.manufacturer == "Fronius"
assert solar_net.model == "fronius-datamanager-card"
assert solar_net.name == "SolarNet"
assert solar_net.sw_version == "3.18.7-1"
inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "123456")})
assert inverter_1.manufacturer == "Fronius"
assert inverter_1.model == "Primo 5.0-1"
assert inverter_1.name == "Primo 5.0-1"
inverter_2 = device_registry.async_get_device(identifiers={(DOMAIN, "234567")})
assert inverter_2.manufacturer == "Fronius"
assert inverter_2.model == "Primo 3.0-1"
assert inverter_2.name == "Primo 3.0-1"
meter = device_registry.async_get_device(
identifiers={(DOMAIN, "solar_net_123.4567890:S0 Meter at inverter 1")}
)
assert meter.manufacturer == "Fronius"
assert meter.model == "S0 Meter at inverter 1"
assert meter.name == "S0 Meter at inverter 1"