From b4212609df4962167ad5278725ca1c74b89ae4ba Mon Sep 17 00:00:00 2001 From: Xavier Moreno Date: Sat, 23 Jan 2021 18:23:43 +0100 Subject: [PATCH] feat(device): add support for ZB-5122 RGB genie related to #228 --- apps/controllerx/cx_devices/rgb_genie.py | 49 +++++++++++++++++- docs/_data/controllers/ZB-5122.yml | 33 ++++++++++++ docs/assets/img/ZB-5122.jpeg | Bin 0 -> 13687 bytes docs/controllers/ZB-5122.md | 5 ++ tests/integ_tests/zb5122/config.yaml | 7 +++ .../zb5122/move_to_color_test.yaml | 12 +++++ tests/unit_tests/cx_devices/rgb_genie_test.py | 22 ++++++++ 7 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 docs/_data/controllers/ZB-5122.yml create mode 100644 docs/assets/img/ZB-5122.jpeg create mode 100644 docs/controllers/ZB-5122.md create mode 100644 tests/integ_tests/zb5122/config.yaml create mode 100644 tests/integ_tests/zb5122/move_to_color_test.yaml create mode 100644 tests/unit_tests/cx_devices/rgb_genie_test.py diff --git a/apps/controllerx/cx_devices/rgb_genie.py b/apps/controllerx/cx_devices/rgb_genie.py index 14620954..1e35c23a 100644 --- a/apps/controllerx/cx_devices/rgb_genie.py +++ b/apps/controllerx/cx_devices/rgb_genie.py @@ -1,5 +1,8 @@ -from cx_const import DefaultActionsMapping, Light +from cx_const import DefaultActionsMapping, Light, PredefinedActionsMapping from cx_core import LightController +from cx_core.controller import action +from cx_core.integration import EventData +from cx_core.integration.zha import ZHAIntegration class ZB5121LightController(LightController): @@ -14,3 +17,47 @@ def get_zha_actions_mapping(self) -> DefaultActionsMapping: # "recall_0_1": "", # Click clapperboard "stop": Light.RELEASE, # long release } + + +class ZB5122LightController(LightController): + + MOVE_TO_COLOR = "move_to_color" + + @action + async def move_to_color(self, extra: EventData) -> None: + if isinstance(self.integration, ZHAIntegration): + await self.on(rgb_color=extra["args"]) + + def get_predefined_actions_mapping(self) -> PredefinedActionsMapping: + parent_mapping = super().get_predefined_actions_mapping() + mapping: PredefinedActionsMapping = { + ZB5122LightController.MOVE_TO_COLOR: self.move_to_color, + } + parent_mapping.update(mapping) + return parent_mapping + + def get_zha_actions_mapping(self) -> DefaultActionsMapping: + return { + "on": Light.ON, # Click light on + "off": Light.OFF, # Click light off + "hold_brightness_up": Light.HOLD_BRIGHTNESS_UP, # Hold light on + "hold_brightness_down": Light.HOLD_BRIGHTNESS_DOWN, # Hold light off + "stop": Light.RELEASE, # long release + "move_to_color": ZB5122LightController.MOVE_TO_COLOR, # click RGB + "move_hue": Light.HOLD_XY_COLOR_UP, # hold RGB + "stop_move_hue": Light.RELEASE, # release RGB + "move_to_color_temp": Light.ON_FULL_COLOR_TEMP, # click CW + "move_color_temp": Light.HOLD_COLOR_TEMP_TOGGLE, # hold CW + "stop_move_step": Light.RELEASE, # release CW + # "recall_0_1": "", # Click clapperboard + } + + def get_zha_action(self, data: EventData) -> str: + command: str = data["command"] + if command == "move_with_on_off": + return ( + "hold_brightness_up" if data["args"][0] == 0 else "hold_brightness_down" + ) + elif command == "move_hue": + return "stop_move_hue" if tuple(data["args"]) == (0, 0) else "move_hue" + return command diff --git a/docs/_data/controllers/ZB-5122.yml b/docs/_data/controllers/ZB-5122.yml new file mode 100644 index 00000000..6139a223 --- /dev/null +++ b/docs/_data/controllers/ZB-5122.yml @@ -0,0 +1,33 @@ +name: ZB-5122 (RGB Genie) +device_support: + - type: Light + domain: light + controller: ZB5122LightController + delay: 350 + mapping: + - "Click light on → Turn on" + - "Click light off → Turn off" + - "Hold light on → Brighten up" + - "Hold light off → Dim down" + - "Click RGB → Change xy color (1 step)" + - "Hold RGB → Change xy color (loop)" + - "Click CW → Warm color" + - "Hold CW → Change color temp (toggle mode)" + +integrations: + - name: ZHA + codename: zha + actions: + - '"on" → Click light on' + - '"off" → Click light off' + - "hold_brightness_up → Hold light on" + - "hold_brightness_down → Hold light off" + - "stop → Release light on or off" + - "move_to_color → Click RGB" + - "move_hue → Hold RGB" + - "stop_move_hue → Release RGB" + - "move_to_color_temp → Click CW" + - "move_color_temp → Hold CW" + - "stop_move_step → Release CW" + - "recall_0_1 → Click clapperboard" + \ No newline at end of file diff --git a/docs/assets/img/ZB-5122.jpeg b/docs/assets/img/ZB-5122.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..faf376527117667997c0740fc05453ed472adb75 GIT binary patch literal 13687 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