Repository: zigpy/zha-device-handlers

Issue:
[Device Support Request] TS0601 _TZE200_9mahtqtg Zemismart Tuya 6 Gangs Smart Wall Light Switch 4x4 TB26
**Is your feature request related to a problem? Please describe.**
Device is detected but no cover entity is exposed only basic_lqi and basic_rssi

**Describe the solution you'd like**
 Add support for TS0601 _TZE200_9mahtqtg Zemismart Tuya 6 Gangs Smart Wall Light Switch 4x4 TB26

<details>
<summary>Device signature - this can be acquired by clicking on the "Zigbee Device Signature" button in the device settings</summary>

```
{
  "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)",
  "endpoints": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0051",
      "in_clusters": [
        "0x0000",
        "0x0004",
        "0x0005",
        "0x0006",
        "0xef00"
      ],
      "out_clusters": [
        "0x000a",
        "0x0019"
      ]
    }
  },
  "manufacturer": "_TZE200_9mahtqtg",
  "model": "TS0601",
  "class": "zigpy.device.Device"
}
```

</details>

<details>
<summary>Diagnostic information - this can be acquired by clicking on the "Download Diagnostics" button in the device settings</summary>

```
{
  "home_assistant": {
    "installation_type": "Home Assistant OS",
    "version": "2022.4.7",
    "dev": false,
    "hassio": true,
    "virtualenv": false,
    "python_version": "3.9.9",
    "docker": true,
    "arch": "aarch64",
    "timezone": "America/Recife",
    "os_name": "Linux",
    "os_version": "5.10.103-v8",
    "supervisor": "2022.05.1",
    "host_os": "Home Assistant OS 7.6",
    "docker_version": "20.10.9",
    "chassis": "embedded",
    "run_as_root": true
  },
  "custom_components": {
    "zha_toolkit": {
      "version": "v0.8.8",
      "requirements": []
    },
    "hacs": {
      "version": "1.24.5",
      "requirements": [
        "aiogithubapi>=21.11.0"
      ]
    },
    "multiscrape": {
      "version": "6.2.0",
      "requirements": [
        "lxml",
        "beautifulsoup4==4.10.0"
      ]
    },
    "alexa_media": {
      "version": "3.11.3",
      "requirements": [
        "alexapy==1.25.5",
        "packaging>=20.3",
        "wrapt>=1.12.1"
      ]
    },
    "smartir": {
      "version": "1.17.6",
      "requirements": [
        "aiofiles==0.6.0"
      ]
    }
  },
  "integration_manifest": {
    "domain": "zha",
    "name": "Zigbee Home Automation",
    "config_flow": true,
    "documentation": "https://www.home-assistant.io/integrations/zha",
    "requirements": [
      "bellows==0.29.0",
      "pyserial==3.5",
      "pyserial-asyncio==0.6",
      "zha-quirks==0.0.72",
      "zigpy-deconz==0.14.0",
      "zigpy==0.44.2",
      "zigpy-xbee==0.14.0",
      "zigpy-zigate==0.7.4",
      "zigpy-znp==0.7.0"
    ],
    "usb": [
      {
        "vid": "10C4",
        "pid": "EA60",
        "description": "*2652*",
        "known_devices": [
          "slae.sh cc2652rb stick"
        ]
      },
      {
        "vid": "10C4",
        "pid": "EA60",
        "description": "*sonoff*plus*",
        "known_devices": [
          "sonoff zigbee dongle plus"
        ]
      },
      {
        "vid": "10C4",
        "pid": "EA60",
        "description": "*tubeszb*",
        "known_devices": [
          "TubesZB Coordinator"
        ]
      },
      {
        "vid": "1A86",
        "pid": "7523",
        "description": "*tubeszb*",
        "known_devices": [
          "TubesZB Coordinator"
        ]
      },
      {
        "vid": "1A86",
        "pid": "7523",
        "description": "*zigstar*",
        "known_devices": [
          "ZigStar Coordinators"
        ]
      },
      {
        "vid": "1CF1",
        "pid": "0030",
        "description": "*conbee*",
        "known_devices": [
          "Conbee II"
        ]
      },
      {
        "vid": "10C4",
        "pid": "8A2A",
        "description": "*zigbee*",
        "known_devices": [
          "Nortek HUSBZB-1"
        ]
      },
      {
        "vid": "10C4",
        "pid": "8B34",
        "description": "*bv 2010/10*",
        "known_devices": [
          "Bitron Video AV2010/10"
        ]
      }
    ],
    "codeowners": [
      "@dmulcahey",
      "@adminiuga"
    ],
    "zeroconf": [
      {
        "type": "_esphomelib._tcp.local.",
        "name": "tube*"
      }
    ],
    "after_dependencies": [
      "usb",
      "zeroconf"
    ],
    "iot_class": "local_polling",
    "loggers": [
      "aiosqlite",
      "bellows",
      "crccheck",
      "pure_pcapy3",
      "zhaquirks",
      "zigpy",
      "zigpy_deconz",
      "zigpy_xbee",
      "zigpy_zigate",
      "zigpy_znp"
    ],
    "is_built_in": true
  },
  "data": {
    "ieee": "**REDACTED**",
    "nwk": 2000,
    "manufacturer": "_TZE200_9mahtqtg",
    "model": "TS0601",
    "name": "_TZE200_9mahtqtg TS0601",
    "quirk_applied": false,
    "quirk_class": "zigpy.device.Device",
    "manufacturer_code": 4417,
    "power_source": "Battery or Unknown",
    "lqi": 255,
    "rssi": -82,
    "last_seen": "2022-05-11T20:41:14",
    "available": true,
    "device_type": "EndDevice",
    "signature": {
      "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)",
      "endpoints": {
        "1": {
          "profile_id": 260,
          "device_type": "0x0051",
          "in_clusters": [
            "0x0000",
            "0x0004",
            "0x0005",
            "0x0006",
            "0xef00"
          ],
          "out_clusters": [
            "0x000a",
            "0x0019"
          ]
        }
      }
    },
    "entities": [],
    "neighbors": [],
    "endpoint_names": [
      {
        "name": "SMART_PLUG"
      }
    ],
    "user_given_name": null,
    "device_reg_id": "ed6af84d6998fe9d5fb07286fd20f775",
    "area_id": null
  }
}
```

</details>

<details>
<summary>Additional logs</summary>

```
Paste any additional debug logs here.
```

</details>

**Additional context**
I created `TuyaSextupleSwitchTO` based on `zhaquirks.tuya.ts0601_switch.TuyaQuadrupleSwitchTO` with a few modifications.
It didn't work with default manufacturer.
I copied `NoManufacturerCluster` and `TuyaOnOffNM` from `zhaquirks.tuya.ts0601_dimmer` to `zhaquirks.tuya.mcu`



Discussion:

Resolving patch:
diff --git a/zhaquirks/tuya/__init__.py b/zhaquirks/tuya/__init__.py
--- a/zhaquirks/tuya/__init__.py
+++ b/zhaquirks/tuya/__init__.py
@@ -271,6 +271,29 @@ class TuyaCommand(t.Struct):
     data: TuyaData
 
 
+# based on https://github.com/zigpy/zha-device-handlers/blob/b802c1fb2cf2682f9a4722bfb57a1958cad9dad7/zhaquirks/tuya/ts0601_dimmer.py#L26
+class NoManufacturerCluster(CustomCluster):
+    """Forces the NO manufacturer id in command."""
+
+    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("Setting the NO manufacturer id in command: %s", command_id)
+        return await super().command(
+            command_id,
+            *args,
+            manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID,
+            expect_reply=expect_reply,
+            tsn=tsn,
+        )
+
+
 class TuyaManufCluster(CustomCluster):
     """Tuya manufacturer specific cluster."""
 
diff --git a/zhaquirks/tuya/mcu/__init__.py b/zhaquirks/tuya/mcu/__init__.py
--- a/zhaquirks/tuya/mcu/__init__.py
+++ b/zhaquirks/tuya/mcu/__init__.py
@@ -13,6 +13,7 @@
     TUYA_MCU_VERSION_RSP,
     TUYA_SET_DATA,
     Data,
+    NoManufacturerCluster,
     PowerOnState,
     TuyaCommand,
     TuyaData,
@@ -304,6 +305,13 @@ async def command(
         ].schema(command_id=command_id, status=foundation.Status.UNSUP_CLUSTER_COMMAND)
 
 
+# based on https://github.com/zigpy/zha-device-handlers/blob/b802c1fb2cf2682f9a4722bfb57a1958cad9dad7/zhaquirks/tuya/ts0601_dimmer.py#L48
+class TuyaOnOffNM(NoManufacturerCluster, TuyaOnOff):
+    """Tuya OnOff cluster with NoManufacturerID."""
+
+    pass
+
+
 class TuyaOnOffManufCluster(TuyaMCUCluster):
     """Tuya with On/Off data points."""
 
@@ -331,6 +339,18 @@ class TuyaOnOffManufCluster(TuyaMCUCluster):
             dp_type=TuyaDPType.BOOL,
             endpoint_id=4,
         ),
+        5: DPToAttributeMapping(
+            TuyaOnOff.ep_attribute,
+            "on_off",
+            dp_type=TuyaDPType.BOOL,
+            endpoint_id=5,
+        ),
+        6: DPToAttributeMapping(
+            TuyaOnOff.ep_attribute,
+            "on_off",
+            dp_type=TuyaDPType.BOOL,
+            endpoint_id=6,
+        ),
     }
 
     data_point_handlers = {
@@ -338,6 +358,8 @@ class TuyaOnOffManufCluster(TuyaMCUCluster):
         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",
     }
 
 
diff --git a/zhaquirks/tuya/ts0601_switch.py b/zhaquirks/tuya/ts0601_switch.py
--- a/zhaquirks/tuya/ts0601_switch.py
+++ b/zhaquirks/tuya/ts0601_switch.py
@@ -11,7 +11,12 @@
     PROFILE_ID,
 )
 from zhaquirks.tuya import TuyaSwitch
-from zhaquirks.tuya.mcu import MoesSwitchManufCluster, TuyaOnOff, TuyaOnOffManufCluster
+from zhaquirks.tuya.mcu import (
+    MoesSwitchManufCluster,
+    TuyaOnOff,
+    TuyaOnOffManufCluster,
+    TuyaOnOffNM,
+)
 
 
 class TuyaSingleSwitchTI(TuyaSwitch):
@@ -285,3 +290,85 @@ class TuyaQuadrupleSwitchTO(TuyaSwitch):
             },
         }
     }
+
+
+class TuyaSextupleSwitchTO(TuyaSwitch):
+    """Tuya sextuple channel switch time on out cluster device."""
+
+    signature = {
+        # <SimpleDescriptor endpoint=1 profile=260 device_type=81
+        # input_clusters=[0x0000,0x0004,0x0005,0xEF00]
+        # output_clusters=[0x000A,0x0019]>
+        MODELS_INFO: [
+            ("_TZE200_9mahtqtg", "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,
+                    TuyaOnOffManufCluster.cluster_id,
+                ],
+                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
+            }
+        },
+    }
+
+    replacement = {
+        ENDPOINTS: {
+            1: {
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
+                INPUT_CLUSTERS: [
+                    Basic.cluster_id,
+                    Groups.cluster_id,
+                    Scenes.cluster_id,
+                    TuyaOnOffManufCluster,
+                    TuyaOnOffNM,
+                ],
+                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
+            },
+            2: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
+                INPUT_CLUSTERS: [
+                    TuyaOnOffNM,
+                ],
+                OUTPUT_CLUSTERS: [],
+            },
+            3: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
+                INPUT_CLUSTERS: [
+                    TuyaOnOffNM,
+                ],
+                OUTPUT_CLUSTERS: [],
+            },
+            4: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
+                INPUT_CLUSTERS: [
+                    TuyaOnOffNM,
+                ],
+                OUTPUT_CLUSTERS: [],
+            },
+            5: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
+                INPUT_CLUSTERS: [
+                    TuyaOnOffNM,
+                ],
+                OUTPUT_CLUSTERS: [],
+            },
+            6: {
+                PROFILE_ID: zha.PROFILE_ID,
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
+                INPUT_CLUSTERS: [
+                    TuyaOnOffNM,
+                ],
+                OUTPUT_CLUSTERS: [],
+            },
+        }
+    }


Test patch: