"""Tuya MCU based cover and blinds.""" from typing import Dict, Optional, Union from zigpy.profiles import zha import zigpy.types as t from zigpy.zcl import foundation from zigpy.zcl.clusters.closures import WindowCovering from zigpy.zcl.clusters.general import ( Basic, GreenPowerProxy, Groups, Ota, Scenes, Time, ) from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.tuya import ( NoManufacturerCluster, TUYA_MCU_COMMAND, TuyaLocalCluster, TuyaWindowCover, ) from zhaquirks.tuya.mcu import ( DPToAttributeMapping, TuyaClusterData, TuyaMCUCluster, ) class TuyaCC(t.enum8): """Tuya cover commands.""" OPEN = 0x00 STOP = 0x01 CLOSE = 0x02 class ZclCC(t.enum8): """ZCL cover commands.""" OPEN = 0x00 CLOSE = 0x01 STOP = 0x02 TUYA2ZB_COMMANDS = { ZclCC.OPEN: TuyaCC.OPEN, ZclCC.CLOSE: TuyaCC.CLOSE, ZclCC.STOP: TuyaCC.STOP, } class TuyaWindowCovering(NoManufacturerCluster, WindowCovering, TuyaLocalCluster): """Tuya MCU WindowCovering cluster.""" """Add additional attributes for direction""" attributes = WindowCovering.attributes.copy() attributes.update( { 0xF000: ("curtain_switch", t.enum8, True), # 0: open, 1: stop, 2: close 0xF001: ("accurate_calibration", t.enum8, True), # 0: calibration started, 1: calibration finished 0xF002: ("motor_steering", t.enum8, True), # 0: default, 1: reverse 0xF003: ("travel", t.uint16_t, True), # 30 to 9000 (units of 0.1 seconds) } ) async def command( self, command_id: Union[foundation.GeneralCommand, int, t.uint8_t], *args, manufacturer: Optional[Union[int, t.uint16_t]] = None, expect_reply: bool = True, tsn: Optional[Union[int, t.uint8_t]] = None, ): """Override the default Cluster command.""" self.debug( "Sending Tuya Cluster Command. Cluster Command is %x, Arguments are %s", command_id, args, ) # (upopen, downclose, stop) if command_id in (0x0002, 0x0000, 0x0001): # ¿0x0003: continue? cluster_data = TuyaClusterData( endpoint_id=self.endpoint.endpoint_id, cluster_name=self.ep_attribute, cluster_attr="curtain_switch", attr_value=TUYA2ZB_COMMANDS[command_id], # convert tuya2zigbee command expect_reply=expect_reply, manufacturer=-1, ) self.endpoint.device.command_bus.listener_event( TUYA_MCU_COMMAND, cluster_data, ) return foundation.GENERAL_COMMANDS[ foundation.GeneralCommand.Default_Response ].schema(command_id=command_id, status=foundation.Status.SUCCESS) # (go_to_lift_percentage) elif command_id == 0x0005: lift_value = args[0] cluster_data = TuyaClusterData( endpoint_id=self.endpoint.endpoint_id, cluster_name=self.ep_attribute, cluster_attr="current_position_lift_percentage", attr_value=lift_value, expect_reply=expect_reply, manufacturer=-1, ) self.endpoint.device.command_bus.listener_event( TUYA_MCU_COMMAND, cluster_data, ) return foundation.GENERAL_COMMANDS[ foundation.GeneralCommand.Default_Response ].schema(command_id=command_id, status=foundation.Status.SUCCESS) self.warning("Unsupported command_id: %s", command_id) return foundation.GENERAL_COMMANDS[ foundation.GeneralCommand.Default_Response ].schema(command_id=command_id, status=foundation.Status.UNSUP_CLUSTER_COMMAND) class TuyaWindowCoverManufCluster(TuyaMCUCluster): """Tuya with WindowCover data points.""" attributes = TuyaMCUCluster.attributes.copy() attributes.update( { 0x5000: ("backlight_mode", t.enum8, True), # 0: off, 1: on 0x8001: ("indicator_status", t.enum8, True), # 0: status, 1: position, 2: off (¿backlight_mode?) } ) dp_to_attribute: Dict[int, DPToAttributeMapping] = { 1: DPToAttributeMapping( TuyaWindowCovering.ep_attribute, "curtain_switch", ), 2: DPToAttributeMapping( TuyaWindowCovering.ep_attribute, "current_position_lift_percentage", # for Slider movement ), 3: DPToAttributeMapping( TuyaWindowCovering.ep_attribute, "current_position_lift_percentage", # for Slider updates ), } data_point_handlers = { 1: "_dp_2_attr_update", 2: "_dp_2_attr_update", 3: "_dp_2_attr_update", 4: "_dp_2_attr_update", 5: "_dp_2_attr_update", 6: "_dp_2_attr_update", 7: "_dp_2_attr_update", 8: "_dp_2_attr_update", 9: "_dp_2_attr_update", 10: "_dp_2_attr_update", 11: "_dp_2_attr_update", 14: "_dp_2_attr_update", } class TuyaCover0601_GP(TuyaWindowCover): """Tuya blind controller device.""" signature = { MODELS_INFO: [ ("_TZE204_r0jdjrvi", "TS0601"), ], ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaWindowCoverManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, 242: { PROFILE_ID: 41440, DEVICE_TYPE: 97, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaWindowCoverManufCluster, TuyaWindowCovering, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, 242: { PROFILE_ID: 41440, DEVICE_TYPE: 97, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, } }