mirror of
https://github.com/home-assistant/core.git
synced 2026-09-16 05:29:11 +01:00
Bump pytradfri to 14.0.0 (#182250)
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from pytradfri import Gateway, RequestError
|
||||
from pytradfri.api.aiocoap_api import APIFactory
|
||||
from pytradfri.api.aiocoap_api import APIFactory, APIRequestProtocol
|
||||
from pytradfri.command import Command
|
||||
from pytradfri.device import Device
|
||||
|
||||
@@ -56,12 +56,12 @@ async def async_setup_entry(
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)
|
||||
)
|
||||
|
||||
api = factory.request
|
||||
api: APIRequestProtocol = factory.request
|
||||
gateway = Gateway()
|
||||
|
||||
try:
|
||||
gateway_info = await api(gateway.get_gateway_info(), timeout=TIMEOUT_API)
|
||||
devices_commands: Command = await api(
|
||||
devices_commands: list[Command[Device]] = await api(
|
||||
gateway.get_devices(), timeout=TIMEOUT_API
|
||||
)
|
||||
devices: list[Device] = await api(devices_commands, timeout=TIMEOUT_API)
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
"""Tradfri DataUpdateCoordinator."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import timedelta
|
||||
from typing import Any, override
|
||||
from typing import cast, override
|
||||
|
||||
from pytradfri import Gateway
|
||||
from pytradfri.api.aiocoap_api import APIFactory
|
||||
from pytradfri.command import Command
|
||||
from pytradfri.api.aiocoap_api import APIFactory, APIRequestProtocol
|
||||
from pytradfri.device import Device
|
||||
from pytradfri.error import RequestError
|
||||
from pytradfri.resource import ApiResource
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
@@ -28,7 +27,7 @@ class TradfriData:
|
||||
|
||||
factory: APIFactory
|
||||
gateway: Gateway
|
||||
api: Callable[[Command | list[Command]], Any]
|
||||
api: APIRequestProtocol
|
||||
coordinator_list: list[TradfriDeviceDataUpdateCoordinator] = field(
|
||||
default_factory=list
|
||||
)
|
||||
@@ -43,7 +42,7 @@ class TradfriDeviceDataUpdateCoordinator(DataUpdateCoordinator[Device]):
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: TradfriConfigEntry,
|
||||
api: Callable[[Command | list[Command]], Any],
|
||||
api: APIRequestProtocol,
|
||||
device: Device,
|
||||
) -> None:
|
||||
"""Initialize device coordinator."""
|
||||
@@ -67,9 +66,9 @@ class TradfriDeviceDataUpdateCoordinator(DataUpdateCoordinator[Device]):
|
||||
await self.async_request_refresh()
|
||||
|
||||
@callback
|
||||
def _observe_update(self, device: Device) -> None:
|
||||
def _observe_update(self, device: ApiResource) -> None:
|
||||
"""Update the coordinator for a device when a change is detected."""
|
||||
self.async_set_updated_data(data=device)
|
||||
self.async_set_updated_data(data=cast(Device, device))
|
||||
|
||||
@callback
|
||||
def _exception_callback(self, exc: Exception) -> None:
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"""Support for IKEA Tradfri covers."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any, cast, override
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
from pytradfri.command import Command
|
||||
from pytradfri.api.aiocoap_api import APIRequestProtocol
|
||||
|
||||
from homeassistant.components.cover import ATTR_POSITION, CoverEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -42,7 +41,7 @@ class TradfriCover(TradfriBaseEntity, CoverEntity):
|
||||
def __init__(
|
||||
self,
|
||||
device_coordinator: TradfriDeviceDataUpdateCoordinator,
|
||||
api: Callable[[Command | list[Command]], Any],
|
||||
api: APIRequestProtocol,
|
||||
gateway_id: str,
|
||||
) -> None:
|
||||
"""Initialize a switch."""
|
||||
@@ -52,13 +51,15 @@ class TradfriCover(TradfriBaseEntity, CoverEntity):
|
||||
gateway_id=gateway_id,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert self._device.blind_control is not None
|
||||
self._device_control = self._device.blind_control
|
||||
self._device_data = self._device_control.blinds[0]
|
||||
|
||||
@override
|
||||
def _refresh(self) -> None:
|
||||
"""Refresh the device."""
|
||||
self._device_data = self.coordinator.data.blind_control.blinds[0]
|
||||
self._device_data = self._device_control.blinds[0]
|
||||
|
||||
@property
|
||||
@override
|
||||
@@ -75,7 +76,7 @@ class TradfriCover(TradfriBaseEntity, CoverEntity):
|
||||
"""
|
||||
if not self._device_data:
|
||||
return None
|
||||
return 100 - cast(int, self._device_data.current_cover_position)
|
||||
return 100 - self._device_data.current_cover_position
|
||||
|
||||
@override
|
||||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Callable, Coroutine
|
||||
from functools import wraps
|
||||
from typing import Any, cast, override
|
||||
from typing import Any, override
|
||||
|
||||
from pytradfri.api.aiocoap_api import APIRequestProtocol
|
||||
from pytradfri.command import Command
|
||||
from pytradfri.const import ATTR_DEVICE_FIRMWARE_VERSION
|
||||
from pytradfri.device import Device
|
||||
from pytradfri.error import RequestError
|
||||
|
||||
@@ -20,7 +20,7 @@ from .coordinator import TradfriDeviceDataUpdateCoordinator
|
||||
|
||||
|
||||
def handle_error(
|
||||
func: Callable[[Command | list[Command]], Any],
|
||||
func: APIRequestProtocol,
|
||||
) -> Callable[[Command | list[Command]], Coroutine[Any, Any, None]]:
|
||||
"""Handle tradfri api call error."""
|
||||
|
||||
@@ -44,7 +44,7 @@ class TradfriBaseEntity(CoordinatorEntity[TradfriDeviceDataUpdateCoordinator]):
|
||||
self,
|
||||
device_coordinator: TradfriDeviceDataUpdateCoordinator,
|
||||
gateway_id: str,
|
||||
api: Callable[[Command | list[Command]], Any],
|
||||
api: APIRequestProtocol,
|
||||
) -> None:
|
||||
"""Initialize a device."""
|
||||
super().__init__(device_coordinator)
|
||||
@@ -62,7 +62,7 @@ class TradfriBaseEntity(CoordinatorEntity[TradfriDeviceDataUpdateCoordinator]):
|
||||
manufacturer=info.manufacturer,
|
||||
model=info.model_number,
|
||||
name=self._device.name,
|
||||
sw_version=info.raw.get(ATTR_DEVICE_FIRMWARE_VERSION),
|
||||
sw_version=info.firmware_version,
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
device_coordinator.hass,
|
||||
(DOMAIN, gateway_id),
|
||||
@@ -90,4 +90,4 @@ class TradfriBaseEntity(CoordinatorEntity[TradfriDeviceDataUpdateCoordinator]):
|
||||
@override
|
||||
def available(self) -> bool:
|
||||
"""Return if entity is available."""
|
||||
return cast(bool, self._device.reachable) and super().available
|
||||
return self._device.reachable and super().available
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"""Represent an air purifier."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any, cast, override
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
from pytradfri.command import Command
|
||||
from pytradfri.api.aiocoap_api import APIRequestProtocol
|
||||
|
||||
from homeassistant.components.fan import FanEntity, FanEntityFeature
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -69,7 +68,7 @@ class TradfriAirPurifierFan(TradfriBaseEntity, FanEntity):
|
||||
def __init__(
|
||||
self,
|
||||
device_coordinator: TradfriDeviceDataUpdateCoordinator,
|
||||
api: Callable[[Command | list[Command]], Any],
|
||||
api: APIRequestProtocol,
|
||||
gateway_id: str,
|
||||
) -> None:
|
||||
"""Initialize a switch."""
|
||||
@@ -79,13 +78,15 @@ class TradfriAirPurifierFan(TradfriBaseEntity, FanEntity):
|
||||
gateway_id=gateway_id,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert self._device.air_purifier_control is not None
|
||||
self._device_control = self._device.air_purifier_control
|
||||
self._device_data = self._device_control.air_purifiers[0]
|
||||
|
||||
@override
|
||||
def _refresh(self) -> None:
|
||||
"""Refresh the device."""
|
||||
self._device_data = self.coordinator.data.air_purifier_control.air_purifiers[0]
|
||||
self._device_data = self._device_control.air_purifiers[0]
|
||||
|
||||
@property
|
||||
@override
|
||||
@@ -93,7 +94,7 @@ class TradfriAirPurifierFan(TradfriBaseEntity, FanEntity):
|
||||
"""Return true if switch is on."""
|
||||
if not self._device_data:
|
||||
return False
|
||||
return cast(bool, self._device_data.state)
|
||||
return self._device_data.state
|
||||
|
||||
@property
|
||||
@override
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"""Support for IKEA Tradfri lights."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any, cast, override
|
||||
from typing import TYPE_CHECKING, Any, cast, override
|
||||
|
||||
from pytradfri.command import Command
|
||||
from pytradfri.api.aiocoap_api import APIRequestProtocol
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
@@ -54,7 +53,7 @@ class TradfriLight(TradfriBaseEntity, LightEntity):
|
||||
def __init__(
|
||||
self,
|
||||
device_coordinator: TradfriDeviceDataUpdateCoordinator,
|
||||
api: Callable[[Command | list[Command]], Any],
|
||||
api: APIRequestProtocol,
|
||||
gateway_id: str,
|
||||
) -> None:
|
||||
"""Initialize a Light."""
|
||||
@@ -64,6 +63,8 @@ class TradfriLight(TradfriBaseEntity, LightEntity):
|
||||
gateway_id=gateway_id,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert self._device.light_control is not None
|
||||
self._device_control = self._device.light_control
|
||||
self._device_data = self._device_control.lights[0]
|
||||
|
||||
@@ -72,32 +73,27 @@ class TradfriLight(TradfriBaseEntity, LightEntity):
|
||||
|
||||
# Calculate supported color modes
|
||||
modes: set[ColorMode] = {ColorMode.ONOFF}
|
||||
if self._device.light_control.can_set_color:
|
||||
if self._device_data.supports_hsb_xy_color:
|
||||
modes.add(ColorMode.HS)
|
||||
if self._device.light_control.can_set_temp:
|
||||
if self._device_data.supports_color_temp:
|
||||
modes.add(ColorMode.COLOR_TEMP)
|
||||
if self._device.light_control.can_set_dimmer:
|
||||
if self._device_data.supports_dimmer:
|
||||
modes.add(ColorMode.BRIGHTNESS)
|
||||
self._attr_supported_color_modes = filter_supported_color_modes(modes)
|
||||
if len(self._attr_supported_color_modes) == 1:
|
||||
self._fixed_color_mode = next(iter(self._attr_supported_color_modes))
|
||||
|
||||
if self._device_control:
|
||||
self._attr_max_color_temp_kelvin = (
|
||||
color_util.color_temperature_mired_to_kelvin(
|
||||
self._device_control.min_mireds
|
||||
)
|
||||
)
|
||||
self._attr_min_color_temp_kelvin = (
|
||||
color_util.color_temperature_mired_to_kelvin(
|
||||
self._device_control.max_mireds
|
||||
)
|
||||
)
|
||||
self._attr_max_color_temp_kelvin = color_util.color_temperature_mired_to_kelvin(
|
||||
self._device_control.min_mireds
|
||||
)
|
||||
self._attr_min_color_temp_kelvin = color_util.color_temperature_mired_to_kelvin(
|
||||
self._device_control.max_mireds
|
||||
)
|
||||
|
||||
@override
|
||||
def _refresh(self) -> None:
|
||||
"""Refresh the device."""
|
||||
self._device_data = self.coordinator.data.light_control.lights[0]
|
||||
self._device_data = self._device_control.lights[0]
|
||||
|
||||
@property
|
||||
@override
|
||||
@@ -105,7 +101,7 @@ class TradfriLight(TradfriBaseEntity, LightEntity):
|
||||
"""Return true if light is on."""
|
||||
if not self._device_data:
|
||||
return False
|
||||
return cast(bool, self._device_data.state)
|
||||
return self._device_data.state
|
||||
|
||||
@property
|
||||
@override
|
||||
@@ -137,23 +133,18 @@ class TradfriLight(TradfriBaseEntity, LightEntity):
|
||||
@override
|
||||
def hs_color(self) -> tuple[float, float] | None:
|
||||
"""HS color of the light."""
|
||||
if not self._device_control or not self._device_data:
|
||||
hsbxy = self._device_data.hsb_xy_color
|
||||
if hsbxy is None:
|
||||
return None
|
||||
if self._device_control.can_set_color:
|
||||
hsbxy = self._device_data.hsb_xy_color
|
||||
hue = hsbxy[0] / (self._device_control.max_hue / 360)
|
||||
sat = hsbxy[1] / (self._device_control.max_saturation / 100)
|
||||
if hue is not None and sat is not None:
|
||||
return hue, sat
|
||||
return None
|
||||
hue = hsbxy[0] / (self._device_control.max_hue / 360)
|
||||
sat = hsbxy[1] / (self._device_control.max_saturation / 100)
|
||||
return hue, sat
|
||||
|
||||
@override
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Instruct the light to turn off."""
|
||||
# This allows transitioning to off, but resets the brightness
|
||||
# to 1 for the next set_state(True) command
|
||||
if not self._device_control:
|
||||
return
|
||||
transition_time = None
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
transition_time = int(kwargs[ATTR_TRANSITION]) * 10
|
||||
@@ -169,8 +160,6 @@ class TradfriLight(TradfriBaseEntity, LightEntity):
|
||||
@override
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Instruct the light to turn on."""
|
||||
if not self._device_control:
|
||||
return
|
||||
transition_time = None
|
||||
if ATTR_TRANSITION in kwargs:
|
||||
transition_time = int(kwargs[ATTR_TRANSITION]) * 10
|
||||
@@ -189,59 +178,55 @@ class TradfriLight(TradfriBaseEntity, LightEntity):
|
||||
dimmer_command = self._device_control.set_state(True)
|
||||
|
||||
color_command = None
|
||||
if ATTR_HS_COLOR in kwargs and self._device_control.can_set_color:
|
||||
if ATTR_HS_COLOR in kwargs and self._device_data.supports_hsb_xy_color:
|
||||
hue = int(kwargs[ATTR_HS_COLOR][0] * (self._device_control.max_hue / 360))
|
||||
sat = int(
|
||||
kwargs[ATTR_HS_COLOR][1] * (self._device_control.max_saturation / 100)
|
||||
)
|
||||
color_data = {
|
||||
"hue": hue,
|
||||
"saturation": sat,
|
||||
"transition_time": transition_time,
|
||||
}
|
||||
color_command = self._device_control.set_hsb(**color_data)
|
||||
color_command = self._device_control.set_hsb(
|
||||
hue=hue, saturation=sat, transition_time=transition_time
|
||||
)
|
||||
transition_time = None
|
||||
|
||||
temp_command = None
|
||||
if ATTR_COLOR_TEMP_KELVIN in kwargs and (
|
||||
self._device_control.can_set_temp or self._device_control.can_set_color
|
||||
self._device_data.supports_color_temp
|
||||
or self._device_data.supports_hsb_xy_color
|
||||
):
|
||||
temp_k = kwargs[ATTR_COLOR_TEMP_KELVIN]
|
||||
# White Spectrum bulb
|
||||
if self._device_control.can_set_temp:
|
||||
if self._device_data.supports_color_temp:
|
||||
temp = color_util.color_temperature_kelvin_to_mired(temp_k)
|
||||
if temp < (min_mireds := self._device_control.min_mireds):
|
||||
temp = min_mireds
|
||||
elif temp > (max_mireds := self._device_control.max_mireds):
|
||||
temp = max_mireds
|
||||
temp_data = {
|
||||
"color_temp": temp,
|
||||
"transition_time": transition_time,
|
||||
}
|
||||
temp_command = self._device_control.set_color_temp(**temp_data)
|
||||
temp_command = self._device_control.set_color_temp(
|
||||
color_temp=temp, transition_time=transition_time
|
||||
)
|
||||
transition_time = None
|
||||
# Color bulb (CWS)
|
||||
# color_temp needs to be set with hue/saturation
|
||||
elif self._device_control.can_set_color:
|
||||
elif self._device_data.supports_hsb_xy_color:
|
||||
hs_color = color_util.color_temperature_to_hs(temp_k)
|
||||
hue = int(hs_color[0] * (self._device_control.max_hue / 360))
|
||||
sat = int(hs_color[1] * (self._device_control.max_saturation / 100))
|
||||
color_data = {
|
||||
"hue": hue,
|
||||
"saturation": sat,
|
||||
"transition_time": transition_time,
|
||||
}
|
||||
color_command = self._device_control.set_hsb(**color_data)
|
||||
color_command = self._device_control.set_hsb(
|
||||
hue=hue, saturation=sat, transition_time=transition_time
|
||||
)
|
||||
transition_time = None
|
||||
|
||||
# HSB can always be set, but color temp + brightness is bulb dependent
|
||||
if (command := dimmer_command) is not None:
|
||||
command += color_command
|
||||
else:
|
||||
command = color_command
|
||||
command = dimmer_command
|
||||
if color_command is not None:
|
||||
command = self._device_control.combine_commands(
|
||||
[dimmer_command, color_command]
|
||||
)
|
||||
|
||||
if self._device_control.can_combine_commands:
|
||||
await self._api(command + temp_command)
|
||||
if self._device_control.can_combine_commands and temp_command is not None:
|
||||
await self._api(
|
||||
self._device_control.combine_commands([command, temp_command])
|
||||
)
|
||||
else:
|
||||
if temp_command is not None:
|
||||
await self._api(temp_command)
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["pytradfri"],
|
||||
"requirements": ["pytradfri[async]==9.0.1"]
|
||||
"requirements": ["pytradfri[async]==14.0.0"]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, cast, override
|
||||
|
||||
from pytradfri.command import Command
|
||||
from pytradfri.api.aiocoap_api import APIRequestProtocol
|
||||
from pytradfri.device import Device
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
@@ -38,17 +38,14 @@ def _get_air_quality(device: Device) -> int | None:
|
||||
): # The sensor returns 65535 if the fan is turned off
|
||||
return None
|
||||
|
||||
return cast(int, device.air_purifier_control.air_purifiers[0].air_quality)
|
||||
return device.air_purifier_control.air_purifiers[0].air_quality
|
||||
|
||||
|
||||
def _get_filter_time_left(device: Device) -> int:
|
||||
"""Fetch the filter's remaining lifetime (in hours)."""
|
||||
assert device.air_purifier_control is not None
|
||||
return round(
|
||||
cast(
|
||||
int, device.air_purifier_control.air_purifiers[0].filter_lifetime_remaining
|
||||
)
|
||||
/ 60
|
||||
device.air_purifier_control.air_purifiers[0].filter_lifetime_remaining / 60
|
||||
)
|
||||
|
||||
|
||||
@@ -163,7 +160,7 @@ class TradfriSensor(TradfriBaseEntity, SensorEntity):
|
||||
def __init__(
|
||||
self,
|
||||
device_coordinator: TradfriDeviceDataUpdateCoordinator,
|
||||
api: Callable[[Command | list[Command]], Any],
|
||||
api: APIRequestProtocol,
|
||||
gateway_id: str,
|
||||
description: TradfriSensorEntityDescription,
|
||||
) -> None:
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"""Support for IKEA Tradfri switches."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any, cast, override
|
||||
from typing import TYPE_CHECKING, Any, override
|
||||
|
||||
from pytradfri.command import Command
|
||||
from pytradfri.api.aiocoap_api import APIRequestProtocol
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -42,7 +41,7 @@ class TradfriSwitch(TradfriBaseEntity, SwitchEntity):
|
||||
def __init__(
|
||||
self,
|
||||
device_coordinator: TradfriDeviceDataUpdateCoordinator,
|
||||
api: Callable[[Command | list[Command]], Any],
|
||||
api: APIRequestProtocol,
|
||||
gateway_id: str,
|
||||
) -> None:
|
||||
"""Initialize a switch."""
|
||||
@@ -52,13 +51,15 @@ class TradfriSwitch(TradfriBaseEntity, SwitchEntity):
|
||||
gateway_id=gateway_id,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert self._device.socket_control is not None
|
||||
self._device_control = self._device.socket_control
|
||||
self._device_data = self._device_control.sockets[0]
|
||||
|
||||
@override
|
||||
def _refresh(self) -> None:
|
||||
"""Refresh the device."""
|
||||
self._device_data = self.coordinator.data.socket_control.sockets[0]
|
||||
self._device_data = self._device_control.sockets[0]
|
||||
|
||||
@property
|
||||
@override
|
||||
@@ -66,7 +67,7 @@ class TradfriSwitch(TradfriBaseEntity, SwitchEntity):
|
||||
"""Return true if switch is on."""
|
||||
if not self._device_data:
|
||||
return False
|
||||
return cast(bool, self._device_data.state)
|
||||
return self._device_data.state
|
||||
|
||||
@override
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
|
||||
Generated
+1
-1
@@ -2872,7 +2872,7 @@ pytouchlinesl==0.6.0
|
||||
pytraccar==3.0.0
|
||||
|
||||
# homeassistant.components.tradfri
|
||||
pytradfri[async]==9.0.1
|
||||
pytradfri[async]==14.0.0
|
||||
|
||||
# homeassistant.components.trafikverket_camera
|
||||
# homeassistant.components.trafikverket_ferry
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Common tools used for the Tradfri test suite."""
|
||||
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
@@ -62,7 +61,7 @@ class CommandStore:
|
||||
assert observe_command
|
||||
|
||||
device_path = "/".join(str(v) for v in device.path)
|
||||
device_state = deepcopy(device.raw)
|
||||
device_state = device.raw.dict(by_alias=True)
|
||||
|
||||
# Create a default observed state based on the sent commands.
|
||||
for command in self.sent_commands:
|
||||
|
||||
@@ -7,13 +7,12 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from pytradfri.command import Command
|
||||
from pytradfri.const import ATTR_FIRMWARE_VERSION, ATTR_GATEWAY_ID
|
||||
from pytradfri.device import Device
|
||||
from pytradfri.gateway import Gateway
|
||||
|
||||
from homeassistant.components.tradfri.const import DOMAIN
|
||||
|
||||
from . import GATEWAY_ID, TRADFRI_PATH
|
||||
from . import TRADFRI_PATH
|
||||
from .common import CommandStore
|
||||
|
||||
from tests.common import load_fixture
|
||||
@@ -28,12 +27,12 @@ def mock_entry_setup() -> Generator[AsyncMock]:
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_gateway", autouse=True)
|
||||
def mock_gateway_fixture(command_store: CommandStore) -> Gateway:
|
||||
def mock_gateway_fixture(command_store: CommandStore, gateway_response: str) -> Gateway:
|
||||
"""Mock a Tradfri gateway."""
|
||||
gateway = Gateway()
|
||||
command_store.register_response(
|
||||
gateway.get_gateway_info(),
|
||||
{ATTR_GATEWAY_ID: GATEWAY_ID, ATTR_FIRMWARE_VERSION: "1.2.1234"},
|
||||
json.loads(gateway_response),
|
||||
)
|
||||
command_store.register_response(
|
||||
gateway.get_devices(),
|
||||
@@ -90,10 +89,16 @@ def device(
|
||||
"""Return a device."""
|
||||
device_response: dict[str, Any] = json.loads(request.getfixturevalue(request.param))
|
||||
device = Device(device_response)
|
||||
command_store.register_device(mock_gateway, device.raw)
|
||||
command_store.register_device(mock_gateway, device_response)
|
||||
return device
|
||||
|
||||
|
||||
@pytest.fixture(scope="package")
|
||||
def gateway_response() -> str:
|
||||
"""Return a gateway response."""
|
||||
return load_fixture("gateway.json", DOMAIN)
|
||||
|
||||
|
||||
@pytest.fixture(scope="package")
|
||||
def air_purifier() -> str:
|
||||
"""Return an air purifier response."""
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"9023": "xyz.pool.ntp.pool",
|
||||
"9029": "1.2.1234",
|
||||
"9054": 0,
|
||||
"9055": 0,
|
||||
"9059": 1509788799,
|
||||
"9060": "2017-11-04T09:46:39.046784Z",
|
||||
"9061": 0,
|
||||
"9062": 0,
|
||||
"9066": 5,
|
||||
"9069": 1509474847,
|
||||
"9071": 1,
|
||||
"9072": 0,
|
||||
"9073": 0,
|
||||
"9074": 0,
|
||||
"9075": 0,
|
||||
"9076": 0,
|
||||
"9077": 0,
|
||||
"9078": 0,
|
||||
"9079": 0,
|
||||
"9080": 0,
|
||||
"9081": "mock-gateway-id",
|
||||
"9082": true,
|
||||
"9083": "123-45-67",
|
||||
"9092": 0,
|
||||
"9093": 0,
|
||||
"9103": "blablablabla12.iot.eu-central-1.amazonaws.com",
|
||||
"9105": 0,
|
||||
"9106": 0,
|
||||
"9107": 0,
|
||||
"9118": 0,
|
||||
"9200": "abc12345-a123-b345-c567-123abc123456",
|
||||
"9201": 1,
|
||||
"9202": 1234567890,
|
||||
"9204": 1,
|
||||
"9208": 1234567890,
|
||||
"9209": 1234567890,
|
||||
"9211": 0,
|
||||
"9232": 1234567,
|
||||
"9234": 1,
|
||||
"9235": "SE",
|
||||
"9236": 3600,
|
||||
"9266": 2
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
"""Tests for Tradfri setup."""
|
||||
|
||||
import json
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from pytradfri.const import ATTR_FIRMWARE_VERSION, ATTR_GATEWAY_ID
|
||||
from pytradfri.const import ATTR_GATEWAY_ID
|
||||
from pytradfri.gateway import Gateway
|
||||
|
||||
from homeassistant.components import tradfri
|
||||
@@ -104,6 +105,7 @@ async def test_migrate_config_entry_and_identifiers(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
command_store: CommandStore,
|
||||
gateway_response: str,
|
||||
) -> None:
|
||||
"""Test migration of device registry identifiers to the unique format.
|
||||
|
||||
@@ -120,7 +122,7 @@ async def test_migrate_config_entry_and_identifiers(
|
||||
},
|
||||
)
|
||||
|
||||
gateway1 = mock_gateway_fixture(command_store, GATEWAY_ID1)
|
||||
gateway1 = mock_gateway_fixture(command_store, GATEWAY_ID1, gateway_response)
|
||||
command_store.register_device(
|
||||
gateway1, await async_load_json_object_fixture(hass, "bulb_w.json", DOMAIN)
|
||||
)
|
||||
@@ -226,12 +228,16 @@ async def test_migrate_config_entry_and_identifiers(
|
||||
assert config_entry3.version == 1
|
||||
|
||||
|
||||
def mock_gateway_fixture(command_store: CommandStore, gateway_id: str) -> Gateway:
|
||||
def mock_gateway_fixture(
|
||||
command_store: CommandStore, gateway_id: str, gateway_response: str
|
||||
) -> Gateway:
|
||||
"""Mock a Tradfri gateway."""
|
||||
gateway = Gateway()
|
||||
gateway_info_response = json.loads(gateway_response)
|
||||
gateway_info_response[ATTR_GATEWAY_ID] = gateway_id
|
||||
command_store.register_response(
|
||||
gateway.get_gateway_info(),
|
||||
{ATTR_GATEWAY_ID: gateway_id, ATTR_FIRMWARE_VERSION: "1.2.1234"},
|
||||
gateway_info_response,
|
||||
)
|
||||
command_store.register_response(
|
||||
gateway.get_devices(),
|
||||
|
||||
@@ -245,10 +245,16 @@ async def test_turn_on(
|
||||
state_attributes: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test turning on a light."""
|
||||
# Make sure the light is off.
|
||||
device.raw[ATTR_LIGHT_CONTROL][0][ATTR_DEVICE_STATE] = 0
|
||||
await setup_integration(hass)
|
||||
|
||||
await command_store.trigger_observe_callback(
|
||||
hass, device, {ATTR_LIGHT_CONTROL: [{ATTR_DEVICE_STATE: 0}]}
|
||||
)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
|
||||
Reference in New Issue
Block a user