mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +00:00
Add miele devices dynamically (#144216)
* Use device class transation * WIP Cleanup tests * First 3 * First 3 * Button * Climate * Light * Switch * New and cleaner variant * Update homeassistant/components/miele/entity.py --------- Co-authored-by: Josef Zweck <josef@zweck.dev>
This commit is contained in:
parent
34dbd1fb10
commit
337c64d69d
@ -263,13 +263,23 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the binary sensor platform."""
|
"""Set up the binary sensor platform."""
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data
|
||||||
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
async_add_entities(
|
def _async_add_new_devices() -> None:
|
||||||
MieleBinarySensor(coordinator, device_id, definition.description)
|
nonlocal added_devices
|
||||||
for device_id, device in coordinator.data.devices.items()
|
|
||||||
for definition in BINARY_SENSOR_TYPES
|
new_devices_set, current_devices = coordinator.async_add_devices(added_devices)
|
||||||
if device.device_type in definition.types
|
added_devices = current_devices
|
||||||
)
|
|
||||||
|
async_add_entities(
|
||||||
|
MieleBinarySensor(coordinator, device_id, definition.description)
|
||||||
|
for device_id, device in coordinator.data.devices.items()
|
||||||
|
for definition in BINARY_SENSOR_TYPES
|
||||||
|
if device_id in new_devices_set and device.device_type in definition.types
|
||||||
|
)
|
||||||
|
|
||||||
|
config_entry.async_on_unload(coordinator.async_add_listener(_async_add_new_devices))
|
||||||
|
_async_add_new_devices()
|
||||||
|
|
||||||
|
|
||||||
class MieleBinarySensor(MieleEntity, BinarySensorEntity):
|
class MieleBinarySensor(MieleEntity, BinarySensorEntity):
|
||||||
|
@ -111,13 +111,22 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the button platform."""
|
"""Set up the button platform."""
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data
|
||||||
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
async_add_entities(
|
def _async_add_new_devices() -> None:
|
||||||
MieleButton(coordinator, device_id, definition.description)
|
nonlocal added_devices
|
||||||
for device_id, device in coordinator.data.devices.items()
|
new_devices_set, current_devices = coordinator.async_add_devices(added_devices)
|
||||||
for definition in BUTTON_TYPES
|
added_devices = current_devices
|
||||||
if device.device_type in definition.types
|
|
||||||
)
|
async_add_entities(
|
||||||
|
MieleButton(coordinator, device_id, definition.description)
|
||||||
|
for device_id, device in coordinator.data.devices.items()
|
||||||
|
for definition in BUTTON_TYPES
|
||||||
|
if device_id in new_devices_set and device.device_type in definition.types
|
||||||
|
)
|
||||||
|
|
||||||
|
config_entry.async_on_unload(coordinator.async_add_listener(_async_add_new_devices))
|
||||||
|
_async_add_new_devices()
|
||||||
|
|
||||||
|
|
||||||
class MieleButton(MieleEntity, ButtonEntity):
|
class MieleButton(MieleEntity, ButtonEntity):
|
||||||
|
@ -131,16 +131,30 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the climate platform."""
|
"""Set up the climate platform."""
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data
|
||||||
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
async_add_entities(
|
def _async_add_new_devices() -> None:
|
||||||
MieleClimate(coordinator, device_id, definition.description)
|
nonlocal added_devices
|
||||||
for device_id, device in coordinator.data.devices.items()
|
|
||||||
for definition in CLIMATE_TYPES
|
new_devices_set, current_devices = coordinator.async_add_devices(added_devices)
|
||||||
if (
|
added_devices = current_devices
|
||||||
device.device_type in definition.types
|
|
||||||
and (definition.description.value_fn(device) not in DISABLED_TEMP_ENTITIES)
|
async_add_entities(
|
||||||
|
MieleClimate(coordinator, device_id, definition.description)
|
||||||
|
for device_id, device in coordinator.data.devices.items()
|
||||||
|
for definition in CLIMATE_TYPES
|
||||||
|
if (
|
||||||
|
device_id in new_devices_set
|
||||||
|
and device.device_type in definition.types
|
||||||
|
and (
|
||||||
|
definition.description.value_fn(device)
|
||||||
|
not in DISABLED_TEMP_ENTITIES
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
config_entry.async_on_unload(coordinator.async_add_listener(_async_add_new_devices))
|
||||||
|
_async_add_new_devices()
|
||||||
|
|
||||||
|
|
||||||
class MieleClimate(MieleEntity, ClimateEntity):
|
class MieleClimate(MieleEntity, ClimateEntity):
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio.timeouts
|
import asyncio.timeouts
|
||||||
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
@ -33,6 +34,11 @@ class MieleCoordinatorData:
|
|||||||
class MieleDataUpdateCoordinator(DataUpdateCoordinator[MieleCoordinatorData]):
|
class MieleDataUpdateCoordinator(DataUpdateCoordinator[MieleCoordinatorData]):
|
||||||
"""Coordinator for Miele data."""
|
"""Coordinator for Miele data."""
|
||||||
|
|
||||||
|
config_entry: MieleConfigEntry
|
||||||
|
new_device_callbacks: list[Callable[[dict[str, MieleDevice]], None]] = []
|
||||||
|
known_devices: set[str] = set()
|
||||||
|
devices: dict[str, MieleDevice] = {}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@ -56,12 +62,20 @@ class MieleDataUpdateCoordinator(DataUpdateCoordinator[MieleCoordinatorData]):
|
|||||||
device_id: MieleDevice(device)
|
device_id: MieleDevice(device)
|
||||||
for device_id, device in devices_json.items()
|
for device_id, device in devices_json.items()
|
||||||
}
|
}
|
||||||
|
self.devices = devices
|
||||||
actions = {}
|
actions = {}
|
||||||
for device_id in devices:
|
for device_id in devices:
|
||||||
actions_json = await self.api.get_actions(device_id)
|
actions_json = await self.api.get_actions(device_id)
|
||||||
actions[device_id] = MieleAction(actions_json)
|
actions[device_id] = MieleAction(actions_json)
|
||||||
return MieleCoordinatorData(devices=devices, actions=actions)
|
return MieleCoordinatorData(devices=devices, actions=actions)
|
||||||
|
|
||||||
|
def async_add_devices(self, added_devices: set[str]) -> tuple[set[str], set[str]]:
|
||||||
|
"""Add devices."""
|
||||||
|
current_devices = set(self.devices)
|
||||||
|
new_devices: set[str] = current_devices - added_devices
|
||||||
|
|
||||||
|
return (new_devices, current_devices)
|
||||||
|
|
||||||
async def callback_update_data(self, devices_json: dict[str, dict]) -> None:
|
async def callback_update_data(self, devices_json: dict[str, dict]) -> None:
|
||||||
"""Handle data update from the API."""
|
"""Handle data update from the API."""
|
||||||
devices = {
|
devices = {
|
||||||
|
@ -65,13 +65,22 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the fan platform."""
|
"""Set up the fan platform."""
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data
|
||||||
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
async_add_entities(
|
def _async_add_new_devices() -> None:
|
||||||
MieleFan(coordinator, device_id, definition.description)
|
nonlocal added_devices
|
||||||
for device_id, device in coordinator.data.devices.items()
|
new_devices_set, current_devices = coordinator.async_add_devices(added_devices)
|
||||||
for definition in FAN_TYPES
|
added_devices = current_devices
|
||||||
if device.device_type in definition.types
|
|
||||||
)
|
async_add_entities(
|
||||||
|
MieleFan(coordinator, device_id, definition.description)
|
||||||
|
for device_id, device in coordinator.data.devices.items()
|
||||||
|
for definition in FAN_TYPES
|
||||||
|
if device_id in new_devices_set and device.device_type in definition.types
|
||||||
|
)
|
||||||
|
|
||||||
|
config_entry.async_on_unload(coordinator.async_add_listener(_async_add_new_devices))
|
||||||
|
_async_add_new_devices()
|
||||||
|
|
||||||
|
|
||||||
class MieleFan(MieleEntity, FanEntity):
|
class MieleFan(MieleEntity, FanEntity):
|
||||||
|
@ -85,13 +85,22 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the light platform."""
|
"""Set up the light platform."""
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data
|
||||||
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
async_add_entities(
|
def _async_add_new_devices() -> None:
|
||||||
MieleLight(coordinator, device_id, definition.description)
|
nonlocal added_devices
|
||||||
for device_id, device in coordinator.data.devices.items()
|
new_devices_set, current_devices = coordinator.async_add_devices(added_devices)
|
||||||
for definition in LIGHT_TYPES
|
added_devices = current_devices
|
||||||
if device.device_type in definition.types
|
|
||||||
)
|
async_add_entities(
|
||||||
|
MieleLight(coordinator, device_id, definition.description)
|
||||||
|
for device_id, device in coordinator.data.devices.items()
|
||||||
|
for definition in LIGHT_TYPES
|
||||||
|
if device_id in new_devices_set and device.device_type in definition.types
|
||||||
|
)
|
||||||
|
|
||||||
|
config_entry.async_on_unload(coordinator.async_add_listener(_async_add_new_devices))
|
||||||
|
_async_add_new_devices()
|
||||||
|
|
||||||
|
|
||||||
class MieleLight(MieleEntity, LightEntity):
|
class MieleLight(MieleEntity, LightEntity):
|
||||||
|
@ -426,33 +426,43 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the sensor platform."""
|
"""Set up the sensor platform."""
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data
|
||||||
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
entities: list = []
|
def _async_add_new_devices() -> None:
|
||||||
entity_class: type[MieleSensor]
|
nonlocal added_devices
|
||||||
for device_id, device in coordinator.data.devices.items():
|
entities: list = []
|
||||||
for definition in SENSOR_TYPES:
|
entity_class: type[MieleSensor]
|
||||||
if device.device_type in definition.types:
|
new_devices_set, current_devices = coordinator.async_add_devices(added_devices)
|
||||||
match definition.description.key:
|
added_devices = current_devices
|
||||||
case "state_status":
|
|
||||||
entity_class = MieleStatusSensor
|
|
||||||
case "state_program_id":
|
|
||||||
entity_class = MieleProgramIdSensor
|
|
||||||
case "state_program_phase":
|
|
||||||
entity_class = MielePhaseSensor
|
|
||||||
case _:
|
|
||||||
entity_class = MieleSensor
|
|
||||||
if (
|
|
||||||
definition.description.device_class == SensorDeviceClass.TEMPERATURE
|
|
||||||
and definition.description.value_fn(device)
|
|
||||||
== DISABLED_TEMPERATURE / 100
|
|
||||||
):
|
|
||||||
# Don't create entity if API signals that datapoint is disabled
|
|
||||||
continue
|
|
||||||
entities.append(
|
|
||||||
entity_class(coordinator, device_id, definition.description)
|
|
||||||
)
|
|
||||||
|
|
||||||
async_add_entities(entities)
|
for device_id, device in coordinator.data.devices.items():
|
||||||
|
for definition in SENSOR_TYPES:
|
||||||
|
if device.device_type in definition.types:
|
||||||
|
match definition.description.key:
|
||||||
|
case "state_status":
|
||||||
|
entity_class = MieleStatusSensor
|
||||||
|
case "state_program_id":
|
||||||
|
entity_class = MieleProgramIdSensor
|
||||||
|
case "state_program_phase":
|
||||||
|
entity_class = MielePhaseSensor
|
||||||
|
case _:
|
||||||
|
entity_class = MieleSensor
|
||||||
|
if (
|
||||||
|
device_id in new_devices_set
|
||||||
|
and definition.description.device_class
|
||||||
|
== SensorDeviceClass.TEMPERATURE
|
||||||
|
and definition.description.value_fn(device)
|
||||||
|
== DISABLED_TEMPERATURE / 100
|
||||||
|
):
|
||||||
|
# Don't create entity if API signals that datapoint is disabled
|
||||||
|
continue
|
||||||
|
entities.append(
|
||||||
|
entity_class(coordinator, device_id, definition.description)
|
||||||
|
)
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
config_entry.async_on_unload(coordinator.async_add_listener(_async_add_new_devices))
|
||||||
|
_async_add_new_devices()
|
||||||
|
|
||||||
|
|
||||||
APPLIANCE_ICONS = {
|
APPLIANCE_ICONS = {
|
||||||
|
@ -116,22 +116,34 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the switch platform."""
|
"""Set up the switch platform."""
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data
|
||||||
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
entities: list = []
|
def _async_add_new_devices() -> None:
|
||||||
entity_class: type[MieleSwitch]
|
nonlocal added_devices
|
||||||
for device_id, device in coordinator.data.devices.items():
|
new_devices_set, current_devices = coordinator.async_add_devices(added_devices)
|
||||||
for definition in SWITCH_TYPES:
|
added_devices = current_devices
|
||||||
if device.device_type in definition.types:
|
|
||||||
match definition.description.key:
|
|
||||||
case "poweronoff":
|
|
||||||
entity_class = MielePowerSwitch
|
|
||||||
case "supercooling" | "superfreezing":
|
|
||||||
entity_class = MieleSuperSwitch
|
|
||||||
|
|
||||||
entities.append(
|
entities = []
|
||||||
entity_class(coordinator, device_id, definition.description)
|
for device_id, device in coordinator.data.devices.items():
|
||||||
)
|
for definition in SWITCH_TYPES:
|
||||||
async_add_entities(entities)
|
if (
|
||||||
|
device_id in new_devices_set
|
||||||
|
and device.device_type in definition.types
|
||||||
|
):
|
||||||
|
entity_class: type[MieleSwitch] = MieleSwitch
|
||||||
|
match definition.description.key:
|
||||||
|
case "poweronoff":
|
||||||
|
entity_class = MielePowerSwitch
|
||||||
|
case "supercooling" | "superfreezing":
|
||||||
|
entity_class = MieleSuperSwitch
|
||||||
|
|
||||||
|
entities.append(
|
||||||
|
entity_class(coordinator, device_id, definition.description)
|
||||||
|
)
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
config_entry.async_on_unload(coordinator.async_add_listener(_async_add_new_devices))
|
||||||
|
_async_add_new_devices()
|
||||||
|
|
||||||
|
|
||||||
class MieleSwitch(MieleEntity, SwitchEntity):
|
class MieleSwitch(MieleEntity, SwitchEntity):
|
||||||
|
@ -141,7 +141,7 @@ async def setup_platform(
|
|||||||
with patch(f"homeassistant.components.{DOMAIN}.PLATFORMS", platforms):
|
with patch(f"homeassistant.components.{DOMAIN}.PLATFORMS", platforms):
|
||||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
yield
|
yield mock_config_entry
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -356,6 +356,106 @@
|
|||||||
"batteryLevel": null
|
"batteryLevel": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"DummyAppliance_18": {
|
||||||
|
"ident": {
|
||||||
|
"type": {
|
||||||
|
"key_localized": "Device type",
|
||||||
|
"value_raw": 18,
|
||||||
|
"value_localized": "Cooker Hood"
|
||||||
|
},
|
||||||
|
"deviceName": "",
|
||||||
|
"protocolVersion": 2,
|
||||||
|
"deviceIdentLabel": {
|
||||||
|
"fabNumber": "<fabNumber3>",
|
||||||
|
"fabIndex": "64",
|
||||||
|
"techType": "Fläkt",
|
||||||
|
"matNumber": "<matNumber3>",
|
||||||
|
"swids": ["<swid1>", "<swid2>", "<swid3>", "<...>"]
|
||||||
|
},
|
||||||
|
"xkmIdentLabel": {
|
||||||
|
"techType": "EK039W",
|
||||||
|
"releaseVersion": "02.72"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"ProgramID": {
|
||||||
|
"value_raw": 1,
|
||||||
|
"value_localized": "Off",
|
||||||
|
"key_localized": "Program name"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"value_raw": 1,
|
||||||
|
"value_localized": "Off",
|
||||||
|
"key_localized": "status"
|
||||||
|
},
|
||||||
|
"programType": {
|
||||||
|
"value_raw": 0,
|
||||||
|
"value_localized": "Program",
|
||||||
|
"key_localized": "Program type"
|
||||||
|
},
|
||||||
|
"programPhase": {
|
||||||
|
"value_raw": 4608,
|
||||||
|
"value_localized": "",
|
||||||
|
"key_localized": "Program phase"
|
||||||
|
},
|
||||||
|
"remainingTime": [0, 0],
|
||||||
|
"startTime": [0, 0],
|
||||||
|
"targetTemperature": [
|
||||||
|
{
|
||||||
|
"value_raw": -32768,
|
||||||
|
"value_localized": null,
|
||||||
|
"unit": "Celsius"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"temperature": [
|
||||||
|
{
|
||||||
|
"value_raw": -32768,
|
||||||
|
"value_localized": null,
|
||||||
|
"unit": "Celsius"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value_raw": -32768,
|
||||||
|
"value_localized": null,
|
||||||
|
"unit": "Celsius"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value_raw": -32768,
|
||||||
|
"value_localized": null,
|
||||||
|
"unit": "Celsius"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"signalInfo": false,
|
||||||
|
"signalFailure": false,
|
||||||
|
"signalDoor": false,
|
||||||
|
"remoteEnable": {
|
||||||
|
"fullRemoteControl": true,
|
||||||
|
"smartGrid": false,
|
||||||
|
"mobileStart": false
|
||||||
|
},
|
||||||
|
"ambientLight": 2,
|
||||||
|
"light": 1,
|
||||||
|
"elapsedTime": {},
|
||||||
|
"spinningSpeed": {
|
||||||
|
"unit": "rpm",
|
||||||
|
"value_raw": null,
|
||||||
|
"value_localized": null,
|
||||||
|
"key_localized": "Spin speed"
|
||||||
|
},
|
||||||
|
"dryingStep": {
|
||||||
|
"value_raw": null,
|
||||||
|
"value_localized": "",
|
||||||
|
"key_localized": "Drying level"
|
||||||
|
},
|
||||||
|
"ventilationStep": {
|
||||||
|
"value_raw": 0,
|
||||||
|
"value_localized": "0",
|
||||||
|
"key_localized": "Fan level"
|
||||||
|
},
|
||||||
|
"plateStep": [],
|
||||||
|
"ecoFeedback": null,
|
||||||
|
"batteryLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
"Dummy_Appliance_4": {
|
"Dummy_Appliance_4": {
|
||||||
"ident": {
|
"ident": {
|
||||||
"type": {
|
"type": {
|
||||||
@ -402,10 +502,28 @@
|
|||||||
{ "value_raw": -32768, "value_localized": null, "unit": "Celsius" },
|
{ "value_raw": -32768, "value_localized": null, "unit": "Celsius" },
|
||||||
{ "value_raw": -32768, "value_localized": null, "unit": "Celsius" }
|
{ "value_raw": -32768, "value_localized": null, "unit": "Celsius" }
|
||||||
],
|
],
|
||||||
|
"coreTargetTemperature": [
|
||||||
|
{ "value_raw": 7500, "value_localized": "75.0", "unit": "Celsius" }
|
||||||
|
],
|
||||||
|
"coreTemperature": [
|
||||||
|
{ "value_raw": 5200, "value_localized": "52.0", "unit": "Celsius" }
|
||||||
|
],
|
||||||
"temperature": [
|
"temperature": [
|
||||||
{ "value_raw": -32768, "value_localized": null, "unit": "Celsius" },
|
{
|
||||||
{ "value_raw": -32768, "value_localized": null, "unit": "Celsius" },
|
"value_raw": 17500,
|
||||||
{ "value_raw": -32768, "value_localized": null, "unit": "Celsius" }
|
"value_localized": "175.0",
|
||||||
|
"unit": "Celsius"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value_raw": -32768,
|
||||||
|
"value_localized": null,
|
||||||
|
"unit": "Celsius"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value_raw": -32768,
|
||||||
|
"value_localized": null,
|
||||||
|
"unit": "Celsius"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"signalInfo": false,
|
"signalInfo": false,
|
||||||
"signalFailure": false,
|
"signalFailure": false,
|
||||||
|
@ -5,7 +5,13 @@
|
|||||||
"startTime": [],
|
"startTime": [],
|
||||||
"ventilationStep": [],
|
"ventilationStep": [],
|
||||||
"programId": [],
|
"programId": [],
|
||||||
"targetTemperature": [],
|
"targetTemperature": [
|
||||||
|
{
|
||||||
|
"zone": 1,
|
||||||
|
"min": -28,
|
||||||
|
"max": -14
|
||||||
|
}
|
||||||
|
],
|
||||||
"deviceName": true,
|
"deviceName": true,
|
||||||
"powerOn": true,
|
"powerOn": true,
|
||||||
"powerOff": false,
|
"powerOff": false,
|
||||||
|
@ -36,6 +36,11 @@
|
|||||||
'startTime': list([
|
'startTime': list([
|
||||||
]),
|
]),
|
||||||
'targetTemperature': list([
|
'targetTemperature': list([
|
||||||
|
dict({
|
||||||
|
'max': -14,
|
||||||
|
'min': -28,
|
||||||
|
'zone': 1,
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
'ventilationStep': list([
|
'ventilationStep': list([
|
||||||
]),
|
]),
|
||||||
@ -64,6 +69,11 @@
|
|||||||
'startTime': list([
|
'startTime': list([
|
||||||
]),
|
]),
|
||||||
'targetTemperature': list([
|
'targetTemperature': list([
|
||||||
|
dict({
|
||||||
|
'max': -14,
|
||||||
|
'min': -28,
|
||||||
|
'zone': 1,
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
'ventilationStep': list([
|
'ventilationStep': list([
|
||||||
]),
|
]),
|
||||||
@ -92,6 +102,11 @@
|
|||||||
'startTime': list([
|
'startTime': list([
|
||||||
]),
|
]),
|
||||||
'targetTemperature': list([
|
'targetTemperature': list([
|
||||||
|
dict({
|
||||||
|
'max': -14,
|
||||||
|
'min': -28,
|
||||||
|
'zone': 1,
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
'ventilationStep': list([
|
'ventilationStep': list([
|
||||||
]),
|
]),
|
||||||
@ -120,6 +135,11 @@
|
|||||||
'startTime': list([
|
'startTime': list([
|
||||||
]),
|
]),
|
||||||
'targetTemperature': list([
|
'targetTemperature': list([
|
||||||
|
dict({
|
||||||
|
'max': -14,
|
||||||
|
'min': -28,
|
||||||
|
'zone': 1,
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
'ventilationStep': list([
|
'ventilationStep': list([
|
||||||
]),
|
]),
|
||||||
@ -689,6 +709,11 @@
|
|||||||
'startTime': list([
|
'startTime': list([
|
||||||
]),
|
]),
|
||||||
'targetTemperature': list([
|
'targetTemperature': list([
|
||||||
|
dict({
|
||||||
|
'max': -14,
|
||||||
|
'min': -28,
|
||||||
|
'zone': 1,
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
'ventilationStep': list([
|
'ventilationStep': list([
|
||||||
]),
|
]),
|
||||||
|
@ -17,11 +17,10 @@ from tests.common import MockConfigEntry, snapshot_platform
|
|||||||
async def test_binary_sensor_states(
|
async def test_binary_sensor_states(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test binary sensor state."""
|
"""Test binary sensor state."""
|
||||||
|
|
||||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||||
|
@ -24,21 +24,20 @@ ENTITY_ID = "button.washing_machine_start"
|
|||||||
async def test_button_states(
|
async def test_button_states(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test button entity state."""
|
"""Test button entity state."""
|
||||||
|
|
||||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
async def test_button_press(
|
async def test_button_press(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test button press."""
|
"""Test button press."""
|
||||||
|
|
||||||
@ -54,7 +53,7 @@ async def test_button_press(
|
|||||||
async def test_api_failure(
|
async def test_api_failure(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test handling of exception from API."""
|
"""Test handling of exception from API."""
|
||||||
mock_miele_client.send_action.side_effect = ClientResponseError("test", "Test")
|
mock_miele_client.send_action.side_effect = ClientResponseError("test", "Test")
|
||||||
|
@ -33,20 +33,19 @@ SERVICE_SET_TEMPERATURE = "set_temperature"
|
|||||||
async def test_climate_states(
|
async def test_climate_states(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test climate entity state."""
|
"""Test climate entity state."""
|
||||||
|
|
||||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||||
|
|
||||||
|
|
||||||
async def test_set_target(
|
async def test_set_target(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the climate can be turned on/off."""
|
"""Test the climate can be turned on/off."""
|
||||||
|
|
||||||
@ -64,7 +63,7 @@ async def test_set_target(
|
|||||||
async def test_api_failure(
|
async def test_api_failure(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test handling of exception from API."""
|
"""Test handling of exception from API."""
|
||||||
mock_miele_client.set_target_temperature.side_effect = ClientError
|
mock_miele_client.set_target_temperature.side_effect = ClientError
|
||||||
|
@ -25,14 +25,13 @@ ENTITY_ID = "fan.hood_fan"
|
|||||||
async def test_fan_states(
|
async def test_fan_states(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test fan entity state."""
|
"""Test fan entity state."""
|
||||||
|
|
||||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("load_device_file", ["fan_devices.json"])
|
@pytest.mark.parametrize("load_device_file", ["fan_devices.json"])
|
||||||
@ -46,7 +45,7 @@ async def test_fan_states(
|
|||||||
async def test_fan_control(
|
async def test_fan_control(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
service: str,
|
service: str,
|
||||||
expected_argument: dict[str, Any],
|
expected_argument: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -74,7 +73,7 @@ async def test_fan_control(
|
|||||||
async def test_fan_set_speed(
|
async def test_fan_set_speed(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
service: str,
|
service: str,
|
||||||
percentage: int,
|
percentage: int,
|
||||||
expected_argument: dict[str, Any],
|
expected_argument: dict[str, Any],
|
||||||
@ -102,7 +101,7 @@ async def test_fan_set_speed(
|
|||||||
async def test_api_failure(
|
async def test_api_failure(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
service: str,
|
service: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test handling of exception from API."""
|
"""Test handling of exception from API."""
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
"""Tests for init module."""
|
"""Tests for init module."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
import http
|
import http
|
||||||
import time
|
import time
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from aiohttp import ClientConnectionError
|
from aiohttp import ClientConnectionError
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
from pymiele import OAUTH2_TOKEN
|
from pymiele import OAUTH2_TOKEN
|
||||||
import pytest
|
import pytest
|
||||||
from syrupy import SnapshotAssertion
|
from syrupy import SnapshotAssertion
|
||||||
@ -17,7 +19,11 @@ from homeassistant.setup import async_setup_component
|
|||||||
|
|
||||||
from . import setup_integration
|
from . import setup_integration
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import (
|
||||||
|
MockConfigEntry,
|
||||||
|
async_fire_time_changed,
|
||||||
|
load_json_object_fixture,
|
||||||
|
)
|
||||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||||
from tests.typing import WebSocketGenerator
|
from tests.typing import WebSocketGenerator
|
||||||
|
|
||||||
@ -157,3 +163,48 @@ async def test_device_remove_devices(
|
|||||||
old_device_entry.id, mock_config_entry.entry_id
|
old_device_entry.id, mock_config_entry.entry_id
|
||||||
)
|
)
|
||||||
assert response["success"]
|
assert response["success"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
async def test_setup_all_platforms(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_miele_client: MagicMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
load_device_file: str,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test that all platforms can be set up."""
|
||||||
|
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
assert hass.states.get("binary_sensor.freezer_door").state == "off"
|
||||||
|
assert hass.states.get("binary_sensor.hood_problem").state == "off"
|
||||||
|
|
||||||
|
assert (
|
||||||
|
hass.states.get("button.washing_machine_start").object_id
|
||||||
|
== "washing_machine_start"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert hass.states.get("climate.freezer").state == "cool"
|
||||||
|
assert hass.states.get("light.hood_light").state == "on"
|
||||||
|
|
||||||
|
assert hass.states.get("sensor.freezer_temperature").state == "-18.0"
|
||||||
|
assert hass.states.get("sensor.washing_machine").state == "off"
|
||||||
|
|
||||||
|
assert hass.states.get("switch.washing_machine_power").state == "off"
|
||||||
|
|
||||||
|
# Add two devices and let the clock tick for 130 seconds
|
||||||
|
freezer.tick(timedelta(seconds=130))
|
||||||
|
mock_miele_client.get_devices.return_value = load_json_object_fixture(
|
||||||
|
"5_devices.json", DOMAIN
|
||||||
|
)
|
||||||
|
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(device_registry.devices) == 6
|
||||||
|
|
||||||
|
# Check a sample sensor for each new device
|
||||||
|
assert hass.states.get("sensor.dishwasher").state == "in_use"
|
||||||
|
assert hass.states.get("sensor.oven_temperature").state == "175.0"
|
||||||
|
@ -23,14 +23,13 @@ ENTITY_ID = "light.hood_light"
|
|||||||
async def test_light_states(
|
async def test_light_states(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test light entity state."""
|
"""Test light entity state."""
|
||||||
|
|
||||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -43,7 +42,7 @@ async def test_light_states(
|
|||||||
async def test_light_toggle(
|
async def test_light_toggle(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
service: str,
|
service: str,
|
||||||
light_state: int,
|
light_state: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -67,7 +66,7 @@ async def test_light_toggle(
|
|||||||
async def test_api_failure(
|
async def test_api_failure(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
service: str,
|
service: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test handling of exception from API."""
|
"""Test handling of exception from API."""
|
||||||
|
@ -17,11 +17,10 @@ from tests.common import MockConfigEntry, snapshot_platform
|
|||||||
async def test_sensor_states(
|
async def test_sensor_states(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test sensor state."""
|
"""Test sensor state."""
|
||||||
|
|
||||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||||
|
@ -23,14 +23,13 @@ ENTITY_ID = "switch.freezer_superfreezing"
|
|||||||
async def test_switch_states(
|
async def test_switch_states(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test switch entity state."""
|
"""Test switch entity state."""
|
||||||
|
|
||||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -51,7 +50,7 @@ async def test_switch_states(
|
|||||||
async def test_switching(
|
async def test_switching(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
service: str,
|
service: str,
|
||||||
entity: str,
|
entity: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -81,7 +80,7 @@ async def test_switching(
|
|||||||
async def test_api_failure(
|
async def test_api_failure(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
setup_platform: None,
|
setup_platform: MockConfigEntry,
|
||||||
service: str,
|
service: str,
|
||||||
entity: str,
|
entity: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user