From 743cb440ed280e5adda2f699f2f2c78d6b819785 Mon Sep 17 00:00:00 2001 From: henricm Date: Thu, 9 Mar 2023 09:43:56 +0100 Subject: [PATCH] fix: support for energy counter being reset Energy counters like `sensor.ferroamp_external_energy_consumed` may be reset in Energy Hub leading to lower values than previously known being received from Ferroamp. Current code ignores all lower values since these sensors should be `TOTAL_INCREASING`. However, the total increasing concept in HA has support for counters being reset, so it's ok to provide a lower value. For more info please see: https://developers.home-assistant.io/docs/core/entity/sensor/#state-class-total_increasing This fix will accept lower value if they are "much lower". If a value is only 10 % lower it will still be ignored, since users have reported that invalid values sometimes comes from Ferroamp. However, if values are lower than this we noe will accept them. The statistics functionality in HA will handle this. However the sensor itself will get the newer lower value. --- custom_components/ferroamp/sensor.py | 6 ++-- tests/test_sensor.py | 50 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/custom_components/ferroamp/sensor.py b/custom_components/ferroamp/sensor.py index e3f7dff..90053ea 100644 --- a/custom_components/ferroamp/sensor.py +++ b/custom_components/ferroamp/sensor.py @@ -862,7 +862,8 @@ def update_state_from_events(self, events): if self._attr_native_value is None\ or (isinstance(self._attr_native_value, str) and not isfloat(self._attr_native_value))\ or self._attr_state_class != SensorStateClass.TOTAL_INCREASING\ - or val > float(self._attr_native_value): + or val > float(self._attr_native_value) \ + or val * 1.1 < float(self._attr_native_value): self._attr_native_value = val if self._precision == 0: self._attr_native_value = int(self._attr_native_value) @@ -986,7 +987,8 @@ def update_state_from_events(self, events): if self._attr_native_value is None \ or (isinstance(self._attr_native_value, str) and not isfloat(self._attr_native_value)) \ or self._attr_state_class != SensorStateClass.TOTAL_INCREASING \ - or val > float(self._attr_native_value): + or val > float(self._attr_native_value) \ + or val * 1.1 < float(self._attr_native_value): self._attr_native_value = val if self._precision == 0: self._attr_native_value = int(self._attr_native_value) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index a30fd7a..2803758 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -1607,6 +1607,31 @@ async def test_always_increasing_unknown_value(hass, mqtt_mock): sensor = hass.data[DOMAIN][DATA_DEVICES][config_entry.unique_id]["ferroamp_ehub"][entity.unique_id] assert sensor.state == 1228.4 +async def test_always_increasing_counter_reset(hass, mqtt_mock): + mock_restore_cache( + hass, + [ + State("sensor.ferroamp_total_solar_energy", "1348.5") + ], + ) + + hass.state = CoreState.starting + + config_entry = create_config() + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + topic = "extapi/data/ehub" + # 100 kWh + msg = '{"id":{"val":"1"},"wpv":{"val": "360000000000"}}' + async_fire_mqtt_message(hass, topic, msg) + await hass.async_block_till_done() + + er = entity_registry.async_get(hass) + entity = er.async_get("sensor.ferroamp_total_solar_energy") + assert entity is not None + sensor = hass.data[DOMAIN][DATA_DEVICES][config_entry.unique_id]["ferroamp_ehub"][entity.unique_id] + assert sensor.state == 100.0 async def test_3phase_always_increasing(hass, mqtt_mock): mock_restore_cache( @@ -1659,6 +1684,31 @@ async def test_3phase_always_increasing_unknown_value(hass, mqtt_mock): sensor = hass.data[DOMAIN][DATA_DEVICES][config_entry.unique_id]["ferroamp_ehub"][entity.unique_id] assert sensor.state == 662.4 +async def test_3phase_always_increasing_counter_reset(hass, mqtt_mock): + mock_restore_cache( + hass, + [ + State("sensor.ferroamp_external_energy_produced", "662.5") + ], + ) + + hass.state = CoreState.starting + + config_entry = create_config() + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + topic = "extapi/data/ehub" + # 10 kWh on each phase + msg = '{"id":{"val":"1"},"wextprodq": {"L2": "36000000000", "L3": "36000000000", "L1": "36000000000"}}' + async_fire_mqtt_message(hass, topic, msg) + await hass.async_block_till_done() + + er = entity_registry.async_get(hass) + entity = er.async_get("sensor.ferroamp_external_energy_produced") + assert entity is not None + sensor = hass.data[DOMAIN][DATA_DEVICES][config_entry.unique_id]["ferroamp_ehub"][entity.unique_id] + assert sensor.state == 30.0 async def test_average_calculation(hass, mqtt_mock): config_entry = MockConfigEntry(