Repository: zigpy/zha-device-handlers
Pull request #3683 (2025-01-06 23:57:25)

Add Nous (Tuya) SZ-T04
## Proposed change
<!--
  Explain your proposed change below.
-->

Adds Nous (Tuya) SZ-T04

Manufacturers:

- _TZE200_lve3dvpy
- _TZE200_c7emyjom
- _TZE200_locansqn
- _TZE200_qrztc3ev
- _TZE200_snloy4rw
- _TZE200_eanjj2pa
- _TZE200_ydrdfkim
- _TZE284_locansqn


## Additional information
<!--
  Please include any additional information that is important to this PR.
  For example, if this PR is a potentially breaking change, mention that here.
  If this PR requires other PRs to be merged in HA Core or other projects, mention that.
  Lastly, if this PR fixes a specific issue, please include "Fixes #xxxx".
-->

See: https://github.com/Koenkk/zigbee-herdsman-converters/blob/40e72bc2515deb67ef35dddab04437342b7b3eb9/src/devices/nous.ts#L21

Closes: https://github.com/zigpy/zha-device-handlers/issues/1286
Closes: #3710


## Checklist
<!--
  Put an 'x' in all boxes that apply.
  Note: You do not need to tick all boxes before creating a PR.
-->

- [x] The changes are tested and work correctly
- [x] `pre-commit` checks pass / the code has been formatted using Black
- [x] Tests have been added to verify that the new code works


TheJulianJES: Illuminance will be updated to use the ZCL sensor with https://github.com/zigpy/zha-device-handlers/pull/3718/.

Patch:
diff --git a/zhaquirks/tuya/ts0601_sensor.py b/zhaquirks/tuya/ts0601_sensor.py
deleted file mode 100644
index e68cff5..0000000
--- a/zhaquirks/tuya/ts0601_sensor.py
+++ /dev/null
@@ -1,124 +0,0 @@
-"""Tuya temp and humidity sensors."""
-
-from zigpy.quirks.v2.homeassistant.sensor import SensorDeviceClass, SensorStateClass
-import zigpy.types as t
-
-from zhaquirks.tuya import TuyaPowerConfigurationCluster2AAA
-from zhaquirks.tuya.builder import TuyaQuirkBuilder, TuyaTemperatureMeasurement
-
-(
-    TuyaQuirkBuilder("_TZE200_bjawzodf", "TS0601")
-    .applies_to("_TZE200_zl1kmjqx", "TS0601")
-    # Not using tuya_temperature because device reports negative values incorrectly
-    .tuya_dp(
-        dp_id=1,
-        ep_attribute=TuyaTemperatureMeasurement.ep_attribute,
-        attribute_name=TuyaTemperatureMeasurement.AttributeDefs.measured_value.name,
-        converter=lambda x: ((x - 0xFFFF if x > 0x2000 else x) * 10),
-    )
-    .adds(TuyaTemperatureMeasurement)
-    .tuya_humidity(dp_id=2, scale=10)
-    .tuya_battery(dp_id=4)
-    .skip_configuration()
-    .add_to_registry()
-)
-
-(
-    TuyaQuirkBuilder("_TZE200_bq5c8xfe", "TS0601")
-    .applies_to("_TZE200_vs0skpuc", "TS0601")
-    .applies_to("_TZE200_qyflbnbj", "TS0601")
-    .applies_to("_TZE284_qyflbnbj", "TS0601")
-    .applies_to("_TZE200_44af8vyi", "TS0601")
-    # Not using tuya_temperature because device reports negative values incorrectly
-    .tuya_dp(
-        dp_id=1,
-        ep_attribute=TuyaTemperatureMeasurement.ep_attribute,
-        attribute_name=TuyaTemperatureMeasurement.AttributeDefs.measured_value.name,
-        converter=lambda x: ((x - 0xFFFF if x > 0x2000 else x) * 10),
-    )
-    .adds(TuyaTemperatureMeasurement)
-    .tuya_humidity(dp_id=2)
-    .tuya_battery(dp_id=4)
-    .skip_configuration()
-    .add_to_registry()
-)
-
-(
-    TuyaQuirkBuilder("_TZE200_a8sdabtg", "TS0601")  # Variant without screen, round
-    .applies_to("_TZE200_qoy0ekbd", "TS0601")
-    .applies_to("_TZE200_znbl8dj5", "TS0601")
-    .applies_to("_TZE200_zppcgbdj", "TS0601")
-    .applies_to("_TZE204_s139roas", "TS0601")
-    .applies_to("_TZE200_s1xgth2u", "TS0601")  # Nedis ZBSC30WT
-    .tuya_temperature(dp_id=1, scale=10)
-    .adds(TuyaTemperatureMeasurement)
-    .tuya_humidity(dp_id=2)
-    .tuya_battery(dp_id=4)
-    .skip_configuration()
-    .add_to_registry()
-)
-
-(
-    TuyaQuirkBuilder("_TZE200_yjjdcqsq", "TS0601")
-    .applies_to("_TZE200_9yapgbuv", "TS0601")
-    .applies_to("_TZE204_9yapgbuv", "TS0601")
-    .applies_to("_TZE204_yjjdcqsq", "TS0601")
-    .applies_to("_TZE200_utkemkbs", "TS0601")
-    .applies_to("_TZE204_utkemkbs", "TS0601")
-    .applies_to("_TZE204_yjjdcqsq", "TS0601")
-    .applies_to("_TZE204_ksz749x8", "TS0601")
-    .tuya_temperature(dp_id=1, scale=10)
-    .tuya_humidity(dp_id=2)
-    .tuya_dp(
-        dp_id=4,
-        ep_attribute=TuyaPowerConfigurationCluster2AAA.ep_attribute,
-        attribute_name="battery_percentage_remaining",
-        converter=lambda x: {0: 50, 1: 100, 2: 200}[x],
-    )
-    .adds(TuyaPowerConfigurationCluster2AAA)
-    .skip_configuration()
-    .add_to_registry()
-)
-
-
-(
-    TuyaQuirkBuilder("_TZE284_aao3yzhs", "TS0601")
-    .applies_to("_TZE284_sgabhwa6", "TS0601")
-    .applies_to("_TZE284_nhgdf6qr", "TS0601")  # Giex GX04
-    .tuya_temperature(dp_id=5, scale=10)
-    .tuya_battery(dp_id=15)
-    .tuya_soil_moisture(dp_id=3)
-    .skip_configuration()
-    .add_to_registry()
-)
-
-
-(
-    TuyaQuirkBuilder("_TZE200_myd45weu", "TS0601")
-    .applies_to("_TZE200_ga1maeof", "TS0601")
-    .applies_to("_TZE200_9cqcpkgb", "TS0601")
-    .applies_to("_TZE204_myd45weu", "TS0601")
-    .applies_to("_TZE200_2se8efxh", "TS0601")  # Immax Neo
-    .tuya_temperature(dp_id=5)
-    .tuya_battery(dp_id=15)
-    .tuya_soil_moisture(dp_id=3)
-    .skip_configuration()
-    .add_to_registry()
-)
-
-(
-    TuyaQuirkBuilder("_TZE200_pay2byax", "TS0601")  # Cusam ZG-102ZL
-    .applies_to("_TZE200_n8dljorx", "TS0601")
-    .tuya_sensor(
-        dp_id=101,
-        attribute_name="measured_value",
-        type=t.uint16_t,
-        fallback_name="Illuminance",
-        device_class=SensorDeviceClass.ILLUMINANCE,
-        state_class=SensorStateClass.MEASUREMENT,
-    )
-    .tuya_contact(dp_id=1)
-    .tuya_battery(dp_id=2)
-    .skip_configuration()
-    .add_to_registry()
-)
diff --git a/zhaquirks/tuya/tuya_sensor.py b/zhaquirks/tuya/tuya_sensor.py
new file mode 100644
index 0000000..0a19164
--- /dev/null
+++ b/zhaquirks/tuya/tuya_sensor.py
@@ -0,0 +1,319 @@
+"""Tuya temp and humidity sensors."""
+
+import copy
+
+from zigpy.quirks.v2 import EntityPlatform, EntityType
+from zigpy.quirks.v2.homeassistant import PERCENTAGE, UnitOfTemperature, UnitOfTime
+from zigpy.quirks.v2.homeassistant.sensor import SensorDeviceClass, SensorStateClass
+import zigpy.types as t
+from zigpy.zcl import foundation
+
+from zhaquirks.tuya import (
+    TUYA_SET_TIME,
+    TuyaPowerConfigurationCluster2AAA,
+    TuyaTimePayload,
+)
+from zhaquirks.tuya.builder import TuyaQuirkBuilder, TuyaTemperatureMeasurement
+from zhaquirks.tuya.mcu import TuyaMCUCluster
+
+
+class TuyaTempUnitConvert(t.enum8):
+    """Tuya temperature unit convert enum."""
+
+    Celsius = 0x00
+    Fahrenheit = 0x01
+
+
+class TuyaNousTempHumiAlarm(t.enum8):
+    """Tuya temperature and humidity alarm enum."""
+
+    LowerAlarm = 0x00
+    UpperAlarm = 0x01
+    Canceled = 0x02
+
+
+(
+    TuyaQuirkBuilder("_TZE200_bjawzodf", "TS0601")
+    .applies_to("_TZE200_zl1kmjqx", "TS0601")
+    # Not using tuya_temperature because device reports negative values incorrectly
+    .tuya_dp(
+        dp_id=1,
+        ep_attribute=TuyaTemperatureMeasurement.ep_attribute,
+        attribute_name=TuyaTemperatureMeasurement.AttributeDefs.measured_value.name,
+        converter=lambda x: ((x - 0xFFFF if x > 0x2000 else x) * 10),
+    )
+    .adds(TuyaTemperatureMeasurement)
+    .tuya_humidity(dp_id=2, scale=10)
+    .tuya_battery(dp_id=4)
+    .skip_configuration()
+    .add_to_registry()
+)
+
+
+(
+    TuyaQuirkBuilder("_TZE200_bq5c8xfe", "TS0601")
+    .applies_to("_TZE200_vs0skpuc", "TS0601")
+    .applies_to("_TZE200_qyflbnbj", "TS0601")
+    .applies_to("_TZE284_qyflbnbj", "TS0601")
+    .applies_to("_TZE200_44af8vyi", "TS0601")
+    # Not using tuya_temperature because device reports negative values incorrectly
+    .tuya_dp(
+        dp_id=1,
+        ep_attribute=TuyaTemperatureMeasurement.ep_attribute,
+        attribute_name=TuyaTemperatureMeasurement.AttributeDefs.measured_value.name,
+        converter=lambda x: ((x - 0xFFFF if x > 0x2000 else x) * 10),
+    )
+    .adds(TuyaTemperatureMeasurement)
+    .tuya_humidity(dp_id=2)
+    .tuya_battery(dp_id=4)
+    .skip_configuration()
+    .add_to_registry()
+)
+
+
+(
+    TuyaQuirkBuilder("_TZE200_a8sdabtg", "TS0601")  # Variant without screen, round
+    .applies_to("_TZE200_qoy0ekbd", "TS0601")
+    .applies_to("_TZE200_znbl8dj5", "TS0601")
+    .applies_to("_TZE200_zppcgbdj", "TS0601")
+    .applies_to("_TZE204_s139roas", "TS0601")
+    .applies_to("_TZE200_s1xgth2u", "TS0601")  # Nedis ZBSC30WT
+    .tuya_temperature(dp_id=1, scale=10)
+    .adds(TuyaTemperatureMeasurement)
+    .tuya_humidity(dp_id=2)
+    .tuya_battery(dp_id=4)
+    .skip_configuration()
+    .add_to_registry()
+)
+
+
+class NoManufTimeTuyaMCUCluster(TuyaMCUCluster):
+    """Tuya Manufacturer Cluster with set_time mod."""
+
+    set_time_offset = 1970
+    set_time_local_offset = 1970
+
+    # Deepcopy required to override 'set_time', without, it will revert
+    server_commands = copy.deepcopy(TuyaMCUCluster.server_commands)
+    server_commands.update(
+        {
+            TUYA_SET_TIME: foundation.ZCLCommandDef(
+                "set_time",
+                {"time": TuyaTimePayload},
+                False,
+                is_manufacturer_specific=False,
+            ),
+        }
+    )
+
+
+# TH01Z - Temperature and humidity sensor with clock
+(
+    TuyaQuirkBuilder("_TZE200_lve3dvpy", "TS0601")
+    .applies_to("_TZE200_c7emyjom", "TS0601")
+    .applies_to("_TZE200_locansqn", "TS0601")
+    .applies_to("_TZE200_qrztc3ev", "TS0601")
+    .applies_to("_TZE200_snloy4rw", "TS0601")
+    .applies_to("_TZE200_eanjj2pa", "TS0601")
+    .applies_to("_TZE200_ydrdfkim", "TS0601")
+    .applies_to("_TZE284_locansqn", "TS0601")
+    .tuya_temperature(dp_id=1, scale=10)
+    .tuya_humidity(dp_id=2)
+    .tuya_battery(dp_id=4)
+    .tuya_number(
+        dp_id=17,
+        attribute_name="temperature_report_interval",
+        type=t.uint16_t,
+        device_class=SensorDeviceClass.DURATION,
+        unit=UnitOfTime.MINUTES,
+        min_value=5,
+        max_value=120,
+        step=5,
+        entity_type=EntityType.CONFIG,
+        translation_key="temperature_report_interval",
+        fallback_name="Temperature report interval",
+    )
+    .tuya_number(
+        dp_id=18,
+        attribute_name="humidity_report_interval",
+        type=t.uint16_t,
+        device_class=SensorDeviceClass.DURATION,
+        unit=UnitOfTime.MINUTES,
+        min_value=5,
+        max_value=120,
+        step=5,
+        entity_type=EntityType.CONFIG,
+        translation_key="humidity_report_interval",
+        fallback_name="Humidity report interval",
+    )
+    .tuya_enum(
+        dp_id=9,
+        attribute_name="display_unit",
+        enum_class=TuyaTempUnitConvert,
+        entity_type=EntityType.CONFIG,
+        translation_key="display_unit",
+        fallback_name="Display unit",
+    )
+    .tuya_enum(
+        dp_id=14,
+        attribute_name="temperature_alarm",
+        enum_class=TuyaNousTempHumiAlarm,
+        entity_platform=EntityPlatform.SENSOR,
+        entity_type=EntityType.STANDARD,
+        translation_key="temperature_alarm",
+        fallback_name="Temperature alarm",
+    )
+    .tuya_number(
+        dp_id=10,
+        attribute_name="alarm_temperature_max",
+        type=t.uint16_t,
+        unit=UnitOfTemperature.CELSIUS,
+        min_value=-20,
+        max_value=60,
+        step=1,
+        multiplier=0.1,
+        entity_type=EntityType.CONFIG,
+        translation_key="alarm_temperature_max",
+        fallback_name="Alarm temperature max",
+    )
+    .tuya_number(
+        dp_id=11,
+        attribute_name="alarm_temperature_min",
+        type=t.uint16_t,
+        unit=UnitOfTemperature.CELSIUS,
+        min_value=-20,
+        max_value=60,
+        step=1,
+        multiplier=0.1,
+        entity_type=EntityType.CONFIG,
+        translation_key="alarm_temperature_min",
+        fallback_name="Alarm temperature min",
+    )
+    .tuya_number(
+        dp_id=19,
+        attribute_name="temperature_sensitivity",
+        type=t.uint16_t,
+        unit=UnitOfTemperature.CELSIUS,
+        min_value=0.1,
+        max_value=50,
+        step=0.1,
+        multiplier=0.1,
+        entity_type=EntityType.CONFIG,
+        translation_key="temperature_sensitivity",
+        fallback_name="Temperature sensitivity",
+    )
+    .tuya_enum(
+        dp_id=15,
+        attribute_name="humidity_alarm",
+        enum_class=TuyaNousTempHumiAlarm,
+        entity_platform=EntityPlatform.SENSOR,
+        entity_type=EntityType.STANDARD,
+        translation_key="humidity_alarm",
+        fallback_name="Humidity alarm",
+    )
+    .tuya_number(
+        dp_id=12,
+        attribute_name="alarm_humidity_max",
+        type=t.uint16_t,
+        unit=PERCENTAGE,
+        min_value=0,
+        max_value=100,
+        step=1,
+        entity_type=EntityType.CONFIG,
+        translation_key="alarm_humidity_max",
+        fallback_name="Alarm humidity max",
+    )
+    .tuya_number(
+        dp_id=13,
+        attribute_name="alarm_humidity_min",
+        type=t.uint16_t,
+        unit=PERCENTAGE,
+        min_value=0,
+        max_value=100,
+        step=1,
+        entity_type=EntityType.CONFIG,
+        translation_key="alarm_humidity_min",
+        fallback_name="Alarm humidity min",
+    )
+    .tuya_number(
+        dp_id=20,
+        attribute_name="humidity_sensitivity",
+        type=t.uint16_t,
+        unit=PERCENTAGE,
+        min_value=1,
+        max_value=100,
+        step=1,
+        entity_type=EntityType.CONFIG,
+        translation_key="humidity_sensitivity",
+        fallback_name="Humidity sensitivity",
+    )
+    .skip_configuration()
+    .add_to_registry(replacement_cluster=NoManufTimeTuyaMCUCluster)
+)
+
+
+(
+    TuyaQuirkBuilder("_TZE200_yjjdcqsq", "TS0601")
+    .applies_to("_TZE200_9yapgbuv", "TS0601")
+    .applies_to("_TZE204_9yapgbuv", "TS0601")
+    .applies_to("_TZE204_yjjdcqsq", "TS0601")
+    .applies_to("_TZE200_utkemkbs", "TS0601")
+    .applies_to("_TZE204_utkemkbs", "TS0601")
+    .applies_to("_TZE204_yjjdcqsq", "TS0601")
+    .applies_to("_TZE204_ksz749x8", "TS0601")
+    .tuya_temperature(dp_id=1, scale=10)
+    .tuya_humidity(dp_id=2)
+    .tuya_dp(
+        dp_id=4,
+        ep_attribute=TuyaPowerConfigurationCluster2AAA.ep_attribute,
+        attribute_name="battery_percentage_remaining",
+        converter=lambda x: {0: 50, 1: 100, 2: 200}[x],
+    )
+    .adds(TuyaPowerConfigurationCluster2AAA)
+    .skip_configuration()
+    .add_to_registry()
+)
+
+
+(
+    TuyaQuirkBuilder("_TZE284_aao3yzhs", "TS0601")
+    .applies_to("_TZE284_sgabhwa6", "TS0601")
+    .applies_to("_TZE284_nhgdf6qr", "TS0601")  # Giex GX04
+    .tuya_temperature(dp_id=5, scale=10)
+    .tuya_battery(dp_id=15)
+    .tuya_soil_moisture(dp_id=3)
+    .skip_configuration()
+    .add_to_registry()
+)
+
+
+(
+    TuyaQuirkBuilder("_TZE200_myd45weu", "TS0601")
+    .applies_to("_TZE200_ga1maeof", "TS0601")
+    .applies_to("_TZE200_9cqcpkgb", "TS0601")
+    .applies_to("_TZE204_myd45weu", "TS0601")
+    .applies_to("_TZE200_2se8efxh", "TS0601")  # Immax Neo
+    .tuya_temperature(dp_id=5)
+    .tuya_battery(dp_id=15)
+    .tuya_soil_moisture(dp_id=3)
+    .skip_configuration()
+    .add_to_registry()
+)
+
+
+(
+    TuyaQuirkBuilder("_TZE200_pay2byax", "TS0601")  # Cusam ZG-102ZL
+    .applies_to("_TZE200_n8dljorx", "TS0601")
+    .tuya_sensor(
+        dp_id=101,
+        attribute_name="measured_value",
+        type=t.uint16_t,
+        fallback_name="Illuminance",
+        device_class=SensorDeviceClass.ILLUMINANCE,
+        state_class=SensorStateClass.MEASUREMENT,
+    )
+    .tuya_contact(dp_id=1)
+    .tuya_battery(dp_id=2)
+    .skip_configuration()
+    .add_to_registry()
+)


Test patch:
diff --git a/tests/test_tuya_builder.py b/tests/test_tuya_builder.py
index f10eed0..27a669f 100644
--- a/tests/test_tuya_builder.py
+++ b/tests/test_tuya_builder.py
@@ -1,5 +1,6 @@
 """Tests for TuyaQuirkBuilder."""
 
+import datetime
 from unittest import mock
 
 import pytest
@@ -9,10 +10,11 @@ import zigpy.types as t
 from zigpy.zcl import foundation
 from zigpy.zcl.clusters.general import Basic, BatterySize
 
-from tests.common import ClusterListener, wait_for_zigpy_tasks
+from tests.common import ClusterListener, MockDatetime, wait_for_zigpy_tasks
 import zhaquirks
 from zhaquirks.tuya import (
     TUYA_QUERY_DATA,
+    TUYA_SET_TIME,
     TuyaPowerConfigurationCluster,
     TuyaPowerConfigurationCluster2AAA,
 )
@@ -31,6 +33,9 @@ from zhaquirks.tuya.builder import (
     TuyaValveWaterConsumed,
 )
 from zhaquirks.tuya.mcu import TuyaMCUCluster, TuyaOnOffNM
+from zhaquirks.tuya.tuya_sensor import NoManufTimeTuyaMCUCluster
+
+ZCL_TUYA_SET_TIME = b"\x09\x12\x24\x0d\x00"
 
 zhaquirks.setup()
 
@@ -140,6 +145,9 @@ async def test_tuya_quirkbuilder(device_mock):
         A = 0x00
         B = 0x01
 
+    class ModTuyaMCUCluster(TuyaMCUCluster):
+        """Modified Cluster."""
+
     entry = (
         TuyaQuirkBuilder(device_mock.manufacturer, device_mock.model, registry=registry)
         .tuya_battery(dp_id=1)
@@ -178,7 +186,7 @@ async def test_tuya_quirkbuilder(device_mock):
             fallback_name="Test enum",
         )
         .skip_configuration()
-        .add_to_registry()
+        .add_to_registry(replacement_cluster=ModTuyaMCUCluster)
     )
 
     # coverage for overridden __eq__ method
@@ -195,6 +203,7 @@ async def test_tuya_quirkbuilder(device_mock):
     assert isinstance(ep.basic, Basic)
 
     assert ep.tuya_manufacturer is not None
+    assert isinstance(ep.tuya_manufacturer, ModTuyaMCUCluster)
     assert isinstance(ep.tuya_manufacturer, TuyaMCUCluster)
 
     tuya_cluster = ep.tuya_manufacturer
@@ -303,3 +312,49 @@ async def test_tuya_spell(device_mock, read_attr_spell, data_query_spell):
             messages += 1
 
         request_mock.reset_mock()
+
+
+async def test_tuya_mcu_set_time(device_mock):
+    """Test TuyaQuirkBuilder replacement cluster, set_time requests (0x24) messages for MCU devices."""
+
+    registry = DeviceRegistry()
+
+    (
+        TuyaQuirkBuilder(device_mock.manufacturer, device_mock.model, registry=registry)
+        .tuya_battery(dp_id=1)
+        .skip_configuration()
+        .add_to_registry(replacement_cluster=NoManufTimeTuyaMCUCluster)
+    )
+
+    quirked = registry.get_device(device_mock)
+    assert isinstance(quirked, CustomDeviceV2)
+    assert quirked in registry
+
+    ep = quirked.endpoints[1]
+
+    assert not ep.tuya_manufacturer._is_manuf_specific
+    assert not ep.tuya_manufacturer.server_commands[
+        TUYA_SET_TIME
+    ].is_manufacturer_specific
+
+    # Mock datetime
+    origdatetime = datetime.datetime
+    datetime.datetime = MockDatetime
+
+    # simulate a SET_TIME message
+    hdr, args = ep.tuya_manufacturer.deserialize(ZCL_TUYA_SET_TIME)
+    assert hdr.command_id == TUYA_SET_TIME
+
+    with mock.patch.object(
+        ep.tuya_manufacturer._endpoint,
+        "request",
+        return_value=foundation.Status.SUCCESS,
+    ) as m1:
+        ep.tuya_manufacturer.handle_message(hdr, args)
+        await wait_for_zigpy_tasks()
+
+        res_hdr = foundation.ZCLHeader.deserialize(m1.await_args[1]["data"])
+        assert not res_hdr[0].manufacturer
+        assert not res_hdr[0].frame_control.is_manufacturer_specific
+
+    datetime.datetime = origdatetime  # restore datetime
diff --git a/tests/test_tuya_sensor.py b/tests/test_tuya_sensor.py
index 8ce1533..649247a 100644
--- a/tests/test_tuya_sensor.py
+++ b/tests/test_tuya_sensor.py
@@ -28,6 +28,14 @@ zhaquirks.setup()
         ("_TZE200_bq5c8xfe", "TS0601", 100, 10, True),
         ("_TZE200_vs0skpuc", "TS0601", 100, 10, True),
         ("_TZE200_44af8vyi", "TS0601", 100, 10, True),
+        ("_TZE200_lve3dvpy", "TS0601", 100, 10, False),  # TH01Z - Temp & humid w/ clock
+        ("_TZE200_c7emyjom", "TS0601", 100, 10, False),
+        ("_TZE200_locansqn", "TS0601", 100, 10, False),
+        ("_TZE200_qrztc3ev", "TS0601", 100, 10, False),
+        ("_TZE200_snloy4rw", "TS0601", 100, 10, False),
+        ("_TZE200_eanjj2pa", "TS0601", 100, 10, False),
+        ("_TZE200_ydrdfkim", "TS0601", 100, 10, False),
+        ("_TZE284_locansqn", "TS0601", 100, 10, False),
     ],
 )
 async def test_handle_get_data(