Update whirlpool-sixth-sense to 0.19.1 (#139987)

This commit is contained in:
Abílio Costa 2025-03-07 12:13:35 +00:00 committed by GitHub
parent 2401d8900a
commit 82d5304b45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 137 additions and 235 deletions

View File

@ -1,6 +1,5 @@
"""The Whirlpool Appliances integration."""
from dataclasses import dataclass
import logging
from aiohttp import ClientError
@ -20,7 +19,7 @@ _LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.CLIMATE, Platform.SENSOR]
type WhirlpoolConfigEntry = ConfigEntry[WhirlpoolData]
type WhirlpoolConfigEntry = ConfigEntry[AppliancesManager]
async def async_setup_entry(hass: HomeAssistant, entry: WhirlpoolConfigEntry) -> bool:
@ -52,8 +51,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: WhirlpoolConfigEntry) ->
if not await appliances_manager.fetch_appliances():
_LOGGER.error("Cannot fetch appliances")
return False
await appliances_manager.connect()
entry.runtime_data = WhirlpoolData(appliances_manager, auth, backend_selector)
entry.runtime_data = appliances_manager
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
@ -61,13 +61,5 @@ async def async_setup_entry(hass: HomeAssistant, entry: WhirlpoolConfigEntry) ->
async def async_unload_entry(hass: HomeAssistant, entry: WhirlpoolConfigEntry) -> bool:
"""Unload a config entry."""
await entry.runtime_data.disconnect()
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
@dataclass
class WhirlpoolData:
"""Whirlpool integaration shared data."""
appliances_manager: AppliancesManager
auth: Auth
backend_selector: BackendSelector

View File

@ -5,10 +5,7 @@ from __future__ import annotations
import logging
from typing import Any
from aiohttp import ClientSession
from whirlpool.aircon import Aircon, FanSpeed as AirconFanSpeed, Mode as AirconMode
from whirlpool.auth import Auth
from whirlpool.backendselector import BackendSelector
from homeassistant.components.climate import (
ENTITY_ID_FORMAT,
@ -25,7 +22,6 @@ from homeassistant.components.climate import (
)
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@ -73,19 +69,8 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up entry."""
whirlpool_data = config_entry.runtime_data
aircons = [
AirConEntity(
hass,
ac_data["SAID"],
ac_data["NAME"],
whirlpool_data.backend_selector,
whirlpool_data.auth,
async_get_clientsession(hass),
)
for ac_data in whirlpool_data.appliances_manager.aircons
]
appliances_manager = config_entry.runtime_data
aircons = [AirConEntity(hass, aircon) for aircon in appliances_manager.aircons]
async_add_entities(aircons, True)
@ -110,36 +95,26 @@ class AirConEntity(ClimateEntity):
_attr_target_temperature_step = SUPPORTED_TARGET_TEMPERATURE_STEP
_attr_temperature_unit = UnitOfTemperature.CELSIUS
def __init__(
self,
hass: HomeAssistant,
said: str,
name: str | None,
backend_selector: BackendSelector,
auth: Auth,
session: ClientSession,
) -> None:
def __init__(self, hass: HomeAssistant, aircon: Aircon) -> None:
"""Initialize the entity."""
self._aircon = Aircon(backend_selector, auth, said, session)
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT, said, hass=hass)
self._attr_unique_id = said
self._aircon = aircon
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT, aircon.said, hass=hass)
self._attr_unique_id = aircon.said
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, said)},
name=name if name is not None else said,
identifiers={(DOMAIN, aircon.said)},
name=aircon.name if aircon.name is not None else aircon.said,
manufacturer="Whirlpool",
model="Sixth Sense",
)
async def async_added_to_hass(self) -> None:
"""Connect aircon to the cloud."""
"""Register updates callback."""
self._aircon.register_attr_callback(self.async_write_ha_state)
await self._aircon.connect()
async def async_will_remove_from_hass(self) -> None:
"""Close Whrilpool Appliance sockets before removing."""
"""Unregister updates callback."""
self._aircon.unregister_attr_callback(self.async_write_ha_state)
await self._aircon.disconnect()
@property
def available(self) -> bool:

View File

@ -4,6 +4,8 @@ from __future__ import annotations
from typing import Any
from whirlpool.appliance import Appliance
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.core import HomeAssistant
@ -26,18 +28,25 @@ async def async_get_config_entry_diagnostics(
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
whirlpool = config_entry.runtime_data
def get_appliance_diagnostics(appliance: Appliance) -> dict[str, Any]:
return {
"data_model": appliance.appliance_info.data_model,
"category": appliance.appliance_info.category,
"model_number": appliance.appliance_info.model_number,
}
appliances_manager = config_entry.runtime_data
diagnostics_data = {
"Washer_dryers": {
wd["NAME"]: dict(wd.items())
for wd in whirlpool.appliances_manager.washer_dryers
"washer_dryers": {
wd.name: get_appliance_diagnostics(wd)
for wd in appliances_manager.washer_dryers
},
"aircons": {
ac["NAME"]: dict(ac.items()) for ac in whirlpool.appliances_manager.aircons
ac.name: get_appliance_diagnostics(ac) for ac in appliances_manager.aircons
},
"ovens": {
oven["NAME"]: dict(oven.items())
for oven in whirlpool.appliances_manager.ovens
oven.name: get_appliance_diagnostics(oven)
for oven in appliances_manager.ovens
},
}

View File

@ -7,5 +7,5 @@
"integration_type": "hub",
"iot_class": "cloud_push",
"loggers": ["whirlpool"],
"requirements": ["whirlpool-sixth-sense==0.18.12"]
"requirements": ["whirlpool-sixth-sense==0.19.1"]
}

View File

@ -16,7 +16,6 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
@ -134,37 +133,16 @@ async def async_setup_entry(
config_entry: WhirlpoolConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Config flow entry for Whrilpool Laundry."""
"""Config flow entry for Whirlpool sensors."""
entities: list = []
whirlpool_data = config_entry.runtime_data
for appliance in whirlpool_data.appliances_manager.washer_dryers:
_wd = WasherDryer(
whirlpool_data.backend_selector,
whirlpool_data.auth,
appliance["SAID"],
async_get_clientsession(hass),
)
await _wd.connect()
appliances_manager = config_entry.runtime_data
for washer_dryer in appliances_manager.washer_dryers:
entities.extend(
[
WasherDryerClass(
appliance["SAID"],
appliance["NAME"],
description,
_wd,
)
for description in SENSORS
]
[WasherDryerClass(washer_dryer, description) for description in SENSORS]
)
entities.extend(
[
WasherDryerTimeClass(
appliance["SAID"],
appliance["NAME"],
description,
_wd,
)
WasherDryerTimeClass(washer_dryer, description)
for description in SENSOR_TIMER
]
)
@ -178,34 +156,30 @@ class WasherDryerClass(SensorEntity):
_attr_has_entity_name = True
def __init__(
self,
said: str,
name: str,
description: WhirlpoolSensorEntityDescription,
washdry: WasherDryer,
self, washer_dryer: WasherDryer, description: WhirlpoolSensorEntityDescription
) -> None:
"""Initialize the washer sensor."""
self._wd: WasherDryer = washdry
self._wd: WasherDryer = washer_dryer
if name == "dryer":
if washer_dryer.name == "dryer":
self._attr_icon = ICON_D
else:
self._attr_icon = ICON_W
self.entity_description: WhirlpoolSensorEntityDescription = description
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, said)},
name=name.capitalize(),
identifiers={(DOMAIN, washer_dryer.said)},
name=washer_dryer.name.capitalize(),
manufacturer="Whirlpool",
)
self._attr_unique_id = f"{said}-{description.key}"
self._attr_unique_id = f"{washer_dryer.said}-{description.key}"
async def async_added_to_hass(self) -> None:
"""Connect washer/dryer to the cloud."""
"""Register updates callback."""
self._wd.register_attr_callback(self.async_write_ha_state)
async def async_will_remove_from_hass(self) -> None:
"""Close Whirlpool Appliance sockets before removing."""
"""Unregister updates callback."""
self._wd.unregister_attr_callback(self.async_write_ha_state)
@property
@ -226,16 +200,12 @@ class WasherDryerTimeClass(RestoreSensor):
_attr_has_entity_name = True
def __init__(
self,
said: str,
name: str,
description: SensorEntityDescription,
washdry: WasherDryer,
self, washer_dryer: WasherDryer, description: SensorEntityDescription
) -> None:
"""Initialize the washer sensor."""
self._wd: WasherDryer = washdry
self._wd: WasherDryer = washer_dryer
if name == "dryer":
if washer_dryer.name == "dryer":
self._attr_icon = ICON_D
else:
self._attr_icon = ICON_W
@ -243,11 +213,11 @@ class WasherDryerTimeClass(RestoreSensor):
self.entity_description: SensorEntityDescription = description
self._running: bool | None = None
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, said)},
name=name.capitalize(),
identifiers={(DOMAIN, washer_dryer.said)},
name=washer_dryer.name.capitalize(),
manufacturer="Whirlpool",
)
self._attr_unique_id = f"{said}-{description.key}"
self._attr_unique_id = f"{washer_dryer.said}-{description.key}"
async def async_added_to_hass(self) -> None:
"""Connect washer/dryer to the cloud."""
@ -259,7 +229,6 @@ class WasherDryerTimeClass(RestoreSensor):
async def async_will_remove_from_hass(self) -> None:
"""Close Whrilpool Appliance sockets before removing."""
self._wd.unregister_attr_callback(self.update_from_latest_data)
await self._wd.disconnect()
@property
def available(self) -> bool:

2
requirements_all.txt generated
View File

@ -3061,7 +3061,7 @@ webmin-xmlrpc==0.0.2
weheat==2025.2.26
# homeassistant.components.whirlpool
whirlpool-sixth-sense==0.18.12
whirlpool-sixth-sense==0.19.1
# homeassistant.components.whois
whois==0.9.27

View File

@ -2465,7 +2465,7 @@ webmin-xmlrpc==0.0.2
weheat==2025.2.26
# homeassistant.components.whirlpool
whirlpool-sixth-sense==0.18.12
whirlpool-sixth-sense==0.19.1
# homeassistant.components.whois
whois==0.9.27

View File

@ -31,5 +31,4 @@ async def init_integration_with_entry(
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
return entry

View File

@ -8,10 +8,7 @@ import whirlpool
import whirlpool.aircon
from whirlpool.backendselector import Brand, Region
MOCK_SAID1 = "said1"
MOCK_SAID2 = "said2"
MOCK_SAID3 = "said3"
MOCK_SAID4 = "said4"
from .const import MOCK_SAID1, MOCK_SAID2, MOCK_SAID3, MOCK_SAID4
@pytest.fixture(
@ -36,7 +33,7 @@ def fixture_brand(request: pytest.FixtureRequest) -> tuple[str, Brand]:
return request.param
@pytest.fixture(name="mock_auth_api")
@pytest.fixture(name="mock_auth_api", autouse=True)
def fixture_mock_auth_api():
"""Set up Auth fixture."""
with (
@ -50,8 +47,10 @@ def fixture_mock_auth_api():
yield mock_auth
@pytest.fixture(name="mock_appliances_manager_api")
def fixture_mock_appliances_manager_api():
@pytest.fixture(name="mock_appliances_manager_api", autouse=True)
def fixture_mock_appliances_manager_api(
mock_aircon1_api, mock_aircon2_api, mock_sensor1_api, mock_sensor2_api
):
"""Set up AppliancesManager fixture."""
with (
mock.patch(
@ -63,28 +62,15 @@ def fixture_mock_appliances_manager_api():
),
):
mock_appliances_manager.return_value.fetch_appliances = AsyncMock()
mock_appliances_manager.return_value.connect = AsyncMock()
mock_appliances_manager.return_value.disconnect = AsyncMock()
mock_appliances_manager.return_value.aircons = [
{"SAID": MOCK_SAID1, "NAME": "TestZone"},
{"SAID": MOCK_SAID2, "NAME": "TestZone"},
mock_aircon1_api,
mock_aircon2_api,
]
mock_appliances_manager.return_value.washer_dryers = [
{"SAID": MOCK_SAID3, "NAME": "washer"},
{"SAID": MOCK_SAID4, "NAME": "dryer"},
]
yield mock_appliances_manager
@pytest.fixture(name="mock_appliances_manager_laundry_api")
def fixture_mock_appliances_manager_laundry_api():
"""Set up AppliancesManager fixture."""
with mock.patch(
"homeassistant.components.whirlpool.AppliancesManager"
) as mock_appliances_manager:
mock_appliances_manager.return_value.fetch_appliances = AsyncMock()
mock_appliances_manager.return_value.aircons = None
mock_appliances_manager.return_value.washer_dryers = [
{"SAID": MOCK_SAID3, "NAME": "washer"},
{"SAID": MOCK_SAID4, "NAME": "dryer"},
mock_sensor1_api,
mock_sensor2_api,
]
yield mock_appliances_manager
@ -107,9 +93,11 @@ def fixture_mock_backend_selector_api():
def get_aircon_mock(said):
"""Get a mock of an air conditioner."""
mock_aircon = mock.Mock(said=said)
mock_aircon.connect = AsyncMock()
mock_aircon.disconnect = AsyncMock()
mock_aircon.name = f"Aircon {said}"
mock_aircon.register_attr_callback = MagicMock()
mock_aircon.appliance_info.data_model = "aircon_model"
mock_aircon.appliance_info.category = "aircon"
mock_aircon.appliance_info.model_number = "12345"
mock_aircon.get_online.return_value = True
mock_aircon.get_power_on.return_value = True
mock_aircon.get_mode.return_value = whirlpool.aircon.Mode.Cool
@ -132,13 +120,13 @@ def get_aircon_mock(said):
@pytest.fixture(name="mock_aircon1_api", autouse=False)
def fixture_mock_aircon1_api(mock_auth_api, mock_appliances_manager_api):
def fixture_mock_aircon1_api():
"""Set up air conditioner API fixture."""
return get_aircon_mock(MOCK_SAID1)
@pytest.fixture(name="mock_aircon2_api", autouse=False)
def fixture_mock_aircon2_api(mock_auth_api, mock_appliances_manager_api):
def fixture_mock_aircon2_api():
"""Set up air conditioner API fixture."""
return get_aircon_mock(MOCK_SAID2)
@ -168,9 +156,11 @@ def side_effect_function(*args, **kwargs):
def get_sensor_mock(said):
"""Get a mock of a sensor."""
mock_sensor = mock.Mock(said=said)
mock_sensor.connect = AsyncMock()
mock_sensor.disconnect = AsyncMock()
mock_sensor.name = f"WasherDryer {said}"
mock_sensor.register_attr_callback = MagicMock()
mock_sensor.appliance_info.data_model = "washer_dryer_model"
mock_sensor.appliance_info.category = "washer_dryer"
mock_sensor.appliance_info.model_number = "12345"
mock_sensor.get_online.return_value = True
mock_sensor.get_machine_state.return_value = (
whirlpool.washerdryer.MachineState.Standby
@ -187,13 +177,13 @@ def get_sensor_mock(said):
@pytest.fixture(name="mock_sensor1_api", autouse=False)
def fixture_mock_sensor1_api(mock_auth_api, mock_appliances_manager_laundry_api):
def fixture_mock_sensor1_api():
"""Set up sensor API fixture."""
return get_sensor_mock(MOCK_SAID3)
@pytest.fixture(name="mock_sensor2_api", autouse=False)
def fixture_mock_sensor2_api(mock_auth_api, mock_appliances_manager_laundry_api):
def fixture_mock_sensor2_api():
"""Set up sensor API fixture."""
return get_sensor_mock(MOCK_SAID4)

View File

@ -0,0 +1,6 @@
"""Constants for the Whirlpool Sixth Sense integration tests."""
MOCK_SAID1 = "said1"
MOCK_SAID2 = "said2"
MOCK_SAID3 = "said3"
MOCK_SAID4 = "said4"

View File

@ -2,24 +2,32 @@
# name: test_entry_diagnostics
dict({
'appliances': dict({
'Washer_dryers': dict({
'dryer': dict({
'NAME': 'dryer',
'SAID': '**REDACTED**',
}),
'washer': dict({
'NAME': 'washer',
'SAID': '**REDACTED**',
}),
}),
'aircons': dict({
'TestZone': dict({
'NAME': 'TestZone',
'SAID': '**REDACTED**',
'Aircon said1': dict({
'category': 'aircon',
'data_model': 'aircon_model',
'model_number': '12345',
}),
'Aircon said2': dict({
'category': 'aircon',
'data_model': 'aircon_model',
'model_number': '12345',
}),
}),
'ovens': dict({
}),
'washer_dryers': dict({
'WasherDryer said3': dict({
'category': 'washer_dryer',
'data_model': 'washer_dryer_model',
'model_number': '12345',
}),
'WasherDryer said4': dict({
'category': 'washer_dryer',
'data_model': 'washer_dryer_model',
'model_number': '12345',
}),
}),
}),
'config_entry': dict({
'data': dict({

View File

@ -68,6 +68,7 @@ async def test_no_appliances(
) -> None:
"""Test the setup of the climate entities when there are no appliances available."""
mock_appliances_manager_api.return_value.aircons = []
mock_appliances_manager_api.return_value.washer_dryers = []
await init_integration(hass)
assert len(hass.states.async_all()) == 0
@ -75,16 +76,15 @@ async def test_no_appliances(
async def test_static_attributes(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_aircon1_api: MagicMock,
mock_aircon_api_instances: MagicMock,
) -> None:
"""Test static climate attributes."""
await init_integration(hass)
for entity_id in ("climate.said1", "climate.said2"):
for said in ("said1", "said2"):
entity_id = f"climate.{said}"
entry = entity_registry.async_get(entity_id)
assert entry
assert entry.unique_id == entity_id.split(".")[1]
assert entry.unique_id == said
state = hass.states.get(entity_id)
assert state is not None
@ -92,7 +92,7 @@ async def test_static_attributes(
assert state.state == HVACMode.COOL
attributes = state.attributes
assert attributes[ATTR_FRIENDLY_NAME] == "TestZone"
assert attributes[ATTR_FRIENDLY_NAME] == f"Aircon {said}"
assert (
attributes[ATTR_SUPPORTED_FEATURES]
@ -123,7 +123,6 @@ async def test_static_attributes(
async def test_dynamic_attributes(
hass: HomeAssistant,
mock_aircon_api_instances: MagicMock,
mock_aircon1_api: MagicMock,
mock_aircon2_api: MagicMock,
) -> None:
@ -212,7 +211,6 @@ async def test_dynamic_attributes(
async def test_service_calls(
hass: HomeAssistant,
mock_aircon_api_instances: MagicMock,
mock_aircon1_api: MagicMock,
mock_aircon2_api: MagicMock,
) -> None:

View File

@ -1,7 +1,5 @@
"""Test Blink diagnostics."""
from unittest.mock import MagicMock
from syrupy import SnapshotAssertion
from syrupy.filters import props
@ -19,9 +17,6 @@ async def test_entry_diagnostics(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
snapshot: SnapshotAssertion,
mock_appliances_manager_api: MagicMock,
mock_aircon1_api: MagicMock,
mock_aircon_api_instances: MagicMock,
) -> None:
"""Test config entry diagnostics."""

View File

@ -21,7 +21,6 @@ async def test_setup(
mock_backend_selector_api: MagicMock,
region,
brand,
mock_aircon_api_instances: MagicMock,
) -> None:
"""Test setup."""
entry = await init_integration(hass, region[0], brand[0])
@ -33,7 +32,6 @@ async def test_setup(
async def test_setup_region_fallback(
hass: HomeAssistant,
mock_backend_selector_api: MagicMock,
mock_aircon_api_instances: MagicMock,
) -> None:
"""Test setup when no region is available on the ConfigEntry.
@ -57,7 +55,6 @@ async def test_setup_brand_fallback(
hass: HomeAssistant,
region,
mock_backend_selector_api: MagicMock,
mock_aircon_api_instances: MagicMock,
) -> None:
"""Test setup when no brand is available on the ConfigEntry.
@ -81,7 +78,6 @@ async def test_setup_brand_fallback(
async def test_setup_http_exception(
hass: HomeAssistant,
mock_auth_api: MagicMock,
mock_aircon_api_instances: MagicMock,
) -> None:
"""Test setup with an http exception."""
mock_auth_api.return_value.do_auth = AsyncMock(
@ -95,7 +91,6 @@ async def test_setup_http_exception(
async def test_setup_auth_failed(
hass: HomeAssistant,
mock_auth_api: MagicMock,
mock_aircon_api_instances: MagicMock,
) -> None:
"""Test setup with failed auth."""
mock_auth_api.return_value.do_auth = AsyncMock()
@ -108,7 +103,6 @@ async def test_setup_auth_failed(
async def test_setup_auth_account_locked(
hass: HomeAssistant,
mock_auth_api: MagicMock,
mock_aircon_api_instances: MagicMock,
) -> None:
"""Test setup with failed auth due to account being locked."""
mock_auth_api.return_value.do_auth.side_effect = AccountLockedError
@ -120,7 +114,6 @@ async def test_setup_auth_account_locked(
async def test_setup_fetch_appliances_failed(
hass: HomeAssistant,
mock_appliances_manager_api: MagicMock,
mock_aircon_api_instances: MagicMock,
) -> None:
"""Test setup with failed fetch_appliances."""
mock_appliances_manager_api.return_value.fetch_appliances.return_value = False
@ -129,11 +122,7 @@ async def test_setup_fetch_appliances_failed(
assert entry.state is ConfigEntryState.SETUP_ERROR
async def test_unload_entry(
hass: HomeAssistant,
mock_aircon_api_instances: MagicMock,
mock_sensor_api_instances: MagicMock,
) -> None:
async def test_unload_entry(hass: HomeAssistant) -> None:
"""Test successful unload of entry."""
entry = await init_integration(hass)
assert len(hass.config_entries.async_entries(DOMAIN)) == 1

View File

@ -12,14 +12,13 @@ from homeassistant.helpers import entity_registry as er
from homeassistant.util.dt import as_timestamp, utc_from_timestamp, utcnow
from . import init_integration
from .const import MOCK_SAID3, MOCK_SAID4
from tests.common import async_fire_time_changed, mock_restore_cache_with_extra_data
async def update_sensor_state(
hass: HomeAssistant,
entity_id: str,
mock_sensor_api_instance: MagicMock,
hass: HomeAssistant, entity_id: str, mock_sensor_api_instance: MagicMock
) -> State:
"""Simulate an update trigger from the API."""
@ -46,10 +45,7 @@ def side_effect_function_open_door(*args, **kwargs):
async def test_dryer_sensor_values(
hass: HomeAssistant,
mock_sensor_api_instances: MagicMock,
mock_sensor2_api: MagicMock,
entity_registry: er.EntityRegistry,
hass: HomeAssistant, mock_sensor2_api: MagicMock, entity_registry: er.EntityRegistry
) -> None:
"""Test the sensor value callbacks."""
hass.set_state(CoreState.not_running)
@ -58,14 +54,11 @@ async def test_dryer_sensor_values(
hass,
(
(
State(
"sensor.washer_end_time",
"1",
),
State(f"sensor.washerdryer_{MOCK_SAID3}_end_time", "1"),
{"native_value": thetimestamp, "native_unit_of_measurement": None},
),
(
State("sensor.dryer_end_time", "1"),
State(f"sensor.washerdryer_{MOCK_SAID4}_end_time", "1"),
{"native_value": thetimestamp, "native_unit_of_measurement": None},
),
),
@ -73,7 +66,7 @@ async def test_dryer_sensor_values(
await init_integration(hass)
entity_id = "sensor.dryer_state"
entity_id = f"sensor.washerdryer_{MOCK_SAID4}_state"
mock_instance = mock_sensor2_api
entry = entity_registry.async_get(entity_id)
assert entry
@ -83,7 +76,7 @@ async def test_dryer_sensor_values(
state = await update_sensor_state(hass, entity_id, mock_instance)
assert state is not None
state_id = f"{entity_id.split('_', maxsplit=1)[0]}_end_time"
state_id = f"sensor.washerdryer_{MOCK_SAID3}_end_time"
state = hass.states.get(state_id)
assert state.state == thetimestamp.isoformat()
@ -110,10 +103,7 @@ async def test_dryer_sensor_values(
async def test_washer_sensor_values(
hass: HomeAssistant,
mock_sensor_api_instances: MagicMock,
mock_sensor1_api: MagicMock,
entity_registry: er.EntityRegistry,
hass: HomeAssistant, mock_sensor1_api: MagicMock, entity_registry: er.EntityRegistry
) -> None:
"""Test the sensor value callbacks."""
hass.set_state(CoreState.not_running)
@ -122,14 +112,11 @@ async def test_washer_sensor_values(
hass,
(
(
State(
"sensor.washer_end_time",
"1",
),
State(f"sensor.washerdryer_{MOCK_SAID3}_end_time", "1"),
{"native_value": thetimestamp, "native_unit_of_measurement": None},
),
(
State("sensor.dryer_end_time", "1"),
State(f"sensor.washerdryer_{MOCK_SAID4}_end_time", "1"),
{"native_value": thetimestamp, "native_unit_of_measurement": None},
),
),
@ -143,7 +130,7 @@ async def test_washer_sensor_values(
)
await hass.async_block_till_done()
entity_id = "sensor.washer_state"
entity_id = f"sensor.washerdryer_{MOCK_SAID3}_state"
mock_instance = mock_sensor1_api
entry = entity_registry.async_get(entity_id)
assert entry
@ -153,11 +140,11 @@ async def test_washer_sensor_values(
state = await update_sensor_state(hass, entity_id, mock_instance)
assert state is not None
state_id = f"{entity_id.split('_', maxsplit=1)[0]}_end_time"
state_id = f"sensor.washerdryer_{MOCK_SAID3}_end_time"
state = hass.states.get(state_id)
assert state.state == thetimestamp.isoformat()
state_id = f"{entity_id.split('_', maxsplit=1)[0]}_detergent_level"
state_id = f"sensor.washerdryer_{MOCK_SAID3}_detergent_level"
entry = entity_registry.async_get(state_id)
assert entry
assert entry.disabled
@ -277,10 +264,7 @@ async def test_washer_sensor_values(
assert state.state == "door_open"
async def test_restore_state(
hass: HomeAssistant,
mock_sensor_api_instances: MagicMock,
) -> None:
async def test_restore_state(hass: HomeAssistant) -> None:
"""Test sensor restore state."""
# Home assistant is not running yet
hass.set_state(CoreState.not_running)
@ -289,14 +273,11 @@ async def test_restore_state(
hass,
(
(
State(
"sensor.washer_end_time",
"1",
),
State(f"sensor.washerdryer_{MOCK_SAID3}_end_time", "1"),
{"native_value": thetimestamp, "native_unit_of_measurement": None},
),
(
State("sensor.dryer_end_time", "1"),
State(f"sensor.washerdryer_{MOCK_SAID4}_end_time", "1"),
{"native_value": thetimestamp, "native_unit_of_measurement": None},
),
),
@ -305,20 +286,18 @@ async def test_restore_state(
# create and add entry
await init_integration(hass)
# restore from cache
state = hass.states.get("sensor.washer_end_time")
state = hass.states.get(f"sensor.washerdryer_{MOCK_SAID3}_end_time")
assert state.state == thetimestamp.isoformat()
state = hass.states.get("sensor.dryer_end_time")
state = hass.states.get(f"sensor.washerdryer_{MOCK_SAID4}_end_time")
assert state.state == thetimestamp.isoformat()
async def test_no_restore_state(
hass: HomeAssistant,
mock_sensor_api_instances: MagicMock,
mock_sensor1_api: MagicMock,
hass: HomeAssistant, mock_sensor1_api: MagicMock
) -> None:
"""Test sensor restore state with no restore."""
# create and add entry
entity_id = "sensor.washer_end_time"
entity_id = f"sensor.washerdryer_{MOCK_SAID3}_end_time"
await init_integration(hass)
# restore from cache
state = hass.states.get(entity_id)
@ -330,11 +309,7 @@ async def test_no_restore_state(
@pytest.mark.freeze_time("2022-11-30 00:00:00")
async def test_callback(
hass: HomeAssistant,
mock_sensor_api_instances: MagicMock,
mock_sensor1_api: MagicMock,
) -> None:
async def test_callback(hass: HomeAssistant, mock_sensor1_api: MagicMock) -> None:
"""Test callback timestamp callback function."""
hass.set_state(CoreState.not_running)
thetimestamp: datetime = datetime(2022, 11, 29, 00, 00, 00, 00, UTC)
@ -342,14 +317,11 @@ async def test_callback(
hass,
(
(
State(
"sensor.washer_end_time",
"1",
),
State(f"sensor.washerdryer_{MOCK_SAID3}_end_time", "1"),
{"native_value": thetimestamp, "native_unit_of_measurement": None},
),
(
State("sensor.dryer_end_time", "1"),
State(f"sensor.washerdryer_{MOCK_SAID4}_end_time", "1"),
{"native_value": thetimestamp, "native_unit_of_measurement": None},
),
),
@ -358,12 +330,12 @@ async def test_callback(
# create and add entry
await init_integration(hass)
# restore from cache
state = hass.states.get("sensor.washer_end_time")
state = hass.states.get(f"sensor.washerdryer_{MOCK_SAID3}_end_time")
assert state.state == thetimestamp.isoformat()
callback = mock_sensor1_api.register_attr_callback.call_args_list[1][0][0]
callback()
state = hass.states.get("sensor.washer_end_time")
state = hass.states.get(f"sensor.washerdryer_{MOCK_SAID3}_end_time")
assert state.state == thetimestamp.isoformat()
mock_sensor1_api.get_machine_state.return_value = MachineState.RunningMainCycle
mock_sensor1_api.get_attribute.side_effect = None
@ -371,19 +343,19 @@ async def test_callback(
callback()
# Test new timestamp when machine starts a cycle.
state = hass.states.get("sensor.washer_end_time")
state = hass.states.get(f"sensor.washerdryer_{MOCK_SAID3}_end_time")
time = state.state
assert state.state != thetimestamp.isoformat()
# Test no timestamp change for < 60 seconds time change.
mock_sensor1_api.get_attribute.return_value = "65"
callback()
state = hass.states.get("sensor.washer_end_time")
state = hass.states.get(f"sensor.washerdryer_{MOCK_SAID3}_end_time")
assert state.state == time
# Test timestamp change for > 60 seconds.
mock_sensor1_api.get_attribute.return_value = "125"
callback()
state = hass.states.get("sensor.washer_end_time")
state = hass.states.get(f"sensor.washerdryer_{MOCK_SAID3}_end_time")
newtime = utc_from_timestamp(as_timestamp(time) + 65)
assert state.state == newtime.isoformat()