diff --git a/tests/components/tradfri/common.py b/tests/components/tradfri/common.py index 9c636e14ee6..b527d02bae4 100644 --- a/tests/components/tradfri/common.py +++ b/tests/components/tradfri/common.py @@ -1,9 +1,12 @@ """Common tools used for the Tradfri test suite.""" from copy import deepcopy +from dataclasses import dataclass from typing import Any -from unittest.mock import Mock +from pytradfri.command import Command +from pytradfri.const import ATTR_ID from pytradfri.device import Device +from pytradfri.gateway import Gateway from homeassistant.components import tradfri from homeassistant.core import HomeAssistant @@ -13,7 +16,69 @@ from . import GATEWAY_ID from tests.common import MockConfigEntry -async def setup_integration(hass): +@dataclass +class CommandStore: + """Store commands and command responses for the API.""" + + sent_commands: list[Command] + mock_responses: dict[str, Any] + + def register_device( + self, gateway: Gateway, device_response: dict[str, Any] + ) -> None: + """Register device response.""" + get_devices_command = gateway.get_devices() + self.register_response(get_devices_command, [device_response[ATTR_ID]]) + get_device_command = gateway.get_device(device_response[ATTR_ID]) + self.register_response(get_device_command, device_response) + + def register_response(self, command: Command, response: Any) -> None: + """Register command response.""" + self.mock_responses[command.path_str] = response + + def process_command(self, command: Command) -> Any | None: + """Process command.""" + response = self.mock_responses.get(command.path_str) + if response is None or command.process_result is None: + return None + return command.process_result(response) + + async def trigger_observe_callback( + self, + hass: HomeAssistant, + device: Device, + new_device_state: dict[str, Any] | None = None, + ) -> None: + """Trigger the observe callback.""" + observe_command = next( + ( + command + for command in self.sent_commands + if command.path == device.path and command.observe + ), + None, + ) + assert observe_command + + device_path = "/".join(str(v) for v in device.path) + device_state = deepcopy(device.raw) + + # Create a default observed state based on the sent commands. + for command in self.sent_commands: + if (data := command.data) is None or command.path_str != device_path: + continue + device_state = modify_state(device_state, data) + + # Allow the test to override the default observed state. + if new_device_state is not None: + device_state = modify_state(device_state, new_device_state) + + observe_command.process_result(device_state) + + await hass.async_block_till_done() + + +async def setup_integration(hass: HomeAssistant) -> MockConfigEntry: """Load the Tradfri integration with a mock gateway.""" entry = MockConfigEntry( domain=tradfri.DOMAIN, @@ -46,31 +111,3 @@ def modify_state( state[key] = value return state - - -async def trigger_observe_callback( - hass: HomeAssistant, - mock_gateway: Mock, - device: Device, - new_device_state: dict[str, Any] | None = None, -) -> None: - """Trigger the observe callback.""" - observe_command = next( - ( - command - for command in mock_gateway.mock_commands - if command.path == device.path and command.observe - ), - None, - ) - assert observe_command - - if new_device_state is not None: - mock_gateway.mock_responses.append(new_device_state) - - device_state = deepcopy(device.raw) - new_state = mock_gateway.mock_responses[-1] - device_state = modify_state(device_state, new_state) - observe_command.process_result(device_state) - - await hass.async_block_till_done() diff --git a/tests/components/tradfri/conftest.py b/tests/components/tradfri/conftest.py index 6ac47d2032f..86e66f37d8f 100644 --- a/tests/components/tradfri/conftest.py +++ b/tests/components/tradfri/conftest.py @@ -1,120 +1,107 @@ """Common tradfri test fixtures.""" from __future__ import annotations -from collections.abc import Generator +from collections.abc import Callable, Generator import json from typing import Any -from unittest.mock import MagicMock, Mock, patch +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.device.air_purifier import AirPurifier -from pytradfri.device.blind import Blind +from pytradfri.gateway import Gateway from homeassistant.components.tradfri.const import DOMAIN from . import GATEWAY_ID, TRADFRI_PATH +from .common import CommandStore from tests.common import load_fixture @pytest.fixture -def mock_gateway_info(): - """Mock get_gateway_info.""" - with patch(f"{TRADFRI_PATH}.config_flow.get_gateway_info") as gateway_info: - yield gateway_info - - -@pytest.fixture -def mock_entry_setup(): +def mock_entry_setup() -> Generator[AsyncMock, None, None]: """Mock entry setup.""" with patch(f"{TRADFRI_PATH}.async_setup_entry") as mock_setup: mock_setup.return_value = True yield mock_setup -@pytest.fixture(name="mock_gateway") -def mock_gateway_fixture(): +@pytest.fixture(name="mock_gateway", autouse=True) +def mock_gateway_fixture(command_store: CommandStore) -> Gateway: """Mock a Tradfri gateway.""" - - def get_devices(): - """Return mock devices.""" - return gateway.mock_devices - - def get_groups(): - """Return mock groups.""" - return gateway.mock_groups - - gateway_info = Mock(id=GATEWAY_ID, firmware_version="1.2.1234") - - def get_gateway_info(): - """Return mock gateway info.""" - return gateway_info - - gateway = Mock( - get_devices=get_devices, - get_groups=get_groups, - get_gateway_info=get_gateway_info, - mock_commands=[], - mock_devices=[], - mock_groups=[], - mock_responses=[], + gateway = Gateway() + command_store.register_response( + gateway.get_gateway_info(), + {ATTR_GATEWAY_ID: GATEWAY_ID, ATTR_FIRMWARE_VERSION: "1.2.1234"}, ) - with patch(f"{TRADFRI_PATH}.Gateway", return_value=gateway), patch( - f"{TRADFRI_PATH}.config_flow.Gateway", return_value=gateway - ): - yield gateway + command_store.register_response( + gateway.get_devices(), + [], + ) + return gateway + + +@pytest.fixture(name="command_store", autouse=True) +def command_store_fixture() -> CommandStore: + """Store commands and command responses for the API.""" + return CommandStore([], {}) @pytest.fixture(name="mock_api") -def mock_api_fixture(mock_gateway): +def mock_api_fixture( + command_store: CommandStore, +) -> Callable[[Command | list[Command], float | None], Any | None]: """Mock api.""" - async def api(command, timeout=None): + async def api( + command: Command | list[Command], timeout: float | None = None + ) -> Any | None: """Mock api function.""" - # Store the data for "real" command objects. - if hasattr(command, "_data") and not isinstance(command, Mock): - mock_gateway.mock_responses.append(command._data) - mock_gateway.mock_commands.append(command) - return command + if isinstance(command, list): + result = [] + for cmd in command: + command_store.sent_commands.append(cmd) + result.append(command_store.process_command(cmd)) + return result + + command_store.sent_commands.append(command) + return command_store.process_command(command) return api -@pytest.fixture -def mock_api_factory(mock_api) -> Generator[MagicMock, None, None]: +@pytest.fixture(autouse=True) +def mock_api_factory( + mock_api: Callable[[Command | list[Command], float | None], Any | None], +) -> Generator[MagicMock, None, None]: """Mock pytradfri api factory.""" - with patch(f"{TRADFRI_PATH}.APIFactory", autospec=True) as factory: - factory.init.return_value = factory.return_value - factory.return_value.request = mock_api - yield factory.return_value + with patch(f"{TRADFRI_PATH}.APIFactory", autospec=True) as factory_class: + factory = factory_class.return_value + factory_class.init.return_value = factory + factory.request = mock_api + yield factory + + +@pytest.fixture +def device( + command_store: CommandStore, mock_gateway: Gateway, request: pytest.FixtureRequest +) -> 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) + return device @pytest.fixture(scope="session") -def air_purifier_response() -> dict[str, Any]: +def air_purifier() -> str: """Return an air purifier response.""" - return json.loads(load_fixture("air_purifier.json", DOMAIN)) - - -@pytest.fixture -def air_purifier(air_purifier_response: dict[str, Any]) -> AirPurifier: - """Return air purifier.""" - device = Device(air_purifier_response) - air_purifier_control = device.air_purifier_control - assert air_purifier_control - return air_purifier_control.air_purifiers[0] + return load_fixture("air_purifier.json", DOMAIN) @pytest.fixture(scope="session") -def blind_response() -> dict[str, Any]: +def blind() -> str: """Return a blind response.""" - return json.loads(load_fixture("blind.json", DOMAIN)) - - -@pytest.fixture -def blind(blind_response: dict[str, Any]) -> Blind: - """Return blind.""" - device = Device(blind_response) - blind_control = device.blind_control - assert blind_control - return blind_control.blinds[0] + return load_fixture("blind.json", DOMAIN) diff --git a/tests/components/tradfri/fixtures/bulb_cws.json b/tests/components/tradfri/fixtures/bulb_cws.json new file mode 100644 index 00000000000..d59e346c9fa --- /dev/null +++ b/tests/components/tradfri/fixtures/bulb_cws.json @@ -0,0 +1,28 @@ +{ + "3": { + "0": "IKEA of Sweden", + "1": "TRADFRI bulb E27 CWS opal 600lm", + "2": "", + "3": "1.3.002", + "6": 1 + }, + "3311": [ + { + "5706": "f1e0b5", + "5707": 5427, + "5708": 42596, + "5709": 30015, + "5710": 26870, + "5850": 1, + "5851": 250, + "9003": 0 + } + ], + "5750": 2, + "9001": "Test CWS", + "9002": 1509924799, + "9003": 65541, + "9019": 1, + "9020": 1510011206, + "9054": 0 +} diff --git a/tests/components/tradfri/fixtures/bulb_w.json b/tests/components/tradfri/fixtures/bulb_w.json new file mode 100644 index 00000000000..4766c2eddec --- /dev/null +++ b/tests/components/tradfri/fixtures/bulb_w.json @@ -0,0 +1,17 @@ +{ + "3": { + "0": "IKEA of Sweden", + "1": "TRADFRI bulb E27 W opal 1000lm", + "2": "", + "3": "1.2.214", + "6": 1 + }, + "3311": [{ "5850": 1, "5851": 250, "9003": 0 }], + "5750": 2, + "9001": "Test W", + "9002": 1509923551, + "9003": 65537, + "9019": 1, + "9020": 1510009959, + "9054": 0 +} diff --git a/tests/components/tradfri/fixtures/bulb_ws.json b/tests/components/tradfri/fixtures/bulb_ws.json new file mode 100644 index 00000000000..a4093b5c099 --- /dev/null +++ b/tests/components/tradfri/fixtures/bulb_ws.json @@ -0,0 +1,27 @@ +{ + "3": { + "0": "IKEA of Sweden", + "1": "TRADFRI bulb E27 WS opal 980lm", + "2": "", + "3": "1.2.217", + "6": 1 + }, + "3311": [ + { + "5706": "0", + "5709": 31103, + "5710": 27007, + "5711": 400, + "5850": 1, + "5851": 250, + "9003": 0 + } + ], + "5750": 2, + "9001": "Test WS", + "9002": 1509923713, + "9003": 65539, + "9019": 1, + "9020": 1510010121, + "9054": 0 +} diff --git a/tests/components/tradfri/test_cover.py b/tests/components/tradfri/test_cover.py index a698d1e9d17..5b669a3cc60 100644 --- a/tests/components/tradfri/test_cover.py +++ b/tests/components/tradfri/test_cover.py @@ -2,29 +2,26 @@ from __future__ import annotations from typing import Any -from unittest.mock import MagicMock, Mock import pytest from pytradfri.const import ATTR_REACHABLE_STATE -from pytradfri.device.blind import Blind +from pytradfri.device import Device from homeassistant.components.cover import ATTR_CURRENT_POSITION, DOMAIN as COVER_DOMAIN from homeassistant.const import STATE_CLOSED, STATE_OPEN, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant -from .common import setup_integration, trigger_observe_callback +from .common import CommandStore, setup_integration +@pytest.mark.parametrize("device", ["blind"], indirect=True) async def test_cover_available( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - blind: Blind, + command_store: CommandStore, + device: Device, ) -> None: """Test cover available property.""" entity_id = "cover.test" - device = blind.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) @@ -33,8 +30,8 @@ async def test_cover_available( assert state.attributes[ATTR_CURRENT_POSITION] == 60 assert state.attributes["model"] == "FYRTUR block-out roller blind" - await trigger_observe_callback( - hass, mock_gateway, device, {ATTR_REACHABLE_STATE: 0} + await command_store.trigger_observe_callback( + hass, device, {ATTR_REACHABLE_STATE: 0} ) state = hass.states.get(entity_id) @@ -42,6 +39,7 @@ async def test_cover_available( assert state.state == STATE_UNAVAILABLE +@pytest.mark.parametrize("device", ["blind"], indirect=True) @pytest.mark.parametrize( ("service", "service_data", "expected_state", "expected_position"), [ @@ -54,9 +52,8 @@ async def test_cover_available( ) async def test_cover_services( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - blind: Blind, + command_store: CommandStore, + device: Device, service: str, service_data: dict[str, Any], expected_state: str, @@ -64,8 +61,6 @@ async def test_cover_services( ) -> None: """Test cover services.""" entity_id = "cover.test" - device = blind.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) @@ -81,7 +76,7 @@ async def test_cover_services( ) await hass.async_block_till_done() - await trigger_observe_callback(hass, mock_gateway, device) + await command_store.trigger_observe_callback(hass, device) state = hass.states.get(entity_id) assert state diff --git a/tests/components/tradfri/test_diagnostics.py b/tests/components/tradfri/test_diagnostics.py index a30b75147ca..0da1cb8aadf 100644 --- a/tests/components/tradfri/test_diagnostics.py +++ b/tests/components/tradfri/test_diagnostics.py @@ -1,9 +1,8 @@ """Tests for Tradfri diagnostics.""" from __future__ import annotations -from unittest.mock import MagicMock, Mock - -from pytradfri.device.air_purifier import AirPurifier +import pytest +from pytradfri.device import Device from homeassistant.core import HomeAssistant @@ -13,16 +12,13 @@ from tests.components.diagnostics import get_diagnostics_for_config_entry from tests.typing import ClientSessionGenerator +@pytest.mark.parametrize("device", ["air_purifier"], indirect=True) async def test_diagnostics( hass: HomeAssistant, hass_client: ClientSessionGenerator, - mock_gateway: Mock, - mock_api_factory: MagicMock, - air_purifier: AirPurifier, + device: Device, ) -> None: """Test diagnostics for config entry.""" - device = air_purifier.device - mock_gateway.mock_devices.append(device) config_entry = await setup_integration(hass) result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) diff --git a/tests/components/tradfri/test_fan.py b/tests/components/tradfri/test_fan.py index a6a683ee6db..8bcad92cb96 100644 --- a/tests/components/tradfri/test_fan.py +++ b/tests/components/tradfri/test_fan.py @@ -2,7 +2,6 @@ from __future__ import annotations from typing import Any -from unittest.mock import MagicMock, Mock import pytest from pytradfri.const import ( @@ -11,7 +10,7 @@ from pytradfri.const import ( ATTR_REACHABLE_STATE, ROOT_AIR_PURIFIER, ) -from pytradfri.device.air_purifier import AirPurifier +from pytradfri.device import Device from homeassistant.components.fan import ( ATTR_PERCENTAGE, @@ -32,19 +31,17 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant -from .common import setup_integration, trigger_observe_callback +from .common import CommandStore, setup_integration +@pytest.mark.parametrize("device", ["air_purifier"], indirect=True) async def test_fan_available( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - air_purifier: AirPurifier, + command_store: CommandStore, + device: Device, ) -> None: """Test fan available property.""" entity_id = "fan.test" - device = air_purifier.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) @@ -56,8 +53,8 @@ async def test_fan_available( assert state.attributes[ATTR_PRESET_MODE] is None assert state.attributes[ATTR_SUPPORTED_FEATURES] == 9 - await trigger_observe_callback( - hass, mock_gateway, device, {ATTR_REACHABLE_STATE: 0} + await command_store.trigger_observe_callback( + hass, device, {ATTR_REACHABLE_STATE: 0} ) state = hass.states.get(entity_id) @@ -65,6 +62,7 @@ async def test_fan_available( assert state.state == STATE_UNAVAILABLE +@pytest.mark.parametrize("device", ["air_purifier"], indirect=True) @pytest.mark.parametrize( ( "service", @@ -153,9 +151,8 @@ async def test_fan_available( ) async def test_services( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - air_purifier: AirPurifier, + command_store: CommandStore, + device: Device, service: str, service_data: dict[str, Any], device_state: dict[str, Any], @@ -165,8 +162,6 @@ async def test_services( ) -> None: """Test fan services.""" entity_id = "fan.test" - device = air_purifier.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) @@ -186,9 +181,8 @@ async def test_services( ) await hass.async_block_till_done() - await trigger_observe_callback( + await command_store.trigger_observe_callback( hass, - mock_gateway, device, {ROOT_AIR_PURIFIER: [device_state]}, ) diff --git a/tests/components/tradfri/test_init.py b/tests/components/tradfri/test_init.py index 8201188fb83..2848fcd0f3d 100644 --- a/tests/components/tradfri/test_init.py +++ b/tests/components/tradfri/test_init.py @@ -1,5 +1,5 @@ """Tests for Tradfri setup.""" -from unittest.mock import patch +from unittest.mock import MagicMock from homeassistant.components import tradfri from homeassistant.core import HomeAssistant @@ -10,9 +10,11 @@ from . import GATEWAY_ID from tests.common import MockConfigEntry -async def test_entry_setup_unload(hass: HomeAssistant, mock_api_factory) -> None: +async def test_entry_setup_unload( + hass: HomeAssistant, device_registry: dr.DeviceRegistry, mock_api_factory: MagicMock +) -> None: """Test config entry setup and unload.""" - entry = MockConfigEntry( + config_entry = MockConfigEntry( domain=tradfri.DOMAIN, data={ tradfri.CONF_HOST: "mock-host", @@ -22,70 +24,69 @@ async def test_entry_setup_unload(hass: HomeAssistant, mock_api_factory) -> None }, ) - entry.add_to_hass(hass) - with patch.object( - hass.config_entries, "async_forward_entry_setup", return_value=True - ) as setup: - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - assert setup.call_count == len(tradfri.PLATFORMS) - - dev_reg = dr.async_get(hass) - dev_entries = dr.async_entries_for_config_entry(dev_reg, entry.entry_id) - - assert dev_entries - dev_entry = dev_entries[0] - assert dev_entry.identifiers == { - (tradfri.DOMAIN, entry.data[tradfri.CONF_GATEWAY_ID]) - } - assert dev_entry.manufacturer == "IKEA of Sweden" - assert dev_entry.name == "Gateway" - assert dev_entry.model == "E1526" - - with patch.object( - hass.config_entries, "async_forward_entry_unload", return_value=True - ) as unload: - assert await hass.config_entries.async_unload(entry.entry_id) - await hass.async_block_till_done() - assert unload.call_count == len(tradfri.PLATFORMS) - assert mock_api_factory.shutdown.call_count == 1 - - -async def test_remove_stale_devices(hass: HomeAssistant, mock_api_factory) -> None: - """Test remove stale device registry entries.""" - entry = MockConfigEntry( - domain=tradfri.DOMAIN, - data={ - tradfri.CONF_HOST: "mock-host", - tradfri.CONF_IDENTITY: "mock-identity", - tradfri.CONF_KEY: "mock-key", - tradfri.CONF_GATEWAY_ID: GATEWAY_ID, - }, - ) - - entry.add_to_hass(hass) - dev_reg = dr.async_get(hass) - dev_reg.async_get_or_create( - config_entry_id=entry.entry_id, - identifiers={(tradfri.DOMAIN, "stale_device_id")}, - ) - dev_entries = dr.async_entries_for_config_entry(dev_reg, entry.entry_id) - - assert len(dev_entries) == 1 - dev_entry = dev_entries[0] - assert dev_entry.identifiers == {(tradfri.DOMAIN, "stale_device_id")} - - await hass.config_entries.async_setup(entry.entry_id) + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - dev_entries = dr.async_entries_for_config_entry(dev_reg, entry.entry_id) + device_entries = dr.async_entries_for_config_entry( + device_registry, config_entry.entry_id + ) + + assert device_entries + device_entry = device_entries[0] + assert device_entry.identifiers == { + (tradfri.DOMAIN, config_entry.data[tradfri.CONF_GATEWAY_ID]) + } + assert device_entry.manufacturer == "IKEA of Sweden" + assert device_entry.name == "Gateway" + assert device_entry.model == "E1526" + + assert await hass.config_entries.async_unload(config_entry.entry_id) + await hass.async_block_till_done() + assert mock_api_factory.shutdown.call_count == 1 + + +async def test_remove_stale_devices( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, +) -> None: + """Test remove stale device registry entries.""" + config_entry = MockConfigEntry( + domain=tradfri.DOMAIN, + data={ + tradfri.CONF_HOST: "mock-host", + tradfri.CONF_IDENTITY: "mock-identity", + tradfri.CONF_KEY: "mock-key", + tradfri.CONF_GATEWAY_ID: GATEWAY_ID, + }, + ) + + config_entry.add_to_hass(hass) + device_registry.async_get_or_create( + config_entry_id=config_entry.entry_id, + identifiers={(tradfri.DOMAIN, "stale_device_id")}, + ) + device_entries = dr.async_entries_for_config_entry( + device_registry, config_entry.entry_id + ) + + assert len(device_entries) == 1 + device_entry = device_entries[0] + assert device_entry.identifiers == {(tradfri.DOMAIN, "stale_device_id")} + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + device_entries = dr.async_entries_for_config_entry( + device_registry, config_entry.entry_id + ) # Check that only the gateway device entry remains. - assert len(dev_entries) == 1 - dev_entry = dev_entries[0] - assert dev_entry.identifiers == { - (tradfri.DOMAIN, entry.data[tradfri.CONF_GATEWAY_ID]) + assert len(device_entries) == 1 + device_entry = device_entries[0] + assert device_entry.identifiers == { + (tradfri.DOMAIN, config_entry.data[tradfri.CONF_GATEWAY_ID]) } - assert dev_entry.manufacturer == "IKEA of Sweden" - assert dev_entry.name == "Gateway" - assert dev_entry.model == "E1526" + assert device_entry.manufacturer == "IKEA of Sweden" + assert device_entry.name == "Gateway" + assert device_entry.model == "E1526" diff --git a/tests/components/tradfri/test_light.py b/tests/components/tradfri/test_light.py index eb218152c97..789ebaae840 100644 --- a/tests/components/tradfri/test_light.py +++ b/tests/components/tradfri/test_light.py @@ -1,310 +1,309 @@ """Tradfri lights platform tests.""" -from copy import deepcopy -from unittest.mock import MagicMock, Mock, PropertyMock, patch +from typing import Any import pytest +from pytradfri.const import ATTR_DEVICE_STATE, ATTR_LIGHT_CONTROL, ATTR_REACHABLE_STATE from pytradfri.device import Device -from pytradfri.device.light import Light -from pytradfri.device.light_control import LightControl +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, + ATTR_COLOR_MODE, + ATTR_COLOR_TEMP, + ATTR_HS_COLOR, + ATTR_MAX_MIREDS, + ATTR_MIN_MIREDS, + ATTR_SUPPORTED_COLOR_MODES, + DOMAIN as LIGHT_DOMAIN, + ColorMode, +) +from homeassistant.components.tradfri.const import DOMAIN +from homeassistant.const import ( + SERVICE_TURN_OFF, + SERVICE_TURN_ON, + STATE_OFF, + STATE_ON, + STATE_UNAVAILABLE, +) from homeassistant.core import HomeAssistant -from .common import setup_integration +from .common import CommandStore, setup_integration -DEFAULT_TEST_FEATURES = { - "can_set_dimmer": False, - "can_set_color": False, - "can_set_temp": False, -} -# [ -# {bulb features}, -# {turn_on arguments}, -# {expected result} -# ] -TURN_ON_TEST_CASES = [ - # Turn On - [{}, {}, {"state": "on"}], - # Brightness > 0 - [{"can_set_dimmer": True}, {"brightness": 100}, {"state": "on", "brightness": 100}], - # Brightness == 1 - [{"can_set_dimmer": True}, {"brightness": 1}, {"brightness": 1}], - # Brightness > 254 - [{"can_set_dimmer": True}, {"brightness": 1000}, {"brightness": 254}], - # color_temp - [{"can_set_temp": True}, {"color_temp": 250}, {"color_temp": 250}], - # color_temp < 250 - [{"can_set_temp": True}, {"color_temp": 1}, {"color_temp": 250}], - # color_temp > 454 - [{"can_set_temp": True}, {"color_temp": 1000}, {"color_temp": 454}], - # hs color +from tests.common import load_fixture + + +@pytest.fixture(scope="module") +def bulb_w() -> str: + """Return a bulb W response.""" + return load_fixture("bulb_w.json", DOMAIN) + + +@pytest.fixture(scope="module") +def bulb_ws() -> str: + """Return a bulb WS response.""" + return load_fixture("bulb_ws.json", DOMAIN) + + +@pytest.fixture(scope="module") +def bulb_cws() -> str: + """Return a bulb CWS response.""" + return load_fixture("bulb_cws.json", DOMAIN) + + +@pytest.mark.parametrize( + ("device", "entity_id", "state_attributes"), [ - {"can_set_color": True}, - {"hs_color": [300, 100]}, - {"state": "on", "hs_color": [300, 100]}, + ( + "bulb_w", + "light.test_w", + { + ATTR_BRIGHTNESS: 250, + ATTR_SUPPORTED_COLOR_MODES: [ColorMode.BRIGHTNESS], + ATTR_COLOR_MODE: ColorMode.BRIGHTNESS, + }, + ), + ( + "bulb_ws", + "light.test_ws", + { + ATTR_BRIGHTNESS: 250, + ATTR_COLOR_TEMP: 400, + ATTR_MIN_MIREDS: 250, + ATTR_MAX_MIREDS: 454, + ATTR_SUPPORTED_COLOR_MODES: [ColorMode.COLOR_TEMP], + ATTR_COLOR_MODE: ColorMode.COLOR_TEMP, + }, + ), + ( + "bulb_cws", + "light.test_cws", + { + ATTR_BRIGHTNESS: 250, + ATTR_HS_COLOR: (29.812, 65.252), + ATTR_SUPPORTED_COLOR_MODES: [ColorMode.HS], + ATTR_COLOR_MODE: ColorMode.HS, + }, + ), ], - # ct + brightness - [ - {"can_set_dimmer": True, "can_set_temp": True}, - {"color_temp": 250, "brightness": 200}, - {"state": "on", "color_temp": 250, "brightness": 200}, - ], - # ct + brightness (no temp support) - [ - {"can_set_dimmer": True, "can_set_temp": False, "can_set_color": True}, - {"color_temp": 250, "brightness": 200}, - {"state": "on", "hs_color": [26.807, 34.869], "brightness": 200}, - ], - # ct + brightness (no temp or color support) - [ - {"can_set_dimmer": True, "can_set_temp": False, "can_set_color": False}, - {"color_temp": 250, "brightness": 200}, - {"state": "on", "brightness": 200}, - ], - # hs + brightness - [ - {"can_set_dimmer": True, "can_set_color": True}, - {"hs_color": [300, 100], "brightness": 200}, - {"state": "on", "hs_color": [300, 100], "brightness": 200}, - ], -] - -# Result of transition is not tested, but data is passed to turn on service. -TRANSITION_CASES_FOR_TESTS = [None, 0, 1] - - -@pytest.fixture(autouse=True, scope="module") -def setup(): - """Set up patches for pytradfri methods.""" - p_1 = patch( - "pytradfri.device.LightControl.raw", - new_callable=PropertyMock, - return_value=[{"mock": "mock"}], - ) - p_2 = patch("pytradfri.device.LightControl.lights") - p_1.start() - p_2.start() - - yield - - p_1.stop() - p_2.stop() - - -async def generate_psk(self, code): - """Mock psk.""" - return "mock" - - -def mock_light(test_features=None, test_state=None, light_number=0): - """Mock a tradfri light.""" - if test_features is None: - test_features = {} - if test_state is None: - test_state = {} - mock_light_data = Mock(**test_state) - - dev_info_mock = MagicMock() - dev_info_mock.manufacturer = "manufacturer" - dev_info_mock.model_number = "model" - dev_info_mock.firmware_version = "1.2.3" - _mock_light = Mock( - id=f"mock-light-id-{light_number}", - reachable=True, - observe=Mock(), - device_info=dev_info_mock, - has_light_control=True, - has_socket_control=False, - has_blind_control=False, - has_signal_repeater_control=False, - has_air_purifier_control=False, - ) - _mock_light.name = f"tradfri_light_{light_number}" - - # Set supported features for the light. - features = {**DEFAULT_TEST_FEATURES, **test_features} - light_control = LightControl(_mock_light) - for attr, value in features.items(): - setattr(light_control, attr, value) - # Store the initial state. - setattr(light_control, "lights", [mock_light_data]) - _mock_light.light_control = light_control - return _mock_light - - -async def test_light(hass: HomeAssistant, mock_gateway, mock_api_factory) -> None: - """Test that lights are correctly added.""" - features = {"can_set_dimmer": True, "can_set_color": True, "can_set_temp": True} - - state = { - "state": True, - "dimmer": 100, - "color_temp": 250, - "hsb_xy_color": (100, 100, 100, 100, 100), - } - - mock_gateway.mock_devices.append( - mock_light(test_features=features, test_state=state) - ) - await setup_integration(hass) - - lamp_1 = hass.states.get("light.tradfri_light_0") - assert lamp_1 is not None - assert lamp_1.state == "on" - assert lamp_1.attributes["brightness"] == 100 - assert lamp_1.attributes["hs_color"] == (0.549, 0.153) - - -async def test_light_observed( - hass: HomeAssistant, mock_gateway, mock_api_factory + indirect=["device"], +) +async def test_light_state( + hass: HomeAssistant, + device: Device, + entity_id: str, + state_attributes: dict[str, Any], ) -> None: - """Test that lights are correctly observed.""" - light = mock_light() - mock_gateway.mock_devices.append(light) + """Test light state.""" await setup_integration(hass) - assert len(light.observe.mock_calls) > 0 + + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_ON + for key, value in state_attributes.items(): + assert state.attributes[key] == value +@pytest.mark.parametrize("device", ["bulb_w"], indirect=True) async def test_light_available( - hass: HomeAssistant, mock_gateway, mock_api_factory + hass: HomeAssistant, + command_store: CommandStore, + device: Device, ) -> None: """Test light available property.""" - light = mock_light({"state": True}, light_number=1) - light.reachable = True - - light2 = mock_light({"state": True}, light_number=2) - light2.reachable = False - - mock_gateway.mock_devices.append(light) - mock_gateway.mock_devices.append(light2) + entity_id = "light.test_w" await setup_integration(hass) - assert hass.states.get("light.tradfri_light_1").state == "on" + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_ON - assert hass.states.get("light.tradfri_light_2").state == "unavailable" + await command_store.trigger_observe_callback( + hass, device, {ATTR_REACHABLE_STATE: 0} + ) + + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_UNAVAILABLE -def create_all_turn_on_cases(): - """Create all turn on test cases.""" - # Combine TURN_ON_TEST_CASES and TRANSITION_CASES_FOR_TESTS - all_turn_on_test_cases = [ - ["test_features", "test_data", "expected_result", "device_id"], - [], - ] - index = 1 - for test_case in TURN_ON_TEST_CASES: - for trans in TRANSITION_CASES_FOR_TESTS: - case = deepcopy(test_case) - if trans is not None: - case[1]["transition"] = trans - case.append(index) - index += 1 - all_turn_on_test_cases[1].append(case) - - return all_turn_on_test_cases - - -@pytest.mark.parametrize(*create_all_turn_on_cases()) +@pytest.mark.parametrize( + "transition", + [{}, {"transition": 0}, {"transition": 1}], + ids=["transition_none", "transition_0", "transition_1"], +) +@pytest.mark.parametrize( + ("device", "entity_id", "service_data", "state_attributes"), + [ + # turn_on + ( + "bulb_w", + "light.test_w", + {}, + {}, + ), + # brightness > 0 + ( + "bulb_w", + "light.test_w", + {"brightness": 100}, + {"brightness": 100}, + ), + # brightness == 1 + ( + "bulb_w", + "light.test_w", + {"brightness": 1}, + {"brightness": 1}, + ), + # brightness > 254 + ( + "bulb_w", + "light.test_w", + {"brightness": 1000}, + {"brightness": 254}, + ), + # color_temp + ( + "bulb_ws", + "light.test_ws", + {"color_temp": 250}, + {"color_temp": 250}, + ), + # color_temp < 250 + ( + "bulb_ws", + "light.test_ws", + {"color_temp": 1}, + {"color_temp": 250}, + ), + # color_temp > 454 + ( + "bulb_ws", + "light.test_ws", + {"color_temp": 1000}, + {"color_temp": 454}, + ), + # hs_color + ( + "bulb_cws", + "light.test_cws", + {"hs_color": [300, 100]}, + {"hs_color": [300, 100]}, + ), + # ct + brightness + ( + "bulb_ws", + "light.test_ws", + {"color_temp": 250, "brightness": 200}, + {"color_temp": 250, "brightness": 200}, + ), + # ct + brightness (no temp support) + ( + "bulb_cws", + "light.test_cws", + {"color_temp": 250, "brightness": 200}, + {"hs_color": [26.807, 34.869], "brightness": 200}, + ), + # ct + brightness (no temp or color support) + ( + "bulb_w", + "light.test_w", + {"color_temp": 250, "brightness": 200}, + {"brightness": 200}, + ), + # hs + brightness + ( + "bulb_cws", + "light.test_cws", + {"hs_color": [300, 100], "brightness": 200}, + {"hs_color": [300, 100], "brightness": 200}, + ), + ], + indirect=["device"], + ids=[ + "turn_on", + "brightness > 0", + "brightness == 1", + "brightness > 254", + "color_temp", + "color_temp < 250", + "color_temp > 454", + "hs_color", + "ct + brightness", + "ct + brightness (no temp support)", + "ct + brightness (no temp or color support)", + "hs + brightness", + ], +) async def test_turn_on( hass: HomeAssistant, - mock_gateway, - mock_api_factory, - test_features, - test_data, - expected_result, - device_id, + command_store: CommandStore, + device: Device, + entity_id: str, + service_data: dict[str, Any], + transition: dict[str, int], + state_attributes: dict[str, Any], ) -> None: """Test turning on a light.""" - # Note pytradfri style, not hass. Values not really important. - initial_state = { - "state": False, - "dimmer": 0, - "color_temp": 250, - "hsb_xy_color": (100, 100, 100, 100, 100), - } - - # Setup the gateway with a mock light. - light = mock_light( - test_features=test_features, test_state=initial_state, light_number=device_id - ) - mock_gateway.mock_devices.append(light) + # Make sure the light is off. + device.raw[ATTR_LIGHT_CONTROL][0][ATTR_DEVICE_STATE] = 0 await setup_integration(hass) - # Use the turn_on service call to change the light state. await hass.services.async_call( - "light", - "turn_on", - {"entity_id": f"light.tradfri_light_{device_id}", **test_data}, + LIGHT_DOMAIN, + SERVICE_TURN_ON, + {"entity_id": entity_id, **service_data, **transition}, blocking=True, ) await hass.async_block_till_done() - # Check that the light is observed. - mock_func = light.observe - assert len(mock_func.mock_calls) > 0 - _, callkwargs = mock_func.call_args - assert "callback" in callkwargs - # Callback function to refresh light state. - callback = callkwargs["callback"] + await command_store.trigger_observe_callback( + hass, device, {ATTR_LIGHT_CONTROL: [{ATTR_DEVICE_STATE: 1}]} + ) - responses = mock_gateway.mock_responses - # State on command data. - data = {"3311": [{"5850": 1}]} - # Add data for all sent commands. - for resp in responses: - data["3311"][0] = {**data["3311"][0], **resp["3311"][0]} - - # Use the callback function to update the light state. - dev = Device(data) - light_data = Light(dev, 0) - light.light_control.lights[0] = light_data - callback(light) - await hass.async_block_till_done() - - # Check that the state is correct. - states = hass.states.get(f"light.tradfri_light_{device_id}") - for result, value in expected_result.items(): - if result == "state": - assert states.state == value - else: - # Allow some rounding error in color conversions. - assert states.attributes[result] == pytest.approx(value, abs=0.01) + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_ON + for key, value in state_attributes.items(): + # Allow some rounding error in color conversions. + assert state.attributes[key] == pytest.approx(value, abs=0.01) -async def test_turn_off(hass: HomeAssistant, mock_gateway, mock_api_factory) -> None: +@pytest.mark.parametrize( + "transition", + [{}, {"transition": 0}, {"transition": 1}], + ids=["transition_none", "transition_0", "transition_1"], +) +@pytest.mark.parametrize( + ("device", "entity_id"), + [ + ("bulb_w", "light.test_w"), + ("bulb_ws", "light.test_ws"), + ("bulb_cws", "light.test_cws"), + ], + indirect=["device"], +) +async def test_turn_off( + hass: HomeAssistant, + command_store: CommandStore, + device: Device, + entity_id: str, + transition: dict[str, int], +) -> None: """Test turning off a light.""" - state = {"state": True, "dimmer": 100} - - light = mock_light(test_state=state) - mock_gateway.mock_devices.append(light) await setup_integration(hass) - # Use the turn_off service call to change the light state. await hass.services.async_call( - "light", "turn_off", {"entity_id": "light.tradfri_light_0"}, blocking=True + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {"entity_id": entity_id, **transition}, + blocking=True, ) await hass.async_block_till_done() - # Check that the light is observed. - mock_func = light.observe - assert len(mock_func.mock_calls) > 0 - _, callkwargs = mock_func.call_args - assert "callback" in callkwargs - # Callback function to refresh light state. - callback = callkwargs["callback"] + await command_store.trigger_observe_callback( + hass, device, {ATTR_LIGHT_CONTROL: [{ATTR_DEVICE_STATE: 0}]} + ) - responses = mock_gateway.mock_responses - data = {"3311": [{}]} - # Add data for all sent commands. - for resp in responses: - data["3311"][0] = {**data["3311"][0], **resp["3311"][0]} - - # Use the callback function to update the light state. - dev = Device(data) - light_data = Light(dev, 0) - light.light_control.lights[0] = light_data - callback(light) - await hass.async_block_till_done() - - # Check that the state is correct. - states = hass.states.get("light.tradfri_light_0") - assert states.state == "off" + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_OFF diff --git a/tests/components/tradfri/test_sensor.py b/tests/components/tradfri/test_sensor.py index d301638ec5d..ccb4eccd702 100644 --- a/tests/components/tradfri/test_sensor.py +++ b/tests/components/tradfri/test_sensor.py @@ -1,10 +1,6 @@ """Tradfri sensor platform tests.""" from __future__ import annotations -import json -from typing import Any -from unittest.mock import MagicMock, Mock - import pytest from pytradfri.const import ( ATTR_AIR_PURIFIER_AIR_QUALITY, @@ -14,8 +10,6 @@ from pytradfri.const import ( ROOT_AIR_PURIFIER, ) from pytradfri.device import Device -from pytradfri.device.air_purifier import AirPurifier -from pytradfri.device.blind import Blind from homeassistant.components.sensor import ( ATTR_STATE_CLASS, @@ -37,33 +31,25 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from . import GATEWAY_ID -from .common import setup_integration, trigger_observe_callback +from .common import CommandStore, setup_integration from tests.common import MockConfigEntry, load_fixture @pytest.fixture(scope="module") -def remote_control_response() -> dict[str, Any]: +def remote_control() -> str: """Return a remote control response.""" - return json.loads(load_fixture("remote_control.json", DOMAIN)) - - -@pytest.fixture -def remote_control(remote_control_response: dict[str, Any]) -> Device: - """Return remote control.""" - return Device(remote_control_response) + return load_fixture("remote_control.json", DOMAIN) +@pytest.mark.parametrize("device", ["remote_control"], indirect=True) async def test_battery_sensor( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - remote_control: Device, + command_store: CommandStore, + device: Device, ) -> None: """Test that a battery sensor is correctly added.""" entity_id = "sensor.test_battery" - device = remote_control - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) @@ -73,8 +59,8 @@ async def test_battery_sensor( assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.BATTERY assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT - await trigger_observe_callback( - hass, mock_gateway, device, {ATTR_DEVICE_INFO: {ATTR_DEVICE_BATTERY: 60}} + await command_store.trigger_observe_callback( + hass, device, {ATTR_DEVICE_INFO: {ATTR_DEVICE_BATTERY: 60}} ) state = hass.states.get(entity_id) @@ -85,16 +71,13 @@ async def test_battery_sensor( assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT +@pytest.mark.parametrize("device", ["blind"], indirect=True) async def test_cover_battery_sensor( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - blind: Blind, + device: Device, ) -> None: """Test that a battery sensor is correctly added for a cover (blind).""" entity_id = "sensor.test_battery" - device = blind.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) @@ -105,16 +88,14 @@ async def test_cover_battery_sensor( assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT +@pytest.mark.parametrize("device", ["air_purifier"], indirect=True) async def test_air_quality_sensor( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - air_purifier: AirPurifier, + command_store: CommandStore, + device: Device, ) -> None: """Test that a battery sensor is correctly added.""" entity_id = "sensor.test_air_quality" - device = air_purifier.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) @@ -128,9 +109,8 @@ async def test_air_quality_sensor( assert ATTR_DEVICE_CLASS not in state.attributes # The sensor returns 65535 if the fan is turned off - await trigger_observe_callback( + await command_store.trigger_observe_callback( hass, - mock_gateway, device, {ROOT_AIR_PURIFIER: [{ATTR_AIR_PURIFIER_AIR_QUALITY: 65535}]}, ) @@ -140,16 +120,13 @@ async def test_air_quality_sensor( assert state.state == STATE_UNKNOWN +@pytest.mark.parametrize("device", ["air_purifier"], indirect=True) async def test_filter_time_left_sensor( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - air_purifier: AirPurifier, + device: Device, ) -> None: """Test that a battery sensor is correctly added.""" entity_id = "sensor.test_filter_time_left" - device = air_purifier.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) @@ -160,24 +137,22 @@ async def test_filter_time_left_sensor( assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT +@pytest.mark.parametrize("device", ["air_purifier"], indirect=True) async def test_sensor_available( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - air_purifier: AirPurifier, + command_store: CommandStore, + device: Device, ) -> None: """Test sensor available property.""" entity_id = "sensor.test_filter_time_left" - device = air_purifier.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) assert state assert state.state == "4320" - await trigger_observe_callback( - hass, mock_gateway, device, {ATTR_REACHABLE_STATE: 0} + await command_store.trigger_observe_callback( + hass, device, {ATTR_REACHABLE_STATE: 0} ) state = hass.states.get(entity_id) @@ -185,14 +160,13 @@ async def test_sensor_available( assert state.state == STATE_UNAVAILABLE +@pytest.mark.parametrize("device", ["remote_control"], indirect=True) async def test_unique_id_migration( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - remote_control: Device, + entity_registry: er.EntityRegistry, + device: Device, ) -> None: """Test unique ID is migrated from old format to new.""" - ent_reg = er.async_get(hass) old_unique_id = f"{GATEWAY_ID}-65536" entry = MockConfigEntry( domain=DOMAIN, @@ -209,7 +183,7 @@ async def test_unique_id_migration( entity_id = "sensor.test" entity_name = entity_id.split(".")[1] - entity_entry = ent_reg.async_get_or_create( + entity_entry = entity_registry.async_get_or_create( SENSOR_DOMAIN, DOMAIN, old_unique_id, @@ -221,15 +195,15 @@ async def test_unique_id_migration( assert entity_entry.entity_id == entity_id assert entity_entry.unique_id == old_unique_id - # Add a sensor to the gateway so that it populates coordinator list - device = remote_control - mock_gateway.mock_devices.append(device) await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() # Check that new RegistryEntry is using new unique ID format new_unique_id = f"{old_unique_id}-battery_level" - migrated_entity_entry = ent_reg.async_get(entity_id) + migrated_entity_entry = entity_registry.async_get(entity_id) assert migrated_entity_entry is not None assert migrated_entity_entry.unique_id == new_unique_id - assert ent_reg.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, old_unique_id) is None + assert ( + entity_registry.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, old_unique_id) + is None + ) diff --git a/tests/components/tradfri/test_switch.py b/tests/components/tradfri/test_switch.py index f6e1926ab93..2380824faa8 100644 --- a/tests/components/tradfri/test_switch.py +++ b/tests/components/tradfri/test_switch.py @@ -1,58 +1,42 @@ """Tradfri switch (recognised as sockets in the IKEA ecosystem) platform tests.""" from __future__ import annotations -import json -from typing import Any -from unittest.mock import MagicMock, Mock - import pytest from pytradfri.const import ATTR_REACHABLE_STATE from pytradfri.device import Device -from pytradfri.device.socket import Socket from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.tradfri.const import DOMAIN from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant -from .common import setup_integration, trigger_observe_callback +from .common import CommandStore, setup_integration from tests.common import load_fixture @pytest.fixture(scope="module") -def outlet() -> dict[str, Any]: +def outlet() -> str: """Return an outlet response.""" - return json.loads(load_fixture("outlet.json", DOMAIN)) - - -@pytest.fixture -def socket(outlet: dict[str, Any]) -> Socket: - """Return socket.""" - device = Device(outlet) - socket_control = device.socket_control - assert socket_control - return socket_control.sockets[0] + return load_fixture("outlet.json", DOMAIN) +@pytest.mark.parametrize("device", ["outlet"], indirect=True) async def test_switch_available( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - socket: Socket, + command_store: CommandStore, + device: Device, ) -> None: """Test switch available property.""" entity_id = "switch.test" - device = socket.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) assert state assert state.state == STATE_OFF - await trigger_observe_callback( - hass, mock_gateway, device, {ATTR_REACHABLE_STATE: 0} + await command_store.trigger_observe_callback( + hass, device, {ATTR_REACHABLE_STATE: 0} ) state = hass.states.get(entity_id) @@ -60,6 +44,7 @@ async def test_switch_available( assert state.state == STATE_UNAVAILABLE +@pytest.mark.parametrize("device", ["outlet"], indirect=True) @pytest.mark.parametrize( ("service", "expected_state"), [ @@ -69,16 +54,13 @@ async def test_switch_available( ) async def test_turn_on_off( hass: HomeAssistant, - mock_gateway: Mock, - mock_api_factory: MagicMock, - socket: Socket, + command_store: CommandStore, + device: Device, service: str, expected_state: str, ) -> None: """Test turning switch on/off.""" entity_id = "switch.test" - device = socket.device - mock_gateway.mock_devices.append(device) await setup_integration(hass) state = hass.states.get(entity_id) @@ -95,7 +77,7 @@ async def test_turn_on_off( ) await hass.async_block_till_done() - await trigger_observe_callback(hass, mock_gateway, device) + await command_store.trigger_observe_callback(hass, device) state = hass.states.get(entity_id) assert state