Repository: zigpy/zha-device-handlers
Pull request #3450 (2024-10-21 20:06:13)

Auto-update pre-commit hooks
<!--pre-commit.ci start-->
updates:
- [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.12.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.2...v1.12.1)
- [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.9...v0.7.0)
<!--pre-commit.ci end-->

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/zigpy/zha-device-handlers/pull/3450?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 89.39%. Comparing base [(`c788758`)](https://app.codecov.io/gh/zigpy/zha-device-handlers/commit/c7887583a83e05ffaed149fd09e3c8a0b1079b0d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy) to head [(`ff87b45`)](https://app.codecov.io/gh/zigpy/zha-device-handlers/commit/ff87b453952b98c979868bdd83ee16a541412251?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy).

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##              dev    #3450   +/-   ##
=======================================
  Coverage   89.39%   89.39%           
=======================================
  Files         309      309           
  Lines       10006    10006           
=======================================
  Hits         8945     8945           
  Misses       1061     1061           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/zigpy/zha-device-handlers/pull/3450?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy).


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 37fd9eb..1bb1568 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -12,12 +12,12 @@ repos:
         args: ["--toml", "pyproject.toml"]
 
   - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: v1.11.2
+    rev: v1.12.1
     hooks:
       - id: mypy
   
   - repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: v0.6.9
+    rev: v0.7.0
     hooks:
       - id: ruff
         args: ["--fix", "--exit-non-zero-on-fix", "--config", "pyproject.toml"]
diff --git a/zhaquirks/lixee/zlinky.py b/zhaquirks/lixee/zlinky.py
index 3891fe0..92a0ccb 100644
--- a/zhaquirks/lixee/zlinky.py
+++ b/zhaquirks/lixee/zlinky.py
@@ -11,6 +11,7 @@ from zigpy.zcl.clusters.general import (
     Identify,
     Ota,
     PowerConfiguration,
+    Time,
 )
 from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement, MeterIdentification
 from zigpy.zcl.clusters.smartenergy import Metering
@@ -24,6 +25,7 @@ from zhaquirks.const import (
     PROFILE_ID,
 )
 from zhaquirks.lixee import LIXEE, ZLINKY_MANUFACTURER_CLUSTER_ID
+from zhaquirks.tuya import TuyaManufCluster
 
 
 class ZLinkyTICManufacturerCluster(CustomCluster):
@@ -200,9 +202,25 @@ class ZLinkyTIC(CustomDevice):
 
 
 class ZLinkyTICFWV12(ZLinkyTIC):
-    """ZLinky_TIC from LiXee with firmware v12.0+."""
+    """ZLinky_TIC from LiXee with firmware v12.0 & v13.0."""
 
     signature = deepcopy(ZLinkyTIC.signature)
 
-    # Insert PowerConfiguration cluster in signature for devices with firmware v12.0+
+    # Insert PowerConfiguration cluster in signature for devices with firmware v12.0 & v13.0
     signature[ENDPOINTS][1][INPUT_CLUSTERS].insert(1, PowerConfiguration.cluster_id)
+
+
+class ZLinkyTICFWV14(ZLinkyTICFWV12):
+    """ZLinky_TIC from LiXee with firmware v14.0+."""
+
+    signature = deepcopy(ZLinkyTICFWV12.signature)
+    replacement = deepcopy(ZLinkyTICFWV12.replacement)
+
+    # Insert Time configuration cluster in signature for devices with firmware v14.0+
+    signature[ENDPOINTS][1][INPUT_CLUSTERS].insert(1, Time.cluster_id)
+
+    # Insert Tuya cluster in signature for devices with firmware v14.0+
+    signature[ENDPOINTS][1][INPUT_CLUSTERS].insert(7, TuyaManufCluster.cluster_id)
+    signature[ENDPOINTS][1][OUTPUT_CLUSTERS].insert(1, TuyaManufCluster.cluster_id)
+
+    replacement[ENDPOINTS][1][INPUT_CLUSTERS].insert(1, Time.cluster_id)
diff --git a/zhaquirks/philips/__init__.py b/zhaquirks/philips/__init__.py
index 8833bfe..e5d0231 100644
--- a/zhaquirks/philips/__init__.py
+++ b/zhaquirks/philips/__init__.py
@@ -69,11 +69,9 @@ class PhilipsBasicCluster(CustomCluster, Basic):
 
     attr_config = {AttributeDefs.philips.id: 0x000B}
 
-    async def bind(self):
-        """Bind cluster."""
-        result = await super().bind()
+    async def apply_custom_configuration(self, *args, **kwargs):
+        """Apply custom configuration."""
         await self.write_attributes(self.attr_config, manufacturer=0x100B)
-        return result
 
 
 class ButtonPressQueue:
diff --git a/zhaquirks/philips/soc001.py b/zhaquirks/philips/soc001.py
index 66ef3a1..d51b65b 100644
--- a/zhaquirks/philips/soc001.py
+++ b/zhaquirks/philips/soc001.py
@@ -51,7 +51,6 @@ class PhilipsContactCluster(CustomCluster):
         endpoint_id=2,
         device_class=BinarySensorDeviceClass.TAMPER,
         entity_type=EntityType.DIAGNOSTIC,
-        translation_key="tamper",
         fallback_name="Tamper",
     )
     .add_to_registry()
diff --git a/zhaquirks/sinope/__init__.py b/zhaquirks/sinope/__init__.py
index 1a2bffb..ff59f31 100644
--- a/zhaquirks/sinope/__init__.py
+++ b/zhaquirks/sinope/__init__.py
@@ -1,21 +1,25 @@
 """Module for Sinope quirks implementations."""
 
 from zigpy.quirks import CustomCluster
+import zigpy.types as t
 from zigpy.zcl.clusters.general import DeviceTemperature
 
 from zhaquirks.const import (
     ARGS,
     ATTRIBUTE_ID,
     ATTRIBUTE_NAME,
+    BUTTON,
     CLUSTER_ID,
     COMMAND,
-    COMMAND_BUTTON_DOUBLE,
-    COMMAND_BUTTON_HOLD,
-    COMMAND_BUTTON_SINGLE,
+    COMMAND_M_INITIAL_PRESS,
+    COMMAND_M_LONG_RELEASE,
+    COMMAND_M_MULTI_PRESS_COMPLETE,
+    COMMAND_M_SHORT_RELEASE,
     DOUBLE_PRESS,
     ENDPOINT_ID,
     LONG_PRESS,
     SHORT_PRESS,
+    SHORT_RELEASE,
     TURN_OFF,
     TURN_ON,
     VALUE,
@@ -25,42 +29,108 @@ SINOPE = "Sinope Technologies"
 SINOPE_MANUFACTURER_CLUSTER_ID = 0xFF01
 ATTRIBUTE_ACTION = "action_report"
 
+
+class ButtonAction(t.enum8):
+    """Action_report values."""
+
+    Pressed_on = 0x01
+    Released_on = 0x02
+    Long_on = 0x03
+    Double_on = 0x04
+    Pressed_off = 0x11
+    Released_off = 0x12
+    Long_off = 0x13
+    Double_off = 0x14
+
+
 LIGHT_DEVICE_TRIGGERS = {
     (SHORT_PRESS, TURN_ON): {
         ENDPOINT_ID: 1,
         CLUSTER_ID: 65281,
-        COMMAND: COMMAND_BUTTON_SINGLE,
-        ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 2},
+        COMMAND: COMMAND_M_INITIAL_PRESS,
+        ARGS: {
+            ATTRIBUTE_ID: 84,
+            ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
+            BUTTON: TURN_ON,
+            VALUE: ButtonAction.Pressed_on,
+        },
     },
     (SHORT_PRESS, TURN_OFF): {
         ENDPOINT_ID: 1,
         CLUSTER_ID: 65281,
-        COMMAND: COMMAND_BUTTON_SINGLE,
-        ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 18},
+        COMMAND: COMMAND_M_INITIAL_PRESS,
+        ARGS: {
+            ATTRIBUTE_ID: 84,
+            ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
+            BUTTON: TURN_OFF,
+            VALUE: ButtonAction.Pressed_off,
+        },
+    },
+    (SHORT_RELEASE, TURN_ON): {
+        ENDPOINT_ID: 1,
+        CLUSTER_ID: 65281,
+        COMMAND: COMMAND_M_SHORT_RELEASE,
+        ARGS: {
+            ATTRIBUTE_ID: 84,
+            ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
+            BUTTON: TURN_ON,
+            VALUE: ButtonAction.Released_on,
+        },
+    },
+    (SHORT_RELEASE, TURN_OFF): {
+        ENDPOINT_ID: 1,
+        CLUSTER_ID: 65281,
+        COMMAND: COMMAND_M_SHORT_RELEASE,
+        ARGS: {
+            ATTRIBUTE_ID: 84,
+            ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
+            BUTTON: TURN_OFF,
+            VALUE: ButtonAction.Released_off,
+        },
     },
     (DOUBLE_PRESS, TURN_ON): {
         ENDPOINT_ID: 1,
         CLUSTER_ID: 65281,
-        COMMAND: COMMAND_BUTTON_DOUBLE,
-        ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 4},
+        COMMAND: COMMAND_M_MULTI_PRESS_COMPLETE,
+        ARGS: {
+            ATTRIBUTE_ID: 84,
+            ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
+            BUTTON: TURN_ON,
+            VALUE: ButtonAction.Double_on,
+        },
     },
     (DOUBLE_PRESS, TURN_OFF): {
         ENDPOINT_ID: 1,
         CLUSTER_ID: 65281,
-        COMMAND: COMMAND_BUTTON_DOUBLE,
-        ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 20},
+        COMMAND: COMMAND_M_MULTI_PRESS_COMPLETE,
+        ARGS: {
+            ATTRIBUTE_ID: 84,
+            ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
+            BUTTON: TURN_OFF,
+            VALUE: ButtonAction.Double_off,
+        },
     },
     (LONG_PRESS, TURN_ON): {
         ENDPOINT_ID: 1,
         CLUSTER_ID: 65281,
-        COMMAND: COMMAND_BUTTON_HOLD,
-        ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 3},
+        COMMAND: COMMAND_M_LONG_RELEASE,
+        ARGS: {
+            ATTRIBUTE_ID: 84,
+            ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
+            BUTTON: TURN_ON,
+            VALUE: ButtonAction.Long_on,
+        },
     },
     (LONG_PRESS, TURN_OFF): {
         ENDPOINT_ID: 1,
         CLUSTER_ID: 65281,
-        COMMAND: COMMAND_BUTTON_HOLD,
-        ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 19},
+        COMMAND: COMMAND_M_LONG_RELEASE,
+        ARGS: {
+            ATTRIBUTE_ID: 84,
+            ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
+            BUTTON: TURN_OFF,
+            VALUE: ButtonAction.Long_off,
+        },
     },
 }
 
diff --git a/zhaquirks/sinope/light.py b/zhaquirks/sinope/light.py
index a95d4b3..328315f 100644
--- a/zhaquirks/sinope/light.py
+++ b/zhaquirks/sinope/light.py
@@ -29,14 +29,20 @@ from zhaquirks import EventableCluster
 from zhaquirks.const import (
     ATTRIBUTE_ID,
     ATTRIBUTE_NAME,
-    COMMAND_BUTTON_DOUBLE,
-    COMMAND_BUTTON_HOLD,
+    BUTTON,
+    COMMAND_M_INITIAL_PRESS,
+    COMMAND_M_LONG_RELEASE,
+    COMMAND_M_MULTI_PRESS_COMPLETE,
+    COMMAND_M_SHORT_RELEASE,
+    DESCRIPTION,
     DEVICE_TYPE,
     ENDPOINTS,
     INPUT_CLUSTERS,
     MODELS_INFO,
     OUTPUT_CLUSTERS,
     PROFILE_ID,
+    TURN_OFF,
+    TURN_ON,
     VALUE,
     ZHA_SEND_EVENT,
 )
@@ -45,6 +51,7 @@ from zhaquirks.sinope import (
     LIGHT_DEVICE_TRIGGERS,
     SINOPE,
     SINOPE_MANUFACTURER_CLUSTER_ID,
+    ButtonAction,
     CustomDeviceTemperatureCluster,
 )
 
@@ -73,19 +80,6 @@ class DoubleFull(t.enum8):
     On = 0x01
 
 
-class ButtonAction(t.enum8):
-    """Action_report values."""
-
-    Single_on = 0x01
-    Single_release_on = 0x02
-    Long_on = 0x03
-    Double_on = 0x04
-    Single_off = 0x11
-    Single_release_off = 0x12
-    Long_off = 0x13
-    Double_off = 0x14
-
-
 class SinopeTechnologiesManufacturerCluster(CustomCluster):
     """SinopeTechnologiesManufacturerCluster manufacturer cluster."""
 
@@ -191,29 +185,53 @@ class SinopeTechnologiesManufacturerCluster(CustomCluster):
                 hdr, args, dst_addressing=dst_addressing
             )
 
-        value = attr.value.value
+        action = self.Action(attr.value.value)
+
+        command, button = self._get_command_from_action(action)
+        if not command or not button:
+            return
+
         event_args = {
             ATTRIBUTE_ID: 84,
             ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
-            VALUE: value.value,
+            BUTTON: button,
+            DESCRIPTION: action.name,
+            VALUE: action.value,
         }
-        action = self._get_command_from_action(self.Action(value))
-        if not action:
-            return
-        self.listener_event(ZHA_SEND_EVENT, action, event_args)
 
-    def _get_command_from_action(self, action: ButtonAction) -> str | None:
-        # const lookup = {2: 'up_single', 3: 'up_hold', 4: 'up_double',
-        #             18: 'down_single', 19: 'down_hold', 20: 'down_double'};
+        self.debug(
+            "SINOPE ZHA_SEND_EVENT command: '%s' event_args: %s",
+            command,
+            event_args,
+        )
+
+        self.listener_event(ZHA_SEND_EVENT, command, event_args)
+
+    def _get_command_from_action(
+        self, action: ButtonAction
+    ) -> tuple[str | None, str | None]:
+        # const lookup = {1: 'up_single', 2: 'up_single_released', 3: 'up_hold', 4: 'up_double',
+        #             17: 'down_single, 18: 'down_single_released', 19: 'down_hold', 20: 'down_double'};
         match action:
-            case self.Action.Single_off | self.Action.Single_on:
-                return None
-            case self.Action.Double_off | self.Action.Double_on:
-                return COMMAND_BUTTON_DOUBLE
-            case self.Action.Long_off | self.Action.Long_on:
-                return COMMAND_BUTTON_HOLD
+            case self.Action.Pressed_off:
+                return COMMAND_M_INITIAL_PRESS, TURN_OFF
+            case self.Action.Pressed_on:
+                return COMMAND_M_INITIAL_PRESS, TURN_ON
+            case self.Action.Released_off:
+                return COMMAND_M_SHORT_RELEASE, TURN_OFF
+            case self.Action.Released_on:
+                return COMMAND_M_SHORT_RELEASE, TURN_ON
+            case self.Action.Double_off:
+                return COMMAND_M_MULTI_PRESS_COMPLETE, TURN_OFF
+            case self.Action.Double_on:
+                return COMMAND_M_MULTI_PRESS_COMPLETE, TURN_ON
+            case self.Action.Long_off:
+                return COMMAND_M_LONG_RELEASE, TURN_OFF
+            case self.Action.Long_on:
+                return COMMAND_M_LONG_RELEASE, TURN_ON
             case _:
-                return None
+                self.debug("SINOPE unhandled action: %s", action)
+                return None, None
 
 
 class LightManufacturerCluster(EventableCluster, SinopeTechnologiesManufacturerCluster):
diff --git a/zhaquirks/tuya/ts0601_trv.py b/zhaquirks/tuya/ts0601_trv.py
index 9ce875f..a3b3d8c 100644
--- a/zhaquirks/tuya/ts0601_trv.py
+++ b/zhaquirks/tuya/ts0601_trv.py
@@ -1527,7 +1527,8 @@ class MoesHY368_Type1(TuyaThermostat):
             ("_TZE200_cwnjrr72", "TS0601"),
             ("_TZE200_2atgpdho", "TS0601"),
             ("_TZE200_pvvbommb", "TS0601"),
-            ("_TZE200_4eeyebrt", "TS0601"),
+            ("_TZE200_4eeyebrt", "TS0601"),  # Immax NEO Smart (v1)
+            ("_TZE200_rufdtfyv", "TS0601"),  # Immax NEO Smart (v2)
             ("_TZE200_cpmgn2cf", "TS0601"),
             ("_TZE200_9sfg7gm0", "TS0601"),
             ("_TZE200_8whxpsiw", "TS0601"),


Test patch:
diff --git a/tests/test_sinope.py b/tests/test_sinope.py
index 0680d45..3f377a8 100644
--- a/tests/test_sinope.py
+++ b/tests/test_sinope.py
@@ -11,7 +11,14 @@ from zigpy.zcl.clusters.measurement import FlowMeasurement
 
 from tests.common import ClusterListener
 import zhaquirks
-from zhaquirks.const import COMMAND_BUTTON_DOUBLE, COMMAND_BUTTON_HOLD
+from zhaquirks.const import (
+    COMMAND_M_INITIAL_PRESS,
+    COMMAND_M_LONG_RELEASE,
+    COMMAND_M_MULTI_PRESS_COMPLETE,
+    COMMAND_M_SHORT_RELEASE,
+    TURN_OFF,
+    TURN_ON,
+)
 from zhaquirks.sinope import SINOPE_MANUFACTURER_CLUSTER_ID
 from zhaquirks.sinope.light import (
     SinopeTechnologieslight,
@@ -92,20 +99,22 @@ def _get_packet_data(
 
 @pytest.mark.parametrize("quirk", (SinopeTechnologieslight,))
 @pytest.mark.parametrize(
-    "press_type,exp_event",
+    "press_type,button,exp_event",
     (
-        (ButtonAction.Single_off, None),
-        (ButtonAction.Single_on, None),
-        (ButtonAction.Double_on, COMMAND_BUTTON_DOUBLE),
-        (ButtonAction.Double_off, COMMAND_BUTTON_DOUBLE),
-        (ButtonAction.Long_on, COMMAND_BUTTON_HOLD),
-        (ButtonAction.Long_off, COMMAND_BUTTON_HOLD),
+        (ButtonAction.Pressed_off, TURN_OFF, COMMAND_M_INITIAL_PRESS),
+        (ButtonAction.Pressed_on, TURN_ON, COMMAND_M_INITIAL_PRESS),
+        (ButtonAction.Released_off, TURN_OFF, COMMAND_M_SHORT_RELEASE),
+        (ButtonAction.Released_on, TURN_ON, COMMAND_M_SHORT_RELEASE),
+        (ButtonAction.Double_on, TURN_ON, COMMAND_M_MULTI_PRESS_COMPLETE),
+        (ButtonAction.Double_off, TURN_OFF, COMMAND_M_MULTI_PRESS_COMPLETE),
+        (ButtonAction.Long_on, TURN_ON, COMMAND_M_LONG_RELEASE),
+        (ButtonAction.Long_off, TURN_OFF, COMMAND_M_LONG_RELEASE),
         # Should gracefully handle broken actions.
-        (t.uint8_t(0x00), None),
+        (t.uint8_t(0x00), None, None),
     ),
 )
 async def test_sinope_light_switch(
-    zigpy_device_from_quirk, quirk, press_type, exp_event
+    zigpy_device_from_quirk, quirk, press_type, button, exp_event
 ):
     """Test that button presses are sent as events."""
     device: Device = zigpy_device_from_quirk(quirk)
@@ -126,7 +135,16 @@ async def test_sinope_light_switch(
         ),
     )
     data = _get_packet_data(foundation.GeneralCommand.Report_Attributes, attr)
-    device.handle_message(260, cluster_id, endpoint_id, endpoint_id, data)
+
+    device.packet_received(
+        t.ZigbeePacket(
+            profile_id=260,
+            cluster_id=cluster_id,
+            src_ep=endpoint_id,
+            dst_ep=endpoint_id,
+            data=t.SerializableBytes(data),
+        )
+    )
 
     if exp_event is None:
         assert cluster_listener.zha_send_event.call_count == 0
@@ -137,6 +155,8 @@ async def test_sinope_light_switch(
             {
                 "attribute_id": 84,
                 "attribute_name": "action_report",
+                "button": button,
+                "description": press_type.name,
                 "value": press_type.value,
             },
         )
@@ -162,7 +182,15 @@ async def test_sinope_light_switch_non_action_report(zigpy_device_from_quirk, qu
 
     # read attributes general command
     data = _get_packet_data(foundation.GeneralCommand.Read_Attributes)
-    device.handle_message(260, cluster_id, endpoint_id, endpoint_id, data)
+    device.packet_received(
+        t.ZigbeePacket(
+            profile_id=260,
+            cluster_id=cluster_id,
+            src_ep=endpoint_id,
+            dst_ep=endpoint_id,
+            data=t.SerializableBytes(data),
+        )
+    )
     # no ZHA events emitted because we only handle Report_Attributes
     assert cluster_listener.zha_send_event.call_count == 0
 
@@ -174,7 +202,15 @@ async def test_sinope_light_switch_non_action_report(zigpy_device_from_quirk, qu
         ),  # 0x29 = t.int16s
     )
     data = _get_packet_data(foundation.GeneralCommand.Report_Attributes, attr)
-    device.handle_message(260, cluster_id, endpoint_id, endpoint_id, data)
+    device.packet_received(
+        t.ZigbeePacket(
+            profile_id=260,
+            cluster_id=cluster_id,
+            src_ep=endpoint_id,
+            dst_ep=endpoint_id,
+            data=t.SerializableBytes(data),
+        )
+    )
     # ZHA event emitted because we pass non "action_report"
     # reports to the base class handler.
     assert cluster_listener.zha_send_event.call_count == 1
diff --git a/tests/test_tuya_builder.py b/tests/test_tuya_builder.py
index 39861be..219ed20 100644
--- a/tests/test_tuya_builder.py
+++ b/tests/test_tuya_builder.py
@@ -67,25 +67,35 @@ async def test_tuya_quirkbuilder(device_mock):
         .tuya_switch(
             dp_id=6,
             attribute_name="test_onoff",
+            translation_key="test_onoff",
+            fallback_name="Test on/off",
         )
         .tuya_number(
             dp_id=7,
             attribute_name="test_number",
             type=t.uint16_t,
+            translation_key="test_number",
+            fallback_name="Test number",
         )
         .tuya_binary_sensor(
             dp_id=8,
             attribute_name="test_binary",
+            translation_key="test_binary",
+            fallback_name="Test binary",
         )
         .tuya_sensor(
             dp_id=9,
             attribute_name="test_sensor",
             type=t.uint8_t,
+            translation_key="test_sensor",
+            fallback_name="Test sensor",
         )
         .tuya_enum(
             dp_id=10,
             attribute_name="test_enum",
             enum_class=TestEnum,
+            translation_key="test_enum",
+            fallback_name="Test enum",
         )
         .tuya_humidity(dp_id=11)
         .add_to_registry()