From fb5d256299dd789536034ca33a91afc646a07251 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 11 Apr 2023 16:38:37 +0200 Subject: [PATCH] Ignore missing default reply for GLEDOPTO AC dimmer (#2330) * Use `NoReplyMixin` for GLEDOPTO dimmer * Move `NoReplyMixin` to main `__init__.py` file --- zhaquirks/__init__.py | 38 +++++++++++++++++ zhaquirks/gledopto/glsd001.py | 77 +++++++++++++++++++++++++++++++++++ zhaquirks/kof/kof_mr101z.py | 40 +----------------- 3 files changed, 116 insertions(+), 39 deletions(-) create mode 100644 zhaquirks/gledopto/glsd001.py diff --git a/zhaquirks/__init__.py b/zhaquirks/__init__.py index 39b6811c95..b4ebdb0ed0 100644 --- a/zhaquirks/__init__.py +++ b/zhaquirks/__init__.py @@ -390,6 +390,44 @@ def from_signature( return device +class NoReplyMixin: + """A simple mixin. + + Allows a cluster to have configurable list of command + ids that do not generate an explicit reply. + """ + + void_input_commands: set[int] = {} + + async def command(self, command, *args, expect_reply=None, **kwargs): + """Override the default Cluster command. + + expect_reply behavior is based on void_input_commands. + Note that this method changes the default value of + expect_reply to None. This allows the caller to explicitly force + expect_reply to true. + """ + + if expect_reply is None and command in self.void_input_commands: + cmd_expect_reply = False + elif expect_reply is None: + cmd_expect_reply = True # the default + else: + cmd_expect_reply = expect_reply + + rsp = await super().command( + command, *args, expect_reply=cmd_expect_reply, **kwargs + ) + + if expect_reply is None and command in self.void_input_commands: + # Pretend we received a default reply + return foundation.GENERAL_COMMANDS[ + foundation.GeneralCommand.Default_Response + ].schema(command_id=command, status=foundation.Status.SUCCESS) + + return rsp + + def setup(custom_quirks_path: str | None = None) -> None: """Register all quirks with zigpy, including optional custom quirks.""" diff --git a/zhaquirks/gledopto/glsd001.py b/zhaquirks/gledopto/glsd001.py new file mode 100644 index 0000000000..4f9d31c9c5 --- /dev/null +++ b/zhaquirks/gledopto/glsd001.py @@ -0,0 +1,77 @@ +"""Quirk for GLEDOPTO GL-SD-001.""" + +from zigpy.profiles import zha +from zigpy.quirks import CustomCluster, CustomDevice +from zigpy.zcl.clusters.general import ( + Basic, + Groups, + Identify, + LevelControl, + OnOff, + Ota, + Scenes, +) +from zigpy.zcl.clusters.lightlink import LightLink + +from zhaquirks import NoReplyMixin +from zhaquirks.const import ( + DEVICE_TYPE, + ENDPOINTS, + INPUT_CLUSTERS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, +) + + +class LevelControlNoReply(NoReplyMixin, CustomCluster, LevelControl): + """LevelControl cluster that does not require default responses.""" + + void_input_commands = {cmd.id for cmd in LevelControl.commands_by_name.values()} + + +class GledoptoGlSd001(CustomDevice): + """Gledopto GL-SD-001 dimmer custom device implementation.""" + + signature = { + MODELS_INFO: [("GLEDOPTO", "GL-SD-001")], + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.DIMMABLE_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + LevelControl.cluster_id, + LightLink.cluster_id, + ], + OUTPUT_CLUSTERS: [ + Ota.cluster_id, + ], + }, + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.DIMMABLE_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + LevelControlNoReply, + LightLink.cluster_id, + ], + OUTPUT_CLUSTERS: [ + Ota.cluster_id, + ], + }, + }, + } diff --git a/zhaquirks/kof/kof_mr101z.py b/zhaquirks/kof/kof_mr101z.py index 5918388705..08cba1e3ea 100644 --- a/zhaquirks/kof/kof_mr101z.py +++ b/zhaquirks/kof/kof_mr101z.py @@ -9,7 +9,6 @@ from zigpy.profiles import zha from zigpy.quirks import CustomCluster, CustomDevice -from zigpy.zcl import foundation from zigpy.zcl.clusters.general import ( Basic, Groups, @@ -21,6 +20,7 @@ ) from zigpy.zcl.clusters.hvac import Fan +from zhaquirks import NoReplyMixin from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, @@ -31,44 +31,6 @@ ) -class NoReplyMixin: - """A simple mixin. - - Allows a cluster to have configurable list of command - ids that do not generate an explicit reply. - """ - - void_input_commands: set[int] = {} - - async def command(self, command, *args, expect_reply=None, **kwargs): - """Override the default Cluster command. - - expect_reply behavior is based on void_input_commands. - Note that this method changes the default value of - expect_reply to None. This allows the caller to explicitly force - expect_reply to true. - """ - - if expect_reply is None and command in self.void_input_commands: - cmd_expect_reply = False - elif expect_reply is None: - cmd_expect_reply = True # the default - else: - cmd_expect_reply = expect_reply - - rsp = await super().command( - command, *args, expect_reply=cmd_expect_reply, **kwargs - ) - - if expect_reply is None and command in self.void_input_commands: - # Pretend we received a default reply - return foundation.GENERAL_COMMANDS[ - foundation.GeneralCommand.Default_Response - ].schema(command_id=command, status=foundation.Status.SUCCESS) - - return rsp - - class KofBasic(NoReplyMixin, CustomCluster, Basic): """KOF Basic Cluster."""