"""EyZEE TS0001-TS0004 endpoint switches for ZHA. Supported fingerprints:ver 28092026 - _TZ3000_bbebkwjk / TS0001 - _TZ3000_zam8nhsp / TS0002 - _TZ3000_sjhtrjg2 / TS0003 - _TZ3000_a4uohq8u / TS0003 - _TZ3000_2zwlkqrc / TS0003 - _TZ3000_vyitm412 / TS0004 - _TZ3000_g0h4wqfu / TS0004 - _TZ3000_k2nx32wy / TS0004 The relays use standard endpoint-specific Zigbee OnOff commands. These Tuya firmware variants must also receive one Basic-cluster Read Attributes request containing attributes 0x0004, 0x0000, 0x0001, 0x0005, 0x0007 and 0xFFFE in a single ZCL frame. zigpy's normal read_attributes() helper splits after five attributes, so apply_custom_configuration() deliberately uses the lower-level request() method. Tuya configuration features retained: - OnOff cluster 0x0006: power-on behaviour (attribute 0x8002) - Tuya cluster 0xE000: indicator/backlight mode - Tuya cluster 0xE001: external switch type (toggle/state/momentary) """ from zigpy.profiles import zgp, zha from zigpy.quirks import CustomDevice from zigpy.zcl import foundation from zigpy.zcl.clusters.general import ( Basic, GreenPowerProxy, Groups, Identify, OnOff, Ota, Scenes, Time, ) from zha.quirks import TUYA_PLUG_ONOFF from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.tuya import ( TuyaZBE000Cluster, TuyaZBExternalSwitchTypeCluster, TuyaZBOnOffAttributeCluster, ) def _signature(manufacturer: str, model: str, gang_count: int) -> dict: """Build the physical device signature for a TS000x GPP switch.""" endpoints = { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBE000Cluster.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, } for endpoint_id in range(2, gang_count + 1): endpoints[endpoint_id] = { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [], } endpoints[242] = { PROFILE_ID: zgp.PROFILE_ID, DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], } return { MODELS_INFO: [(manufacturer, model)], ENDPOINTS: endpoints, } def _replacement(gang_count: int) -> dict: """Build independent relay endpoints as switch entities.""" endpoints = { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, INPUT_CLUSTERS: [ Basic, Identify, Groups, Scenes, TuyaZBOnOffAttributeCluster, TuyaZBE000Cluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [Time, Ota], }, } for endpoint_id in range(2, gang_count + 1): endpoints[endpoint_id] = { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, INPUT_CLUSTERS: [ Groups, Scenes, TuyaZBOnOffAttributeCluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [], } endpoints[242] = { PROFILE_ID: zgp.PROFILE_ID, DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy], } return {ENDPOINTS: endpoints} class _EyZEE_TS000x_Base: """Common initialization required by the EyZEE TS000x firmware.""" quirk_id = TUYA_PLUG_ONOFF async def apply_custom_configuration(self): """Send the unchunked six-attribute initialization request.""" basic_cluster = self.endpoints[1].in_clusters[Basic.cluster_id] command = foundation.GeneralCommand.Read_Attributes schema = foundation.GENERAL_COMMANDS[command].schema await basic_cluster.request( True, command, schema, attribute_ids=[ 0x0004, # manufacturerName 0x0000, # zclVersion 0x0001, # appVersion 0x0005, # modelId 0x0007, # powerSource 0xFFFE, # attributeReportingStatus ], manufacturer=None, expect_reply=True, disable_default_response=True, ) class EyZEE_TS0001_1Gang(_EyZEE_TS000x_Base, CustomDevice): """EyZEE one-gang TS0001 switch.""" signature = _signature("_TZ3000_bbebkwjk", "TS0001", 1) replacement = _replacement(1) class EyZEE_TS0002_2Gang(_EyZEE_TS000x_Base, CustomDevice): """EyZEE two-gang TS0002 switch.""" signature = _signature("_TZ3000_zam8nhsp", "TS0002", 2) replacement = _replacement(2) class EyZEE_TS0003_3Gang(_EyZEE_TS000x_Base, CustomDevice): """EyZEE three-gang TS0003 switch.""" signature = _signature("_TZ3000_sjhtrjg2", "TS0003", 3) signature[MODELS_INFO].extend( [ ("_TZ3000_a4uohq8u", "TS0003"), ("_TZ3000_2zwlkqrc", "TS0003"), ] ) replacement = _replacement(3) class EyZEE_TS0004_4Gang(_EyZEE_TS000x_Base, CustomDevice): """EyZEE four-gang TS0004 switch.""" signature = _signature("_TZ3000_vyitm412", "TS0004", 4) signature[MODELS_INFO].extend( [ ("_TZ3000_g0h4wqfu", "TS0004"), ("_TZ3000_k2nx32wy", "TS0004"), ] ) replacement = _replacement(4)