diff --git a/zhaquirks/const.py b/zhaquirks/const.py index 5d0d81480f..502f482fe6 100644 --- a/zhaquirks/const.py +++ b/zhaquirks/const.py @@ -14,6 +14,7 @@ ) import zigpy.types as t +ACTION = "action" ARGS = "args" ATTR_ID = "attr_id" ATTRIBUTE_ID = "attribute_id" @@ -73,6 +74,7 @@ COMMAND_TILT = "Tilt" COMMAND_TOGGLE = "toggle" COMMAND_TRIPLE = "triple" +COMMAND_SLIDER_EVENT = "slider_event" DESCRIPTION = "description" DEVICE_TYPE = SIG_EP_TYPE DIM_DOWN = "dim_down" @@ -116,9 +118,15 @@ SKIP_CONFIGURATION = SIG_SKIP_CONFIG SHORT_RELEASE = "remote_button_short_release" TOGGLE = "toggle" +SLIDER = "slider" TRIPLE_PRESS = "remote_button_triple_press" TURN_OFF = "turn_off" TURN_ON = "turn_on" +SLIDER_SINGLE = "slider_single" +SLIDER_DOUBLE = "slider_double" +SLIDER_HOLD = "slider_hold" +SLIDER_UP = "slider_up" +SLIDER_DOWN = "slider_down" UNKNOWN = "Unknown" VALUE = "value" ZHA_SEND_EVENT = "zha_send_event" diff --git a/zhaquirks/xiaomi/__init__.py b/zhaquirks/xiaomi/__init__.py index 4d2f53670a..41a34aaa59 100644 --- a/zhaquirks/xiaomi/__init__.py +++ b/zhaquirks/xiaomi/__init__.py @@ -44,7 +44,13 @@ ATTRIBUTE_ID, ATTRIBUTE_NAME, COMMAND_ATTRIBUTE_UPDATED, + COMMAND_SLIDER_EVENT, COMMAND_TRIPLE, + SLIDER_DOUBLE, + SLIDER_DOWN, + SLIDER_HOLD, + SLIDER_SINGLE, + SLIDER_UP, UNKNOWN, VALUE, ZHA_SEND_EVENT, @@ -733,6 +739,106 @@ def command( ) +class AqaraZ1ProManufacturerSpecificCluster(CustomCluster): + """Custom cluster for Aqara Z1 Pro manufacturer specific events.""" + + cluster_id = 0xFCC0 + + # Attribute IDs + ATTR_SLIDER_ACTION = 0x028C # 652 + ATTR_SLIDE_TIME = 0x0231 # 561 + ATTR_SLIDE_SPEED = 0x0232 # 562 + ATTR_SLIDE_RELATIVE_DISPLACEMENT = 0x0233 # 563 + ATTR_SLIDE_TIME_DELTA = 0x0301 # 769 + + # ATTR_DEVICE_ID_SHADE = 0x0200 # 512 + # ATTR_LOCK_RELAY = 0x0285 # 645 + # ATTR_SWITCH_MODE = 0x0004 # 4 + # ATTR_POWER_ON_BEHAVIOR = 0x0517 # 1303 + # ATTR_CLICK_MODE = 0x0125 # 293 + + # Action mapping + ACTION_MAPPING = { + 1: SLIDER_SINGLE, + 2: SLIDER_DOUBLE, + 3: SLIDER_HOLD, + 4: SLIDER_UP, + 5: SLIDER_DOWN, + } + + def __init__(self, *args, **kwargs): + """Init.""" + super().__init__(*args, **kwargs) + self._attr_id = self.ATTR_SLIDER_ACTION + _LOGGER.debug( + "AqaraZ1ProManufacturerSpecificCluster initialized for device %s", + self._endpoint.device.ieee, + ) + + def _update_attribute(self, attrid, value): + """Handle attribute updates.""" + + # Store all attributes for the event + if not hasattr(self, "_manufacturer_attrs"): + self._manufacturer_attrs = {} + + # Update the attribute value + self._manufacturer_attrs[attrid] = value + + # If this is the slider action attribute, send the event + if attrid == self.ATTR_SLIDER_ACTION: + # Get the action name from the mapping + action = self.ACTION_MAPPING.get(value, f"slider_unknown_{value}") + _LOGGER.debug( + "AqaraZ1ProManufacturerSpecificCluster detected action: %s (value: %s)", + action, + value, + ) + + # Prepare the event data + event_data = { + "action": action, + "value": value, + "slide_time": self._manufacturer_attrs.get(self.ATTR_SLIDE_TIME), + "slide_speed": self._manufacturer_attrs.get(self.ATTR_SLIDE_SPEED), + "slide_relative_displacement": self._manufacturer_attrs.get( + self.ATTR_SLIDE_RELATIVE_DISPLACEMENT + ), + "slide_time_delta": self._manufacturer_attrs.get( + self.ATTR_SLIDE_TIME_DELTA + ), + } + + _LOGGER.debug( + "AqaraZ1ProManufacturerSpecificCluster sending event data: %s", + event_data, + ) + + # Send the event + self.listener_event( + ZHA_SEND_EVENT, + action, + event_data, + ) + + # Also send a generic slider event for easier automation + self.listener_event( + ZHA_SEND_EVENT, + COMMAND_SLIDER_EVENT, + event_data, + ) + _LOGGER.debug( + "AqaraZ1ProManufacturerSpecificCluster events sent successfully" + ) + + _LOGGER.debug( + "AqaraZ1ProManufacturerSpecificCluster attribute update: attrid=0x%04x, value=%s", + attrid, + value, + ) + super()._update_attribute(attrid, value) + + def handle_quick_init( sender: zigpy.device.Device, profile: int, diff --git a/zhaquirks/xiaomi/aqara/aqara_z1_pro_double.py b/zhaquirks/xiaomi/aqara/aqara_z1_pro_double.py new file mode 100644 index 0000000000..708c09bd36 --- /dev/null +++ b/zhaquirks/xiaomi/aqara/aqara_z1_pro_double.py @@ -0,0 +1,271 @@ +"""Aqara Z1 Pro double rocker switch quirks.""" + +import logging +import sys + +from zigpy.profiles import zha +from zigpy.zcl.clusters.general import ( + AnalogInput, + Basic, + Groups, + Identify, + MultistateInput, + OnOff, + Ota, + Scenes, + Time, +) + +from zhaquirks.const import ( + ACTION, + ARGS, + ATTRIBUTE_ID, + BUTTON_1, + BUTTON_2, + CLUSTER_ID, + COMMAND, + COMMAND_SLIDER_EVENT, + DEVICE_TYPE, + DIM_DOWN, + DIM_UP, + DOUBLE_PRESS, + ENDPOINT_ID, + ENDPOINTS, + INPUT_CLUSTERS, + LONG_PRESS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, + SHORT_PRESS, + SLIDER, + SLIDER_DOUBLE, + SLIDER_DOWN, + SLIDER_HOLD, + SLIDER_SINGLE, + SLIDER_UP, + VALUE, +) +from zhaquirks.xiaomi import ( + AnalogInputCluster, + AqaraZ1ProManufacturerSpecificCluster, + BasicCluster, + ElectricalMeasurementCluster, + MeteringCluster, + OnOffCluster, + XiaomiCustomDevice, +) +from zhaquirks.xiaomi.aqara.opple_remote import MultistateInputCluster + +# Set up logging with a more visible format +_LOGGER = logging.getLogger(__name__) +_LOGGER.setLevel(logging.DEBUG) + +# Add a console handler to ensure logs go to stdout +console_handler = logging.StreamHandler(sys.stdout) +console_handler.setLevel(logging.DEBUG) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +console_handler.setFormatter(formatter) +_LOGGER.addHandler(console_handler) + +# Log at module level to verify the file is being loaded +_LOGGER.debug( + "AqaraZ1ProDoubleRockerSwitch quirk module is being loaded! ZHA Profile ID: 0x%04x, Device Type: 0x%04x", + zha.PROFILE_ID, + zha.DeviceType.ON_OFF_SWITCH, +) + + +class AqaraZ1ProDoubleRockerSwitch(XiaomiCustomDevice): + """Aqara Z1 Pro Double Rocker Switch.""" + + MANUFACTURER_SPECIFIC_CLUSTER_ID = 0xFCC0 + XIAOMI_COMMAND_SINGLE_1 = "1_single" + XIAOMI_COMMAND_SINGLE_2 = "2_single" + + def __init__(self, *args, **kwargs): + """Init.""" + super().__init__(*args, **kwargs) + _LOGGER.debug( + "AqaraZ1ProDoubleRockerSwitch device initialized with IEEE: %s", self.ieee + ) + + signature = { + MODELS_INFO: [("Aqara", "lumi.switch.acn057")], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MeteringCluster.cluster_id, + ElectricalMeasurementCluster.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + # + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 3: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 4: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + BasicCluster, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MeteringCluster, + ElectricalMeasurementCluster, + AqaraZ1ProManufacturerSpecificCluster, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 3: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 4: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInputCluster, + ], + OUTPUT_CLUSTERS: [], + }, + }, + } + + device_automation_triggers = { + (SHORT_PRESS, BUTTON_1): { + COMMAND: XIAOMI_COMMAND_SINGLE_1, + CLUSTER_ID: 18, + ENDPOINT_ID: 1, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, BUTTON_2): { + COMMAND: XIAOMI_COMMAND_SINGLE_2, + CLUSTER_ID: 18, + ENDPOINT_ID: 2, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_SINGLE, VALUE: 1}, + }, + (DOUBLE_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_DOUBLE, VALUE: 2}, + }, + (LONG_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_HOLD, VALUE: 3}, + }, + (DIM_UP, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_UP, VALUE: 4}, + }, + (DIM_DOWN, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_DOWN, VALUE: 5}, + }, + } diff --git a/zhaquirks/xiaomi/aqara/aqara_z1_pro_quadruple.py b/zhaquirks/xiaomi/aqara/aqara_z1_pro_quadruple.py new file mode 100644 index 0000000000..e5ff1e2e40 --- /dev/null +++ b/zhaquirks/xiaomi/aqara/aqara_z1_pro_quadruple.py @@ -0,0 +1,304 @@ +"""Aqara Z1 Pro quadruple rocker switch quirks.""" + +import logging +import sys + +from zigpy.profiles import zha +from zigpy.zcl.clusters.general import ( + AnalogInput, + Basic, + Groups, + Identify, + MultistateInput, + OnOff, + Ota, + Scenes, + Time, +) + +from zhaquirks.const import ( + ACTION, + ARGS, + ATTRIBUTE_ID, + BUTTON_1, + BUTTON_2, + BUTTON_3, + BUTTON_4, + CLUSTER_ID, + COMMAND, + COMMAND_SLIDER_EVENT, + DEVICE_TYPE, + DIM_DOWN, + DIM_UP, + DOUBLE_PRESS, + ENDPOINT_ID, + ENDPOINTS, + INPUT_CLUSTERS, + LONG_PRESS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, + SHORT_PRESS, + SLIDER, + SLIDER_DOUBLE, + SLIDER_DOWN, + SLIDER_HOLD, + SLIDER_SINGLE, + SLIDER_UP, + VALUE, +) +from zhaquirks.xiaomi import ( + AnalogInputCluster, + AqaraZ1ProManufacturerSpecificCluster, + BasicCluster, + ElectricalMeasurementCluster, + MeteringCluster, + OnOffCluster, + XiaomiCustomDevice, +) +from zhaquirks.xiaomi.aqara.opple_remote import MultistateInputCluster + +# Set up logging with a more visible format +_LOGGER = logging.getLogger(__name__) +_LOGGER.setLevel(logging.DEBUG) + +# Add a console handler to ensure logs go to stdout +console_handler = logging.StreamHandler(sys.stdout) +console_handler.setLevel(logging.DEBUG) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +console_handler.setFormatter(formatter) +_LOGGER.addHandler(console_handler) + +# Log at module level to verify the file is being loaded +_LOGGER.debug( + "AqaraZ1ProQuadrupleRockerSwitch quirk module is being loaded! ZHA Profile ID: 0x%04x, Device Type: 0x%04x", + zha.PROFILE_ID, + zha.DeviceType.ON_OFF_SWITCH, +) + + +class AqaraZ1ProQuadrupleRockerSwitch(XiaomiCustomDevice): + """Aqara Z1 Pro Quadruple Rocker Switch.""" + + MANUFACTURER_SPECIFIC_CLUSTER_ID = 0xFCC0 + XIAOMI_COMMAND_SINGLE_1 = "1_single" + XIAOMI_COMMAND_SINGLE_2 = "2_single" + XIAOMI_COMMAND_SINGLE_3 = "3_single" + XIAOMI_COMMAND_SINGLE_4 = "4_single" + + def __init__(self, *args, **kwargs): + """Init.""" + super().__init__(*args, **kwargs) + _LOGGER.debug( + "AqaraZ1ProQuadrupleRockerSwitch device initialized with IEEE: %s", + self.ieee, + ) + + signature = { + MODELS_INFO: [("Aqara", "lumi.switch.acn059")], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MeteringCluster.cluster_id, + ElectricalMeasurementCluster.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + # + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 3: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 4: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + BasicCluster, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MeteringCluster, + ElectricalMeasurementCluster, + AqaraZ1ProManufacturerSpecificCluster, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 3: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 4: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInputCluster, + ], + OUTPUT_CLUSTERS: [], + }, + }, + } + + device_automation_triggers = { + (SHORT_PRESS, BUTTON_1): { + COMMAND: XIAOMI_COMMAND_SINGLE_1, + CLUSTER_ID: 18, + ENDPOINT_ID: 1, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, BUTTON_2): { + COMMAND: XIAOMI_COMMAND_SINGLE_2, + CLUSTER_ID: 18, + ENDPOINT_ID: 2, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, BUTTON_3): { + COMMAND: XIAOMI_COMMAND_SINGLE_3, + CLUSTER_ID: 18, + ENDPOINT_ID: 3, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, BUTTON_4): { + COMMAND: XIAOMI_COMMAND_SINGLE_4, + CLUSTER_ID: 18, + ENDPOINT_ID: 4, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_SINGLE, VALUE: 1}, + }, + (DOUBLE_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_DOUBLE, VALUE: 2}, + }, + (LONG_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_HOLD, VALUE: 3}, + }, + (DIM_UP, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_UP, VALUE: 4}, + }, + (DIM_DOWN, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_DOWN, VALUE: 5}, + }, + } diff --git a/zhaquirks/xiaomi/aqara/aqara_z1_pro_single.py b/zhaquirks/xiaomi/aqara/aqara_z1_pro_single.py new file mode 100644 index 0000000000..b871887d00 --- /dev/null +++ b/zhaquirks/xiaomi/aqara/aqara_z1_pro_single.py @@ -0,0 +1,255 @@ +"""Aqara Z1 Pro single rocker switch quirks.""" + +import logging +import sys + +from zigpy.profiles import zha +from zigpy.zcl.clusters.general import ( + AnalogInput, + Basic, + Groups, + Identify, + MultistateInput, + OnOff, + Ota, + Scenes, + Time, +) + +from zhaquirks.const import ( + ACTION, + ARGS, + ATTRIBUTE_ID, + BUTTON, + CLUSTER_ID, + COMMAND, + COMMAND_SLIDER_EVENT, + DEVICE_TYPE, + DIM_DOWN, + DIM_UP, + DOUBLE_PRESS, + ENDPOINT_ID, + ENDPOINTS, + INPUT_CLUSTERS, + LONG_PRESS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, + SHORT_PRESS, + SLIDER, + SLIDER_DOUBLE, + SLIDER_DOWN, + SLIDER_HOLD, + SLIDER_SINGLE, + SLIDER_UP, + VALUE, +) +from zhaquirks.xiaomi import ( + AnalogInputCluster, + AqaraZ1ProManufacturerSpecificCluster, + BasicCluster, + ElectricalMeasurementCluster, + MeteringCluster, + OnOffCluster, + XiaomiCustomDevice, +) +from zhaquirks.xiaomi.aqara.opple_remote import MultistateInputCluster + +# Set up logging with a more visible format +_LOGGER = logging.getLogger(__name__) +_LOGGER.setLevel(logging.DEBUG) + +# Add a console handler to ensure logs go to stdout +console_handler = logging.StreamHandler(sys.stdout) +console_handler.setLevel(logging.DEBUG) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +console_handler.setFormatter(formatter) +_LOGGER.addHandler(console_handler) + +# Log at module level to verify the file is being loaded +_LOGGER.debug( + "AqaraZ1ProSingleRockerSwitch quirk module is being loaded! ZHA Profile ID: 0x%04x, Device Type: 0x%04x", + zha.PROFILE_ID, + zha.DeviceType.ON_OFF_SWITCH, +) + + +class AqaraZ1ProSingleRockerSwitch(XiaomiCustomDevice): + """Aqara Z1 Pro Single Rocker Switch.""" + + MANUFACTURER_SPECIFIC_CLUSTER_ID = 0xFCC0 + XIAOMI_COMMAND_SINGLE = "1_single" + + def __init__(self, *args, **kwargs): + """Init.""" + super().__init__(*args, **kwargs) + _LOGGER.debug( + "AqaraZ1ProSingleRockerSwitch device initialized with IEEE: %s", self.ieee + ) + + signature = { + MODELS_INFO: [("Aqara", "lumi.switch.acn056")], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MeteringCluster.cluster_id, + ElectricalMeasurementCluster.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + # + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 3: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 4: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + BasicCluster, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MeteringCluster, + ElectricalMeasurementCluster, + AqaraZ1ProManufacturerSpecificCluster, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 3: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 4: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInputCluster, + ], + OUTPUT_CLUSTERS: [], + }, + }, + } + + device_automation_triggers = { + (SHORT_PRESS, BUTTON): { + COMMAND: XIAOMI_COMMAND_SINGLE, + CLUSTER_ID: 18, + ENDPOINT_ID: 1, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_SINGLE, VALUE: 1}, + }, + (DOUBLE_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_DOUBLE, VALUE: 2}, + }, + (LONG_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_HOLD, VALUE: 3}, + }, + (DIM_UP, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_UP, VALUE: 4}, + }, + (DIM_DOWN, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_DOWN, VALUE: 5}, + }, + } diff --git a/zhaquirks/xiaomi/aqara/aqara_z1_pro_triple.py b/zhaquirks/xiaomi/aqara/aqara_z1_pro_triple.py new file mode 100644 index 0000000000..8ab598052a --- /dev/null +++ b/zhaquirks/xiaomi/aqara/aqara_z1_pro_triple.py @@ -0,0 +1,287 @@ +"""Aqara Z1 Pro triple rocker switch quirks.""" + +import logging +import sys + +from zigpy.profiles import zha +from zigpy.zcl.clusters.general import ( + AnalogInput, + Basic, + Groups, + Identify, + MultistateInput, + OnOff, + Ota, + Scenes, + Time, +) + +from zhaquirks.const import ( + ACTION, + ARGS, + ATTRIBUTE_ID, + BUTTON_1, + BUTTON_2, + BUTTON_3, + CLUSTER_ID, + COMMAND, + COMMAND_SLIDER_EVENT, + DEVICE_TYPE, + DIM_DOWN, + DIM_UP, + DOUBLE_PRESS, + ENDPOINT_ID, + ENDPOINTS, + INPUT_CLUSTERS, + LONG_PRESS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, + SHORT_PRESS, + SLIDER, + SLIDER_DOUBLE, + SLIDER_DOWN, + SLIDER_HOLD, + SLIDER_SINGLE, + SLIDER_UP, + VALUE, +) +from zhaquirks.xiaomi import ( + AnalogInputCluster, + AqaraZ1ProManufacturerSpecificCluster, + BasicCluster, + ElectricalMeasurementCluster, + MeteringCluster, + OnOffCluster, + XiaomiCustomDevice, +) +from zhaquirks.xiaomi.aqara.opple_remote import MultistateInputCluster + +# Set up logging with a more visible format +_LOGGER = logging.getLogger(__name__) +_LOGGER.setLevel(logging.DEBUG) + +# Add a console handler to ensure logs go to stdout +console_handler = logging.StreamHandler(sys.stdout) +console_handler.setLevel(logging.DEBUG) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +console_handler.setFormatter(formatter) +_LOGGER.addHandler(console_handler) + +# Log at module level to verify the file is being loaded +_LOGGER.debug( + "AqaraZ1ProTripleRockerSwitch quirk module is being loaded! ZHA Profile ID: 0x%04x, Device Type: 0x%04x", + zha.PROFILE_ID, + zha.DeviceType.ON_OFF_SWITCH, +) + + +class AqaraZ1ProTripleRockerSwitch(XiaomiCustomDevice): + """Aqara Z1 Pro Triple Rocker Switch.""" + + MANUFACTURER_SPECIFIC_CLUSTER_ID = 0xFCC0 + XIAOMI_COMMAND_SINGLE_1 = "1_single" + XIAOMI_COMMAND_SINGLE_2 = "2_single" + XIAOMI_COMMAND_SINGLE_3 = "3_single" + + def __init__(self, *args, **kwargs): + """Init.""" + super().__init__(*args, **kwargs) + _LOGGER.debug( + "AqaraZ1ProTripleRockerSwitch device initialized with IEEE: %s", self.ieee + ) + + signature = { + MODELS_INFO: [("Aqara", "lumi.switch.acn058")], + ENDPOINTS: { + # + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MeteringCluster.cluster_id, + ElectricalMeasurementCluster.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + # + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 3: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 4: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + # + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + BasicCluster, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MeteringCluster, + ElectricalMeasurementCluster, + AqaraZ1ProManufacturerSpecificCluster, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 3: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 4: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MANUFACTURER_SPECIFIC_CLUSTER_ID, + ], + OUTPUT_CLUSTERS: [], + }, + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInputCluster, + ], + OUTPUT_CLUSTERS: [], + }, + }, + } + + device_automation_triggers = { + (SHORT_PRESS, BUTTON_1): { + COMMAND: XIAOMI_COMMAND_SINGLE_1, + CLUSTER_ID: 18, + ENDPOINT_ID: 1, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, BUTTON_2): { + COMMAND: XIAOMI_COMMAND_SINGLE_2, + CLUSTER_ID: 18, + ENDPOINT_ID: 2, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, BUTTON_3): { + COMMAND: XIAOMI_COMMAND_SINGLE_3, + CLUSTER_ID: 18, + ENDPOINT_ID: 3, + ARGS: {ATTRIBUTE_ID: 85, VALUE: 1}, + }, + (SHORT_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_SINGLE, VALUE: 1}, + }, + (DOUBLE_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_DOUBLE, VALUE: 2}, + }, + (LONG_PRESS, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_HOLD, VALUE: 3}, + }, + (DIM_UP, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_UP, VALUE: 4}, + }, + (DIM_DOWN, SLIDER): { + COMMAND: COMMAND_SLIDER_EVENT, + CLUSTER_ID: 64704, + ENDPOINT_ID: 1, + ARGS: {ACTION: SLIDER_DOWN, VALUE: 5}, + }, + }