From e1f5692354a51bc518f79b457698fba9c2b7d8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Christian=20Seime?= Date: Fri, 26 Apr 2024 08:12:00 +0200 Subject: [PATCH] Support creating scenes with controller trigger (#74) * Support creating scenes with controller trigger * Make `Trigger.id` optional * Handle sunset triggers --- README.md | 17 +++++++- src/dirigera/devices/scene.py | 23 +++++++++- tests/test_scenes.py | 82 ++++++++++++++++++++++++++++++++--- 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6414254..99eba6b 100644 --- a/README.md +++ b/README.md @@ -371,11 +371,26 @@ Triggers look like this: ```python class Trigger(BaseIkeaModel): - id: str + id: Optional[str] = None type: str triggered_at: Optional[datetime.datetime] = None disabled: bool + trigger: Optional[TriggerDetails] = None +``` +Example how to create scene with trigger: +```python +from dirigera.devices.scene import Info, Icon, Trigger, SceneType, TriggerDetails, ControllerType, ClickPattern + +scene = dirigera_hub.create_scene( + info=Info(name="Scene with trigger", icon=Icon.SCENES_HEART), + scene_type=SceneType.USER_SCENE, + triggers=[ + Trigger(type="app", disabled=False), + Trigger(type="controller", disabled=False, + trigger=TriggerDetails(clickPattern=ClickPattern.SINGLE_PRESS, buttonIndex=0, + deviceId="0000aaaa-0000-0000-aa00-0a0aa0a000a0_1", + controllerType=ControllerType.SHORTCUT_CONTROLLER))]) ``` All available icons can be found here: [Icons](./src/dirigera/devices/scene.py) diff --git a/src/dirigera/devices/scene.py b/src/dirigera/devices/scene.py index 414a2cd..e737657 100644 --- a/src/dirigera/devices/scene.py +++ b/src/dirigera/devices/scene.py @@ -60,10 +60,31 @@ class Info(BaseIkeaModel): class Trigger(BaseIkeaModel): - id: str + id: Optional[str] = None # Optional to allow creation of Trigger instances for create_scene() type: str triggered_at: Optional[datetime.datetime] = None disabled: bool + trigger: Optional[TriggerDetails] = None + + +class TriggerDetails(BaseIkeaModel): + days: Optional[List[str]] = None + controllerType: Optional[ControllerType] = None + buttonIndex: Optional[int] = None + clickPattern: Optional[ClickPattern] = None + deviceId: Optional[str] = None + offset: Optional[int] = None + type: Optional[str] = None + + +class ControllerType(Enum): + SHORTCUT_CONTROLLER = "shortcutController" + + +class ClickPattern(Enum): + LONG_PRESS = "longPress" + DOUBLE_PRESS = "doublePress" + SINGLE_PRESS = "singlePress" class ActionAttributes(BaseIkeaModel, extra="allow"): diff --git a/tests/test_scenes.py b/tests/test_scenes.py index 78fe596..07d3eb6 100644 --- a/tests/test_scenes.py +++ b/tests/test_scenes.py @@ -2,7 +2,7 @@ from typing import Any, Dict import pytest from src.dirigera.hub.abstract_smart_home_hub import FakeDirigeraHub -from src.dirigera.devices.scene import Scene, dict_to_scene +from src.dirigera.devices.scene import Scene, dict_to_scene, ControllerType TEST_ID = "c9bbf831-6dcd-4442-8195-53eedb66a598" TEST_NAME = "Tesscene" @@ -57,7 +57,7 @@ def test_trigger(fake_scene: Scene, fake_client: FakeDirigeraHub) -> None: def test_dict_to_scene(fake_client: FakeDirigeraHub) -> None: - data: Dict[str, Any] = { + data1: Dict[str, Any] = { "id": "c9bbf831-6dcd-4442-8195-53eedb66a598", "info": {"name": "Tesscene", "icon": "scenes_clean_sparkles"}, "type": "userScene", @@ -84,10 +84,78 @@ def test_dict_to_scene(fake_client: FakeDirigeraHub) -> None: "lastUndo": "2023-08-27T10:29:33.049Z", } - scene = dict_to_scene(data, fake_client) - assert scene.id == TEST_ID - assert scene.info.name == TEST_NAME - assert scene.info.icon.value == TEST_ICON - assert scene.last_completed == datetime.datetime.strptime( + scene1 = dict_to_scene(data1, fake_client) + assert scene1.id == TEST_ID + assert scene1.info.name == TEST_NAME + assert scene1.info.icon.value == TEST_ICON + assert scene1.last_completed == datetime.datetime.strptime( TEST_LAST_COMPLETED, "%Y-%m-%dT%H:%M:%S.%f%z" ) + + data2: Dict[str, Any] = { + "id": "00a0a00a-0aa0-0000-a000-00a0000a0a00", + "info": { + "name": "Night", + "icon": "scenes_clean_sparkles" + }, + "type": "userScene", + "triggers": [ + { + "id": "0000a0a0-0a00-000a-0a00-aaaaaa000000", + "type": "app", + "triggeredAt": "2024-04-23T21:34:51.619Z", + "disabled": False + }, + { + "id": "a0000000-a000-0000-aaaa-0aaa000aa000", + "type": "controller", + "triggeredAt": "2024-04-20T05:49:20.178Z", + "disabled": False, + "trigger": { + "days": [ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun" + ], + "controllerType": "shortcutController", + "clickPattern": "doublePress", + "buttonIndex": 0, + "deviceId": "0000aaaa-0000-0000-aa00-0a0aa0a000a0_2" + } + }, + { + "id": "a0000000-a000-0000-aaaa-0aaa000aa000", + "type": "sunriseSunset", + "triggeredAt": "2024-04-24T17:49:00.989Z", + "disabled": False, + "trigger": { + "days": [ + "Tue", + "Wed", + "Sun", + "Mon", + "Fri", + "Sat", + "Thu" + ], + "type": "sunset", + "offset": 0 + }, + "nextTriggerAt": "2024-04-25T17:49:00.000Z" + } + ], + "actions": [], + "commands": [], + "createdAt": "2023-11-06T22:10:14.806Z", + "lastCompleted": "2024-04-24T05:13:24.352Z", + "lastTriggered": "2024-04-24T05:13:24.352Z", + "undoAllowedDuration": 30 + } + + scene2 = dict_to_scene(data2, fake_client) + assert scene2.triggers[1].trigger is not None + assert scene2.triggers[1].trigger.controllerType == ControllerType.SHORTCUT_CONTROLLER