diff --git a/tests/components/overkiz/fixtures/scenarios/cozytouch.json b/tests/components/overkiz/fixtures/scenarios/cozytouch.json new file mode 100644 index 0000000000000..64d42ad5deed8 --- /dev/null +++ b/tests/components/overkiz/fixtures/scenarios/cozytouch.json @@ -0,0 +1,46 @@ +[ + { + "creationTime": 1763489130000, + "lastUpdateTime": 1763489130000, + "label": "Label 1", + "metadata": "a56b34aa-2f6f-4e24-b73a-c2ed23f0ac91", + "shortcut": false, + "notificationTypeMask": 0, + "notificationCondition": "NEVER", + "actions": [ + { + "deviceURL": "io://1234-5678-1234/13880042", + "commands": [ + { + "type": 1, + "name": "setHeatingLevel", + "parameters": ["eco"] + } + ] + } + ], + "oid": "0a0589bb-9471-4667-a2a9-4602beb2a2e8" + }, + { + "creationTime": 1763214463000, + "lastUpdateTime": 1763214463000, + "label": "Label 2", + "metadata": "f6206578-b730-4f66-b322-e8d0a5625407", + "shortcut": false, + "notificationTypeMask": 0, + "notificationCondition": "NEVER", + "actions": [ + { + "deviceURL": "io://1234-5678-1234/15581081", + "commands": [ + { + "type": 1, + "name": "setHeatingLevel", + "parameters": ["comfort"] + } + ] + } + ], + "oid": "50d39fc3-9368-49c9-bcbf-c74f3ce1678a" + } +] diff --git a/tests/components/overkiz/fixtures/scenarios/tahoma_switch.json b/tests/components/overkiz/fixtures/scenarios/tahoma_switch.json new file mode 100644 index 0000000000000..bece51a7c100f --- /dev/null +++ b/tests/components/overkiz/fixtures/scenarios/tahoma_switch.json @@ -0,0 +1,26 @@ +[ + { + "creationTime": 1766929020000, + "lastUpdateTime": 1766929020000, + "label": "I'm arriving", + "metadata": "{\"tahoma\":{\"version\":1,\"icon\":\"at_home\",\"template\":\"arriving\"}}", + "shortcut": false, + "notificationTypeMask": 16, + "notificationCondition": "ON_ERROR", + "notificationText": "Your scene I'm arriving could not be played correctly due to an error in one of your products.", + "notificationTitle": "[$[setupLabel]] : Impossible to play the scene I'm arriving", + "actions": [ + { + "deviceURL": "rts://1234-5678-1234/16756006", + "commands": [ + { + "type": 1, + "name": "open", + "parameters": [1] + } + ] + } + ], + "oid": "d1b689e1-4087-473d-b726-d3b24770856f" + } +] diff --git a/tests/components/overkiz/snapshots/test_scene.ambr b/tests/components/overkiz/snapshots/test_scene.ambr new file mode 100644 index 0000000000000..93209f71145f7 --- /dev/null +++ b/tests/components/overkiz/snapshots/test_scene.ambr @@ -0,0 +1,151 @@ +# serializer version: 1 +# name: test_scene_entities_snapshot[scenarios/cozytouch.json][scene.label_1-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'scene', + 'entity_category': None, + 'entity_id': 'scene.label_1', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Label 1', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Label 1', + 'platform': 'overkiz', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '0a0589bb-9471-4667-a2a9-4602beb2a2e8', + 'unit_of_measurement': None, + }) +# --- +# name: test_scene_entities_snapshot[scenarios/cozytouch.json][scene.label_1-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Label 1', + }), + 'context': , + 'entity_id': 'scene.label_1', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_scene_entities_snapshot[scenarios/cozytouch.json][scene.label_2-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'scene', + 'entity_category': None, + 'entity_id': 'scene.label_2', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Label 2', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Label 2', + 'platform': 'overkiz', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '50d39fc3-9368-49c9-bcbf-c74f3ce1678a', + 'unit_of_measurement': None, + }) +# --- +# name: test_scene_entities_snapshot[scenarios/cozytouch.json][scene.label_2-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Label 2', + }), + 'context': , + 'entity_id': 'scene.label_2', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_scene_entities_snapshot[scenarios/tahoma_switch.json][scene.i_m_arriving-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'scene', + 'entity_category': None, + 'entity_id': 'scene.i_m_arriving', + 'has_entity_name': False, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': "I'm arriving", + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': "I'm arriving", + 'platform': 'overkiz', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': 'd1b689e1-4087-473d-b726-d3b24770856f', + 'unit_of_measurement': None, + }) +# --- +# name: test_scene_entities_snapshot[scenarios/tahoma_switch.json][scene.i_m_arriving-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': "I'm arriving", + }), + 'context': , + 'entity_id': 'scene.i_m_arriving', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- diff --git a/tests/components/overkiz/test_scene.py b/tests/components/overkiz/test_scene.py new file mode 100644 index 0000000000000..443d01b8b3d97 --- /dev/null +++ b/tests/components/overkiz/test_scene.py @@ -0,0 +1,128 @@ +"""Tests for the Overkiz scene platform.""" + +from collections.abc import Generator +from unittest.mock import AsyncMock, patch + +import humps +from pyoverkiz.models import Scenario +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.components.overkiz.const import DOMAIN +from homeassistant.components.scene import DOMAIN as SCENE_DOMAIN +from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON, Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from .conftest import MockOverkizClient, SetupOverkizIntegration + +from tests.common import load_json_array_fixture, snapshot_platform + +SCENARIO_FIXTURES = [ + "scenarios/cozytouch.json", + "scenarios/tahoma_switch.json", +] + + +def load_scenarios_fixture(fixture: str) -> list[Scenario]: + """Load scenario fixture and return Scenario objects.""" + data = load_json_array_fixture(fixture, DOMAIN) + return [Scenario(**humps.decamelize(s)) for s in data] + + +@pytest.fixture(autouse=True) +def fixture_platforms() -> Generator[None]: + """Limit platforms to scene only.""" + with patch("homeassistant.components.overkiz.PLATFORMS", [Platform.SCENE]): + yield + + +@pytest.mark.parametrize("fixture", SCENARIO_FIXTURES) +async def test_scene_entities_snapshot( + hass: HomeAssistant, + setup_overkiz_integration: SetupOverkizIntegration, + entity_registry: er.EntityRegistry, + mock_client: MockOverkizClient, + snapshot: SnapshotAssertion, + fixture: str, +) -> None: + """Test scene entities via snapshot for each fixture.""" + scenarios = load_scenarios_fixture(fixture) + mock_client.get_scenarios = AsyncMock(return_value=scenarios) + + config_entry = await setup_overkiz_integration() + + await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id) + + +async def test_scene_activate( + hass: HomeAssistant, + setup_overkiz_integration: SetupOverkizIntegration, + mock_client: MockOverkizClient, +) -> None: + """Test activating a scene calls execute_scenario with the correct OID.""" + scenarios = load_scenarios_fixture("scenarios/tahoma_switch.json") + mock_client.get_scenarios = AsyncMock(return_value=scenarios) + mock_client.execute_scenario = AsyncMock(return_value="exec-1") + + await setup_overkiz_integration() + + await hass.services.async_call( + SCENE_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "scene.i_m_arriving"}, + blocking=True, + ) + + mock_client.execute_scenario.assert_awaited_once_with( + "d1b689e1-4087-473d-b726-d3b24770856f" + ) + + +async def test_scene_activate_multiple( + hass: HomeAssistant, + setup_overkiz_integration: SetupOverkizIntegration, + mock_client: MockOverkizClient, +) -> None: + """Test activating different scenes uses the correct OID for each.""" + scenarios = load_scenarios_fixture("scenarios/cozytouch.json") + mock_client.get_scenarios = AsyncMock(return_value=scenarios) + mock_client.execute_scenario = AsyncMock(return_value="exec-1") + + await setup_overkiz_integration() + + await hass.services.async_call( + SCENE_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "scene.label_1"}, + blocking=True, + ) + + mock_client.execute_scenario.assert_awaited_once_with( + "0a0589bb-9471-4667-a2a9-4602beb2a2e8" + ) + + mock_client.execute_scenario.reset_mock() + + await hass.services.async_call( + SCENE_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "scene.label_2"}, + blocking=True, + ) + + mock_client.execute_scenario.assert_awaited_once_with( + "50d39fc3-9368-49c9-bcbf-c74f3ce1678a" + ) + + +async def test_no_scenes_when_empty( + hass: HomeAssistant, + setup_overkiz_integration: SetupOverkizIntegration, + mock_client: MockOverkizClient, +) -> None: + """Test no scene entities are created when there are no scenarios.""" + await setup_overkiz_integration() + + states = hass.states.async_entity_ids(SCENE_DOMAIN) + assert len(states) == 0