From 77edc149ec20f91cdd027a00fc344f801cb48e3d Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Sat, 22 Jun 2024 11:12:21 +0200 Subject: [PATCH] Add distinct import / export entities to Fronius (#116535) --- homeassistant/components/fronius/sensor.py | 60 ++++++++++++++++++- homeassistant/components/fronius/strings.json | 18 ++++++ tests/components/fronius/test_sensor.py | 43 +++++++------ 3 files changed, 103 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/fronius/sensor.py b/homeassistant/components/fronius/sensor.py index 3b283c33326..31f080c1f51 100644 --- a/homeassistant/components/fronius/sensor.py +++ b/homeassistant/components/fronius/sensor.py @@ -549,6 +549,25 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [ native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + FroniusSensorEntityDescription( + key="power_battery_discharge", + response_key="power_battery", + default_value=0, + value_fn=lambda value: max(value, 0), # type: ignore[type-var] + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + FroniusSensorEntityDescription( + key="power_battery_charge", + response_key="power_battery", + default_value=0, + value_fn=lambda value: max(0 - value, 0), # type: ignore[operator] + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, ), FroniusSensorEntityDescription( key="power_grid", @@ -556,6 +575,25 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [ native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + FroniusSensorEntityDescription( + key="power_grid_import", + response_key="power_grid", + default_value=0, + value_fn=lambda value: max(value, 0), # type: ignore[type-var] + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + ), + FroniusSensorEntityDescription( + key="power_grid_export", + response_key="power_grid", + default_value=0, + value_fn=lambda value: max(0 - value, 0), # type: ignore[operator] + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, ), FroniusSensorEntityDescription( key="power_load", @@ -563,6 +601,26 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [ native_unit_of_measurement=UnitOfPower.WATT, device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + FroniusSensorEntityDescription( + key="power_load_generated", + response_key="power_load", + default_value=0, + value_fn=lambda value: max(value, 0), # type: ignore[type-var] + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + entity_registry_enabled_default=False, + ), + FroniusSensorEntityDescription( + key="power_load_consumed", + response_key="power_load", + default_value=0, + value_fn=lambda value: max(0 - value, 0), # type: ignore[operator] + native_unit_of_measurement=UnitOfPower.WATT, + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, ), FroniusSensorEntityDescription( key="power_photovoltaics", @@ -670,7 +728,7 @@ class _FroniusSensorEntity(CoordinatorEntity["FroniusCoordinatorBase"], SensorEn if self.entity_description.invalid_when_falsy and not new_value: return None if self.entity_description.value_fn is not None: - return self.entity_description.value_fn(new_value) + new_value = self.entity_description.value_fn(new_value) if isinstance(new_value, float): return round(new_value, 4) return new_value diff --git a/homeassistant/components/fronius/strings.json b/homeassistant/components/fronius/strings.json index de066704644..af93694284a 100644 --- a/homeassistant/components/fronius/strings.json +++ b/homeassistant/components/fronius/strings.json @@ -234,12 +234,30 @@ "power_battery": { "name": "Power battery" }, + "power_battery_discharge": { + "name": "Power battery discharge" + }, + "power_battery_charge": { + "name": "Power battery charge" + }, "power_grid": { "name": "Power grid" }, + "power_grid_import": { + "name": "Power grid import" + }, + "power_grid_export": { + "name": "Power grid export" + }, "power_load": { "name": "Power load" }, + "power_load_generated": { + "name": "Power load generated" + }, + "power_load_consumed": { + "name": "Power load consumed" + }, "power_photovoltaics": { "name": "Power photovoltaics" }, diff --git a/tests/components/fronius/test_sensor.py b/tests/components/fronius/test_sensor.py index f5e77660271..04c25ce26f2 100644 --- a/tests/components/fronius/test_sensor.py +++ b/tests/components/fronius/test_sensor.py @@ -34,14 +34,14 @@ async def test_symo_inverter( mock_responses(aioclient_mock, night=True) config_entry = await setup_fronius_integration(hass) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 21 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 22 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 len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 58 assert_state("sensor.symo_20_dc_current", 0) assert_state("sensor.symo_20_energy_day", 10828) assert_state("sensor.symo_20_total_energy", 44186900) @@ -54,14 +54,14 @@ async def test_symo_inverter( 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 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 62 await enable_all_entities( hass, freezer, config_entry.entry_id, FroniusInverterUpdateCoordinator.default_interval, ) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 60 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 64 # 4 additional AC entities assert_state("sensor.symo_20_dc_current", 2.19) assert_state("sensor.symo_20_energy_day", 1113) @@ -97,7 +97,7 @@ async def test_symo_logger( mock_responses(aioclient_mock) await setup_fronius_integration(hass) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 25 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 26 # states are rounded to 4 decimals assert_state("sensor.solarnet_grid_export_tariff", 0.078) assert_state("sensor.solarnet_co2_factor", 0.53) @@ -119,14 +119,14 @@ async def test_symo_meter( mock_responses(aioclient_mock) config_entry = await setup_fronius_integration(hass) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 25 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 26 await enable_all_entities( hass, freezer, config_entry.entry_id, FroniusMeterUpdateCoordinator.default_interval, ) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 60 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 64 # 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) @@ -222,20 +222,23 @@ async def test_symo_power_flow( mock_responses(aioclient_mock, night=True) config_entry = await setup_fronius_integration(hass) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 21 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 22 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 len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 58 # 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_grid_import", 975.31) + assert_state("sensor.solarnet_power_grid_export", 0) assert_state("sensor.solarnet_power_load", -975.31) + assert_state("sensor.solarnet_power_load_consumed", 975.31) assert_state("sensor.solarnet_relative_autonomy", 0) # Second test at daytime when inverter is producing @@ -244,12 +247,16 @@ async def test_symo_power_flow( 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 len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 60 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_grid_import", 1703.74) + assert_state("sensor.solarnet_power_grid_export", 0) assert_state("sensor.solarnet_power_load", -2814.74) + assert_state("sensor.solarnet_power_load_generated", 0) + assert_state("sensor.solarnet_power_load_consumed", 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) @@ -259,7 +266,7 @@ async def test_symo_power_flow( 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 len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 60 assert_state("sensor.solarnet_energy_day", 10828) assert_state("sensor.solarnet_total_energy", 44186900) assert_state("sensor.solarnet_energy_year", 25507686) @@ -285,14 +292,14 @@ async def test_gen24( 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 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 24 await enable_all_entities( hass, freezer, config_entry.entry_id, FroniusMeterUpdateCoordinator.default_interval, ) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 54 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 58 # inverter 1 assert_state("sensor.inverter_name_ac_current", 0.1589) assert_state("sensor.inverter_name_dc_current_2", 0.0754) @@ -386,14 +393,14 @@ async def test_gen24_storage( hass, is_logger=False, unique_id="12345678" ) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 35 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 37 await enable_all_entities( hass, freezer, config_entry.entry_id, FroniusMeterUpdateCoordinator.default_interval, ) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 66 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 72 # inverter 1 assert_state("sensor.gen24_storage_dc_current", 0.3952) assert_state("sensor.gen24_storage_dc_voltage_2", 318.8103) @@ -452,6 +459,8 @@ async def test_gen24_storage( # power_flow assert_state("sensor.solarnet_power_grid", 2274.9) assert_state("sensor.solarnet_power_battery", 0.1591) + assert_state("sensor.solarnet_power_battery_charge", 0) + assert_state("sensor.solarnet_power_battery_discharge", 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) @@ -514,14 +523,14 @@ async def test_primo_s0( 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 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 31 await enable_all_entities( hass, freezer, config_entry.entry_id, FroniusMeterUpdateCoordinator.default_interval, ) - assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 43 + assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 47 # logger assert_state("sensor.solarnet_grid_export_tariff", 1) assert_state("sensor.solarnet_co2_factor", 0.53)