Files
core/tests/components/miele/test_sensor.py
2025-10-30 07:53:54 +01:00

682 lines
29 KiB
Python

"""Tests for miele sensor module."""
from datetime import timedelta
from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory
from pymiele import MieleDevices
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.miele.const import DOMAIN
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
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,
)
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_states(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
) -> None:
"""Test sensor state after polling the API for data."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_states_api_push(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
push_data_and_actions: None,
) -> None:
"""Test sensor state when the API pushes data via SSE."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("load_device_file", ["hob.json"])
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_hob_sensor_states(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: None,
) -> None:
"""Test sensor state."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("load_device_file", ["fridge_freezer.json"])
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_fridge_freezer_sensor_states(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: None,
) -> None:
"""Test sensor state."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("load_device_file", ["oven.json"])
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
async def test_oven_temperatures_scenario(
hass: HomeAssistant,
mock_miele_client: MagicMock,
setup_platform: None,
mock_config_entry: MockConfigEntry,
device_fixture: MieleDevices,
freezer: FrozenDateTimeFactory,
) -> None:
"""Parametrized test for verifying temperature sensors for oven devices."""
# Initial state when the oven is and created for the first time - don't know if it supports core temperature (probe)
check_sensor_state(hass, "sensor.oven_temperature", "unknown", 0)
check_sensor_state(hass, "sensor.oven_target_temperature", "unknown", 0)
check_sensor_state(hass, "sensor.oven_core_temperature", None, 0)
check_sensor_state(hass, "sensor.oven_core_target_temperature", None, 0)
# Simulate temperature settings, no probe temperature
device_fixture["DummyOven"]["state"]["targetTemperature"][0]["value_raw"] = 8000
device_fixture["DummyOven"]["state"]["targetTemperature"][0]["value_localized"] = (
80.0
)
device_fixture["DummyOven"]["state"]["temperature"][0]["value_raw"] = 2150
device_fixture["DummyOven"]["state"]["temperature"][0]["value_localized"] = 21.5
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
check_sensor_state(hass, "sensor.oven_temperature", "21.5", 1)
check_sensor_state(hass, "sensor.oven_target_temperature", "80.0", 1)
check_sensor_state(hass, "sensor.oven_core_temperature", None, 1)
check_sensor_state(hass, "sensor.oven_core_target_temperature", None, 1)
# Simulate unsetting temperature
device_fixture["DummyOven"]["state"]["targetTemperature"][0]["value_raw"] = -32768
device_fixture["DummyOven"]["state"]["targetTemperature"][0]["value_localized"] = (
None
)
device_fixture["DummyOven"]["state"]["temperature"][0]["value_raw"] = -32768
device_fixture["DummyOven"]["state"]["temperature"][0]["value_localized"] = None
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
check_sensor_state(hass, "sensor.oven_temperature", "unknown", 2)
check_sensor_state(hass, "sensor.oven_target_temperature", "unknown", 2)
check_sensor_state(hass, "sensor.oven_core_temperature", None, 2)
check_sensor_state(hass, "sensor.oven_core_target_temperature", None, 2)
# Simulate temperature settings with probe temperature
device_fixture["DummyOven"]["state"]["targetTemperature"][0]["value_raw"] = 8000
device_fixture["DummyOven"]["state"]["targetTemperature"][0]["value_localized"] = (
80.0
)
device_fixture["DummyOven"]["state"]["coreTargetTemperature"][0]["value_raw"] = 3000
device_fixture["DummyOven"]["state"]["coreTargetTemperature"][0][
"value_localized"
] = 30.0
device_fixture["DummyOven"]["state"]["temperature"][0]["value_raw"] = 2183
device_fixture["DummyOven"]["state"]["temperature"][0]["value_localized"] = 21.83
device_fixture["DummyOven"]["state"]["coreTemperature"][0]["value_raw"] = 2200
device_fixture["DummyOven"]["state"]["coreTemperature"][0]["value_localized"] = 22.0
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
check_sensor_state(hass, "sensor.oven_temperature", "21.83", 3)
check_sensor_state(hass, "sensor.oven_target_temperature", "80.0", 3)
check_sensor_state(hass, "sensor.oven_core_temperature", "22.0", 2)
check_sensor_state(hass, "sensor.oven_core_target_temperature", "30.0", 3)
# Simulate unsetting temperature
device_fixture["DummyOven"]["state"]["targetTemperature"][0]["value_raw"] = -32768
device_fixture["DummyOven"]["state"]["targetTemperature"][0]["value_localized"] = (
None
)
device_fixture["DummyOven"]["state"]["coreTargetTemperature"][0][
"value_raw"
] = -32768
device_fixture["DummyOven"]["state"]["coreTargetTemperature"][0][
"value_localized"
] = None
device_fixture["DummyOven"]["state"]["temperature"][0]["value_raw"] = -32768
device_fixture["DummyOven"]["state"]["temperature"][0]["value_localized"] = None
device_fixture["DummyOven"]["state"]["coreTemperature"][0]["value_raw"] = -32768
device_fixture["DummyOven"]["state"]["coreTemperature"][0]["value_localized"] = None
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
check_sensor_state(hass, "sensor.oven_temperature", "unknown", 4)
check_sensor_state(hass, "sensor.oven_target_temperature", "unknown", 4)
check_sensor_state(hass, "sensor.oven_core_temperature", "unknown", 4)
check_sensor_state(hass, "sensor.oven_core_target_temperature", "unknown", 4)
def check_sensor_state(
hass: HomeAssistant,
sensor_entity: str,
expected: str,
step: int,
):
"""Check the state of sensor matches the expected state."""
state = hass.states.get(sensor_entity)
if expected is None:
assert state is None, (
f"[{sensor_entity}] Step {step + 1}: got {state.state}, expected nothing"
)
else:
assert state is not None, f"Missing entity: {sensor_entity}"
assert state.state == expected, (
f"[{sensor_entity}] Step {step + 1}: got {state.state}, expected {expected}"
)
@pytest.mark.parametrize("load_device_file", ["oven.json"])
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
async def test_temperature_sensor_registry_lookup(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_miele_client: MagicMock,
setup_platform: None,
device_fixture: MieleDevices,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test that core temperature sensor is provided by the integration after looking up in entity registry."""
# Initial state, the oven is showing core temperature (probe)
freezer.tick(timedelta(seconds=130))
device_fixture["DummyOven"]["state"]["coreTemperature"][0]["value_raw"] = 2200
device_fixture["DummyOven"]["state"]["coreTemperature"][0]["value_localized"] = 22.0
async_fire_time_changed(hass)
await hass.async_block_till_done()
entity_id = "sensor.oven_core_temperature"
assert hass.states.get(entity_id) is not None
assert hass.states.get(entity_id).state == "22.0"
# reload device when turned off, reporting the invalid value
mock_miele_client.get_devices.return_value = await async_load_json_object_fixture(
hass, "oven.json", DOMAIN
)
# unload config entry and reload to make sure that the entity is still provided
await hass.config_entries.async_unload(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == "unavailable"
await hass.config_entries.async_reload(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == "unknown"
@pytest.mark.parametrize("load_device_file", ["vacuum_device.json"])
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_vacuum_sensor_states(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: None,
) -> None:
"""Test robot vacuum cleaner sensor state."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("load_device_file", ["fan_devices.json"])
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_fan_hob_sensor_states(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: None,
) -> None:
"""Test robot fan / hob sensor state."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("load_device_file", ["coffee_system.json"])
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_coffee_system_sensor_states(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: None,
) -> None:
"""Test coffee system sensor state."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("load_device_file", ["laundry.json"])
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
async def test_laundry_wash_scenario(
hass: HomeAssistant,
mock_miele_client: MagicMock,
setup_platform: None,
mock_config_entry: MockConfigEntry,
device_fixture: MieleDevices,
freezer: FrozenDateTimeFactory,
) -> None:
"""Parametrized test for verifying time sensors for wahsing machine devices when API glitches at program end."""
step = 0
# Initial state when the washing machine is off
check_sensor_state(hass, "sensor.washing_machine", "off", step)
check_sensor_state(hass, "sensor.washing_machine_program", "no_program", step)
check_sensor_state(
hass, "sensor.washing_machine_program_phase", "not_running", step
)
check_sensor_state(
hass, "sensor.washing_machine_target_temperature", "unknown", step
)
check_sensor_state(hass, "sensor.washing_machine_spin_speed", "unknown", step)
# OFF -> remaining forced to unknown
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)
# consumption sensors have to report "unknown" when the device is not working
check_sensor_state(
hass, "sensor.washing_machine_energy_consumption", "unknown", step
)
check_sensor_state(
hass, "sensor.washing_machine_water_consumption", "unknown", step
)
# Simulate program started
device_fixture["DummyWasher"]["state"]["status"]["value_raw"] = 5
device_fixture["DummyWasher"]["state"]["status"]["value_localized"] = "In use"
device_fixture["DummyWasher"]["state"]["ProgramID"]["value_raw"] = 3
device_fixture["DummyWasher"]["state"]["ProgramID"]["value_localized"] = (
"Minimum iron"
)
device_fixture["DummyWasher"]["state"]["programPhase"]["value_raw"] = 260
device_fixture["DummyWasher"]["state"]["programPhase"]["value_localized"] = (
"Main wash"
)
device_fixture["DummyWasher"]["state"]["remainingTime"][0] = 1
device_fixture["DummyWasher"]["state"]["remainingTime"][1] = 45
device_fixture["DummyWasher"]["state"]["targetTemperature"][0]["value_raw"] = 3000
device_fixture["DummyWasher"]["state"]["targetTemperature"][0][
"value_localized"
] = 30.0
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 0
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 12
device_fixture["DummyWasher"]["state"]["spinningSpeed"]["value_raw"] = 1200
device_fixture["DummyWasher"]["state"]["spinningSpeed"]["value_localized"] = "1200"
device_fixture["DummyWasher"]["state"]["ecoFeedback"] = {
"currentEnergyConsumption": {
"value": 0.9,
"unit": "kWh",
},
"currentWaterConsumption": {
"value": 52,
"unit": "l",
},
}
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# at this point, appliance is working, but it started reporting a value from last cycle, so it is forced to 0
check_sensor_state(hass, "sensor.washing_machine_energy_consumption", "0", step)
check_sensor_state(hass, "sensor.washing_machine_water_consumption", "0", step)
# intermediate step, only to report new consumption values
device_fixture["DummyWasher"]["state"]["ecoFeedback"] = {
"currentEnergyConsumption": {
"value": 0.0,
"unit": "kWh",
},
"currentWaterConsumption": {
"value": 0,
"unit": "l",
},
}
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
step += 1
check_sensor_state(hass, "sensor.washing_machine", "in_use", step)
check_sensor_state(hass, "sensor.washing_machine_program", "minimum_iron", step)
check_sensor_state(hass, "sensor.washing_machine_program_phase", "main_wash", step)
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_energy_consumption", "0.0", step)
check_sensor_state(hass, "sensor.washing_machine_water_consumption", "0", step)
# intermediate step, only to report new consumption values
device_fixture["DummyWasher"]["state"]["ecoFeedback"] = {
"currentEnergyConsumption": {
"value": 0.1,
"unit": "kWh",
},
"currentWaterConsumption": {
"value": 7,
"unit": "l",
},
}
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# at this point, it starts reporting value from API
check_sensor_state(hass, "sensor.washing_machine_energy_consumption", "0.1", step)
check_sensor_state(hass, "sensor.washing_machine_water_consumption", "7", step)
# Simulate rinse hold phase
device_fixture["DummyWasher"]["state"]["status"]["value_raw"] = 11
device_fixture["DummyWasher"]["state"]["status"]["value_localized"] = "Rinse hold"
device_fixture["DummyWasher"]["state"]["programPhase"]["value_raw"] = 262
device_fixture["DummyWasher"]["state"]["programPhase"]["value_localized"] = (
"Rinse hold"
)
device_fixture["DummyWasher"]["state"]["remainingTime"][0] = 0
device_fixture["DummyWasher"]["state"]["remainingTime"][1] = 8
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 1
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 49
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
step += 1
check_sensor_state(hass, "sensor.washing_machine", "rinse_hold", step)
check_sensor_state(hass, "sensor.washing_machine_program", "minimum_iron", step)
check_sensor_state(hass, "sensor.washing_machine_program_phase", "rinse_hold", step)
check_sensor_state(hass, "sensor.washing_machine_target_temperature", "30.0", step)
check_sensor_state(hass, "sensor.washing_machine_spin_speed", "1200", step)
# 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)
# Simulate program ended
device_fixture["DummyWasher"]["state"]["status"]["value_raw"] = 7
device_fixture["DummyWasher"]["state"]["status"]["value_localized"] = "Finished"
device_fixture["DummyWasher"]["state"]["programPhase"]["value_raw"] = 267
device_fixture["DummyWasher"]["state"]["programPhase"]["value_localized"] = (
"Anti-crease"
)
device_fixture["DummyWasher"]["state"]["remainingTime"][0] = 0
device_fixture["DummyWasher"]["state"]["remainingTime"][1] = 0
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 0
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 0
device_fixture["DummyWasher"]["state"]["ecoFeedback"] = None
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
step += 1
check_sensor_state(hass, "sensor.washing_machine", "program_ended", step)
check_sensor_state(hass, "sensor.washing_machine_program", "minimum_iron", step)
check_sensor_state(
hass, "sensor.washing_machine_program_phase", "anti_crease", step
)
check_sensor_state(hass, "sensor.washing_machine_target_temperature", "30.0", step)
check_sensor_state(hass, "sensor.washing_machine_spin_speed", "1200", step)
# PROGRAM_ENDED -> remaining time forced to 0
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)
# 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)
# Simulate when door is opened after program ended
device_fixture["DummyWasher"]["state"]["status"]["value_raw"] = 3
device_fixture["DummyWasher"]["state"]["status"]["value_localized"] = (
"Programme selected"
)
device_fixture["DummyWasher"]["state"]["programPhase"]["value_raw"] = 256
device_fixture["DummyWasher"]["state"]["programPhase"]["value_localized"] = ""
device_fixture["DummyWasher"]["state"]["targetTemperature"][0]["value_raw"] = 4000
device_fixture["DummyWasher"]["state"]["targetTemperature"][0][
"value_localized"
] = 40.0
device_fixture["DummyWasher"]["state"]["remainingTime"][0] = 1
device_fixture["DummyWasher"]["state"]["remainingTime"][1] = 59
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 0
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 0
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
step += 1
check_sensor_state(hass, "sensor.washing_machine", "programmed", step)
check_sensor_state(hass, "sensor.washing_machine_program", "minimum_iron", step)
check_sensor_state(
hass, "sensor.washing_machine_program_phase", "not_running", step
)
check_sensor_state(hass, "sensor.washing_machine_target_temperature", "40.0", step)
check_sensor_state(hass, "sensor.washing_machine_spin_speed", "1200", step)
# 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)
@pytest.mark.parametrize("load_device_file", ["laundry.json"])
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
async def test_laundry_dry_scenario(
hass: HomeAssistant,
mock_miele_client: MagicMock,
setup_platform: None,
mock_config_entry: MockConfigEntry,
device_fixture: MieleDevices,
freezer: FrozenDateTimeFactory,
) -> None:
"""Parametrized test for verifying time sensors for tumble dryer devices when API reports time value from last cycle, when device is off."""
step = 0
# Initial state when the washing machine is off
check_sensor_state(hass, "sensor.tumble_dryer", "off", step)
check_sensor_state(hass, "sensor.tumble_dryer_program", "no_program", step)
check_sensor_state(hass, "sensor.tumble_dryer_program_phase", "not_running", step)
check_sensor_state(hass, "sensor.tumble_dryer_drying_step", "unknown", step)
# 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)
# Simulate program started
device_fixture["DummyDryer"]["state"]["status"]["value_raw"] = 5
device_fixture["DummyDryer"]["state"]["status"]["value_localized"] = "In use"
device_fixture["DummyDryer"]["state"]["ProgramID"]["value_raw"] = 3
device_fixture["DummyDryer"]["state"]["ProgramID"]["value_localized"] = (
"Minimum iron"
)
device_fixture["DummyDryer"]["state"]["programPhase"]["value_raw"] = 514
device_fixture["DummyDryer"]["state"]["programPhase"]["value_localized"] = "Drying"
device_fixture["DummyDryer"]["state"]["remainingTime"][0] = 0
device_fixture["DummyDryer"]["state"]["remainingTime"][1] = 49
device_fixture["DummyDryer"]["state"]["elapsedTime"][0] = 0
device_fixture["DummyDryer"]["state"]["elapsedTime"][1] = 20
device_fixture["DummyDryer"]["state"]["dryingStep"]["value_raw"] = 2
device_fixture["DummyDryer"]["state"]["dryingStep"]["value_localized"] = "Normal"
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
step += 1
check_sensor_state(hass, "sensor.tumble_dryer", "in_use", step)
check_sensor_state(hass, "sensor.tumble_dryer_program", "minimum_iron", step)
check_sensor_state(hass, "sensor.tumble_dryer_program_phase", "drying", step)
check_sensor_state(hass, "sensor.tumble_dryer_drying_step", "normal", step)
# 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)
# Simulate program end
device_fixture["DummyDryer"]["state"]["status"]["value_raw"] = 7
device_fixture["DummyDryer"]["state"]["status"]["value_localized"] = "Finished"
device_fixture["DummyDryer"]["state"]["programPhase"]["value_raw"] = 522
device_fixture["DummyDryer"]["state"]["programPhase"]["value_localized"] = (
"Finished"
)
device_fixture["DummyDryer"]["state"]["remainingTime"][0] = 0
device_fixture["DummyDryer"]["state"]["remainingTime"][1] = 0
device_fixture["DummyDryer"]["state"]["elapsedTime"][0] = 1
device_fixture["DummyDryer"]["state"]["elapsedTime"][1] = 18
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
step += 1
check_sensor_state(hass, "sensor.tumble_dryer", "program_ended", step)
check_sensor_state(hass, "sensor.tumble_dryer_program", "minimum_iron", step)
check_sensor_state(hass, "sensor.tumble_dryer_program_phase", "finished", step)
check_sensor_state(hass, "sensor.tumble_dryer_drying_step", "normal", step)
# PROGRAM_ENDED -> remaining time forced to 0
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)
@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(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_miele_client: MagicMock,
setup_platform: None,
device_fixture: MieleDevices,
freezer: FrozenDateTimeFactory,
restore_state,
) -> None:
"""Test that elapsed time returns the restored value when program ended."""
entity_id = "sensor.washing_machine_elapsed_time"
# Simulate program started
device_fixture["DummyWasher"]["state"]["status"]["value_raw"] = 5
device_fixture["DummyWasher"]["state"]["status"]["value_localized"] = "In use"
device_fixture["DummyWasher"]["state"]["ProgramID"]["value_raw"] = 3
device_fixture["DummyWasher"]["state"]["ProgramID"]["value_localized"] = (
"Minimum iron"
)
device_fixture["DummyWasher"]["state"]["programPhase"]["value_raw"] = 260
device_fixture["DummyWasher"]["state"]["programPhase"]["value_localized"] = (
"Main wash"
)
device_fixture["DummyWasher"]["state"]["remainingTime"][0] = 1
device_fixture["DummyWasher"]["state"]["remainingTime"][1] = 45
device_fixture["DummyWasher"]["state"]["targetTemperature"][0]["value_raw"] = 3000
device_fixture["DummyWasher"]["state"]["targetTemperature"][0][
"value_localized"
] = 30.0
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 0
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 12
device_fixture["DummyWasher"]["state"]["spinningSpeed"]["value_raw"] = 1200
device_fixture["DummyWasher"]["state"]["spinningSpeed"]["value_localized"] = "1200"
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == "12"
# Simulate program ended
device_fixture["DummyWasher"]["state"]["status"]["value_raw"] = 7
device_fixture["DummyWasher"]["state"]["status"]["value_localized"] = "Finished"
device_fixture["DummyWasher"]["state"]["programPhase"]["value_raw"] = 267
device_fixture["DummyWasher"]["state"]["programPhase"]["value_localized"] = (
"Anti-crease"
)
device_fixture["DummyWasher"]["state"]["remainingTime"][0] = 0
device_fixture["DummyWasher"]["state"]["remainingTime"][1] = 0
device_fixture["DummyWasher"]["state"]["elapsedTime"][0] = 0
device_fixture["DummyWasher"]["state"]["elapsedTime"][1] = 0
freezer.tick(timedelta(seconds=130))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# unload config entry and reload to make sure that the state is restored
await hass.config_entries.async_unload(mock_config_entry.entry_id)
await hass.async_block_till_done()
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()
# check that elapsed time is the one restored and not the value reported by API (0)
state = hass.states.get(entity_id)
assert state is not None
assert state.state == "12"