mirror of
https://github.com/home-assistant/core.git
synced 2026-07-04 23:51:32 +02:00
Miele time sensors 3/3 - Add absolute time sensors (#146055)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
@@ -41,6 +41,9 @@
|
||||
"energy_forecast": {
|
||||
"default": "mdi:lightning-bolt-outline"
|
||||
},
|
||||
"finish": {
|
||||
"default": "mdi:clock-end"
|
||||
},
|
||||
"plate": {
|
||||
"default": "mdi:circle-outline",
|
||||
"state": {
|
||||
@@ -83,6 +86,9 @@
|
||||
"spin_speed": {
|
||||
"default": "mdi:sync"
|
||||
},
|
||||
"start": {
|
||||
"default": "mdi:clock-start"
|
||||
},
|
||||
"start_time": {
|
||||
"default": "mdi:clock-start"
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Mapping
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from typing import Any, Final, cast
|
||||
|
||||
@@ -29,6 +30,7 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
COFFEE_SYSTEM_PROFILE,
|
||||
@@ -102,12 +104,47 @@ def _get_coffee_profile(value: MieleDevice) -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def _convert_start_timestamp(
|
||||
elapsed_time_list: list[int], start_time_list: list[int]
|
||||
) -> datetime | None:
|
||||
"""Convert raw values representing time into start timestamp."""
|
||||
now = dt_util.utcnow()
|
||||
elapsed_duration = _convert_duration(elapsed_time_list)
|
||||
delayed_start_duration = _convert_duration(start_time_list)
|
||||
if (elapsed_duration is None or elapsed_duration == 0) and (
|
||||
delayed_start_duration is None or delayed_start_duration == 0
|
||||
):
|
||||
return None
|
||||
if elapsed_duration is not None and elapsed_duration > 0:
|
||||
duration = -elapsed_duration
|
||||
elif delayed_start_duration is not None and delayed_start_duration > 0:
|
||||
duration = delayed_start_duration
|
||||
delta = timedelta(minutes=duration)
|
||||
return (now + delta).replace(second=0, microsecond=0)
|
||||
|
||||
|
||||
def _convert_finish_timestamp(
|
||||
remaining_time_list: list[int], start_time_list: list[int]
|
||||
) -> datetime | None:
|
||||
"""Convert raw values representing time into finish timestamp."""
|
||||
now = dt_util.utcnow()
|
||||
program_duration = _convert_duration(remaining_time_list)
|
||||
delayed_start_duration = _convert_duration(start_time_list)
|
||||
if program_duration is None or program_duration == 0:
|
||||
return None
|
||||
duration = program_duration + (
|
||||
delayed_start_duration if delayed_start_duration is not None else 0
|
||||
)
|
||||
delta = timedelta(minutes=duration)
|
||||
return (now + delta).replace(second=0, microsecond=0)
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class MieleSensorDescription(SensorEntityDescription):
|
||||
"""Class describing Miele sensor entities."""
|
||||
|
||||
value_fn: Callable[[MieleDevice], StateType]
|
||||
end_value_fn: Callable[[StateType], StateType] | None = None
|
||||
value_fn: Callable[[MieleDevice], StateType | datetime]
|
||||
end_value_fn: Callable[[StateType | datetime], StateType | datetime] | None = None
|
||||
extra_attributes: dict[str, Callable[[MieleDevice], StateType]] | None = None
|
||||
zone: int | None = None
|
||||
unique_id_fn: Callable[[str, MieleSensorDescription], str] | None = None
|
||||
@@ -428,6 +465,60 @@ SENSOR_TYPES: Final[tuple[MieleSensorDefinition, ...]] = (
|
||||
suggested_unit_of_measurement=UnitOfTime.HOURS,
|
||||
),
|
||||
),
|
||||
MieleSensorDefinition(
|
||||
types=(
|
||||
MieleAppliance.WASHING_MACHINE,
|
||||
MieleAppliance.WASHING_MACHINE_SEMI_PROFESSIONAL,
|
||||
MieleAppliance.TUMBLE_DRYER,
|
||||
MieleAppliance.TUMBLE_DRYER_SEMI_PROFESSIONAL,
|
||||
MieleAppliance.DISHWASHER,
|
||||
MieleAppliance.OVEN,
|
||||
MieleAppliance.OVEN_MICROWAVE,
|
||||
MieleAppliance.STEAM_OVEN,
|
||||
MieleAppliance.MICROWAVE,
|
||||
MieleAppliance.ROBOT_VACUUM_CLEANER,
|
||||
MieleAppliance.WASHER_DRYER,
|
||||
MieleAppliance.STEAM_OVEN_COMBI,
|
||||
MieleAppliance.STEAM_OVEN_MICRO,
|
||||
MieleAppliance.DIALOG_OVEN,
|
||||
MieleAppliance.STEAM_OVEN_MK2,
|
||||
),
|
||||
description=MieleSensorDescription(
|
||||
key="state_finish_timestamp",
|
||||
translation_key="finish",
|
||||
value_fn=lambda value: _convert_finish_timestamp(
|
||||
value.state_remaining_time, value.state_start_time
|
||||
),
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
),
|
||||
MieleSensorDefinition(
|
||||
types=(
|
||||
MieleAppliance.WASHING_MACHINE,
|
||||
MieleAppliance.TUMBLE_DRYER,
|
||||
MieleAppliance.DISHWASHER,
|
||||
MieleAppliance.OVEN,
|
||||
MieleAppliance.OVEN_MICROWAVE,
|
||||
MieleAppliance.STEAM_OVEN,
|
||||
MieleAppliance.MICROWAVE,
|
||||
MieleAppliance.WASHER_DRYER,
|
||||
MieleAppliance.STEAM_OVEN_COMBI,
|
||||
MieleAppliance.STEAM_OVEN_MICRO,
|
||||
MieleAppliance.DIALOG_OVEN,
|
||||
MieleAppliance.ROBOT_VACUUM_CLEANER,
|
||||
MieleAppliance.STEAM_OVEN_MK2,
|
||||
),
|
||||
description=MieleSensorDescription(
|
||||
key="state_start_timestamp",
|
||||
translation_key="start",
|
||||
value_fn=lambda value: _convert_start_timestamp(
|
||||
value.state_elapsed_time, value.state_start_time
|
||||
),
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
),
|
||||
MieleSensorDefinition(
|
||||
types=(
|
||||
MieleAppliance.TUMBLE_DRYER_SEMI_PROFESSIONAL,
|
||||
@@ -620,6 +711,8 @@ async def async_setup_entry(
|
||||
"state_elapsed_time": MieleTimeSensor,
|
||||
"state_remaining_time": MieleTimeSensor,
|
||||
"state_start_time": MieleTimeSensor,
|
||||
"state_start_timestamp": MieleAbsoluteTimeSensor,
|
||||
"state_finish_timestamp": MieleAbsoluteTimeSensor,
|
||||
"current_energy_consumption": MieleConsumptionSensor,
|
||||
"current_water_consumption": MieleConsumptionSensor,
|
||||
}.get(definition.description.key, MieleSensor)
|
||||
@@ -743,7 +836,7 @@ class MieleSensor(MieleEntity, SensorEntity):
|
||||
self._attr_unique_id = description.unique_id_fn(device_id, description)
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
def native_value(self) -> StateType | datetime:
|
||||
"""Return the state of the sensor."""
|
||||
return self.entity_description.value_fn(self.device)
|
||||
|
||||
@@ -761,7 +854,7 @@ class MieleSensor(MieleEntity, SensorEntity):
|
||||
class MieleRestorableSensor(MieleSensor, RestoreSensor):
|
||||
"""Representation of a Sensor whose internal state can be restored."""
|
||||
|
||||
_attr_native_value: StateType
|
||||
_attr_native_value: StateType | datetime
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""When entity is added to hass."""
|
||||
@@ -773,7 +866,7 @@ class MieleRestorableSensor(MieleSensor, RestoreSensor):
|
||||
self._attr_native_value = last_data.native_value # type: ignore[assignment]
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
def native_value(self) -> StateType | datetime:
|
||||
"""Return the state of the sensor.
|
||||
|
||||
It is necessary to override `native_value` to fall back to the default
|
||||
@@ -934,6 +1027,40 @@ class MieleTimeSensor(MieleRestorableSensor):
|
||||
self._attr_native_value = current_value
|
||||
|
||||
|
||||
class MieleAbsoluteTimeSensor(MieleRestorableSensor):
|
||||
"""Representation of absolute time sensors handling precision correctness."""
|
||||
|
||||
_previous_value: StateType | datetime = 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)
|
||||
|
||||
# The API reports with minute precision, to avoid changing
|
||||
# the value too often, we keep the cached value if it differs
|
||||
# less than 90s from the new value
|
||||
if (
|
||||
isinstance(self._previous_value, datetime)
|
||||
and isinstance(current_value, datetime)
|
||||
and (
|
||||
self._previous_value - timedelta(seconds=90)
|
||||
< current_value
|
||||
< self._previous_value + timedelta(seconds=90)
|
||||
)
|
||||
) or current_status == StateStatus.PROGRAM_ENDED:
|
||||
return
|
||||
|
||||
# force unknown when appliance is not working (some devices are keeping last value until a new cycle starts)
|
||||
if current_status in (StateStatus.OFF, StateStatus.ON, StateStatus.IDLE):
|
||||
self._attr_native_value = None
|
||||
|
||||
# otherwise, cache value and return it
|
||||
else:
|
||||
self._attr_native_value = current_value
|
||||
self._previous_value = current_value
|
||||
|
||||
|
||||
class MieleConsumptionSensor(MieleRestorableSensor):
|
||||
"""Representation of consumption sensors keeping state from cache."""
|
||||
|
||||
|
||||
@@ -216,6 +216,9 @@
|
||||
"energy_forecast": {
|
||||
"name": "Energy forecast"
|
||||
},
|
||||
"finish": {
|
||||
"name": "Finish"
|
||||
},
|
||||
"plate": {
|
||||
"name": "Plate {plate_no}",
|
||||
"state": {
|
||||
@@ -1015,6 +1018,9 @@
|
||||
"spin_speed": {
|
||||
"name": "Spin speed"
|
||||
},
|
||||
"start": {
|
||||
"name": "Start"
|
||||
},
|
||||
"start_time": {
|
||||
"name": "Start in"
|
||||
},
|
||||
|
||||
@@ -2873,6 +2873,55 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.oven_finish-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.oven_finish',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Finish',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'finish',
|
||||
'unique_id': 'DummyAppliance_12-state_finish_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.oven_finish-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Oven Finish',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.oven_finish',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.oven_program-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -3422,6 +3471,55 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.oven_start-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.oven_start',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Start',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'start',
|
||||
'unique_id': 'DummyAppliance_12-state_start_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.oven_start-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Oven Start',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.oven_start',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.oven_start_in-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -3986,6 +4084,55 @@
|
||||
'state': '10.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.washing_machine_finish-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.washing_machine_finish',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Finish',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'finish',
|
||||
'unique_id': 'Dummy_Appliance_3-state_finish_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.washing_machine_finish-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Washing machine Finish',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.washing_machine_finish',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.washing_machine_program-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -4366,6 +4513,55 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.washing_machine_start-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.washing_machine_start',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Start',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'start',
|
||||
'unique_id': 'Dummy_Appliance_3-state_start_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.washing_machine_start-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Washing machine Start',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.washing_machine_start',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[platforms0][sensor.washing_machine_start_in-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -5021,6 +5217,55 @@
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.oven_finish-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.oven_finish',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Finish',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'finish',
|
||||
'unique_id': 'DummyAppliance_12-state_finish_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.oven_finish-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Oven Finish',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.oven_finish',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '2025-05-31T12:35:00+00:00',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.oven_program-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -5570,6 +5815,55 @@
|
||||
'state': '5',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.oven_start-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.oven_start',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Start',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'start',
|
||||
'unique_id': 'DummyAppliance_12-state_start_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.oven_start-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Oven Start',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.oven_start',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.oven_start_in-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -6134,6 +6428,55 @@
|
||||
'state': '10.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_finish-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.washing_machine_finish',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Finish',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'finish',
|
||||
'unique_id': 'Dummy_Appliance_3-state_finish_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_finish-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Washing machine Finish',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.washing_machine_finish',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_program-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -6514,6 +6857,55 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.washing_machine_start',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Start',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'start',
|
||||
'unique_id': 'Dummy_Appliance_3-state_start_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Washing machine Start',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.washing_machine_start',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states_api_push[platforms0][sensor.washing_machine_start_in-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -6925,6 +7317,55 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.robot_vacuum_cleaner_finish-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.robot_vacuum_cleaner_finish',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Finish',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'finish',
|
||||
'unique_id': 'Dummy_Vacuum_1-state_finish_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.robot_vacuum_cleaner_finish-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Robot vacuum cleaner Finish',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.robot_vacuum_cleaner_finish',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.robot_vacuum_cleaner_program-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -7106,3 +7547,52 @@
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.robot_vacuum_cleaner_start-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.robot_vacuum_cleaner_start',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Start',
|
||||
'platform': 'miele',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'start',
|
||||
'unique_id': 'Dummy_Vacuum_1-state_start_timestamp',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_vacuum_sensor_states[platforms0-vacuum_device.json][sensor.robot_vacuum_cleaner_start-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Robot vacuum cleaner Start',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.robot_vacuum_cleaner_start',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Tests for miele sensor module."""
|
||||
|
||||
from datetime import timedelta
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
@@ -23,6 +23,7 @@ from tests.common import (
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2025-05-31 12:30:00+00:00")
|
||||
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_sensor_states(
|
||||
@@ -37,6 +38,7 @@ async def test_sensor_states(
|
||||
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2025-05-31 12:30:00+00:00")
|
||||
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_sensor_states_api_push(
|
||||
@@ -302,6 +304,7 @@ async def test_laundry_wash_scenario(
|
||||
"""Parametrized test for verifying time sensors for wahsing machine devices when API glitches at program end."""
|
||||
|
||||
step = 0
|
||||
freezer.move_to("2025-05-31T12:00:00+00:00")
|
||||
|
||||
# Initial state when the washing machine is off
|
||||
check_sensor_state(hass, "sensor.washing_machine", "off", step)
|
||||
@@ -317,6 +320,8 @@ async def test_laundry_wash_scenario(
|
||||
check_sensor_state(hass, "sensor.washing_machine_remaining_time", "unknown", step)
|
||||
# OFF -> elapsed forced to unknown (some devices continue reporting last value of last cycle)
|
||||
check_sensor_state(hass, "sensor.washing_machine_elapsed_time", "unknown", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_start", "unknown", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_finish", "unknown", step)
|
||||
# consumption sensors have to report "unknown" when the device is not working
|
||||
check_sensor_state(
|
||||
hass, "sensor.washing_machine_energy_consumption", "unknown", step
|
||||
@@ -357,7 +362,7 @@ async def test_laundry_wash_scenario(
|
||||
},
|
||||
}
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to("2025-05-31T12:30:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@@ -376,8 +381,12 @@ async def test_laundry_wash_scenario(
|
||||
"unit": "l",
|
||||
},
|
||||
}
|
||||
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 0
|
||||
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 14
|
||||
device_fixture["DummyWasher"]["state"]["remainingTime"][0] = 1
|
||||
device_fixture["DummyWasher"]["state"]["remainingTime"][1] = 43
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to("2025-05-31T12:32:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@@ -389,8 +398,14 @@ async def test_laundry_wash_scenario(
|
||||
check_sensor_state(hass, "sensor.washing_machine_target_temperature", "30.0", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_spin_speed", "1200", step)
|
||||
# IN_USE -> elapsed, remaining time from API (normal case)
|
||||
check_sensor_state(hass, "sensor.washing_machine_remaining_time", "105", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_elapsed_time", "12", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_remaining_time", "103", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_elapsed_time", "14", step)
|
||||
check_sensor_state(
|
||||
hass, "sensor.washing_machine_start", "2025-05-31T12:18:00+00:00", step
|
||||
)
|
||||
check_sensor_state(
|
||||
hass, "sensor.washing_machine_finish", "2025-05-31T14:15:00+00:00", step
|
||||
)
|
||||
check_sensor_state(hass, "sensor.washing_machine_energy_consumption", "0.0", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_water_consumption", "0", step)
|
||||
|
||||
@@ -406,7 +421,7 @@ async def test_laundry_wash_scenario(
|
||||
},
|
||||
}
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to("2025-05-31T12:34:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@@ -426,7 +441,7 @@ async def test_laundry_wash_scenario(
|
||||
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 1
|
||||
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 49
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to("2025-05-31T14:07:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
step += 1
|
||||
@@ -439,6 +454,12 @@ async def test_laundry_wash_scenario(
|
||||
# RINSE HOLD -> elapsed, remaining time from API (normal case)
|
||||
check_sensor_state(hass, "sensor.washing_machine_remaining_time", "8", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_elapsed_time", "109", step)
|
||||
check_sensor_state(
|
||||
hass, "sensor.washing_machine_start", "2025-05-31T12:18:00+00:00", step
|
||||
)
|
||||
check_sensor_state(
|
||||
hass, "sensor.washing_machine_finish", "2025-05-31T14:15:00+00:00", step
|
||||
)
|
||||
|
||||
# Simulate program ended
|
||||
device_fixture["DummyWasher"]["state"]["status"]["value_raw"] = 7
|
||||
@@ -453,7 +474,7 @@ async def test_laundry_wash_scenario(
|
||||
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 0
|
||||
device_fixture["DummyWasher"]["state"]["ecoFeedback"] = None
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to("2025-05-31T14:30:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
step += 1
|
||||
@@ -469,6 +490,12 @@ async def test_laundry_wash_scenario(
|
||||
check_sensor_state(hass, "sensor.washing_machine_remaining_time", "0", step)
|
||||
# PROGRAM_ENDED -> elapsed time kept from last program (some devices immediately go to 0)
|
||||
check_sensor_state(hass, "sensor.washing_machine_elapsed_time", "109", step)
|
||||
check_sensor_state(
|
||||
hass, "sensor.washing_machine_start", "2025-05-31T12:18:00+00:00", step
|
||||
)
|
||||
check_sensor_state(
|
||||
hass, "sensor.washing_machine_finish", "2025-05-31T14:15:00+00:00", step
|
||||
)
|
||||
# consumption values now are reporting last known value, API might start reporting null object
|
||||
check_sensor_state(hass, "sensor.washing_machine_energy_consumption", "0.1", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_water_consumption", "7", step)
|
||||
@@ -489,7 +516,7 @@ async def test_laundry_wash_scenario(
|
||||
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 0
|
||||
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 0
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to("2025-05-31T14:32:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
step += 1
|
||||
@@ -504,6 +531,10 @@ async def test_laundry_wash_scenario(
|
||||
# PROGRAMMED -> elapsed, remaining time from API (normal case)
|
||||
check_sensor_state(hass, "sensor.washing_machine_remaining_time", "119", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_elapsed_time", "0", step)
|
||||
check_sensor_state(hass, "sensor.washing_machine_start", "unknown", step)
|
||||
check_sensor_state(
|
||||
hass, "sensor.washing_machine_finish", "2025-05-31T16:31:00+00:00", step
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("load_device_file", ["laundry.json"])
|
||||
@@ -519,6 +550,7 @@ async def test_laundry_dry_scenario(
|
||||
"""Parametrized test for verifying time sensors for tumble dryer devices when API reports time value from last cycle, when device is off."""
|
||||
|
||||
step = 0
|
||||
freezer.move_to("2025-05-31T12:00:00+00:00")
|
||||
|
||||
# Initial state when the washing machine is off
|
||||
check_sensor_state(hass, "sensor.tumble_dryer", "off", step)
|
||||
@@ -528,6 +560,8 @@ async def test_laundry_dry_scenario(
|
||||
# OFF -> elapsed, remaining forced to unknown (some devices continue reporting last value of last cycle)
|
||||
check_sensor_state(hass, "sensor.tumble_dryer_remaining_time", "unknown", step)
|
||||
check_sensor_state(hass, "sensor.tumble_dryer_elapsed_time", "unknown", step)
|
||||
check_sensor_state(hass, "sensor.tumble_dryer_start", "unknown", step)
|
||||
check_sensor_state(hass, "sensor.tumble_dryer_finish", "unknown", step)
|
||||
|
||||
# Simulate program started
|
||||
device_fixture["DummyDryer"]["state"]["status"]["value_raw"] = 5
|
||||
@@ -545,7 +579,7 @@ async def test_laundry_dry_scenario(
|
||||
device_fixture["DummyDryer"]["state"]["dryingStep"]["value_raw"] = 2
|
||||
device_fixture["DummyDryer"]["state"]["dryingStep"]["value_localized"] = "Normal"
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to("2025-05-31T12:30:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
step += 1
|
||||
@@ -557,6 +591,12 @@ async def test_laundry_dry_scenario(
|
||||
# IN_USE -> elapsed, remaining time from API (normal case)
|
||||
check_sensor_state(hass, "sensor.tumble_dryer_remaining_time", "49", step)
|
||||
check_sensor_state(hass, "sensor.tumble_dryer_elapsed_time", "20", step)
|
||||
check_sensor_state(
|
||||
hass, "sensor.tumble_dryer_start", "2025-05-31T12:10:00+00:00", step
|
||||
)
|
||||
check_sensor_state(
|
||||
hass, "sensor.tumble_dryer_finish", "2025-05-31T13:19:00+00:00", step
|
||||
)
|
||||
|
||||
# Simulate program end
|
||||
device_fixture["DummyDryer"]["state"]["status"]["value_raw"] = 7
|
||||
@@ -570,7 +610,7 @@ async def test_laundry_dry_scenario(
|
||||
device_fixture["DummyDryer"]["state"]["elapsedTime"][0] = 1
|
||||
device_fixture["DummyDryer"]["state"]["elapsedTime"][1] = 18
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to("2025-05-31T14:30:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
step += 1
|
||||
@@ -583,9 +623,18 @@ async def test_laundry_dry_scenario(
|
||||
check_sensor_state(hass, "sensor.tumble_dryer_remaining_time", "0", step)
|
||||
# PROGRAM_ENDED -> elapsed time kept from last program (some devices immediately go to 0)
|
||||
check_sensor_state(hass, "sensor.tumble_dryer_elapsed_time", "20", step)
|
||||
check_sensor_state(
|
||||
hass, "sensor.tumble_dryer_start", "2025-05-31T12:10:00+00:00", step
|
||||
)
|
||||
check_sensor_state(
|
||||
hass, "sensor.tumble_dryer_finish", "2025-05-31T13:19:00+00:00", step
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("restore_state", ["45", STATE_UNKNOWN, STATE_UNAVAILABLE])
|
||||
@pytest.mark.parametrize(
|
||||
"restore_state_abs", ["2025-05-31T13:19:00+00:00", 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(
|
||||
@@ -596,10 +645,12 @@ async def test_elapsed_time_sensor_restored(
|
||||
device_fixture: MieleDevices,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
restore_state,
|
||||
restore_state_abs,
|
||||
) -> None:
|
||||
"""Test that elapsed time returns the restored value when program ended."""
|
||||
|
||||
entity_id = "sensor.washing_machine_elapsed_time"
|
||||
entity_id_abs = "sensor.washing_machine_finish"
|
||||
|
||||
# Simulate program started
|
||||
device_fixture["DummyWasher"]["state"]["status"]["value_raw"] = 5
|
||||
@@ -623,11 +674,12 @@ async def test_elapsed_time_sensor_restored(
|
||||
device_fixture["DummyWasher"]["state"]["spinningSpeed"]["value_raw"] = 1200
|
||||
device_fixture["DummyWasher"]["state"]["spinningSpeed"]["value_localized"] = "1200"
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to(datetime(2025, 5, 31, 12, 30, tzinfo=UTC))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "12"
|
||||
assert hass.states.get(entity_id_abs).state == "2025-05-31T14:15:00+00:00"
|
||||
|
||||
# Simulate program ended
|
||||
device_fixture["DummyWasher"]["state"]["status"]["value_raw"] = 7
|
||||
@@ -641,7 +693,7 @@ async def test_elapsed_time_sensor_restored(
|
||||
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 0
|
||||
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 0
|
||||
|
||||
freezer.tick(timedelta(seconds=130))
|
||||
freezer.move_to(datetime(2025, 5, 31, 14, 20, tzinfo=UTC))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@@ -651,6 +703,7 @@ async def test_elapsed_time_sensor_restored(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "unavailable"
|
||||
assert hass.states.get(entity_id_abs).state == "unavailable"
|
||||
|
||||
# simulate restore with state different from native value
|
||||
mock_restore_cache_with_extra_data(
|
||||
@@ -669,9 +722,19 @@ async def test_elapsed_time_sensor_restored(
|
||||
"native_unit_of_measurement": "min",
|
||||
},
|
||||
),
|
||||
(
|
||||
State(
|
||||
entity_id_abs,
|
||||
restore_state_abs,
|
||||
{"device_class": "timestamp"},
|
||||
),
|
||||
{
|
||||
"native_value": datetime(2025, 5, 31, 14, 15, tzinfo=UTC),
|
||||
"native_unit_of_measurement": None,
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
await hass.config_entries.async_reload(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@@ -679,3 +742,8 @@ async def test_elapsed_time_sensor_restored(
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == "12"
|
||||
|
||||
# check that absolute time is the one restored and not the value reported by API
|
||||
state = hass.states.get(entity_id_abs)
|
||||
assert state is not None
|
||||
assert state.state == "2025-05-31T14:15:00+00:00"
|
||||
|
||||
Reference in New Issue
Block a user