Troubleshooting TZE284 Temp/Humid sensor Custom quirk in ZHA

Hi,

I tried to connect a TZE284 sensor as described creating a config/custom_zha_quirks folder in the homeassistant directory. I created a file named ts0601_temperature.py and pasted the python code as found in zha_quirks/ts0601_temphumid.py at dcfc3beb98008bbb40780dbcf4f38af82c117e5d · jacekk015/zha_quirks · GitHub.

Restarded home assistant and reloaded the zha integration. Only found this:

As you can see there are no sensors visable as this device should have temperature and humidity.

Also added the following lines to configuration.yaml:

#Enable ZHA Custom Quirks
zha:
custom_quirks_path: config/custom_zha_quirks/

Can you please help and and tell me what I am missing here?

Format this part of the code properly please. If there’s an indentation error no one will be able to help and your quirk will not be picked up.

Also. post that screenshot with the Zigbee Info section expanded. If your quirk is indeed picked up, then it will be listed there

@ShadowFist, I deleted ZHA and installed Mqtt where it found the sensor imidiately.

Yep, the general consensus is that Z2M integrates devices faster than ZHA because people’s submitted quirks are integrated faster.

Thank whoever figured this out before you and submitted a PR in Z2M., and thank the devs for merging it in quickly.

Which Z2M version did you use? My TZE284 didn’t show any sensor data. It connected successfully but i wasn’t able to get data.

Other question @ShadowFist i have a problem configuring the custom quirk for my tze284. It doesn’t seem to be picked up. (see Screenshot and code). Any idea what could be the problem?
Any hints are highly appreciated. Thank you!

grafik
Generally the quirks are loaded:

Logger: zhaquirks
Quelle: /usr/local/lib/python3.12/site-packages/zhaquirks/__init__.py:469
Erstmals aufgetreten: 15:12:49 (1 Vorkommnisse)
Zuletzt protokolliert: 15:12:49

Loaded custom quirks. Please contribute them to https://github.com/zigpy/zha-device-handlers
"""Tuya temp and humidity sensors."""

from typing import Any, Dict

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.measurement import (
    RelativeHumidity,
    SoilMoisture,
    TemperatureMeasurement,
)

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
    SKIP_CONFIGURATION,
)
from zhaquirks.tuya import TuyaLocalCluster, TuyaPowerConfigurationCluster2AAA
from zhaquirks.tuya.mcu import DPToAttributeMapping, TuyaMCUCluster


class TuyaTemperatureMeasurement(TemperatureMeasurement, TuyaLocalCluster):
    """Tuya local TemperatureMeasurement cluster."""


class TuyaSoilMoisture(SoilMoisture, TuyaLocalCluster):
    """Tuya local SoilMoisture cluster with a device RH_MULTIPLIER factor if required."""


class TuyaRelativeHumidity(RelativeHumidity, TuyaLocalCluster):
    """Tuya local RelativeHumidity cluster with a device RH_MULTIPLIER factor."""

    def update_attribute(self, attr_name: str, value: Any) -> None:
        """Apply a correction factor to value."""

        if attr_name == "measured_value":
            value = value * (
                self.endpoint.device.RH_MULTIPLIER
                if hasattr(self.endpoint.device, "RH_MULTIPLIER")
                else 100
            )
        return super().update_attribute(attr_name, value)


class TemperatureHumidityManufCluster(TuyaMCUCluster):
    """Tuya Manufacturer Cluster with Temperature and Humidity data points."""

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "measured_value",
            converter=lambda x: x * 10,  # decidegree to centidegree
        ),
        2: DPToAttributeMapping(
            TuyaRelativeHumidity.ep_attribute,
            "measured_value",
            # converter=lambda x: x * 10,  --> move conversion to TuyaRelativeHumidity cluster
        ),
        4: DPToAttributeMapping(
            TuyaPowerConfigurationCluster2AAA.ep_attribute,
            "battery_percentage_remaining",
            converter=lambda x: x * 2,  # double reported percentage
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
    }


class TuyaTempHumiditySensor(CustomDevice):
    """Custom device representing tuya temp and humidity sensor with e-ink screen."""

    # RelativeHumidity multiplier
    RH_MULTIPLIER = 10

    signature = {
        # <SimpleDescriptor endpoint=1, profile=260, device_type=81
        # device_version=1
        # input_clusters=[4, 5, 61184, 0]
        # output_clusters=[25, 10]>
        MODELS_INFO: [
            ("_TZE200_bjawzodf", "TS0601"),
            ("_TZE200_zl1kmjqx", "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,
                    TemperatureHumidityManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

    replacement = {
        SKIP_CONFIGURATION: True,
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR,
                INPUT_CLUSTERS: [
                    TemperatureHumidityManufCluster,  # Single bus for temp, humidity, and battery
                    TuyaTemperatureMeasurement,
                    TuyaRelativeHumidity,
                    TuyaPowerConfigurationCluster2AAA,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }


class TuyaTempHumiditySensor_Square(CustomDevice):
    """Custom device representing tuya temp and humidity sensor with e-ink screen."""

    # RelativeHumidity multiplier
    # RH_MULTIPLIER = 100

    signature = {
        MODELS_INFO: [
            ("_TZE200_a8sdabtg", "TS0601"),  # Variant without screen, round
            ("_TZE200_qoy0ekbd", "TS0601"),
            ("_TZE200_znbl8dj5", "TS0601"),
        ],
        ENDPOINTS: {
            1: {
                # "profile_id": 260, "device_type": "0x0302",
                # "in_clusters": ["0x0000","0x0001","0x0402","0x0405"],
                # "out_clusters": ["0x000a","0x0019"]
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    TuyaPowerConfigurationCluster2AAA.cluster_id,
                    TemperatureMeasurement.cluster_id,
                    RelativeHumidity.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

    replacement = {
        SKIP_CONFIGURATION: True,
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    TuyaPowerConfigurationCluster2AAA,
                    TemperatureHumidityManufCluster,
                    TuyaTemperatureMeasurement,
                    TuyaRelativeHumidity,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }


class TuyaTempHumiditySensorVar03(CustomDevice):
    """Tuya temp and humidity sensor (variation 03)."""

    signature = {
        # "profile_id": 260,
        # "device_type": "0x0051",
        # "in_clusters": ["0x0000","0x0004","0x0005","0xef00"],
        # "out_clusters": ["0x000a","0x0019"]
        MODELS_INFO: [
            ("_TZE200_yjjdcqsq", "TS0601"),
            ("_TZE200_9yapgbuv", "TS0601"),
            ("_TZE200_qyflbnbj", "TS0601"),
            ("_TZE200_utkemkbs", "TS0601"),
            ("_TZE284_myd45weu", "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,
                    TemperatureHumidityManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

    replacement = {
        SKIP_CONFIGURATION: True,
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TemperatureHumidityManufCluster,
                    TuyaTemperatureMeasurement,
                    TuyaRelativeHumidity,
                    TuyaPowerConfigurationCluster2AAA,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }


class SoilManufCluster(TuyaMCUCluster):
    """Tuya Manufacturer Cluster with Temperature and Humidity data points."""

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        5: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
        3: DPToAttributeMapping(
            TuyaSoilMoisture.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
        15: DPToAttributeMapping(
            TuyaPowerConfigurationCluster2AAA.ep_attribute,
            "battery_percentage_remaining",
            converter=lambda x: x * 2,  # double reported percentage
        ),
    }

    data_point_handlers = {
        3: "_dp_2_attr_update",
        5: "_dp_2_attr_update",
        15: "_dp_2_attr_update",
    }


class TuyaSoilSensor(CustomDevice):
    """Tuya temp and humidity sensor (variation 03)."""

    signature = {
        # "profile_id": 260,
        # "device_type": "0x0051",
        # "in_clusters": ["0x0000","0x0004","0x0005","0xef00"],
        # "out_clusters": ["0x000a","0x0019"]
        MODELS_INFO: [("_TZE284_myd45weu", "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,
                    TemperatureHumidityManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

    replacement = {
        SKIP_CONFIGURATION: True,
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    SoilManufCluster,
                    TuyaTemperatureMeasurement,
                    TuyaSoilMoisture,
                    TuyaPowerConfigurationCluster2AAA,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }


One strange finding i just made is that another sensor (aqara magnetic door sensor) has a custom quirk mentioned in details although i didn’t manually add it and it’s not in my custom quirk folder.

Look at zha-device-handlers/zhaquirks/tuya/tuya_sensor.py at 8b3560ee6d9cc105df7b698cec021ceacf14af1f · zigpy/zha-device-handlers · GitHub for examples, should be able to just add another applies_to.

1 Like

Thanks for your help! As I’m completely new to custom quirks: where exactly do i have to add the new applies_to?
I copied the whole source code from your link. And my idea would be to simply add one line here. Is this correct?

(
    TuyaQuirkBuilder("_TZE284_aao3yzhs", "TS0601")
    .applies_to("_TZE284_sgabhwa6", "TS0601")
    .applies_to("_TZE284_nhgdf6qr", "TS0601")  # Giex GX04
    .applies_to("_TZE284_myd45weu", "TS0601") # -> this is my new line
    .tuya_temperature(dp_id=5, scale=10)
    .tuya_battery(dp_id=15)
    .tuya_soil_moisture(dp_id=3)
    .skip_configuration()
    .add_to_registry()
)

When doing what i wrote before, i get the following error.
ModuleNotFoundError: No module named ‘zhaquirks.tuya.builder’

Logger: zhaquirks
Quelle: /usr/local/lib/python3.12/site-packages/zhaquirks/__init__.py:464
Erstmals aufgetreten: 16:00:44 (1 Vorkommnisse)
Zuletzt protokolliert: 16:00:44

Unexpected exception importing custom quirk 'ts_0601_sensor'
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/zhaquirks/__init__.py", line 462, in setup
    spec.loader.exec_module(module)
  File "<frozen importlib._bootstrap_external>", line 995, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/config/config/custom_zha_quir/ts_0601_sensor.py", line 16, in <module>
    from zhaquirks.tuya.builder import TuyaQuirkBuilder, TuyaTemperatureMeasurement
ModuleNotFoundError: No module named 'zhaquirks.tuya.builder'

Solved and working (more or less). I manuallz installed zha quirks using

pip install zha-quirks --upgrade

Now the sensor shows data. But that data isn’t reasonable (temp 2.3C, although in a room with around 20 C). And the custom quirk doesn’t show up in the detailed info of the device. But at least a big step forward. Thank you!

Sounds like the scale is off, set the quirk to be just for your device, then delete the scale on temperature.

1 Like

Makes sense and seems to work. Thank you so much!

is this a soil sensor? I’ll submit a PR to add it but want to make sure we have it correct.

Working on new version : QT-07S TS0601 Soil Moisture Sensor Support · Koenkk/zigbee2mqtt · Discussion #26073 · GitHub