mirror of
https://github.com/home-assistant/core.git
synced 2026-02-15 07:36:16 +00:00
Miele RestoreSensor: restore native value rather than stringified state (#152750)
Co-authored-by: Erik Montnemery <erik@montnemery.com> Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
This commit is contained in:
@@ -19,7 +19,6 @@ from homeassistant.components.sensor import (
|
||||
from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
REVOLUTIONS_PER_MINUTE,
|
||||
STATE_UNKNOWN,
|
||||
EntityCategory,
|
||||
UnitOfEnergy,
|
||||
UnitOfTemperature,
|
||||
@@ -762,40 +761,35 @@ class MieleSensor(MieleEntity, SensorEntity):
|
||||
class MieleRestorableSensor(MieleSensor, RestoreSensor):
|
||||
"""Representation of a Sensor whose internal state can be restored."""
|
||||
|
||||
_last_value: StateType
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: MieleDataUpdateCoordinator,
|
||||
device_id: str,
|
||||
description: MieleSensorDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator, device_id, description)
|
||||
self._last_value = None
|
||||
_attr_native_value: StateType
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""When entity is added to hass."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
# recover last value from cache when adding entity
|
||||
last_value = await self.async_get_last_state()
|
||||
if last_value and last_value.state != STATE_UNKNOWN:
|
||||
self._last_value = last_value.state
|
||||
last_data = await self.async_get_last_sensor_data()
|
||||
if last_data:
|
||||
self._attr_native_value = last_data.native_value # type: ignore[assignment]
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state of the sensor."""
|
||||
return self._last_value
|
||||
"""Return the state of the sensor.
|
||||
|
||||
def _update_last_value(self) -> None:
|
||||
"""Update the last value of the sensor."""
|
||||
self._last_value = self.entity_description.value_fn(self.device)
|
||||
It is necessary to override `native_value` to fall back to the default
|
||||
attribute-based implementation, instead of the function-based
|
||||
implementation in `MieleSensor`.
|
||||
"""
|
||||
return self._attr_native_value
|
||||
|
||||
def _update_native_value(self) -> None:
|
||||
"""Update the native value attribute of the sensor."""
|
||||
self._attr_native_value = self.entity_description.value_fn(self.device)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle updated data from the coordinator."""
|
||||
self._update_last_value()
|
||||
self._update_native_value()
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
|
||||
@@ -912,7 +906,7 @@ class MieleProgramIdSensor(MieleSensor):
|
||||
class MieleTimeSensor(MieleRestorableSensor):
|
||||
"""Representation of time sensors keeping state from cache."""
|
||||
|
||||
def _update_last_value(self) -> None:
|
||||
def _update_native_value(self) -> None:
|
||||
"""Update the last value of the sensor."""
|
||||
|
||||
current_value = self.entity_description.value_fn(self.device)
|
||||
@@ -923,7 +917,9 @@ class MieleTimeSensor(MieleRestorableSensor):
|
||||
current_status == StateStatus.PROGRAM_ENDED
|
||||
and self.entity_description.end_value_fn is not None
|
||||
):
|
||||
self._last_value = self.entity_description.end_value_fn(self._last_value)
|
||||
self._attr_native_value = self.entity_description.end_value_fn(
|
||||
self._attr_native_value
|
||||
)
|
||||
|
||||
# keep value when program ends if no function is specified
|
||||
elif current_status == StateStatus.PROGRAM_ENDED:
|
||||
@@ -931,11 +927,11 @@ class MieleTimeSensor(MieleRestorableSensor):
|
||||
|
||||
# force unknown when appliance is not working (some devices are keeping last value until a new cycle starts)
|
||||
elif current_status in (StateStatus.OFF, StateStatus.ON, StateStatus.IDLE):
|
||||
self._last_value = None
|
||||
self._attr_native_value = None
|
||||
|
||||
# otherwise, cache value and return it
|
||||
else:
|
||||
self._last_value = current_value
|
||||
self._attr_native_value = current_value
|
||||
|
||||
|
||||
class MieleConsumptionSensor(MieleRestorableSensor):
|
||||
@@ -943,13 +939,13 @@ class MieleConsumptionSensor(MieleRestorableSensor):
|
||||
|
||||
_is_reporting: bool = False
|
||||
|
||||
def _update_last_value(self) -> None:
|
||||
def _update_native_value(self) -> None:
|
||||
"""Update the last value of the sensor."""
|
||||
current_value = self.entity_description.value_fn(self.device)
|
||||
current_status = StateStatus(self.device.state_status)
|
||||
last_value = (
|
||||
float(cast(str, self._last_value))
|
||||
if self._last_value is not None and self._last_value != STATE_UNKNOWN
|
||||
float(cast(str, self._attr_native_value))
|
||||
if self._attr_native_value is not None
|
||||
else 0
|
||||
)
|
||||
|
||||
@@ -963,7 +959,7 @@ class MieleConsumptionSensor(MieleRestorableSensor):
|
||||
StateStatus.SERVICE,
|
||||
):
|
||||
self._is_reporting = False
|
||||
self._last_value = None
|
||||
self._attr_native_value = None
|
||||
|
||||
# appliance might report the last value for consumption of previous cycle and it will report 0
|
||||
# only after a while, so it is necessary to force 0 until we see the 0 value coming from API, unless
|
||||
@@ -973,7 +969,7 @@ class MieleConsumptionSensor(MieleRestorableSensor):
|
||||
and not self._is_reporting
|
||||
and last_value > 0
|
||||
):
|
||||
self._last_value = current_value
|
||||
self._attr_native_value = current_value
|
||||
self._is_reporting = True
|
||||
|
||||
elif (
|
||||
@@ -982,12 +978,12 @@ class MieleConsumptionSensor(MieleRestorableSensor):
|
||||
and current_value is not None
|
||||
and cast(int, current_value) > 0
|
||||
):
|
||||
self._last_value = 0
|
||||
self._attr_native_value = 0
|
||||
|
||||
# keep value when program ends
|
||||
elif current_status == StateStatus.PROGRAM_ENDED:
|
||||
pass
|
||||
|
||||
else:
|
||||
self._last_value = current_value
|
||||
self._attr_native_value = current_value
|
||||
self._is_reporting = True
|
||||
|
||||
@@ -10,13 +10,15 @@ from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.miele.const import DOMAIN
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
async_fire_time_changed,
|
||||
async_load_json_object_fixture,
|
||||
mock_restore_cache_with_extra_data,
|
||||
snapshot_platform,
|
||||
)
|
||||
|
||||
@@ -583,6 +585,7 @@ async def test_laundry_dry_scenario(
|
||||
check_sensor_state(hass, "sensor.tumble_dryer_elapsed_time", "20", step)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("restore_state", ["45", STATE_UNKNOWN, STATE_UNAVAILABLE])
|
||||
@pytest.mark.parametrize("load_device_file", ["laundry.json"])
|
||||
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
|
||||
async def test_elapsed_time_sensor_restored(
|
||||
@@ -592,6 +595,7 @@ async def test_elapsed_time_sensor_restored(
|
||||
setup_platform: None,
|
||||
device_fixture: MieleDevices,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
restore_state,
|
||||
) -> None:
|
||||
"""Test that elapsed time returns the restored value when program ended."""
|
||||
|
||||
@@ -648,6 +652,26 @@ async def test_elapsed_time_sensor_restored(
|
||||
|
||||
assert hass.states.get(entity_id).state == "unavailable"
|
||||
|
||||
# simulate restore with state different from native value
|
||||
mock_restore_cache_with_extra_data(
|
||||
hass,
|
||||
[
|
||||
(
|
||||
State(
|
||||
entity_id,
|
||||
restore_state,
|
||||
{
|
||||
"unit_of_measurement": "min",
|
||||
},
|
||||
),
|
||||
{
|
||||
"native_value": "12",
|
||||
"native_unit_of_measurement": "min",
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
await hass.config_entries.async_reload(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user