Temperature Humidity sensor ZHA configuration (TZE200 bjawzodf TS0601)

Hi,

I’ve been working through adding a quirk for a temperature / humidity sensor that I got from Ali Express (link).

So far I’ve got Humidity and Temperature, but not battery. This is definitely a guess/check situation- I don’t have much idea of what I’m working with.

Before I dump the quirk so far, the issues I think I have (I’m also very new to HA / ZHA / Python):

  1. When I add the device via ZHA I don’t see the sensors appear, I have to wait for ~30 seconds and then click Reconfigure Device. Not the end of the world, but irritating.
  2. The battery is showing up as unknown, I feel like it’s 100% (brand new device), but I could be looking at the completely wrong data.
  3. The sensor names are coming up as aa648b18 humidity, also not the end of the world.

If anyone has any tips to make this nicer it’d be great to fix the code up so I can get a pull request in.

"""Tuya temp and humidity sensor with e-ink screen."""

import logging
from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import (
    Basic,
    Groups,
    Ota,
    PowerConfiguration,
    Scenes,
    Time,
)
from zigpy.zcl.clusters.measurement import RelativeHumidity, TemperatureMeasurement
from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks import Bus, LocalDataCluster, PowerConfigurationCluster
from zhaquirks.tuya import TuyaManufCluster, TuyaManufClusterAttributes, TuyaPowerConfigurationCluster

#### NOTES ####
# The data comes in as a string on cluster, if there is nothing set up you may see these lines in the logs:
# Unknown message (b'19830100a40102000400000118') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          28.0 degrees
# Unknown message (b'19840100a5020200040000022c') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          55.6% humid
# Unknown message (b'19850100a60402000400000064') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          100% battery

# Constants that map to the string values in the previously `Unknown message` lines.
TUYA_TEMPERATURE_ATTR = 0x0201  # [0, 0, 0, 237] temperature in decidegree
TUYA_HUMIDITY_ATTR = 0x0202  # [0, 0, 2, 64] humidity
TUYA_BATTERY_ATTR = 0x0204  # [0, 0, 0, 100] battery?

_LOGGER = logging.getLogger(__name__)

class TuyaTempHumidityDetectorCluster(TuyaManufClusterAttributes):
    """Manufacturer Specific Cluster of the TS0601 temp and humidity sensor with e-ink screen."""

    # Add attributes to respond to in `TuyaManufClusterAttributes`
    manufacturer_attributes = {
        TUYA_TEMPERATURE_ATTR: ("temperature", t.uint32_t),
        TUYA_HUMIDITY_ATTR: ("humidity", t.uint32_t),
        TUYA_BATTERY_ATTR: ("battery", t.uint8_t),
    }

    def _update_attribute(self, attrid, value):
        super()._update_attribute(attrid, value)
        if attrid == TUYA_TEMPERATURE_ATTR:
            _LOGGER.warning("Temperature reported: %s", value)
            self.endpoint.device.temperature_bus.listener_event(
                "temperature_reported", value * 10 # decidegree to centidegree
            )
        elif attrid == TUYA_HUMIDITY_ATTR:
            _LOGGER.warning("Humidity reported: %s", value)
            self.endpoint.device.humidity_bus.listener_event(
                "humidity_reported", value * 10 # decipercent to centipercent
            )
        elif attrid == TUYA_BATTERY_ATTR:
            _LOGGER.warning("Battery reported: %s", value)
            self.endpoint.device.battery_bus.listener_event(
                "battery_reported", value * 100  # whole percentage to 1/1000th
            )
        else:
            _LOGGER.warning(
                "[0x%04x:%s:0x%04x] unhandled attribute: 0x%04x",
                self.endpoint.device.nwk,
                self.endpoint.endpoint_id,
                self.cluster_id,
                attrid,
            )

class TuyaTemperatureMeasurement(LocalDataCluster, TemperatureMeasurement):
    """Temperature cluster acting from events from temperature bus."""

    cluster_id = TemperatureMeasurement.cluster_id
    ATTR_ID = 0

    def __init__(self, *args, **kwargs):
        """Init."""
        super().__init__(*args, **kwargs)
        self.endpoint.device.temperature_bus.add_listener(self)

    def temperature_reported(self, value):
        """Temperature reported."""
        self._update_attribute(self.ATTR_ID, value)

class TuyaRelativeHumidity(LocalDataCluster, RelativeHumidity):
    """Humidity cluster acting from events from humidity bus."""

    cluster_id = RelativeHumidity.cluster_id
    ATTR_ID = 0

    def __init__(self, *args, **kwargs):
        """Init."""
        super().__init__(*args, **kwargs)
        self.endpoint.device.humidity_bus.add_listener(self)

    def humidity_reported(self, value):
        """Humidity reported."""
        self._update_attribute(self.ATTR_ID, value)

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

    def __init__(self, *args, **kwargs):
        """Init device."""
        self.temperature_bus = Bus()
        self.humidity_bus = Bus()
        self.battery_bus = Bus()
        super().__init__(*args, **kwargs)

    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")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaManufCluster.cluster_id,
                    Basic.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR,
                INPUT_CLUSTERS: [
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaTempHumidityDetectorCluster, ## Single bus for temp, humidity, and battery
                    Basic.cluster_id,
                    TuyaTemperatureMeasurement,
                    TuyaRelativeHumidity,
                    TuyaPowerConfigurationCluster,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

Thank you @jenko1080 ! Super useful :slight_smile:

Best to discuss quirks modification here → https://github.com/zigpy/zha-device-handlers/issues

@jenko1080 Even if you’re doing development yourself it is best to post a " Device support request" →

https://github.com/zigpy/zha-device-handlers/issues/new/choose

Posted my updated implementation here [Device Support Request] _TZE200_bjawzodf · Issue #1561 · zigpy/zha-device-handlers · GitHub @jenko1080’s needed some updating, but was super useful :slight_smile:

1 Like