From 6b06af789bd8a97c42a24bd05c370ecfadd6f363 Mon Sep 17 00:00:00 2001 From: bs42 Date: Tue, 17 Mar 2020 11:26:17 -0700 Subject: [PATCH 1/6] Add support for the SAGE Doorbell (AKA Echostar Bell) (#303) * Add support for te SAGE Doorbell (AKA Echostar Bell) * Correct model_info * lint corrections * isort * Fix Lint Fix Lint * Lint * Add second doorbell sensor Use BUTTON_1 and BUTTON_2 instead to cleanly handle the distinction * lint Co-authored-by: David F. Mulcahey --- zhaquirks/echostar/__init__.py | 1 + zhaquirks/echostar/bell.py | 87 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 zhaquirks/echostar/__init__.py create mode 100644 zhaquirks/echostar/bell.py diff --git a/zhaquirks/echostar/__init__.py b/zhaquirks/echostar/__init__.py new file mode 100644 index 0000000000..8e3d9fbc61 --- /dev/null +++ b/zhaquirks/echostar/__init__.py @@ -0,0 +1 @@ +"""Module for Echostar quirks implementations.""" diff --git a/zhaquirks/echostar/bell.py b/zhaquirks/echostar/bell.py new file mode 100644 index 0000000000..64809afdd1 --- /dev/null +++ b/zhaquirks/echostar/bell.py @@ -0,0 +1,87 @@ +"""Echostar Sage Doorbell Sensor Device.""" +from zigpy.profiles import zha +from zigpy.quirks import CustomDevice +from zigpy.zcl.clusters.general import ( + Alarms, + Basic, + Identify, + LevelControl, + OnOff, + Ota, + PowerConfiguration, +) + +from ..const import ( + BUTTON_1, + BUTTON_2, + CLUSTER_ID, + COMMAND, + COMMAND_ON, + COMMAND_OFF, + DEVICE_TYPE, + ENDPOINT_ID, + ENDPOINTS, + INPUT_CLUSTERS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, + SHORT_PRESS, +) + +MANUFACTURER = " Echostar" +MODEL = " Bell" + + +class Bell(CustomDevice): + """Echostar Bell device.""" + + signature = { + # + MODELS_INFO: [(MANUFACTURER, MODEL)], + ENDPOINTS: { + 18: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.DIMMER_SWITCH, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Alarms.cluster_id, + PowerConfiguration.cluster_id, + ], + OUTPUT_CLUSTERS: [ + Identify.cluster_id, + OnOff.cluster_id, + LevelControl.cluster_id, + Ota.cluster_id, + ], + } + }, + } + + replacement = { + ENDPOINTS: { + 18: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Alarms.cluster_id, + PowerConfiguration.cluster_id, + ], + OUTPUT_CLUSTERS: [ + Identify.cluster_id, + OnOff.cluster_id, + LevelControl.cluster_id, + Ota.cluster_id, + ], + } + } + } + device_automation_triggers = { + (SHORT_PRESS, BUTTON_1): {COMMAND: COMMAND_ON, CLUSTER_ID: 6, ENDPOINT_ID: 18}, + (SHORT_PRESS, BUTTON_2): {COMMAND: COMMAND_OFF, CLUSTER_ID: 6, ENDPOINT_ID: 18}, + } From 1f8659ac6210ab83ae431676db4ba9dbfb70be83 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Thu, 26 Mar 2020 12:16:33 -0400 Subject: [PATCH 2/6] SmartThings motion quirk update. (#312) Update signature to include motionv5 model. --- zhaquirks/smartthings/{motionv4.py => motion.py} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename zhaquirks/smartthings/{motionv4.py => motion.py} (92%) diff --git a/zhaquirks/smartthings/motionv4.py b/zhaquirks/smartthings/motion.py similarity index 92% rename from zhaquirks/smartthings/motionv4.py rename to zhaquirks/smartthings/motion.py index 1337bda059..edf17244a2 100755 --- a/zhaquirks/smartthings/motionv4.py +++ b/zhaquirks/smartthings/motion.py @@ -18,15 +18,15 @@ ) -class SmartThingsMotionV4(CustomDevice): - """SmartThingsMotionV4.""" +class SmartThingsMotion(CustomDevice): + """SmartThingsMotionV4 or V5.""" signature = { # - MODELS_INFO: [(SMART_THINGS, "motionv4")], + MODELS_INFO: [(SMART_THINGS, "motionv4"), (SMART_THINGS, "motionv5")], ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, From d1fb0f96f87640a16f5740334962bef0ebbc0729 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 27 Mar 2020 20:41:33 -0400 Subject: [PATCH 3/6] konke motion alternate version (#314) --- zhaquirks/konke/motion.py | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/zhaquirks/konke/motion.py b/zhaquirks/konke/motion.py index 1209b13da5..04d5141b3a 100644 --- a/zhaquirks/konke/motion.py +++ b/zhaquirks/konke/motion.py @@ -68,3 +68,52 @@ def __init__(self, *args, **kwargs): } } } + + +class KonkeMotionB(CustomDevice): + """Custom device representing konke motion sensors.""" + + def __init__(self, *args, **kwargs): + """Init.""" + self.occupancy_bus = Bus() + super().__init__(*args, **kwargs) + + signature = { + # + MODELS_INFO: [ + (KONKE, "3AFE28010402000D"), + (KONKE, "3AFE14010402000D"), + (KONKE, "3AFE27010402000D"), + ], + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.IAS_ZONE, + INPUT_CLUSTERS: [ + Basic.cluster_id, + PowerConfiguration.cluster_id, + Identify.cluster_id, + IasZone.cluster_id, + ], + OUTPUT_CLUSTERS: [Identify.cluster_id], + } + }, + } + + replacement = { + ENDPOINTS: { + 1: { + INPUT_CLUSTERS: [ + Basic.cluster_id, + PowerConfigurationCluster, + Identify.cluster_id, + OccupancyCluster, + MotionCluster, + ], + OUTPUT_CLUSTERS: [Identify.cluster_id], + } + } + } From ea6d031a8494c95eaa95478580e68bfea9bf26eb Mon Sep 17 00:00:00 2001 From: bs42 Date: Fri, 27 Mar 2020 17:41:45 -0700 Subject: [PATCH 4/6] Add support for the older rwl020 remote (#304) * Add support for the older rwl020 remote * This remote only has SHORT_PRESS * Add support for "stop" event. * Remove RELEASE action due to lack of clarity * forgot to remove import * also remote COMMAND_STOP --- zhaquirks/philips/rwl020.py | 139 ++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 zhaquirks/philips/rwl020.py diff --git a/zhaquirks/philips/rwl020.py b/zhaquirks/philips/rwl020.py new file mode 100644 index 0000000000..c0dbc537e0 --- /dev/null +++ b/zhaquirks/philips/rwl020.py @@ -0,0 +1,139 @@ +"""Phillips RWL020 device.""" +from zigpy.profiles import zha, zll +from zigpy.quirks import CustomCluster, CustomDevice +import zigpy.types as t +from zigpy.zcl.clusters.general import ( + Basic, + BinaryInput, + Groups, + Identify, + LevelControl, + OnOff, + Ota, + PowerConfiguration, +) + +from ..const import ( + ARGS, + CLUSTER_ID, + COMMAND, + COMMAND_OFF_WITH_EFFECT, + COMMAND_ON, + COMMAND_STEP, + DEVICE_TYPE, + DIM_DOWN, + DIM_UP, + ENDPOINT_ID, + ENDPOINTS, + INPUT_CLUSTERS, + LONG_PRESS, + OUTPUT_CLUSTERS, + PROFILE_ID, + SHORT_PRESS, + TURN_OFF, + TURN_ON, +) + +DIAGNOSTICS_CLUSTER_ID = 0x0B05 # decimal = 2821 + + +class BasicCluster(CustomCluster, Basic): + """Centralite acceleration cluster.""" + + attributes = Basic.attributes.copy() + attributes.update({0x0031: ("phillips", t.bitmap16)}) + + +class PhilipsRWL020(CustomDevice): + """Phillips RWL020 device.""" + + signature = { + # + ENDPOINTS: { + 1: { + PROFILE_ID: zll.PROFILE_ID, + DEVICE_TYPE: zll.DeviceType.CONTROLLER, + INPUT_CLUSTERS: [Basic.cluster_id], + OUTPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + OnOff.cluster_id, + LevelControl.cluster_id, + ], + }, + # + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.SIMPLE_SENSOR, + INPUT_CLUSTERS: [ + Basic.cluster_id, + PowerConfiguration.cluster_id, + Identify.cluster_id, + BinaryInput.cluster_id, + 64512, + ], + OUTPUT_CLUSTERS: [Ota.cluster_id], + }, + } + } + + replacement = { + ENDPOINTS: { + 1: { + INPUT_CLUSTERS: [Basic.cluster_id], + OUTPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + OnOff.cluster_id, + LevelControl.cluster_id, + ], + }, + 2: { + INPUT_CLUSTERS: [ + BasicCluster, + PowerConfiguration.cluster_id, + Identify.cluster_id, + BinaryInput.cluster_id, + 64512, + ], + OUTPUT_CLUSTERS: [Ota.cluster_id], + }, + } + } + + device_automation_triggers = { + (SHORT_PRESS, TURN_ON): {COMMAND: COMMAND_ON}, + (SHORT_PRESS, TURN_OFF): {COMMAND: COMMAND_OFF_WITH_EFFECT}, + (SHORT_PRESS, DIM_UP): { + COMMAND: COMMAND_STEP, + CLUSTER_ID: 8, + ENDPOINT_ID: 1, + ARGS: [0, 30, 9], + }, + (LONG_PRESS, DIM_UP): { + COMMAND: COMMAND_STEP, + CLUSTER_ID: 8, + ENDPOINT_ID: 1, + ARGS: [0, 56, 9], + }, + (SHORT_PRESS, DIM_DOWN): { + COMMAND: COMMAND_STEP, + CLUSTER_ID: 8, + ENDPOINT_ID: 1, + ARGS: [1, 30, 9], + }, + (LONG_PRESS, DIM_DOWN): { + COMMAND: COMMAND_STEP, + CLUSTER_ID: 8, + ENDPOINT_ID: 1, + ARGS: [1, 56, 9], + }, + } From d1f1d7cb8ff030e551adf015f0a665a87714c2ee Mon Sep 17 00:00:00 2001 From: Balazs Sandor Date: Mon, 30 Mar 2020 21:54:43 +0200 Subject: [PATCH 5/6] Add support lumi.ctrl_neutral1, Aqara QBKG04LM (#310) * WIP: lumi.ctrl_neutral1 * lumi.ctrl_neutral1 is working now * lint fixes * fix lint errors * review fix --- tests/test_ctrl_neutral1.py | 50 +++++++ zhaquirks/xiaomi/aqara/ctrl_neutral1.py | 180 ++++++++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 tests/test_ctrl_neutral1.py create mode 100644 zhaquirks/xiaomi/aqara/ctrl_neutral1.py diff --git a/tests/test_ctrl_neutral1.py b/tests/test_ctrl_neutral1.py new file mode 100644 index 0000000000..a6a60b6b09 --- /dev/null +++ b/tests/test_ctrl_neutral1.py @@ -0,0 +1,50 @@ +"""Tests for xiaomi.""" +from unittest import mock +from unittest.mock import call + +import zigpy.application +from zigpy.device import Device + +from zhaquirks.xiaomi.aqara.ctrl_neutral1 import CtrlNeutral1 + + +# zigbee-herdsman:controller:endpoint Command 0x00158d00024be541/2 genOnOff.on({}, +# {"timeout":6000,"manufacturerCode":null,"disableDefaultResponse":false}) +# zigbee-herdsman:adapter:zStack:znp:SREQ --> AF - dataRequest - +# {"dstaddr":65311,"destendpoint":2,"srcendpoint":1,"clusterid":6,"transid":17,"options":0,"radius":30,"len":3, +# "data":{"type":"Buffer","data":[1,8,1]}} +# zigbee-herdsman:adapter:zStack:unpi:writer -->frame [254,13,36,1,31,255,2,1,6,0,17,0,30,3,1,8,1,201] + + +# zigbee-herdsman:controller:endpoint Command 0x00158d00024be541/2 genOnOff.off({}, +# {"timeout":6000,"manufacturerCode":null,"disableDefaultResponse":false}) +# zigbee-herdsman:adapter:zStack:znp:SREQ --> AF - dataRequest - +# {"dstaddr":65311,"destendpoint":2,"srcendpoint":1,"clusterid":6,"transid":16,"options":0,"radius":30,"len":3, +# "data":{"type":"Buffer","data":[1,7,0]}} +# zigbee-herdsman:adapter:zStack:unpi:writer --> frame [254,13,36,1,31,255,2,1,6,0,16,0,30,3,1,7,0,198] + + +def test_ctrl_neutral1(): + """Test ctrl neutral 1 sends correct request.""" + sec = 8 + ieee = 0 + nwk = 1234 + data = b"\x01\x08\x01" + cluster = 6 + src_ep = 1 + dst_ep = 2 + + app = zigpy.application.ControllerApplication() + app.request = mock.MagicMock() + app.get_sequence = mock.MagicMock(return_value=sec) + + rep = Device(app, ieee, nwk) + rep.add_endpoint(1) + rep.add_endpoint(2) + + dev = CtrlNeutral1(app, ieee, nwk, rep) + dev[2].in_clusters[cluster].command(1) + + assert app.request.call_args == call( + dev, 260, cluster, src_ep, dst_ep, sec, data, expect_reply=True + ) diff --git a/zhaquirks/xiaomi/aqara/ctrl_neutral1.py b/zhaquirks/xiaomi/aqara/ctrl_neutral1.py new file mode 100644 index 0000000000..f23251dbd1 --- /dev/null +++ b/zhaquirks/xiaomi/aqara/ctrl_neutral1.py @@ -0,0 +1,180 @@ +"""Xiaomi aqara single key wall switch devices.""" +import logging + +from zigpy.profiles import zha +from zigpy.quirks import CustomCluster +from zigpy.zcl.clusters.general import ( + AnalogInput, + Basic, + Groups, + Identify, + MultistateInput, + OnOff, + Ota, + Scenes, + DeviceTemperature, + Time, + BinaryOutput, +) + +from .. import LUMI, BasicCluster, PowerConfigurationCluster, XiaomiCustomDevice +from ...const import ( + DEVICE_TYPE, + ENDPOINTS, + INPUT_CLUSTERS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, + SKIP_CONFIGURATION, +) + +DOUBLE = "double" +HOLD = "long press" +PRESS_TYPES = {0: "long press", 1: "single", 2: "double"} +SINGLE = "single" +STATUS_TYPE_ATTR = 0x0055 # decimal = 85 +XIAOMI_CLUSTER_ID = 0xFFFF +XIAOMI_DEVICE_TYPE = 0x5F01 +XIAOMI_DEVICE_TYPE2 = 0x5F02 +XIAOMI_DEVICE_TYPE3 = 0x5F03 + +_LOGGER = logging.getLogger(__name__) + +# click attr 0xF000 +# single click 0x3FF1F00 +# double click 0xCFF1F00 + + +class XiaomiOnOffCluster(OnOff, CustomCluster): + """Aqara wall switch cluster.""" + + server_commands = {0x0000: ("off", (), False), 0x0001: ("on", (), False)} + + def command(self, command, *args, manufacturer=None, expect_reply=True): + """Command handler.""" + src_ep = 1 + dst_ep = 2 + seq = self._endpoint.device.application.get_sequence() + return self._endpoint.device.application.request( + self._endpoint.device, + zha.PROFILE_ID, + OnOff.cluster_id, + src_ep, + dst_ep, + seq, + bytes([src_ep, seq, command]), + expect_reply=expect_reply, + ) + + +class CtrlNeutral1(XiaomiCustomDevice): + """Aqara single key switch device.""" + + signature = { + MODELS_INFO: [(LUMI, "lumi.ctrl_neutral1")], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + PowerConfigurationCluster.cluster_id, + DeviceTemperature.cluster_id, + Ota.cluster_id, + Time.cluster_id, + ], + OUTPUT_CLUSTERS: [Basic.cluster_id, Time.cluster_id, Ota.cluster_id], + }, + # + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + BinaryOutput.cluster_id, + OnOff.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + # + 4: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [MultistateInput.cluster_id, OnOff.cluster_id], + OUTPUT_CLUSTERS: [], + }, + # + 5: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [MultistateInput.cluster_id, OnOff.cluster_id], + OUTPUT_CLUSTERS: [], + }, + # + 6: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [MultistateInput.cluster_id, OnOff.cluster_id], + OUTPUT_CLUSTERS: [], + }, + # + 8: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.METER_INTERFACE, + INPUT_CLUSTERS: [AnalogInput.cluster_id], + OUTPUT_CLUSTERS: [], + }, + }, + } + + replacement = { + SKIP_CONFIGURATION: True, + ENDPOINTS: { + 1: { + DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL, + INPUT_CLUSTERS: [BasicCluster], + OUTPUT_CLUSTERS: [], + }, + 2: { + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [BasicCluster, XiaomiOnOffCluster], + OUTPUT_CLUSTERS: [], + }, + }, + } From b58a55075ed9f11c5862d57994fc704fe275f2ca Mon Sep 17 00:00:00 2001 From: David Mulcahey Date: Tue, 31 Mar 2020 20:35:29 -0400 Subject: [PATCH 6/6] bump version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 910f876182..ed5e0feea2 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import find_packages, setup -VERSION = "0.0.37" +VERSION = "0.0.38" def readme():