Add distinct import / export entities to Fronius (#116535)

This commit is contained in:
Matthias Alphart 2024-06-22 11:12:21 +02:00 committed by GitHub
parent 3d9f053256
commit 77edc149ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 103 additions and 18 deletions

View file

@ -549,6 +549,25 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT, 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( FroniusSensorEntityDescription(
key="power_grid", key="power_grid",
@ -556,6 +575,25 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT, 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( FroniusSensorEntityDescription(
key="power_load", key="power_load",
@ -563,6 +601,26 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT, 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( FroniusSensorEntityDescription(
key="power_photovoltaics", key="power_photovoltaics",
@ -670,7 +728,7 @@ class _FroniusSensorEntity(CoordinatorEntity["FroniusCoordinatorBase"], SensorEn
if self.entity_description.invalid_when_falsy and not new_value: if self.entity_description.invalid_when_falsy and not new_value:
return None return None
if self.entity_description.value_fn is not 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): if isinstance(new_value, float):
return round(new_value, 4) return round(new_value, 4)
return new_value return new_value

View file

@ -234,12 +234,30 @@
"power_battery": { "power_battery": {
"name": "Power battery" "name": "Power battery"
}, },
"power_battery_discharge": {
"name": "Power battery discharge"
},
"power_battery_charge": {
"name": "Power battery charge"
},
"power_grid": { "power_grid": {
"name": "Power grid" "name": "Power grid"
}, },
"power_grid_import": {
"name": "Power grid import"
},
"power_grid_export": {
"name": "Power grid export"
},
"power_load": { "power_load": {
"name": "Power load" "name": "Power load"
}, },
"power_load_generated": {
"name": "Power load generated"
},
"power_load_consumed": {
"name": "Power load consumed"
},
"power_photovoltaics": { "power_photovoltaics": {
"name": "Power photovoltaics" "name": "Power photovoltaics"
}, },

View file

@ -34,14 +34,14 @@ async def test_symo_inverter(
mock_responses(aioclient_mock, night=True) mock_responses(aioclient_mock, night=True)
config_entry = await setup_fronius_integration(hass) 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( await enable_all_entities(
hass, hass,
freezer, freezer,
config_entry.entry_id, config_entry.entry_id,
FroniusInverterUpdateCoordinator.default_interval, 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_dc_current", 0)
assert_state("sensor.symo_20_energy_day", 10828) assert_state("sensor.symo_20_energy_day", 10828)
assert_state("sensor.symo_20_total_energy", 44186900) assert_state("sensor.symo_20_total_energy", 44186900)
@ -54,14 +54,14 @@ async def test_symo_inverter(
freezer.tick(FroniusInverterUpdateCoordinator.default_interval) freezer.tick(FroniusInverterUpdateCoordinator.default_interval)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() 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( await enable_all_entities(
hass, hass,
freezer, freezer,
config_entry.entry_id, config_entry.entry_id,
FroniusInverterUpdateCoordinator.default_interval, 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 # 4 additional AC entities
assert_state("sensor.symo_20_dc_current", 2.19) assert_state("sensor.symo_20_dc_current", 2.19)
assert_state("sensor.symo_20_energy_day", 1113) assert_state("sensor.symo_20_energy_day", 1113)
@ -97,7 +97,7 @@ async def test_symo_logger(
mock_responses(aioclient_mock) mock_responses(aioclient_mock)
await setup_fronius_integration(hass) 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 # states are rounded to 4 decimals
assert_state("sensor.solarnet_grid_export_tariff", 0.078) assert_state("sensor.solarnet_grid_export_tariff", 0.078)
assert_state("sensor.solarnet_co2_factor", 0.53) assert_state("sensor.solarnet_co2_factor", 0.53)
@ -119,14 +119,14 @@ async def test_symo_meter(
mock_responses(aioclient_mock) mock_responses(aioclient_mock)
config_entry = await setup_fronius_integration(hass) 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( await enable_all_entities(
hass, hass,
freezer, freezer,
config_entry.entry_id, config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval, 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 # 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_1", 7.755)
assert_state("sensor.smart_meter_63a_current_phase_2", 6.68) 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) mock_responses(aioclient_mock, night=True)
config_entry = await setup_fronius_integration(hass) 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( await enable_all_entities(
hass, hass,
freezer, freezer,
config_entry.entry_id, config_entry.entry_id,
FroniusInverterUpdateCoordinator.default_interval, 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 # states are rounded to 4 decimals
assert_state("sensor.solarnet_energy_day", 10828) assert_state("sensor.solarnet_energy_day", 10828)
assert_state("sensor.solarnet_total_energy", 44186900) assert_state("sensor.solarnet_total_energy", 44186900)
assert_state("sensor.solarnet_energy_year", 25507686) assert_state("sensor.solarnet_energy_year", 25507686)
assert_state("sensor.solarnet_power_grid", 975.31) 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", -975.31)
assert_state("sensor.solarnet_power_load_consumed", 975.31)
assert_state("sensor.solarnet_relative_autonomy", 0) assert_state("sensor.solarnet_relative_autonomy", 0)
# Second test at daytime when inverter is producing # Second test at daytime when inverter is producing
@ -244,12 +247,16 @@ async def test_symo_power_flow(
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
# 54 because power_flow `rel_SelfConsumption` and `P_PV` is not `null` anymore # 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_energy_day", 1101.7001)
assert_state("sensor.solarnet_total_energy", 44188000) assert_state("sensor.solarnet_total_energy", 44188000)
assert_state("sensor.solarnet_energy_year", 25508788) assert_state("sensor.solarnet_energy_year", 25508788)
assert_state("sensor.solarnet_power_grid", 1703.74) 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", -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_power_photovoltaics", 1111)
assert_state("sensor.solarnet_relative_autonomy", 39.4708) assert_state("sensor.solarnet_relative_autonomy", 39.4708)
assert_state("sensor.solarnet_relative_self_consumption", 100) assert_state("sensor.solarnet_relative_self_consumption", 100)
@ -259,7 +266,7 @@ async def test_symo_power_flow(
freezer.tick(FroniusPowerFlowUpdateCoordinator.default_interval) freezer.tick(FroniusPowerFlowUpdateCoordinator.default_interval)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() 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_energy_day", 10828)
assert_state("sensor.solarnet_total_energy", 44186900) assert_state("sensor.solarnet_total_energy", 44186900)
assert_state("sensor.solarnet_energy_year", 25507686) assert_state("sensor.solarnet_energy_year", 25507686)
@ -285,14 +292,14 @@ async def test_gen24(
mock_responses(aioclient_mock, fixture_set="gen24") mock_responses(aioclient_mock, fixture_set="gen24")
config_entry = await setup_fronius_integration(hass, is_logger=False) 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( await enable_all_entities(
hass, hass,
freezer, freezer,
config_entry.entry_id, config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval, 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 # inverter 1
assert_state("sensor.inverter_name_ac_current", 0.1589) assert_state("sensor.inverter_name_ac_current", 0.1589)
assert_state("sensor.inverter_name_dc_current_2", 0.0754) 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" 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( await enable_all_entities(
hass, hass,
freezer, freezer,
config_entry.entry_id, config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval, 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 # inverter 1
assert_state("sensor.gen24_storage_dc_current", 0.3952) 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_voltage_2", 318.8103)
@ -452,6 +459,8 @@ async def test_gen24_storage(
# power_flow # power_flow
assert_state("sensor.solarnet_power_grid", 2274.9) assert_state("sensor.solarnet_power_grid", 2274.9)
assert_state("sensor.solarnet_power_battery", 0.1591) 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_power_load", -2459.3092)
assert_state("sensor.solarnet_relative_self_consumption", 100.0) assert_state("sensor.solarnet_relative_self_consumption", 100.0)
assert_state("sensor.solarnet_power_photovoltaics", 216.4328) 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]) mock_responses(aioclient_mock, fixture_set="primo_s0", inverter_ids=[1, 2])
config_entry = await setup_fronius_integration(hass, is_logger=True) 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( await enable_all_entities(
hass, hass,
freezer, freezer,
config_entry.entry_id, config_entry.entry_id,
FroniusMeterUpdateCoordinator.default_interval, 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 # logger
assert_state("sensor.solarnet_grid_export_tariff", 1) assert_state("sensor.solarnet_grid_export_tariff", 1)
assert_state("sensor.solarnet_co2_factor", 0.53) assert_state("sensor.solarnet_co2_factor", 0.53)