Avoid mutating entity descriptions in sunweg (#105982)

This commit is contained in:
Erik Montnemery 2023-12-18 21:03:40 +01:00 committed by GitHub
parent 27f81b3f63
commit 1d1cd6be57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 72 deletions

View File

@ -10,11 +10,10 @@ from homeassistant import config_entries
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType, UndefinedType
from homeassistant.util import Throttle from homeassistant.util import Throttle
from .const import CONF_PLANT_ID, DOMAIN, PLATFORMS, DeviceType from .const import CONF_PLANT_ID, DOMAIN, PLATFORMS, DeviceType
from .sensor_types.sensor_entity_description import SunWEGSensorEntityDescription
SCAN_INTERVAL = datetime.timedelta(minutes=5) SCAN_INTERVAL = datetime.timedelta(minutes=5)
@ -102,24 +101,30 @@ class SunWEGData:
def get_data( def get_data(
self, self,
entity_description: SunWEGSensorEntityDescription, *,
api_variable_key: str,
api_variable_unit: str | None,
deep_name: str | None,
device_type: DeviceType, device_type: DeviceType,
inverter_id: int = 0, inverter_id: int,
deep_name: str | None = None, name: str | UndefinedType | None,
) -> StateType | datetime.datetime: native_unit_of_measurement: str | None,
never_resets: bool,
previous_value_drop_threshold: float | None,
) -> tuple[StateType | datetime.datetime, str | None]:
"""Get the data.""" """Get the data."""
_LOGGER.debug( _LOGGER.debug(
"Data request for: %s", "Data request for: %s",
entity_description.name, name,
) )
variable = entity_description.api_variable_key variable = api_variable_key
previous_unit = entity_description.native_unit_of_measurement previous_unit = native_unit_of_measurement
api_value = self.get_api_value(variable, device_type, inverter_id, deep_name) api_value = self.get_api_value(variable, device_type, inverter_id, deep_name)
previous_value = self.previous_values.get(variable) previous_value = self.previous_values.get(variable)
return_value = api_value return_value = api_value
if entity_description.api_variable_unit is not None: if api_variable_unit is not None:
entity_description.native_unit_of_measurement = self.get_api_value( native_unit_of_measurement = self.get_api_value(
entity_description.api_variable_unit, api_variable_unit,
device_type, device_type,
inverter_id, inverter_id,
deep_name, deep_name,
@ -127,18 +132,18 @@ class SunWEGData:
# If we have a 'drop threshold' specified, then check it and correct if needed # If we have a 'drop threshold' specified, then check it and correct if needed
if ( if (
entity_description.previous_value_drop_threshold is not None previous_value_drop_threshold is not None
and previous_value is not None and previous_value is not None
and api_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( _LOGGER.debug(
( (
"%s - Drop threshold specified (%s), checking for drop... API" "%s - Drop threshold specified (%s), checking for drop... API"
" Value: %s, Previous Value: %s" " Value: %s, Previous Value: %s"
), ),
entity_description.name, name,
entity_description.previous_value_drop_threshold, previous_value_drop_threshold,
api_value, api_value,
previous_value, previous_value,
) )
@ -149,7 +154,7 @@ class SunWEGData:
# Note - The energy dashboard takes care of drops within 10% # Note - The energy dashboard takes care of drops within 10%
# of the current value, however if the value is low e.g. 0.2 # of the current value, however if the value is low e.g. 0.2
# and drops by 0.1 it classes as a reset. # 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( _LOGGER.debug(
( (
"Diff is negative, but only by a small amount therefore not a" "Diff is negative, but only by a small amount therefore not a"
@ -161,9 +166,7 @@ class SunWEGData:
) )
return_value = previous_value return_value = previous_value
else: else:
_LOGGER.debug( _LOGGER.debug("%s - No drop detected, using API value", name)
"%s - No drop detected, using API value", entity_description.name
)
# Lifetime total values should always be increasing, they will never reset, # 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 # 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 # - Previous value will not exist meaning 0 will be returned
# - This is an edge case that would be better handled by looking # - This is an edge case that would be better handled by looking
# up the previous value of the entity from the recorder # 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( _LOGGER.debug(
( (
"API value is 0, but this value should never reset, returning" "API value is 0, but this value should never reset, returning"
@ -190,4 +193,4 @@ class SunWEGData:
self.previous_values[variable] = return_value self.previous_values[variable] = return_value
return return_value return (return_value, native_unit_of_measurement)

View File

@ -1,7 +1,6 @@
"""Read status of SunWEG inverters.""" """Read status of SunWEG inverters."""
from __future__ import annotations from __future__ import annotations
import datetime
import logging import logging
from types import MappingProxyType from types import MappingProxyType
from typing import Any from typing import Any
@ -16,7 +15,6 @@ from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from . import SunWEGData from . import SunWEGData
from .const import CONF_PLANT_ID, DEFAULT_PLANT_ID, DOMAIN, DeviceType from .const import CONF_PLANT_ID, DEFAULT_PLANT_ID, DOMAIN, DeviceType
@ -160,18 +158,20 @@ class SunWEGInverter(SensorEntity):
name=name, 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: def update(self) -> None:
"""Get the latest data from the Sun WEG API and updates the state.""" """Get the latest data from the Sun WEG API and updates the state."""
self.probe.update() 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,
)

View File

@ -95,26 +95,41 @@ async def test_sunwegdata_get_data_drop_threshold() -> None:
) )
entity_description.previous_value_drop_threshold = 0.1 entity_description.previous_value_drop_threshold = 0.1
data.get_api_value.return_value = 3.0 data.get_api_value.return_value = 3.0
assert ( assert data.get_data(
data.get_data( api_variable_key=entity_description.api_variable_key,
entity_description=entity_description, device_type=DeviceType.TOTAL api_variable_unit=entity_description.api_variable_unit,
) deep_name=None,
== 3.0 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 data.get_api_value.return_value = 2.91
assert ( assert data.get_data(
data.get_data( api_variable_key=entity_description.api_variable_key,
entity_description=entity_description, device_type=DeviceType.TOTAL api_variable_unit=entity_description.api_variable_unit,
) deep_name=None,
== 3.0 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 data.get_api_value.return_value = 2.8
assert ( assert data.get_data(
data.get_data( api_variable_key=entity_description.api_variable_key,
entity_description=entity_description, device_type=DeviceType.TOTAL api_variable_unit=entity_description.api_variable_unit,
) deep_name=None,
== 2.8 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: 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 entity_description.never_resets = True
data.get_api_value.return_value = 3.0 data.get_api_value.return_value = 3.0
assert ( assert data.get_data(
data.get_data( api_variable_key=entity_description.api_variable_key,
entity_description=entity_description, device_type=DeviceType.TOTAL api_variable_unit=entity_description.api_variable_unit,
) deep_name=None,
== 3.0 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 data.get_api_value.return_value = 0
assert ( assert data.get_data(
data.get_data( api_variable_key=entity_description.api_variable_key,
entity_description=entity_description, device_type=DeviceType.TOTAL api_variable_unit=entity_description.api_variable_unit,
) deep_name=None,
== 3.0 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 data.get_api_value.return_value = 2.8
assert ( assert data.get_data(
data.get_data( api_variable_key=entity_description.api_variable_key,
entity_description=entity_description, device_type=DeviceType.TOTAL api_variable_unit=entity_description.api_variable_unit,
) deep_name=None,
== 2.8 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)