mirror of
https://github.com/home-assistant/core.git
synced 2025-07-11 23:37:18 +00:00
Make devices dynamic in Sensibo (#134935)
This commit is contained in:
parent
64752af4c2
commit
fe8cae8eb5
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
import logging
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from pysensibo.model import MotionSensor, SensiboDevice
|
from pysensibo.model import MotionSensor, SensiboDevice
|
||||||
@ -18,6 +19,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import SensiboConfigEntry
|
from . import SensiboConfigEntry
|
||||||
|
from .const import LOGGER
|
||||||
from .coordinator import SensiboDataUpdateCoordinator
|
from .coordinator import SensiboDataUpdateCoordinator
|
||||||
from .entity import SensiboDeviceBaseEntity, SensiboMotionBaseEntity
|
from .entity import SensiboDeviceBaseEntity, SensiboMotionBaseEntity
|
||||||
|
|
||||||
@ -122,32 +124,55 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
coordinator = entry.runtime_data
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
entities: list[SensiboMotionSensor | SensiboDeviceSensor] = []
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
for device_id, device_data in coordinator.data.parsed.items():
|
def _add_remove_devices() -> None:
|
||||||
if device_data.motion_sensors:
|
"""Handle additions of devices and sensors."""
|
||||||
|
entities: list[SensiboMotionSensor | SensiboDeviceSensor] = []
|
||||||
|
nonlocal added_devices
|
||||||
|
new_devices, remove_devices, added_devices = coordinator.get_devices(
|
||||||
|
added_devices
|
||||||
|
)
|
||||||
|
|
||||||
|
if LOGGER.isEnabledFor(logging.DEBUG):
|
||||||
|
LOGGER.debug(
|
||||||
|
"New devices: %s, Removed devices: %s, Existing devices: %s",
|
||||||
|
new_devices,
|
||||||
|
remove_devices,
|
||||||
|
added_devices,
|
||||||
|
)
|
||||||
|
|
||||||
|
if new_devices:
|
||||||
entities.extend(
|
entities.extend(
|
||||||
SensiboMotionSensor(
|
SensiboMotionSensor(
|
||||||
coordinator, device_id, sensor_id, sensor_data, description
|
coordinator, device_id, sensor_id, sensor_data, description
|
||||||
)
|
)
|
||||||
|
for device_id, device_data in coordinator.data.parsed.items()
|
||||||
|
if device_data.motion_sensors
|
||||||
for sensor_id, sensor_data in device_data.motion_sensors.items()
|
for sensor_id, sensor_data in device_data.motion_sensors.items()
|
||||||
|
if sensor_id in new_devices
|
||||||
for description in MOTION_SENSOR_TYPES
|
for description in MOTION_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
entities.extend(
|
|
||||||
SensiboDeviceSensor(coordinator, device_id, description)
|
|
||||||
for description in MOTION_DEVICE_SENSOR_TYPES
|
|
||||||
for device_id, device_data in coordinator.data.parsed.items()
|
|
||||||
if device_data.motion_sensors
|
|
||||||
)
|
|
||||||
entities.extend(
|
|
||||||
SensiboDeviceSensor(coordinator, device_id, description)
|
|
||||||
for device_id, device_data in coordinator.data.parsed.items()
|
|
||||||
for description in DESCRIPTION_BY_MODELS.get(
|
|
||||||
device_data.model, DEVICE_SENSOR_TYPES
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
async_add_entities(entities)
|
entities.extend(
|
||||||
|
SensiboDeviceSensor(coordinator, device_id, description)
|
||||||
|
for device_id, device_data in coordinator.data.parsed.items()
|
||||||
|
if device_data.motion_sensors and device_id in new_devices
|
||||||
|
for description in MOTION_DEVICE_SENSOR_TYPES
|
||||||
|
)
|
||||||
|
entities.extend(
|
||||||
|
SensiboDeviceSensor(coordinator, device_id, description)
|
||||||
|
for device_id, device_data in coordinator.data.parsed.items()
|
||||||
|
if device_id in new_devices
|
||||||
|
for description in DESCRIPTION_BY_MODELS.get(
|
||||||
|
device_data.model, DEVICE_SENSOR_TYPES
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
entry.async_on_unload(coordinator.async_add_listener(_add_remove_devices))
|
||||||
|
_add_remove_devices()
|
||||||
|
|
||||||
|
|
||||||
class SensiboMotionSensor(SensiboMotionBaseEntity, BinarySensorEntity):
|
class SensiboMotionSensor(SensiboMotionBaseEntity, BinarySensorEntity):
|
||||||
|
@ -41,10 +41,22 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
coordinator = entry.runtime_data
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
async_add_entities(
|
added_devices: set[str] = set()
|
||||||
SensiboDeviceButton(coordinator, device_id, DEVICE_BUTTON_TYPES)
|
|
||||||
for device_id, device_data in coordinator.data.parsed.items()
|
def _add_remove_devices() -> None:
|
||||||
)
|
"""Handle additions of devices and sensors."""
|
||||||
|
nonlocal added_devices
|
||||||
|
new_devices, _, added_devices = coordinator.get_devices(added_devices)
|
||||||
|
|
||||||
|
if new_devices:
|
||||||
|
async_add_entities(
|
||||||
|
SensiboDeviceButton(coordinator, device_id, DEVICE_BUTTON_TYPES)
|
||||||
|
for device_id in coordinator.data.parsed
|
||||||
|
if device_id in new_devices
|
||||||
|
)
|
||||||
|
|
||||||
|
entry.async_on_unload(coordinator.async_add_listener(_add_remove_devices))
|
||||||
|
_add_remove_devices()
|
||||||
|
|
||||||
|
|
||||||
class SensiboDeviceButton(SensiboDeviceBaseEntity, ButtonEntity):
|
class SensiboDeviceButton(SensiboDeviceBaseEntity, ButtonEntity):
|
||||||
|
@ -144,12 +144,22 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
coordinator = entry.runtime_data
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
entities = [
|
added_devices: set[str] = set()
|
||||||
SensiboClimate(coordinator, device_id)
|
|
||||||
for device_id, device_data in coordinator.data.parsed.items()
|
|
||||||
]
|
|
||||||
|
|
||||||
async_add_entities(entities)
|
def _add_remove_devices() -> None:
|
||||||
|
"""Handle additions of devices and sensors."""
|
||||||
|
nonlocal added_devices
|
||||||
|
new_devices, _, added_devices = coordinator.get_devices(added_devices)
|
||||||
|
|
||||||
|
if new_devices:
|
||||||
|
async_add_entities(
|
||||||
|
SensiboClimate(coordinator, device_id)
|
||||||
|
for device_id in coordinator.data.parsed
|
||||||
|
if device_id in new_devices
|
||||||
|
)
|
||||||
|
|
||||||
|
entry.async_on_unload(coordinator.async_add_listener(_add_remove_devices))
|
||||||
|
_add_remove_devices()
|
||||||
|
|
||||||
platform = entity_platform.async_get_current_platform()
|
platform = entity_platform.async_get_current_platform()
|
||||||
platform.async_register_entity_service(
|
platform.async_register_entity_service(
|
||||||
|
@ -12,6 +12,7 @@ from pysensibo.model import SensiboData
|
|||||||
from homeassistant.const import CONF_API_KEY
|
from homeassistant.const import CONF_API_KEY
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||||
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.debounce import Debouncer
|
from homeassistant.helpers.debounce import Debouncer
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
@ -48,6 +49,25 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator[SensiboData]):
|
|||||||
session=async_get_clientsession(hass),
|
session=async_get_clientsession(hass),
|
||||||
timeout=TIMEOUT,
|
timeout=TIMEOUT,
|
||||||
)
|
)
|
||||||
|
self.previous_devices: set[str] = set()
|
||||||
|
|
||||||
|
def get_devices(
|
||||||
|
self, added_devices: set[str]
|
||||||
|
) -> tuple[set[str], set[str], set[str]]:
|
||||||
|
"""Addition and removal of devices."""
|
||||||
|
data = self.data
|
||||||
|
motion_sensors = {
|
||||||
|
sensor_id
|
||||||
|
for device_data in data.parsed.values()
|
||||||
|
if device_data.motion_sensors
|
||||||
|
for sensor_id in device_data.motion_sensors
|
||||||
|
}
|
||||||
|
devices: set[str] = set(data.parsed)
|
||||||
|
new_devices: set[str] = motion_sensors | devices - added_devices
|
||||||
|
remove_devices = added_devices - devices - motion_sensors
|
||||||
|
added_devices = (added_devices - remove_devices) | new_devices
|
||||||
|
|
||||||
|
return (new_devices, remove_devices, added_devices)
|
||||||
|
|
||||||
async def _async_update_data(self) -> SensiboData:
|
async def _async_update_data(self) -> SensiboData:
|
||||||
"""Fetch data from Sensibo."""
|
"""Fetch data from Sensibo."""
|
||||||
@ -67,4 +87,23 @@ class SensiboDataUpdateCoordinator(DataUpdateCoordinator[SensiboData]):
|
|||||||
|
|
||||||
if not data.raw:
|
if not data.raw:
|
||||||
raise UpdateFailed(translation_domain=DOMAIN, translation_key="no_data")
|
raise UpdateFailed(translation_domain=DOMAIN, translation_key="no_data")
|
||||||
|
|
||||||
|
current_devices = set(data.parsed)
|
||||||
|
for device_data in data.parsed.values():
|
||||||
|
if device_data.motion_sensors:
|
||||||
|
for motion_sensor_id in device_data.motion_sensors:
|
||||||
|
current_devices.add(motion_sensor_id)
|
||||||
|
|
||||||
|
if stale_devices := self.previous_devices - current_devices:
|
||||||
|
LOGGER.debug("Removing stale devices: %s", stale_devices)
|
||||||
|
device_registry = dr.async_get(self.hass)
|
||||||
|
for _id in stale_devices:
|
||||||
|
device = device_registry.async_get_device(identifiers={(DOMAIN, _id)})
|
||||||
|
if device:
|
||||||
|
device_registry.async_update_device(
|
||||||
|
device_id=device.id,
|
||||||
|
remove_config_entry_id=self.config_entry.entry_id,
|
||||||
|
)
|
||||||
|
self.previous_devices = current_devices
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -71,11 +71,23 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
coordinator = entry.runtime_data
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
async_add_entities(
|
added_devices: set[str] = set()
|
||||||
SensiboNumber(coordinator, device_id, description)
|
|
||||||
for device_id, device_data in coordinator.data.parsed.items()
|
def _add_remove_devices() -> None:
|
||||||
for description in DEVICE_NUMBER_TYPES
|
"""Handle additions of devices and sensors."""
|
||||||
)
|
nonlocal added_devices
|
||||||
|
new_devices, _, added_devices = coordinator.get_devices(added_devices)
|
||||||
|
|
||||||
|
if new_devices:
|
||||||
|
async_add_entities(
|
||||||
|
SensiboNumber(coordinator, device_id, description)
|
||||||
|
for device_id in coordinator.data.parsed
|
||||||
|
for description in DEVICE_NUMBER_TYPES
|
||||||
|
if device_id in new_devices
|
||||||
|
)
|
||||||
|
|
||||||
|
entry.async_on_unload(coordinator.async_add_listener(_add_remove_devices))
|
||||||
|
_add_remove_devices()
|
||||||
|
|
||||||
|
|
||||||
class SensiboNumber(SensiboDeviceBaseEntity, NumberEntity):
|
class SensiboNumber(SensiboDeviceBaseEntity, NumberEntity):
|
||||||
|
@ -54,7 +54,7 @@ rules:
|
|||||||
entity-category: done
|
entity-category: done
|
||||||
entity-disabled-by-default: done
|
entity-disabled-by-default: done
|
||||||
discovery: done
|
discovery: done
|
||||||
stale-devices: todo
|
stale-devices: done
|
||||||
diagnostics:
|
diagnostics:
|
||||||
status: done
|
status: done
|
||||||
comment: |
|
comment: |
|
||||||
@ -62,7 +62,7 @@ rules:
|
|||||||
exception-translations: done
|
exception-translations: done
|
||||||
icon-translations: done
|
icon-translations: done
|
||||||
reconfiguration-flow: done
|
reconfiguration-flow: done
|
||||||
dynamic-devices: todo
|
dynamic-devices: done
|
||||||
discovery-update-info:
|
discovery-update-info:
|
||||||
status: exempt
|
status: exempt
|
||||||
comment: |
|
comment: |
|
||||||
|
@ -108,17 +108,27 @@ async def async_setup_entry(
|
|||||||
"entity": entity_id,
|
"entity": entity_id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
entities.extend(
|
|
||||||
[
|
|
||||||
SensiboSelect(coordinator, device_id, description)
|
|
||||||
for device_id, device_data in coordinator.data.parsed.items()
|
|
||||||
for description in DEVICE_SELECT_TYPES
|
|
||||||
if description.key in device_data.full_features
|
|
||||||
]
|
|
||||||
)
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
|
def _add_remove_devices() -> None:
|
||||||
|
"""Handle additions of devices and sensors."""
|
||||||
|
nonlocal added_devices
|
||||||
|
new_devices, _, added_devices = coordinator.get_devices(added_devices)
|
||||||
|
|
||||||
|
if new_devices:
|
||||||
|
async_add_entities(
|
||||||
|
SensiboSelect(coordinator, device_id, description)
|
||||||
|
for device_id, device_data in coordinator.data.parsed.items()
|
||||||
|
if device_id in new_devices
|
||||||
|
for description in DEVICE_SELECT_TYPES
|
||||||
|
if description.key in device_data.full_features
|
||||||
|
)
|
||||||
|
|
||||||
|
entry.async_on_unload(coordinator.async_add_listener(_add_remove_devices))
|
||||||
|
_add_remove_devices()
|
||||||
|
|
||||||
|
|
||||||
class SensiboSelect(SensiboDeviceBaseEntity, SelectEntity):
|
class SensiboSelect(SensiboDeviceBaseEntity, SelectEntity):
|
||||||
"""Representation of a Sensibo Select."""
|
"""Representation of a Sensibo Select."""
|
||||||
|
@ -246,25 +246,40 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
coordinator = entry.runtime_data
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
entities: list[SensiboMotionSensor | SensiboDeviceSensor] = []
|
added_devices: set[str] = set()
|
||||||
|
|
||||||
for device_id, device_data in coordinator.data.parsed.items():
|
def _add_remove_devices() -> None:
|
||||||
if device_data.motion_sensors:
|
"""Handle additions of devices and sensors."""
|
||||||
|
|
||||||
|
entities: list[SensiboMotionSensor | SensiboDeviceSensor] = []
|
||||||
|
nonlocal added_devices
|
||||||
|
new_devices, remove_devices, added_devices = coordinator.get_devices(
|
||||||
|
added_devices
|
||||||
|
)
|
||||||
|
|
||||||
|
if new_devices:
|
||||||
entities.extend(
|
entities.extend(
|
||||||
SensiboMotionSensor(
|
SensiboMotionSensor(
|
||||||
coordinator, device_id, sensor_id, sensor_data, description
|
coordinator, device_id, sensor_id, sensor_data, description
|
||||||
)
|
)
|
||||||
|
for device_id, device_data in coordinator.data.parsed.items()
|
||||||
|
if device_data.motion_sensors
|
||||||
for sensor_id, sensor_data in device_data.motion_sensors.items()
|
for sensor_id, sensor_data in device_data.motion_sensors.items()
|
||||||
|
if sensor_id in new_devices
|
||||||
for description in MOTION_SENSOR_TYPES
|
for description in MOTION_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
entities.extend(
|
entities.extend(
|
||||||
SensiboDeviceSensor(coordinator, device_id, description)
|
SensiboDeviceSensor(coordinator, device_id, description)
|
||||||
for device_id, device_data in coordinator.data.parsed.items()
|
for device_id, device_data in coordinator.data.parsed.items()
|
||||||
for description in DESCRIPTION_BY_MODELS.get(
|
if device_id in new_devices
|
||||||
device_data.model, DEVICE_SENSOR_TYPES
|
for description in DESCRIPTION_BY_MODELS.get(
|
||||||
)
|
device_data.model, DEVICE_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
async_add_entities(entities)
|
)
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
entry.async_on_unload(coordinator.async_add_listener(_add_remove_devices))
|
||||||
|
_add_remove_devices()
|
||||||
|
|
||||||
|
|
||||||
class SensiboMotionSensor(SensiboMotionBaseEntity, SensorEntity):
|
class SensiboMotionSensor(SensiboMotionBaseEntity, SensorEntity):
|
||||||
|
@ -84,13 +84,25 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
coordinator = entry.runtime_data
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
async_add_entities(
|
added_devices: set[str] = set()
|
||||||
SensiboDeviceSwitch(coordinator, device_id, description)
|
|
||||||
for device_id, device_data in coordinator.data.parsed.items()
|
def _add_remove_devices() -> None:
|
||||||
for description in DESCRIPTION_BY_MODELS.get(
|
"""Handle additions of devices and sensors."""
|
||||||
device_data.model, DEVICE_SWITCH_TYPES
|
nonlocal added_devices
|
||||||
)
|
new_devices, _, added_devices = coordinator.get_devices(added_devices)
|
||||||
)
|
|
||||||
|
if new_devices:
|
||||||
|
async_add_entities(
|
||||||
|
SensiboDeviceSwitch(coordinator, device_id, description)
|
||||||
|
for device_id, device_data in coordinator.data.parsed.items()
|
||||||
|
if device_id in new_devices
|
||||||
|
for description in DESCRIPTION_BY_MODELS.get(
|
||||||
|
device_data.model, DEVICE_SWITCH_TYPES
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
entry.async_on_unload(coordinator.async_add_listener(_add_remove_devices))
|
||||||
|
_add_remove_devices()
|
||||||
|
|
||||||
|
|
||||||
class SensiboDeviceSwitch(SensiboDeviceBaseEntity, SwitchEntity):
|
class SensiboDeviceSwitch(SensiboDeviceBaseEntity, SwitchEntity):
|
||||||
|
@ -51,12 +51,24 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
coordinator = entry.runtime_data
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
async_add_entities(
|
added_devices: set[str] = set()
|
||||||
SensiboDeviceUpdate(coordinator, device_id, description)
|
|
||||||
for description in DEVICE_SENSOR_TYPES
|
def _add_remove_devices() -> None:
|
||||||
for device_id, device_data in coordinator.data.parsed.items()
|
"""Handle additions of devices and sensors."""
|
||||||
if description.value_available(device_data) is not None
|
nonlocal added_devices
|
||||||
)
|
new_devices, _, added_devices = coordinator.get_devices(added_devices)
|
||||||
|
|
||||||
|
if new_devices:
|
||||||
|
async_add_entities(
|
||||||
|
SensiboDeviceUpdate(coordinator, device_id, description)
|
||||||
|
for device_id, device_data in coordinator.data.parsed.items()
|
||||||
|
if device_id in new_devices
|
||||||
|
for description in DEVICE_SENSOR_TYPES
|
||||||
|
if description.value_available(device_data) is not None
|
||||||
|
)
|
||||||
|
|
||||||
|
entry.async_on_unload(coordinator.async_add_listener(_add_remove_devices))
|
||||||
|
_add_remove_devices()
|
||||||
|
|
||||||
|
|
||||||
class SensiboDeviceUpdate(SensiboDeviceBaseEntity, UpdateEntity):
|
class SensiboDeviceUpdate(SensiboDeviceBaseEntity, UpdateEntity):
|
||||||
|
@ -2,8 +2,14 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
from typing import Any
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
from pysensibo.model import SensiboData
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.sensibo.const import DOMAIN
|
from homeassistant.components.sensibo.const import DOMAIN
|
||||||
from homeassistant.components.sensibo.util import NoUsernameError
|
from homeassistant.components.sensibo.util import NoUsernameError
|
||||||
from homeassistant.config_entries import SOURCE_USER, ConfigEntry, ConfigEntryState
|
from homeassistant.config_entries import SOURCE_USER, ConfigEntry, ConfigEntryState
|
||||||
@ -13,7 +19,7 @@ from homeassistant.setup import async_setup_component
|
|||||||
|
|
||||||
from . import ENTRY_CONFIG
|
from . import ENTRY_CONFIG
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
from tests.typing import WebSocketGenerator
|
from tests.typing import WebSocketGenerator
|
||||||
|
|
||||||
|
|
||||||
@ -103,3 +109,73 @@ async def test_device_remove_devices(
|
|||||||
)
|
)
|
||||||
response = await client.remove_device(dead_device_entry.id, load_int.entry_id)
|
response = await client.remove_device(dead_device_entry.id, load_int.entry_id)
|
||||||
assert response["success"]
|
assert response["success"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("entity_id", "device_ids"),
|
||||||
|
[
|
||||||
|
# Device is ABC999111
|
||||||
|
("climate.hallway", ["ABC999111"]),
|
||||||
|
("binary_sensor.hallway_filter_clean_required", ["ABC999111"]),
|
||||||
|
("number.hallway_temperature_calibration", ["ABC999111"]),
|
||||||
|
("sensor.hallway_filter_last_reset", ["ABC999111"]),
|
||||||
|
("update.hallway_firmware", ["ABC999111"]),
|
||||||
|
# Device is AABBCC belonging to device ABC999111
|
||||||
|
("binary_sensor.hallway_motion_sensor_motion", ["ABC999111", "AABBCC"]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
async def test_automatic_device_addition_and_removal(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
load_int: ConfigEntry,
|
||||||
|
mock_client: MagicMock,
|
||||||
|
get_data: tuple[SensiboData, dict[str, Any], dict[str, Any]],
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
entity_id: str,
|
||||||
|
device_ids: list[str],
|
||||||
|
) -> None:
|
||||||
|
"""Test for automatic device addition and removal."""
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert entity_registry.async_get(entity_id)
|
||||||
|
for device_id in device_ids:
|
||||||
|
assert device_registry.async_get_device(identifiers={(DOMAIN, device_id)})
|
||||||
|
|
||||||
|
# Remove one of the devices
|
||||||
|
new_device_list = [
|
||||||
|
device for device in get_data[2]["result"] if device["id"] != device_ids[0]
|
||||||
|
]
|
||||||
|
mock_client.async_get_devices.return_value = {
|
||||||
|
"status": "success",
|
||||||
|
"result": new_device_list,
|
||||||
|
}
|
||||||
|
new_data = {k: v for k, v in get_data[0].parsed.items() if k != device_ids[0]}
|
||||||
|
new_raw = mock_client.async_get_devices.return_value["result"]
|
||||||
|
mock_client.async_get_devices_data.return_value = SensiboData(new_raw, new_data)
|
||||||
|
|
||||||
|
freezer.tick(timedelta(minutes=5))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert not state
|
||||||
|
assert not entity_registry.async_get(entity_id)
|
||||||
|
for device_id in device_ids:
|
||||||
|
assert not device_registry.async_get_device(identifiers={(DOMAIN, device_id)})
|
||||||
|
|
||||||
|
# Add the device back
|
||||||
|
mock_client.async_get_devices.return_value = get_data[2]
|
||||||
|
mock_client.async_get_devices_data.return_value = get_data[0]
|
||||||
|
|
||||||
|
freezer.tick(timedelta(minutes=5))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert entity_registry.async_get(entity_id)
|
||||||
|
for device_id in device_ids:
|
||||||
|
assert device_registry.async_get_device(identifiers={(DOMAIN, device_id)})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user