From 82cb53f8e934b34855653363f71366b5aee33fba Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Wed, 24 Jan 2024 17:42:03 +0100 Subject: [PATCH 1/7] Include the check for yearEnergy and monthEnergy also. See https://github.com/tijsverkoyen/HomeAssistant-FusionSolar/issues/135 --- .../fusion_solar/fusion_solar/energy_sensor.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/energy_sensor.py b/custom_components/fusion_solar/fusion_solar/energy_sensor.py index 6f0d3ec..353c61e 100644 --- a/custom_components/fusion_solar/fusion_solar/energy_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/energy_sensor.py @@ -5,7 +5,8 @@ from homeassistant.components.sensor import SensorEntity, SensorDeviceClass, SensorStateClass from homeassistant.const import UnitOfEnergy -from .const import ATTR_TOTAL_LIFETIME_ENERGY, ATTR_REALTIME_POWER +from .const import ATTR_TOTAL_LIFETIME_ENERGY, ATTR_TOTAL_CURRENT_YEAR_ENERGY, ATTR_TOTAL_CURRENT_MONTH_ENERGY, \ + ATTR_REALTIME_POWER _LOGGER = logging.getLogger(__name__) @@ -52,10 +53,14 @@ def name(self) -> str: @property def native_value(self) -> float: - # It seems like Huawei Fusion Solar returns some invalid data for the lifetime energy just before midnight - # Therefore we validate if the new value is higher than the current value - if ATTR_TOTAL_LIFETIME_ENERGY == self._attribute: - # Grab the current data + # It seems like Huawei Fusion Solar returns some invalid data for the cumulativeEnergy, yearEnergy and + # monthEnergy just before midnight. + # So we update the value only if the system is producing power at the moment. + if ( + ATTR_TOTAL_LIFETIME_ENERGY == self._attribute + or ATTR_TOTAL_CURRENT_YEAR_ENERGY == self._attribute + or ATTR_TOTAL_CURRENT_MONTH_ENERGY == self._attribute + ): entity = self.hass.states.get(self.entity_id) if entity is not None: From 72a628f96eb8f58a8ffbe10f1831914d3accaf84 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Wed, 24 Jan 2024 17:42:31 +0100 Subject: [PATCH 2/7] Cleanup: remove unused method. --- .../fusion_solar/fusion_solar/energy_sensor.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/energy_sensor.py b/custom_components/fusion_solar/fusion_solar/energy_sensor.py index 353c61e..9b28790 100644 --- a/custom_components/fusion_solar/fusion_solar/energy_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/energy_sensor.py @@ -11,14 +11,6 @@ _LOGGER = logging.getLogger(__name__) -def isfloat(num) -> bool: - try: - float(num) - return True - except ValueError: - return False - - class FusionSolarEnergySensor(CoordinatorEntity, SensorEntity): """Base class for all FusionSolarEnergySensor sensors.""" From 80f83b5e4364b74589659a46cf562eeed94497d5 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Wed, 24 Jan 2024 17:44:32 +0100 Subject: [PATCH 3/7] Handle all non float values for the current state of the entity. --- .../fusion_solar/fusion_solar/energy_sensor.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/energy_sensor.py b/custom_components/fusion_solar/fusion_solar/energy_sensor.py index 9b28790..3e593e3 100644 --- a/custom_components/fusion_solar/fusion_solar/energy_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/energy_sensor.py @@ -56,9 +56,10 @@ def native_value(self) -> float: entity = self.hass.states.get(self.entity_id) if entity is not None: - current_value = entity.state - if current_value == 'unavailable': - _LOGGER.info(f'{self.entity_id}: not available.') + try: + current_value = float(entity.state) + except ValueError: + _LOGGER.info(f'{self.entity_id}: not available, so no update to prevent issues.') return realtime_power = self.coordinator.data[self._data_name][ATTR_REALTIME_POWER] From 0680f61d8822df095457a51556ec3f6c3893727a Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Wed, 24 Jan 2024 17:46:51 +0100 Subject: [PATCH 4/7] Make sure we are working with floats --- .../fusion_solar/energy_sensor.py | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/energy_sensor.py b/custom_components/fusion_solar/fusion_solar/energy_sensor.py index 3e593e3..bd69ee4 100644 --- a/custom_components/fusion_solar/fusion_solar/energy_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/energy_sensor.py @@ -54,7 +54,6 @@ def native_value(self) -> float: or ATTR_TOTAL_CURRENT_MONTH_ENERGY == self._attribute ): entity = self.hass.states.get(self.entity_id) - if entity is not None: try: current_value = float(entity.state) @@ -67,14 +66,12 @@ def native_value(self) -> float: _LOGGER.info(f'{self.entity_id}: not producing any power, so no energy update to prevent glitches.') return float(current_value) - if self._data_name not in self.coordinator.data: + try: + return self.get_float_value_from_coordinator(self._attribute) + except FusionSolarEnergySensorException as e: + _LOGGER.error(e) return None - if self._attribute not in self.coordinator.data[self._data_name]: - return None - - return float(self.coordinator.data[self._data_name][self._attribute]) - @property def native_unit_of_measurement(self) -> str: return UnitOfEnergy.KILO_WATT_HOUR @@ -87,6 +84,25 @@ def state_class(self) -> str: def device_info(self) -> dict: return self._device_info + def get_float_value_from_coordinator(self, attribute_name: str) -> float: + if self.coordinator.data is False: + raise FusionSolarEnergySensorException('Coordinator data is False') + if self._data_name not in self.coordinator.data: + raise FusionSolarEnergySensorException(f'Attribute {self._data_name} not in coordinator data') + if self._attribute not in self.coordinator.data[self._data_name]: + raise FusionSolarEnergySensorException(f'Attribute {attribute_name} not in coordinator data') + + if self.coordinator.data[self._data_name][attribute_name] is None: + raise FusionSolarEnergySensorException(f'Attribute {attribute_name} has value None') + elif self.coordinator.data[self._data_name][attribute_name] == 'N/A': + raise FusionSolarEnergySensorException(f'Attribute {attribute_name} has value N/A') + + try: + return float(self.coordinator.data[self._data_name][attribute_name]) + except ValueError: + raise FusionSolarEnergySensorException( + f'Attribute {self._attribute} has value {self.coordinator.data[self._data_name][attribute_name]} which is not a float') + class FusionSolarEnergySensorTotalCurrentDay(FusionSolarEnergySensor): pass @@ -102,3 +118,7 @@ class FusionSolarEnergySensorTotalCurrentYear(FusionSolarEnergySensor): class FusionSolarEnergySensorTotalLifetime(FusionSolarEnergySensor): pass + + +class FusionSolarEnergySensorException(Exception): + pass From 2d92d5a9ec48801657c640425db2a4817607868d Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Wed, 24 Jan 2024 17:47:31 +0100 Subject: [PATCH 5/7] Move check for producing into separate method --- .../fusion_solar/fusion_solar/energy_sensor.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/energy_sensor.py b/custom_components/fusion_solar/fusion_solar/energy_sensor.py index bd69ee4..91fc27a 100644 --- a/custom_components/fusion_solar/fusion_solar/energy_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/energy_sensor.py @@ -61,10 +61,10 @@ def native_value(self) -> float: _LOGGER.info(f'{self.entity_id}: not available, so no update to prevent issues.') return - realtime_power = self.coordinator.data[self._data_name][ATTR_REALTIME_POWER] - if math.isclose(float(realtime_power), 0, abs_tol = 0.001): - _LOGGER.info(f'{self.entity_id}: not producing any power, so no energy update to prevent glitches.') - return float(current_value) + # Return the current value if the system is not producing + if not self.is_producing_at_the_moment(): + _LOGGER.info(f'{self.entity_id}: not producing any power, so no update to prevent glitches.') + return current_value try: return self.get_float_value_from_coordinator(self._attribute) @@ -84,6 +84,14 @@ def state_class(self) -> str: def device_info(self) -> dict: return self._device_info + def is_producing_at_the_moment(self) -> bool: + try: + realtime_power = self.get_float_value_from_coordinator(ATTR_REALTIME_POWER) + return not math.isclose(realtime_power, 0, abs_tol=0.001) + except FusionSolarEnergySensorException as e: + _LOGGER.info(e) + return False + def get_float_value_from_coordinator(self, attribute_name: str) -> float: if self.coordinator.data is False: raise FusionSolarEnergySensorException('Coordinator data is False') From 1aed151a5d1d5338b16fb4543b27427a0bec3f41 Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Fri, 26 Jan 2024 16:42:26 +0100 Subject: [PATCH 6/7] Revert "Include the check for yearEnergy and monthEnergy also. See https://github.com/tijsverkoyen/HomeAssistant-FusionSolar/issues/135" This reverts commit 82cb53f8e934b34855653363f71366b5aee33fba. --- .../fusion_solar/fusion_solar/energy_sensor.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/energy_sensor.py b/custom_components/fusion_solar/fusion_solar/energy_sensor.py index 91fc27a..c4ede2d 100644 --- a/custom_components/fusion_solar/fusion_solar/energy_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/energy_sensor.py @@ -5,8 +5,7 @@ from homeassistant.components.sensor import SensorEntity, SensorDeviceClass, SensorStateClass from homeassistant.const import UnitOfEnergy -from .const import ATTR_TOTAL_LIFETIME_ENERGY, ATTR_TOTAL_CURRENT_YEAR_ENERGY, ATTR_TOTAL_CURRENT_MONTH_ENERGY, \ - ATTR_REALTIME_POWER +from .const import ATTR_TOTAL_LIFETIME_ENERGY, ATTR_REALTIME_POWER _LOGGER = logging.getLogger(__name__) @@ -45,14 +44,10 @@ def name(self) -> str: @property def native_value(self) -> float: - # It seems like Huawei Fusion Solar returns some invalid data for the cumulativeEnergy, yearEnergy and - # monthEnergy just before midnight. - # So we update the value only if the system is producing power at the moment. - if ( - ATTR_TOTAL_LIFETIME_ENERGY == self._attribute - or ATTR_TOTAL_CURRENT_YEAR_ENERGY == self._attribute - or ATTR_TOTAL_CURRENT_MONTH_ENERGY == self._attribute - ): + # It seems like Huawei Fusion Solar returns some invalid data for the lifetime energy just before midnight + # Therefore we validate if the new value is higher than the current value + if ATTR_TOTAL_LIFETIME_ENERGY == self._attribute: + # Grab the current data entity = self.hass.states.get(self.entity_id) if entity is not None: try: From 52db361f9e1185ea19c848f484dbc98dd1e1b13c Mon Sep 17 00:00:00 2001 From: Tijs Verkoyen Date: Fri, 26 Jan 2024 16:47:57 +0100 Subject: [PATCH 7/7] Update code comments, as we don't check if it is lower --- custom_components/fusion_solar/fusion_solar/energy_sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/fusion_solar/fusion_solar/energy_sensor.py b/custom_components/fusion_solar/fusion_solar/energy_sensor.py index c4ede2d..0c217da 100644 --- a/custom_components/fusion_solar/fusion_solar/energy_sensor.py +++ b/custom_components/fusion_solar/fusion_solar/energy_sensor.py @@ -44,8 +44,8 @@ def name(self) -> str: @property def native_value(self) -> float: - # It seems like Huawei Fusion Solar returns some invalid data for the lifetime energy just before midnight - # Therefore we validate if the new value is higher than the current value + # It seems like Huawei Fusion Solar returns some invalid data for the cumulativeEnergy just before midnight. + # So we update the value only if the system is producing power at the moment. if ATTR_TOTAL_LIFETIME_ENERGY == self._attribute: # Grab the current data entity = self.hass.states.get(self.entity_id)