Bump whirlpool to 0.21.1 (#147611)

This commit is contained in:
Abílio Costa 2025-06-27 09:33:49 +01:00 committed by GitHub
parent 8cc4105984
commit 78c2405e61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 320 additions and 176 deletions

View File

@ -42,14 +42,21 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Config flow entry for Whirlpool binary sensors."""
entities: list = []
appliances_manager = config_entry.runtime_data
for washer_dryer in appliances_manager.washer_dryers:
entities.extend(
WhirlpoolBinarySensor(washer_dryer, description)
for description in WASHER_DRYER_SENSORS
)
async_add_entities(entities)
washer_binary_sensors = [
WhirlpoolBinarySensor(washer, description)
for washer in appliances_manager.washers
for description in WASHER_DRYER_SENSORS
]
dryer_binary_sensors = [
WhirlpoolBinarySensor(dryer, description)
for dryer in appliances_manager.dryers
for description in WASHER_DRYER_SENSORS
]
async_add_entities([*washer_binary_sensors, *dryer_binary_sensors])
class WhirlpoolBinarySensor(WhirlpoolEntity, BinarySensorEntity):

View File

@ -70,7 +70,11 @@ async def authenticate(
appliances_manager = AppliancesManager(backend_selector, auth, session)
await appliances_manager.fetch_appliances()
if not appliances_manager.aircons and not appliances_manager.washer_dryers:
if (
not appliances_manager.aircons
and not appliances_manager.washers
and not appliances_manager.dryers
):
return "no_appliances"
return None

View File

@ -37,9 +37,13 @@ async def async_get_config_entry_diagnostics(
appliances_manager = config_entry.runtime_data
diagnostics_data = {
"washer_dryers": {
wd.name: get_appliance_diagnostics(wd)
for wd in appliances_manager.washer_dryers
"washers": {
washer.name: get_appliance_diagnostics(washer)
for washer in appliances_manager.washers
},
"dryers": {
dryer.name: get_appliance_diagnostics(dryer)
for dryer in appliances_manager.dryers
},
"aircons": {
ac.name: get_appliance_diagnostics(ac) for ac in appliances_manager.aircons

View File

@ -8,5 +8,5 @@
"iot_class": "cloud_push",
"loggers": ["whirlpool"],
"quality_scale": "bronze",
"requirements": ["whirlpool-sixth-sense==0.20.0"]
"requirements": ["whirlpool-sixth-sense==0.21.1"]
}

View File

@ -1,12 +1,14 @@
"""The Washer/Dryer Sensor for Whirlpool Appliances."""
from abc import ABC, abstractmethod
from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import override
from whirlpool.appliance import Appliance
from whirlpool.washerdryer import MachineState, WasherDryer
from whirlpool.dryer import Dryer, MachineState as DryerMachineState
from whirlpool.washer import MachineState as WasherMachineState, Washer
from homeassistant.components.sensor import (
RestoreSensor,
@ -33,26 +35,49 @@ WASHER_TANK_FILL = {
5: "active",
}
WASHER_DRYER_MACHINE_STATE = {
MachineState.Standby: "standby",
MachineState.Setting: "setting",
MachineState.DelayCountdownMode: "delay_countdown",
MachineState.DelayPause: "delay_paused",
MachineState.SmartDelay: "smart_delay",
MachineState.SmartGridPause: "smart_grid_pause",
MachineState.Pause: "pause",
MachineState.RunningMainCycle: "running_maincycle",
MachineState.RunningPostCycle: "running_postcycle",
MachineState.Exceptions: "exception",
MachineState.Complete: "complete",
MachineState.PowerFailure: "power_failure",
MachineState.ServiceDiagnostic: "service_diagnostic_mode",
MachineState.FactoryDiagnostic: "factory_diagnostic_mode",
MachineState.LifeTest: "life_test",
MachineState.CustomerFocusMode: "customer_focus_mode",
MachineState.DemoMode: "demo_mode",
MachineState.HardStopOrError: "hard_stop_or_error",
MachineState.SystemInit: "system_initialize",
WASHER_MACHINE_STATE = {
WasherMachineState.Standby: "standby",
WasherMachineState.Setting: "setting",
WasherMachineState.DelayCountdownMode: "delay_countdown",
WasherMachineState.DelayPause: "delay_paused",
WasherMachineState.SmartDelay: "smart_delay",
WasherMachineState.SmartGridPause: "smart_grid_pause",
WasherMachineState.Pause: "pause",
WasherMachineState.RunningMainCycle: "running_maincycle",
WasherMachineState.RunningPostCycle: "running_postcycle",
WasherMachineState.Exceptions: "exception",
WasherMachineState.Complete: "complete",
WasherMachineState.PowerFailure: "power_failure",
WasherMachineState.ServiceDiagnostic: "service_diagnostic_mode",
WasherMachineState.FactoryDiagnostic: "factory_diagnostic_mode",
WasherMachineState.LifeTest: "life_test",
WasherMachineState.CustomerFocusMode: "customer_focus_mode",
WasherMachineState.DemoMode: "demo_mode",
WasherMachineState.HardStopOrError: "hard_stop_or_error",
WasherMachineState.SystemInit: "system_initialize",
}
DRYER_MACHINE_STATE = {
DryerMachineState.Standby: "standby",
DryerMachineState.Setting: "setting",
DryerMachineState.DelayCountdownMode: "delay_countdown",
DryerMachineState.DelayPause: "delay_paused",
DryerMachineState.SmartDelay: "smart_delay",
DryerMachineState.SmartGridPause: "smart_grid_pause",
DryerMachineState.Pause: "pause",
DryerMachineState.RunningMainCycle: "running_maincycle",
DryerMachineState.RunningPostCycle: "running_postcycle",
DryerMachineState.Exceptions: "exception",
DryerMachineState.Complete: "complete",
DryerMachineState.PowerFailure: "power_failure",
DryerMachineState.ServiceDiagnostic: "service_diagnostic_mode",
DryerMachineState.FactoryDiagnostic: "factory_diagnostic_mode",
DryerMachineState.LifeTest: "life_test",
DryerMachineState.CustomerFocusMode: "customer_focus_mode",
DryerMachineState.DemoMode: "demo_mode",
DryerMachineState.HardStopOrError: "hard_stop_or_error",
DryerMachineState.SystemInit: "system_initialize",
DryerMachineState.Cancelled: "cancelled",
}
STATE_CYCLE_FILLING = "cycle_filling"
@ -64,29 +89,44 @@ STATE_CYCLE_WASHING = "cycle_washing"
STATE_DOOR_OPEN = "door_open"
def washer_dryer_state(washer_dryer: WasherDryer) -> str | None:
"""Determine correct states for a washer/dryer."""
def washer_state(washer: Washer) -> str | None:
"""Determine correct states for a washer."""
if washer_dryer.get_door_open():
if washer.get_door_open():
return STATE_DOOR_OPEN
machine_state = washer_dryer.get_machine_state()
machine_state = washer.get_machine_state()
if machine_state == MachineState.RunningMainCycle:
if washer_dryer.get_cycle_status_filling():
if machine_state == WasherMachineState.RunningMainCycle:
if washer.get_cycle_status_filling():
return STATE_CYCLE_FILLING
if washer_dryer.get_cycle_status_rinsing():
if washer.get_cycle_status_rinsing():
return STATE_CYCLE_RINSING
if washer_dryer.get_cycle_status_sensing():
if washer.get_cycle_status_sensing():
return STATE_CYCLE_SENSING
if washer_dryer.get_cycle_status_soaking():
if washer.get_cycle_status_soaking():
return STATE_CYCLE_SOAKING
if washer_dryer.get_cycle_status_spinning():
if washer.get_cycle_status_spinning():
return STATE_CYCLE_SPINNING
if washer_dryer.get_cycle_status_washing():
if washer.get_cycle_status_washing():
return STATE_CYCLE_WASHING
return WASHER_DRYER_MACHINE_STATE.get(machine_state)
return WASHER_MACHINE_STATE.get(machine_state)
def dryer_state(dryer: Dryer) -> str | None:
"""Determine correct states for a dryer."""
if dryer.get_door_open():
return STATE_DOOR_OPEN
machine_state = dryer.get_machine_state()
if machine_state == DryerMachineState.RunningMainCycle:
if dryer.get_cycle_status_sensing():
return STATE_CYCLE_SENSING
return DRYER_MACHINE_STATE.get(machine_state)
@dataclass(frozen=True, kw_only=True)
@ -96,8 +136,8 @@ class WhirlpoolSensorEntityDescription(SensorEntityDescription):
value_fn: Callable[[Appliance], str | None]
WASHER_DRYER_STATE_OPTIONS = [
*WASHER_DRYER_MACHINE_STATE.values(),
WASHER_STATE_OPTIONS = [
*WASHER_MACHINE_STATE.values(),
STATE_CYCLE_FILLING,
STATE_CYCLE_RINSING,
STATE_CYCLE_SENSING,
@ -107,13 +147,19 @@ WASHER_DRYER_STATE_OPTIONS = [
STATE_DOOR_OPEN,
]
DRYER_STATE_OPTIONS = [
*DRYER_MACHINE_STATE.values(),
STATE_CYCLE_SENSING,
STATE_DOOR_OPEN,
]
WASHER_SENSORS: tuple[WhirlpoolSensorEntityDescription, ...] = (
WhirlpoolSensorEntityDescription(
key="state",
translation_key="washer_state",
device_class=SensorDeviceClass.ENUM,
options=WASHER_DRYER_STATE_OPTIONS,
value_fn=washer_dryer_state,
options=WASHER_STATE_OPTIONS,
value_fn=washer_state,
),
WhirlpoolSensorEntityDescription(
key="DispenseLevel",
@ -130,8 +176,8 @@ DRYER_SENSORS: tuple[WhirlpoolSensorEntityDescription, ...] = (
key="state",
translation_key="dryer_state",
device_class=SensorDeviceClass.ENUM,
options=WASHER_DRYER_STATE_OPTIONS,
value_fn=washer_dryer_state,
options=DRYER_STATE_OPTIONS,
value_fn=dryer_state,
),
)
@ -151,24 +197,40 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Config flow entry for Whirlpool sensors."""
entities: list = []
appliances_manager = config_entry.runtime_data
for washer_dryer in appliances_manager.washer_dryers:
sensor_descriptions = (
DRYER_SENSORS
if "dryer" in washer_dryer.appliance_info.data_model.lower()
else WASHER_SENSORS
)
entities.extend(
WhirlpoolSensor(washer_dryer, description)
for description in sensor_descriptions
)
entities.extend(
WasherDryerTimeSensor(washer_dryer, description)
for description in WASHER_DRYER_TIME_SENSORS
)
async_add_entities(entities)
washer_sensors = [
WhirlpoolSensor(washer, description)
for washer in appliances_manager.washers
for description in WASHER_SENSORS
]
washer_time_sensors = [
WasherTimeSensor(washer, description)
for washer in appliances_manager.washers
for description in WASHER_DRYER_TIME_SENSORS
]
dryer_sensors = [
WhirlpoolSensor(dryer, description)
for dryer in appliances_manager.dryers
for description in DRYER_SENSORS
]
dryer_time_sensors = [
DryerTimeSensor(dryer, description)
for dryer in appliances_manager.dryers
for description in WASHER_DRYER_TIME_SENSORS
]
async_add_entities(
[
*washer_sensors,
*washer_time_sensors,
*dryer_sensors,
*dryer_time_sensors,
]
)
class WhirlpoolSensor(WhirlpoolEntity, SensorEntity):
@ -187,22 +249,30 @@ class WhirlpoolSensor(WhirlpoolEntity, SensorEntity):
return self.entity_description.value_fn(self._appliance)
class WasherDryerTimeSensor(WhirlpoolEntity, RestoreSensor):
"""A timestamp class for the Whirlpool washer/dryer."""
class WasherDryerTimeSensorBase(WhirlpoolEntity, RestoreSensor, ABC):
"""Abstract base class for Whirlpool washer/dryer time sensors."""
_attr_should_poll = True
_appliance: Washer | Dryer
def __init__(
self, washer_dryer: WasherDryer, description: SensorEntityDescription
self, appliance: Washer | Dryer, description: SensorEntityDescription
) -> None:
"""Initialize the washer sensor."""
super().__init__(washer_dryer, unique_id_suffix=f"-{description.key}")
"""Initialize the washer/dryer sensor."""
super().__init__(appliance, unique_id_suffix=f"-{description.key}")
self.entity_description = description
self._wd = washer_dryer
self._running: bool | None = None
self._value: datetime | None = None
@abstractmethod
def _is_machine_state_finished(self) -> bool:
"""Return true if the machine is in a finished state."""
@abstractmethod
def _is_machine_state_running(self) -> bool:
"""Return true if the machine is in a running state."""
async def async_added_to_hass(self) -> None:
"""Register attribute updates callback."""
if restored_data := await self.async_get_last_sensor_data():
@ -212,28 +282,62 @@ class WasherDryerTimeSensor(WhirlpoolEntity, RestoreSensor):
async def async_update(self) -> None:
"""Update status of Whirlpool."""
await self._wd.fetch_data()
await self._appliance.fetch_data()
@override
@property
def native_value(self) -> datetime | None:
"""Calculate the time stamp for completion."""
machine_state = self._wd.get_machine_state()
now = utcnow()
if (
machine_state.value
in {MachineState.Complete.value, MachineState.Standby.value}
and self._running
):
if self._is_machine_state_finished() and self._running:
self._running = False
self._value = now
if machine_state is MachineState.RunningMainCycle:
if self._is_machine_state_running():
self._running = True
new_timestamp = now + timedelta(seconds=self._wd.get_time_remaining())
new_timestamp = now + timedelta(
seconds=self._appliance.get_time_remaining()
)
if self._value is None or (
isinstance(self._value, datetime)
and abs(new_timestamp - self._value) > timedelta(seconds=60)
):
self._value = new_timestamp
return self._value
class WasherTimeSensor(WasherDryerTimeSensorBase):
"""A timestamp class for Whirlpool washers."""
_appliance: Washer
def _is_machine_state_finished(self) -> bool:
"""Return true if the machine is in a finished state."""
return self._appliance.get_machine_state() in {
WasherMachineState.Complete,
WasherMachineState.Standby,
}
def _is_machine_state_running(self) -> bool:
"""Return true if the machine is in a running state."""
return (
self._appliance.get_machine_state() is WasherMachineState.RunningMainCycle
)
class DryerTimeSensor(WasherDryerTimeSensorBase):
"""A timestamp class for Whirlpool dryers."""
_appliance: Dryer
def _is_machine_state_finished(self) -> bool:
"""Return true if the machine is in a finished state."""
return self._appliance.get_machine_state() in {
DryerMachineState.Complete,
DryerMachineState.Standby,
}
def _is_machine_state_running(self) -> bool:
"""Return true if the machine is in a running state."""
return self._appliance.get_machine_state() is DryerMachineState.RunningMainCycle

2
requirements_all.txt generated
View File

@ -3102,7 +3102,7 @@ webmin-xmlrpc==0.0.2
weheat==2025.6.10
# homeassistant.components.whirlpool
whirlpool-sixth-sense==0.20.0
whirlpool-sixth-sense==0.21.1
# homeassistant.components.whois
whois==0.9.27

View File

@ -2555,7 +2555,7 @@ webmin-xmlrpc==0.0.2
weheat==2025.6.10
# homeassistant.components.whirlpool
whirlpool-sixth-sense==0.20.0
whirlpool-sixth-sense==0.21.1
# homeassistant.components.whois
whois==0.9.27

View File

@ -4,7 +4,7 @@ from unittest import mock
from unittest.mock import Mock
import pytest
from whirlpool import aircon, appliancesmanager, auth, washerdryer
from whirlpool import aircon, appliancesmanager, auth, dryer, washer
from whirlpool.backendselector import Brand, Region
from .const import MOCK_SAID1, MOCK_SAID2
@ -66,10 +66,8 @@ def fixture_mock_appliances_manager_api(
mock_aircon1_api,
mock_aircon2_api,
]
mock_appliances_manager.return_value.washer_dryers = [
mock_washer_api,
mock_dryer_api,
]
mock_appliances_manager.return_value.washers = [mock_washer_api]
mock_appliances_manager.return_value.dryers = [mock_dryer_api]
yield mock_appliances_manager
@ -123,15 +121,13 @@ def fixture_mock_aircon2_api():
@pytest.fixture
def mock_washer_api():
"""Get a mock of a washer."""
mock_washer = Mock(spec=washerdryer.WasherDryer, said="said_washer")
mock_washer = Mock(spec=washer.Washer, said="said_washer")
mock_washer.name = "Washer"
mock_washer.appliance_info = Mock(
data_model="washer", category="washer_dryer", model_number="12345"
)
mock_washer.get_online.return_value = True
mock_washer.get_machine_state.return_value = (
washerdryer.MachineState.RunningMainCycle
)
mock_washer.get_machine_state.return_value = washer.MachineState.RunningMainCycle
mock_washer.get_door_open.return_value = False
mock_washer.get_dispense_1_level.return_value = 3
mock_washer.get_time_remaining.return_value = 3540
@ -148,21 +144,14 @@ def mock_washer_api():
@pytest.fixture
def mock_dryer_api():
"""Get a mock of a dryer."""
mock_dryer = mock.Mock(spec=washerdryer.WasherDryer, said="said_dryer")
mock_dryer = mock.Mock(spec=dryer.Dryer, said="said_dryer")
mock_dryer.name = "Dryer"
mock_dryer.appliance_info = Mock(
data_model="dryer", category="washer_dryer", model_number="12345"
)
mock_dryer.get_online.return_value = True
mock_dryer.get_machine_state.return_value = (
washerdryer.MachineState.RunningMainCycle
)
mock_dryer.get_machine_state.return_value = dryer.MachineState.RunningMainCycle
mock_dryer.get_door_open.return_value = False
mock_dryer.get_time_remaining.return_value = 3540
mock_dryer.get_cycle_status_filling.return_value = False
mock_dryer.get_cycle_status_rinsing.return_value = False
mock_dryer.get_cycle_status_sensing.return_value = False
mock_dryer.get_cycle_status_soaking.return_value = False
mock_dryer.get_cycle_status_spinning.return_value = False
mock_dryer.get_cycle_status_washing.return_value = False
return mock_dryer

View File

@ -14,14 +14,16 @@
'model_number': '12345',
}),
}),
'ovens': dict({
}),
'washer_dryers': dict({
'dryers': dict({
'Dryer': dict({
'category': 'washer_dryer',
'data_model': 'dryer',
'model_number': '12345',
}),
}),
'ovens': dict({
}),
'washers': dict({
'Washer': dict({
'category': 'washer_dryer',
'data_model': 'washer',

View File

@ -75,12 +75,8 @@
'demo_mode',
'hard_stop_or_error',
'system_initialize',
'cycle_filling',
'cycle_rinsing',
'cancelled',
'cycle_sensing',
'cycle_soaking',
'cycle_spinning',
'cycle_washing',
'door_open',
]),
}),
@ -138,12 +134,8 @@
'demo_mode',
'hard_stop_or_error',
'system_initialize',
'cycle_filling',
'cycle_rinsing',
'cancelled',
'cycle_sensing',
'cycle_soaking',
'cycle_spinning',
'cycle_washing',
'door_open',
]),
}),

View File

@ -208,7 +208,8 @@ async def test_no_appliances_flow(
original_aircons = mock_appliances_manager_api.return_value.aircons
mock_appliances_manager_api.return_value.aircons = []
mock_appliances_manager_api.return_value.washer_dryers = []
mock_appliances_manager_api.return_value.washers = []
mock_appliances_manager_api.return_value.dryers = []
result = await hass.config_entries.flow.async_configure(
result["flow_id"], CONFIG_INPUT | {CONF_REGION: region[0], CONF_BRAND: brand[0]}
)

View File

@ -80,7 +80,8 @@ async def test_setup_no_appliances(
) -> None:
"""Test setup when there are no appliances available."""
mock_appliances_manager_api.return_value.aircons = []
mock_appliances_manager_api.return_value.washer_dryers = []
mock_appliances_manager_api.return_value.washers = []
mock_appliances_manager_api.return_value.dryers = []
await init_integration(hass)
assert len(hass.states.async_all()) == 0

View File

@ -5,7 +5,8 @@ from datetime import UTC, datetime, timedelta
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
from whirlpool.washerdryer import MachineState
from whirlpool.dryer import MachineState as DryerMachineState
from whirlpool.washer import MachineState as WasherMachineState
from homeassistant.components.whirlpool.sensor import SCAN_INTERVAL
from homeassistant.const import STATE_UNKNOWN, Platform
@ -63,7 +64,7 @@ async def test_washer_dryer_time_sensor(
)
mock_instance = request.getfixturevalue(mock_fixture)
mock_instance.get_machine_state.return_value = MachineState.Pause
mock_instance.get_machine_state.return_value = WasherMachineState.Pause
await init_integration(hass)
# Test restored state.
@ -77,7 +78,15 @@ async def test_washer_dryer_time_sensor(
assert state.state == restored_datetime.isoformat()
# Test new time when machine starts a cycle.
mock_instance.get_machine_state.return_value = MachineState.RunningMainCycle
if "washer" in entity_id:
mock_instance.get_machine_state.return_value = (
WasherMachineState.RunningMainCycle
)
else:
mock_instance.get_machine_state.return_value = (
DryerMachineState.RunningMainCycle
)
mock_instance.get_time_remaining.return_value = 60
await trigger_attr_callback(hass, mock_instance)
@ -127,7 +136,10 @@ async def test_washer_dryer_time_sensor_no_restore(
now = utcnow()
mock_instance = request.getfixturevalue(mock_fixture)
mock_instance.get_machine_state.return_value = MachineState.Pause
if "washer" in entity_id:
mock_instance.get_machine_state.return_value = WasherMachineState.Pause
else:
mock_instance.get_machine_state.return_value = DryerMachineState.Pause
await init_integration(hass)
state = hass.states.get(entity_id)
@ -140,7 +152,14 @@ async def test_washer_dryer_time_sensor_no_restore(
assert state.state == STATE_UNKNOWN
# Test new time when machine starts a cycle.
mock_instance.get_machine_state.return_value = MachineState.RunningMainCycle
if "washer" in entity_id:
mock_instance.get_machine_state.return_value = (
WasherMachineState.RunningMainCycle
)
else:
mock_instance.get_machine_state.return_value = (
DryerMachineState.RunningMainCycle
)
mock_instance.get_time_remaining.return_value = 60
await trigger_attr_callback(hass, mock_instance)
@ -149,63 +168,87 @@ async def test_washer_dryer_time_sensor_no_restore(
assert state.state == expected_time
@pytest.mark.parametrize(
("entity_id", "mock_fixture"),
[
("sensor.washer_state", "mock_washer_api"),
("sensor.dryer_state", "mock_dryer_api"),
],
)
@pytest.mark.parametrize(
("machine_state", "expected_state"),
[
(MachineState.Standby, "standby"),
(MachineState.Setting, "setting"),
(MachineState.DelayCountdownMode, "delay_countdown"),
(MachineState.DelayPause, "delay_paused"),
(MachineState.SmartDelay, "smart_delay"),
(MachineState.SmartGridPause, "smart_grid_pause"),
(MachineState.Pause, "pause"),
(MachineState.RunningMainCycle, "running_maincycle"),
(MachineState.RunningPostCycle, "running_postcycle"),
(MachineState.Exceptions, "exception"),
(MachineState.Complete, "complete"),
(MachineState.PowerFailure, "power_failure"),
(MachineState.ServiceDiagnostic, "service_diagnostic_mode"),
(MachineState.FactoryDiagnostic, "factory_diagnostic_mode"),
(MachineState.LifeTest, "life_test"),
(MachineState.CustomerFocusMode, "customer_focus_mode"),
(MachineState.DemoMode, "demo_mode"),
(MachineState.HardStopOrError, "hard_stop_or_error"),
(MachineState.SystemInit, "system_initialize"),
(WasherMachineState.Standby, "standby"),
(WasherMachineState.Setting, "setting"),
(WasherMachineState.DelayCountdownMode, "delay_countdown"),
(WasherMachineState.DelayPause, "delay_paused"),
(WasherMachineState.SmartDelay, "smart_delay"),
(WasherMachineState.SmartGridPause, "smart_grid_pause"),
(WasherMachineState.Pause, "pause"),
(WasherMachineState.RunningMainCycle, "running_maincycle"),
(WasherMachineState.RunningPostCycle, "running_postcycle"),
(WasherMachineState.Exceptions, "exception"),
(WasherMachineState.Complete, "complete"),
(WasherMachineState.PowerFailure, "power_failure"),
(WasherMachineState.ServiceDiagnostic, "service_diagnostic_mode"),
(WasherMachineState.FactoryDiagnostic, "factory_diagnostic_mode"),
(WasherMachineState.LifeTest, "life_test"),
(WasherMachineState.CustomerFocusMode, "customer_focus_mode"),
(WasherMachineState.DemoMode, "demo_mode"),
(WasherMachineState.HardStopOrError, "hard_stop_or_error"),
(WasherMachineState.SystemInit, "system_initialize"),
],
)
async def test_washer_dryer_machine_states(
async def test_washer_machine_states(
hass: HomeAssistant,
entity_id: str,
mock_fixture: str,
machine_state: MachineState,
machine_state: WasherMachineState,
expected_state: str,
request: pytest.FixtureRequest,
mock_washer_api,
) -> None:
"""Test Washer/Dryer machine states."""
mock_instance = request.getfixturevalue(mock_fixture)
"""Test Washer machine states."""
await init_integration(hass)
mock_instance.get_machine_state.return_value = machine_state
await trigger_attr_callback(hass, mock_instance)
state = hass.states.get(entity_id)
mock_washer_api.get_machine_state.return_value = machine_state
await trigger_attr_callback(hass, mock_washer_api)
state = hass.states.get("sensor.washer_state")
assert state is not None
assert state.state == expected_state
@pytest.mark.parametrize(
("entity_id", "mock_fixture"),
("machine_state", "expected_state"),
[
("sensor.washer_state", "mock_washer_api"),
("sensor.dryer_state", "mock_dryer_api"),
(DryerMachineState.Standby, "standby"),
(DryerMachineState.Setting, "setting"),
(DryerMachineState.DelayCountdownMode, "delay_countdown"),
(DryerMachineState.DelayPause, "delay_paused"),
(DryerMachineState.SmartDelay, "smart_delay"),
(DryerMachineState.SmartGridPause, "smart_grid_pause"),
(DryerMachineState.Pause, "pause"),
(DryerMachineState.RunningMainCycle, "running_maincycle"),
(DryerMachineState.RunningPostCycle, "running_postcycle"),
(DryerMachineState.Exceptions, "exception"),
(DryerMachineState.Complete, "complete"),
(DryerMachineState.PowerFailure, "power_failure"),
(DryerMachineState.ServiceDiagnostic, "service_diagnostic_mode"),
(DryerMachineState.FactoryDiagnostic, "factory_diagnostic_mode"),
(DryerMachineState.LifeTest, "life_test"),
(DryerMachineState.CustomerFocusMode, "customer_focus_mode"),
(DryerMachineState.DemoMode, "demo_mode"),
(DryerMachineState.HardStopOrError, "hard_stop_or_error"),
(DryerMachineState.SystemInit, "system_initialize"),
(DryerMachineState.Cancelled, "cancelled"),
],
)
async def test_dryer_machine_states(
hass: HomeAssistant,
machine_state: DryerMachineState,
expected_state: str,
mock_dryer_api,
) -> None:
"""Test Dryer machine states."""
await init_integration(hass)
mock_dryer_api.get_machine_state.return_value = machine_state
await trigger_attr_callback(hass, mock_dryer_api)
state = hass.states.get("sensor.dryer_state")
assert state is not None
assert state.state == expected_state
@pytest.mark.parametrize(
(
"filling",
@ -225,10 +268,8 @@ async def test_washer_dryer_machine_states(
(False, False, False, False, False, True, "cycle_washing"),
],
)
async def test_washer_dryer_running_states(
async def test_washer_running_states(
hass: HomeAssistant,
entity_id: str,
mock_fixture: str,
filling: bool,
rinsing: bool,
sensing: bool,
@ -236,22 +277,21 @@ async def test_washer_dryer_running_states(
spinning: bool,
washing: bool,
expected_state: str,
request: pytest.FixtureRequest,
mock_washer_api,
) -> None:
"""Test Washer/Dryer machine states for RunningMainCycle."""
mock_instance = request.getfixturevalue(mock_fixture)
"""Test Washer machine states for RunningMainCycle."""
await init_integration(hass)
mock_instance.get_machine_state.return_value = MachineState.RunningMainCycle
mock_instance.get_cycle_status_filling.return_value = filling
mock_instance.get_cycle_status_rinsing.return_value = rinsing
mock_instance.get_cycle_status_sensing.return_value = sensing
mock_instance.get_cycle_status_soaking.return_value = soaking
mock_instance.get_cycle_status_spinning.return_value = spinning
mock_instance.get_cycle_status_washing.return_value = washing
mock_washer_api.get_machine_state.return_value = WasherMachineState.RunningMainCycle
mock_washer_api.get_cycle_status_filling.return_value = filling
mock_washer_api.get_cycle_status_rinsing.return_value = rinsing
mock_washer_api.get_cycle_status_sensing.return_value = sensing
mock_washer_api.get_cycle_status_soaking.return_value = soaking
mock_washer_api.get_cycle_status_spinning.return_value = spinning
mock_washer_api.get_cycle_status_washing.return_value = washing
await trigger_attr_callback(hass, mock_instance)
state = hass.states.get(entity_id)
await trigger_attr_callback(hass, mock_washer_api)
state = hass.states.get("sensor.washer_state")
assert state is not None
assert state.state == expected_state