From 6f23c02bb52d00a531738762cfc7e99bc05dacae Mon Sep 17 00:00:00 2001 From: Jamie Magee Date: Tue, 15 Sep 2026 11:57:09 -0700 Subject: [PATCH] Bump pytradfri to 14.0.0 (#182250) --- homeassistant/components/tradfri/__init__.py | 6 +- .../components/tradfri/coordinator.py | 15 ++- homeassistant/components/tradfri/cover.py | 13 ++- homeassistant/components/tradfri/entity.py | 12 +- homeassistant/components/tradfri/fan.py | 13 ++- homeassistant/components/tradfri/light.py | 103 ++++++++---------- .../components/tradfri/manifest.json | 2 +- homeassistant/components/tradfri/sensor.py | 11 +- homeassistant/components/tradfri/switch.py | 13 ++- requirements_all.txt | 2 +- tests/components/tradfri/common.py | 3 +- tests/components/tradfri/conftest.py | 15 ++- .../components/tradfri/fixtures/gateway.json | 44 ++++++++ tests/components/tradfri/test_init.py | 14 ++- tests/components/tradfri/test_light.py | 10 +- 15 files changed, 160 insertions(+), 116 deletions(-) create mode 100644 tests/components/tradfri/fixtures/gateway.json diff --git a/homeassistant/components/tradfri/__init__.py b/homeassistant/components/tradfri/__init__.py index 06dfdf65ec32..4bfffd62de01 100644 --- a/homeassistant/components/tradfri/__init__.py +++ b/homeassistant/components/tradfri/__init__.py @@ -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) diff --git a/homeassistant/components/tradfri/coordinator.py b/homeassistant/components/tradfri/coordinator.py index 81919af71105..2bdd82bd3c92 100644 --- a/homeassistant/components/tradfri/coordinator.py +++ b/homeassistant/components/tradfri/coordinator.py @@ -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: diff --git a/homeassistant/components/tradfri/cover.py b/homeassistant/components/tradfri/cover.py index 96c0772289d1..1ad25e41dba7 100644 --- a/homeassistant/components/tradfri/cover.py +++ b/homeassistant/components/tradfri/cover.py @@ -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: diff --git a/homeassistant/components/tradfri/entity.py b/homeassistant/components/tradfri/entity.py index 306e361743d0..430f99f1a522 100644 --- a/homeassistant/components/tradfri/entity.py +++ b/homeassistant/components/tradfri/entity.py @@ -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 diff --git a/homeassistant/components/tradfri/fan.py b/homeassistant/components/tradfri/fan.py index 72ffbb343348..d28126fe0e40 100644 --- a/homeassistant/components/tradfri/fan.py +++ b/homeassistant/components/tradfri/fan.py @@ -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 diff --git a/homeassistant/components/tradfri/light.py b/homeassistant/components/tradfri/light.py index bd23b3dc1c3d..f95b7aa75b4c 100644 --- a/homeassistant/components/tradfri/light.py +++ b/homeassistant/components/tradfri/light.py @@ -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) diff --git a/homeassistant/components/tradfri/manifest.json b/homeassistant/components/tradfri/manifest.json index e0488e0be390..9743109c1d24 100644 --- a/homeassistant/components/tradfri/manifest.json +++ b/homeassistant/components/tradfri/manifest.json @@ -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"] } diff --git a/homeassistant/components/tradfri/sensor.py b/homeassistant/components/tradfri/sensor.py index cbdda2bc4bcc..84b61e099789 100644 --- a/homeassistant/components/tradfri/sensor.py +++ b/homeassistant/components/tradfri/sensor.py @@ -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: diff --git a/homeassistant/components/tradfri/switch.py b/homeassistant/components/tradfri/switch.py index 5eef92247f10..1df5c529efc8 100644 --- a/homeassistant/components/tradfri/switch.py +++ b/homeassistant/components/tradfri/switch.py @@ -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: diff --git a/requirements_all.txt b/requirements_all.txt index 707fab0ebec9..03937333354d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 diff --git a/tests/components/tradfri/common.py b/tests/components/tradfri/common.py index ab3f6fb71c18..474a894a7ce3 100644 --- a/tests/components/tradfri/common.py +++ b/tests/components/tradfri/common.py @@ -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: diff --git a/tests/components/tradfri/conftest.py b/tests/components/tradfri/conftest.py index 6b9c2ab77c3d..7a45adba2479 100644 --- a/tests/components/tradfri/conftest.py +++ b/tests/components/tradfri/conftest.py @@ -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.""" diff --git a/tests/components/tradfri/fixtures/gateway.json b/tests/components/tradfri/fixtures/gateway.json new file mode 100644 index 000000000000..e72e95eff10d --- /dev/null +++ b/tests/components/tradfri/fixtures/gateway.json @@ -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 +} diff --git a/tests/components/tradfri/test_init.py b/tests/components/tradfri/test_init.py index 3b89ed121249..66009d8974c6 100644 --- a/tests/components/tradfri/test_init.py +++ b/tests/components/tradfri/test_init.py @@ -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(), diff --git a/tests/components/tradfri/test_light.py b/tests/components/tradfri/test_light.py index 5f6ce41e1760..43f3b436f5e1 100644 --- a/tests/components/tradfri/test_light.py +++ b/tests/components/tradfri/test_light.py @@ -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,