From 1d1cd6be575585da2563309f103cdc4c2df93e08 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 18 Dec 2023 21:03:40 +0100 Subject: [PATCH] Avoid mutating entity descriptions in sunweg (#105982) --- homeassistant/components/sunweg/__init__.py | 47 ++++----- homeassistant/components/sunweg/sensor.py | 28 +++--- tests/components/sunweg/test_init.py | 102 +++++++++++++------- 3 files changed, 105 insertions(+), 72 deletions(-) diff --git a/homeassistant/components/sunweg/__init__.py b/homeassistant/components/sunweg/__init__.py index cdf7cc123fc..9da91ccda0f 100644 --- a/homeassistant/components/sunweg/__init__.py +++ b/homeassistant/components/sunweg/__init__.py @@ -10,11 +10,10 @@ from homeassistant import config_entries from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant -from homeassistant.helpers.typing import StateType +from homeassistant.helpers.typing import StateType, UndefinedType from homeassistant.util import Throttle from .const import CONF_PLANT_ID, DOMAIN, PLATFORMS, DeviceType -from .sensor_types.sensor_entity_description import SunWEGSensorEntityDescription SCAN_INTERVAL = datetime.timedelta(minutes=5) @@ -102,24 +101,30 @@ class SunWEGData: def get_data( self, - entity_description: SunWEGSensorEntityDescription, + *, + api_variable_key: str, + api_variable_unit: str | None, + deep_name: str | None, device_type: DeviceType, - inverter_id: int = 0, - deep_name: str | None = None, - ) -> StateType | datetime.datetime: + inverter_id: int, + name: str | UndefinedType | None, + native_unit_of_measurement: str | None, + never_resets: bool, + previous_value_drop_threshold: float | None, + ) -> tuple[StateType | datetime.datetime, str | None]: """Get the data.""" _LOGGER.debug( "Data request for: %s", - entity_description.name, + name, ) - variable = entity_description.api_variable_key - previous_unit = entity_description.native_unit_of_measurement + variable = api_variable_key + previous_unit = native_unit_of_measurement api_value = self.get_api_value(variable, device_type, inverter_id, deep_name) previous_value = self.previous_values.get(variable) return_value = api_value - if entity_description.api_variable_unit is not None: - entity_description.native_unit_of_measurement = self.get_api_value( - entity_description.api_variable_unit, + if api_variable_unit is not None: + native_unit_of_measurement = self.get_api_value( + api_variable_unit, device_type, inverter_id, deep_name, @@ -127,18 +132,18 @@ class SunWEGData: # If we have a 'drop threshold' specified, then check it and correct if needed if ( - entity_description.previous_value_drop_threshold is not None + previous_value_drop_threshold is not None and previous_value is not None and api_value is not None - and previous_unit == entity_description.native_unit_of_measurement + and previous_unit == native_unit_of_measurement ): _LOGGER.debug( ( "%s - Drop threshold specified (%s), checking for drop... API" " Value: %s, Previous Value: %s" ), - entity_description.name, - entity_description.previous_value_drop_threshold, + name, + previous_value_drop_threshold, api_value, previous_value, ) @@ -149,7 +154,7 @@ class SunWEGData: # Note - The energy dashboard takes care of drops within 10% # of the current value, however if the value is low e.g. 0.2 # and drops by 0.1 it classes as a reset. - if -(entity_description.previous_value_drop_threshold) <= diff < 0: + if -(previous_value_drop_threshold) <= diff < 0: _LOGGER.debug( ( "Diff is negative, but only by a small amount therefore not a" @@ -161,9 +166,7 @@ class SunWEGData: ) return_value = previous_value else: - _LOGGER.debug( - "%s - No drop detected, using API value", entity_description.name - ) + _LOGGER.debug("%s - No drop detected, using API value", name) # Lifetime total values should always be increasing, they will never reset, # however the API sometimes returns 0 values when the clock turns to 00:00 @@ -178,7 +181,7 @@ class SunWEGData: # - Previous value will not exist meaning 0 will be returned # - This is an edge case that would be better handled by looking # up the previous value of the entity from the recorder - if entity_description.never_resets and api_value == 0 and previous_value: + if never_resets and api_value == 0 and previous_value: _LOGGER.debug( ( "API value is 0, but this value should never reset, returning" @@ -190,4 +193,4 @@ class SunWEGData: self.previous_values[variable] = return_value - return return_value + return (return_value, native_unit_of_measurement) diff --git a/homeassistant/components/sunweg/sensor.py b/homeassistant/components/sunweg/sensor.py index 5759e3a6251..42a3dc33d2b 100644 --- a/homeassistant/components/sunweg/sensor.py +++ b/homeassistant/components/sunweg/sensor.py @@ -1,7 +1,6 @@ """Read status of SunWEG inverters.""" from __future__ import annotations -import datetime import logging from types import MappingProxyType from typing import Any @@ -16,7 +15,6 @@ from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import StateType from . import SunWEGData from .const import CONF_PLANT_ID, DEFAULT_PLANT_ID, DOMAIN, DeviceType @@ -160,18 +158,20 @@ class SunWEGInverter(SensorEntity): name=name, ) - @property - def native_value( - self, - ) -> StateType | datetime.datetime: - """Return the state of the sensor.""" - return self.probe.get_data( - self.entity_description, - device_type=self.device_type, - inverter_id=self.inverter_id, - deep_name=self.deep_name, - ) - def update(self) -> None: """Get the latest data from the Sun WEG API and updates the state.""" self.probe.update() + ( + self._attr_native_value, + self._attr_native_unit_of_measurement, + ) = self.probe.get_data( + api_variable_key=self.entity_description.api_variable_key, + api_variable_unit=self.entity_description.api_variable_unit, + deep_name=self.deep_name, + device_type=self.device_type, + inverter_id=self.inverter_id, + name=self.entity_description.name, + native_unit_of_measurement=self.native_unit_of_measurement, + never_resets=self.entity_description.never_resets, + previous_value_drop_threshold=self.entity_description.previous_value_drop_threshold, + ) diff --git a/tests/components/sunweg/test_init.py b/tests/components/sunweg/test_init.py index 12c482f6b53..ca191469377 100644 --- a/tests/components/sunweg/test_init.py +++ b/tests/components/sunweg/test_init.py @@ -95,26 +95,41 @@ async def test_sunwegdata_get_data_drop_threshold() -> None: ) entity_description.previous_value_drop_threshold = 0.1 data.get_api_value.return_value = 3.0 - assert ( - data.get_data( - entity_description=entity_description, device_type=DeviceType.TOTAL - ) - == 3.0 - ) + assert data.get_data( + api_variable_key=entity_description.api_variable_key, + api_variable_unit=entity_description.api_variable_unit, + deep_name=None, + device_type=DeviceType.TOTAL, + inverter_id=0, + name=entity_description.name, + native_unit_of_measurement=entity_description.native_unit_of_measurement, + never_resets=entity_description.never_resets, + previous_value_drop_threshold=entity_description.previous_value_drop_threshold, + ) == (3.0, None) data.get_api_value.return_value = 2.91 - assert ( - data.get_data( - entity_description=entity_description, device_type=DeviceType.TOTAL - ) - == 3.0 - ) + assert data.get_data( + api_variable_key=entity_description.api_variable_key, + api_variable_unit=entity_description.api_variable_unit, + deep_name=None, + device_type=DeviceType.TOTAL, + inverter_id=0, + name=entity_description.name, + native_unit_of_measurement=entity_description.native_unit_of_measurement, + never_resets=entity_description.never_resets, + previous_value_drop_threshold=entity_description.previous_value_drop_threshold, + ) == (3.0, None) data.get_api_value.return_value = 2.8 - assert ( - data.get_data( - entity_description=entity_description, device_type=DeviceType.TOTAL - ) - == 2.8 - ) + assert data.get_data( + api_variable_key=entity_description.api_variable_key, + api_variable_unit=entity_description.api_variable_unit, + deep_name=None, + device_type=DeviceType.TOTAL, + inverter_id=0, + name=entity_description.name, + native_unit_of_measurement=entity_description.native_unit_of_measurement, + never_resets=entity_description.never_resets, + previous_value_drop_threshold=entity_description.previous_value_drop_threshold, + ) == (2.8, None) async def test_sunwegdata_get_data_never_reset() -> None: @@ -127,23 +142,38 @@ async def test_sunwegdata_get_data_never_reset() -> None: ) entity_description.never_resets = True data.get_api_value.return_value = 3.0 - assert ( - data.get_data( - entity_description=entity_description, device_type=DeviceType.TOTAL - ) - == 3.0 - ) + assert data.get_data( + api_variable_key=entity_description.api_variable_key, + api_variable_unit=entity_description.api_variable_unit, + deep_name=None, + device_type=DeviceType.TOTAL, + inverter_id=0, + name=entity_description.name, + native_unit_of_measurement=entity_description.native_unit_of_measurement, + never_resets=entity_description.never_resets, + previous_value_drop_threshold=entity_description.previous_value_drop_threshold, + ) == (3.0, None) data.get_api_value.return_value = 0 - assert ( - data.get_data( - entity_description=entity_description, device_type=DeviceType.TOTAL - ) - == 3.0 - ) + assert data.get_data( + api_variable_key=entity_description.api_variable_key, + api_variable_unit=entity_description.api_variable_unit, + deep_name=None, + device_type=DeviceType.TOTAL, + inverter_id=0, + name=entity_description.name, + native_unit_of_measurement=entity_description.native_unit_of_measurement, + never_resets=entity_description.never_resets, + previous_value_drop_threshold=entity_description.previous_value_drop_threshold, + ) == (3.0, None) data.get_api_value.return_value = 2.8 - assert ( - data.get_data( - entity_description=entity_description, device_type=DeviceType.TOTAL - ) - == 2.8 - ) + assert data.get_data( + api_variable_key=entity_description.api_variable_key, + api_variable_unit=entity_description.api_variable_unit, + deep_name=None, + device_type=DeviceType.TOTAL, + inverter_id=0, + name=entity_description.name, + native_unit_of_measurement=entity_description.native_unit_of_measurement, + never_resets=entity_description.never_resets, + previous_value_drop_threshold=entity_description.previous_value_drop_threshold, + ) == (2.8, None)