"""Device handler for loratap TS130F smart curtain switch.""" from typing import Final from zigpy.profiles import zgp, zha from zigpy.quirks import CustomCluster, CustomDevice import zigpy.types as t from zigpy.zcl.clusters.closures import WindowCovering from zigpy.zcl.clusters.general import ( Basic, GreenPowerProxy, Groups, Identify, OnOff, Ota, Scenes, Time, ) from zigpy.zcl.foundation import ZCLAttributeDef from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODEL, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.tuya import SwitchBackLight, TuyaZBExternalSwitchTypeCluster ATTR_CURRENT_POSITION_LIFT_PERCENTAGE = 0x0008 CMD_GO_TO_LIFT_PERCENTAGE = 0x0005 class TuyaWithBacklightOnOffCluster(CustomCluster, OnOff): """Tuya Zigbee On Off cluster with extra attributes.""" class AttributeDefs(OnOff.AttributeDefs): """Attribute definitions.""" backlight_mode: Final = ZCLAttributeDef(id=0x8001, type=SwitchBackLight) class MotorMode(t.enum8): """Tuya motor mode enum.""" STRONG_MOTOR = 0x00 WEAK_MOTOR = 0x01 class TuyaCoveringCluster(CustomCluster, WindowCovering): """TuyaSmartCurtainWindowCoveringCluster: Allow to setup Window covering tuya devices.""" class AttributeDefs(WindowCovering.AttributeDefs): """Attribute definitions.""" motor_mode: Final = ZCLAttributeDef(id=0x8000, type=MotorMode) tuya_moving_state: Final = ZCLAttributeDef(id=0xF000, type=t.enum8) calibration: Final = ZCLAttributeDef(id=0xF001, type=t.enum8) motor_reversal: Final = ZCLAttributeDef(id=0xF002, type=t.enum8) calibration_time: Final = ZCLAttributeDef(id=0xF003, type=t.uint16_t) def _update_attribute(self, attrid, value): if attrid == ATTR_CURRENT_POSITION_LIFT_PERCENTAGE: # Invert the percentage value (cf https://github.com/dresden-elektronik/deconz-rest-plugin/issues/3757) value = 100 - value super()._update_attribute(attrid, value) async def command( self, command_id, *args, manufacturer=None, expect_reply=True, tsn=None ): """Override default command to invert percent lift value.""" if command_id == CMD_GO_TO_LIFT_PERCENTAGE: percent = args[0] # Invert the percentage value (cf https://github.com/dresden-elektronik/deconz-rest-plugin/issues/3757) percent = 100 - percent v = (percent,) return await super().command(command_id, *v) return await super().command( command_id, *args, manufacturer=manufacturer, expect_reply=expect_reply, tsn=tsn, ) class TuyaTS130FTI(CustomDevice): """Tuya smart curtain roller shutter Time In.""" signature = { # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=0x0202, device_version=1, input_clusters=[0, 4, 5, 6, 10, 0x0102], output_clusters=[25])) MODEL: "TS130F", ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, Time.cluster_id, OnOff.cluster_id, WindowCovering.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, Time.cluster_id, TuyaWithBacklightOnOffCluster, TuyaCoveringCluster, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, }, } class TuyaZemismartTS130F(CustomDevice): """Tuya ZemiSmart smart curtain roller shutter.""" signature = { # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=0x0202, device_version=1, input_clusters=[0x0000, 0x0004, 0x0005, 0x0006, 0x0102], output_clusters=[0x000a, 0x0019])) MODEL: "TS130F", ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, WindowCovering.cluster_id, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaWithBacklightOnOffCluster, TuyaCoveringCluster, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, }, } class TuyaZemismartWNEC1ETS130F(CustomDevice): """Tuya ZemiSmart smart curtain roller shutter. WN-EC1E version.""" signature = { MODEL: "TS130F", ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, WindowCovering.cluster_id, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaWithBacklightOnOffCluster, TuyaCoveringCluster, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, }, } class TuyaTS130FTOGP(CustomDevice): """Tuya Oxt smart curtain roller shutter.""" signature = { MODEL: "TS130F", ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, WindowCovering.cluster_id, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, # 242: { PROFILE_ID: zgp.PROFILE_ID, DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaCoveringCluster, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, 242: { PROFILE_ID: zgp.PROFILE_ID, DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } class TuyaTS130FTI2(CustomDevice): """Tuya smart curtain roller shutter Time In.""" signature = { # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=0x0203, device_version=1, input_clusters=[0x0000, 0x0004, 0x0005, 0x0006, 0x0102], output_clusters=[0x000a, 0x0019])) MODEL: "TS130F", ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_CONTROLLER, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, WindowCovering.cluster_id, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_CONTROLLER, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaWithBacklightOnOffCluster, TuyaCoveringCluster, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, }, } class TuyaTS130FUL(CustomDevice): """Tuya Cover variant from UseeLink.""" signature = { # This signature was copied from TuyaTS130FTO and changed to fit the UseeLink device. MODEL: "TS130F", ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, WindowCovering.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaWithBacklightOnOffCluster, TuyaCoveringCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id], }, }, } class TuyaTS130FTO(CustomDevice): """Tuya smart curtain roller shutter Time Out.""" signature = { # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=0x0202, device_version=1, input_clusters=[0, 4, 5, 6, 10, 0x0102], output_clusters=[25])) # This signature is not correct is one copy of the first one and the cluster is not inline with the device. MODEL: "TS130F", ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, WindowCovering.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaCoveringCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, }, } class TuyaTS130GP(CustomDevice): """Tuya ZemiSmart smart curtain roller shutter with Green Power.""" signature = { # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=0x0202, device_version=1, input_clusters=[0, 4, 5, 6, 0x0102], output_clusters=[0x000a, 0x0019])) MODEL: "TS130F", ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, WindowCovering.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, 242: { #