zigpy / zha-device-handlers

ZHA device handlers bridge the functionality gap created when manufacturers deviate from the ZCL specification, handling deviations and exceptions by parsing custom messages to and from Zigbee devices.
Apache License 2.0
734 stars 673 forks source link

[Device Support Request] Tuya Temperature&Humidity Sensor with backlight and Battery report TZE200_vvmbj46n #2732

Open WLCD opened 11 months ago

WLCD commented 11 months ago

Problem description

This Unit is TS601 based sensor, I tried to integrate it by copying the code from Variation 04 with some success : I can get the Temp and Humidity readings, but the battery doesn't seem to report correctly, plus I don't know what to do to update the Date & time on the device itself, but I'm pretty sure the gateway is supposed to do that.

Here is the link to the device itself : https://fr.aliexpress.com/item/1005005897159196.html?spm=a2g0o.order_list.order_list_main.11.7a875e5bryD8IU&gatewayAdapt=glo2fra

Solution description

I'm using the custom quirk for the Tuya air quality sensor which is awesome, I am no coder, but I'm willing to provide help if someone needs more information to create a custom quirks for this model. Thanks a bunch !

Screenshots/Video

Screenshots/Video [Paste/upload your media here]

Device signature

Device signature ```json { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=, *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": "0x0104", "device_type": "0x0302", "input_clusters": [ "0x0000", "0x0001", "0x0004", "0x0005", "0x0402", "0x0405", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE200_vvmbj46n", "model": "TS0601", "class": "ts0601_sensor.TuyaTempHumiditySensorVar05" } ```

Diagnostic information

Diagnostic information ```json { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2023.11.1", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.11.6", "docker": true, "arch": "x86_64", "timezone": "Europe/Paris", "os_name": "Linux", "os_version": "6.1.59", "supervisor": "2023.11.0", "host_os": "Home Assistant OS 11.1", "docker_version": "24.0.6", "chassis": "embedded", "run_as_root": true }, "custom_components": { "hacs": { "version": "1.32.1", "requirements": [ "aiogithubapi>=22.10.1" ] }, "trakt": { "version": "2.0.3", "requirements": [ "trakt==2.14.0" ] }, "moonraker": { "version": "0.9.2", "requirements": [ "moonraker-api==2.0.5" ] }, "truenas": { "version": "0.0.0", "requirements": [] }, "auto_backup": { "version": "1.4.1", "requirements": [] }, "rte_ecowatt": { "version": "0.6.3", "requirements": [ "Async-OAuthlib==0.0.9" ] }, "monitor_docker": { "version": "1.10", "requirements": [ "aiodocker==0.21.0", "python-dateutil==2.8.2" ] }, "plex_recently_added": { "version": "0.3.8", "requirements": [] } }, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "after_dependencies": [ "onboarding", "usb" ], "codeowners": [ "@dmulcahey", "@adminiuga", "@puddly" ], "config_flow": true, "dependencies": [ "file_upload" ], "documentation": "https://www.home-assistant.io/integrations/zha", "iot_class": "local_polling", "loggers": [ "aiosqlite", "bellows", "crccheck", "pure_pcapy3", "zhaquirks", "zigpy", "zigpy_deconz", "zigpy_xbee", "zigpy_zigate", "zigpy_znp", "universal_silabs_flasher" ], "requirements": [ "bellows==0.36.8", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.106", "zigpy-deconz==0.21.1", "zigpy==0.59.0", "zigpy-xbee==0.19.0", "zigpy-zigate==0.11.0", "zigpy-znp==0.11.6", "universal-silabs-flasher==0.0.14", "pyserial-asyncio-fast==0.11" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "1A86", "pid": "55D4", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus v2" ] }, { "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": "0403", "pid": "6015", "description": "*zigate*", "known_devices": [ "ZiGate+" ] }, { "vid": "10C4", "pid": "EA60", "description": "*zigate*", "known_devices": [ "ZiGate" ] }, { "vid": "10C4", "pid": "8B34", "description": "*bv 2010/10*", "known_devices": [ "Bitron Video AV2010/10" ] } ], "zeroconf": [ { "type": "_esphomelib._tcp.local.", "name": "tube*" }, { "type": "_zigate-zigbee-gateway._tcp.local.", "name": "*zigate*" }, { "type": "_zigstar_gw._tcp.local.", "name": "*zigstar*" }, { "type": "_uzg-01._tcp.local.", "name": "uzg-01*" }, { "type": "_slzb-06._tcp.local.", "name": "slzb-06*" } ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 54575, "manufacturer": "_TZE200_vvmbj46n", "model": "TS0601", "name": "_TZE200_vvmbj46n TS0601", "quirk_applied": true, "quirk_class": "ts0601_sensor.TuyaTempHumiditySensorVar05", "quirk_id": null, "manufacturer_code": 4417, "power_source": "Battery or Unknown", "lqi": 148, "rssi": -63, "last_seen": "2023-11-08T21:00:47", "available": true, "device_type": "EndDevice", "signature": { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=, *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": "0x0104", "device_type": "0x0302", "input_clusters": [ "0x0000", "0x0001", "0x0004", "0x0005", "0x0402", "0x0405", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE200_vvmbj46n", "model": "TS0601" }, "active_coordinator": false, "entities": [ { "entity_id": "sensor.t_h_salle_des_machines_batterie", "name": "_TZE200_vvmbj46n TS0601" }, { "entity_id": "sensor.t_h_salle_des_machines_temperature", "name": "_TZE200_vvmbj46n TS0601" }, { "entity_id": "sensor.t_h_salle_des_machines_humidite", "name": "_TZE200_vvmbj46n TS0601" } ], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "TEMPERATURE_SENSOR" } ], "user_given_name": "T/H Salle des machines", "device_reg_id": "7c0214190ca183d99d37ec743bf43847", "area_id": "salle_des_machines", "cluster_details": { "1": { "device_type": { "name": "TEMPERATURE_SENSOR", "id": 770 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0001": { "attribute_name": "app_version", "value": 72 }, "0x0004": { "attribute_name": "manufacturer", "value": "_TZE200_vvmbj46n" }, "0x0005": { "attribute_name": "model", "value": "TS0601" } }, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0xef00": { "endpoint_attribute": "tuya_manufacturer", "attributes": { "0xef00": { "attribute_name": "mcu_version", "value": "1.0.0" } }, "unsupported_attributes": {} }, "0x0402": { "endpoint_attribute": "temperature", "attributes": { "0x0000": { "attribute_name": "measured_value", "value": 1750 } }, "unsupported_attributes": {} }, "0x0405": { "endpoint_attribute": "humidity", "attributes": { "0x0000": { "attribute_name": "measured_value", "value": 5900 } }, "unsupported_attributes": {} }, "0x0001": { "endpoint_attribute": "power", "attributes": { "0x0033": { "attribute_name": "battery_quantity", "value": 2 }, "0x0034": { "attribute_name": "battery_rated_voltage", "value": 15 }, "0x0031": { "attribute_name": "battery_size", "value": 4 } }, "unsupported_attributes": {} } }, "out_clusters": { "0x0019": { "endpoint_attribute": "ota", "attributes": {}, "unsupported_attributes": {} }, "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} } } } } } } ```

Logs

Logs ```python [Paste the logs here] ```

Custom quirk

Custom quirk ```python class TuyaTempHumiditySensorVar05(CustomDevice): """Tuya temp and humidity sensor (variation 05, backlit screen).""" signature = { # "profile_id": 260, # "device_type": "0x0302", # "in_clusters": ["0x0000","0x0001","0x0004","0x0005","0x0402","0x0405","0xef00"], # "out_clusters": ["0x000a","0x0019"] MODELS_INFO: [ ("_TZE200_vvmbj46n", "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, TemperatureHumidityBatteryStatesManufCluster, TuyaTemperatureMeasurement, TuyaRelativeHumidity, TuyaPowerConfigurationCluster2AAA, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], } }, } ```

Additional information

No response

WLCD commented 10 months ago

Just added my customized code, the battery status won't show up, unfortunately.

joji90 commented 7 months ago

anyone found a working quirk for this?

paulsijben commented 7 months ago

I have a quirk that now gives me values but the humidity is off by a factor of 10x . Does anyone have an idea to fix this?

This is the snippet of the quirk I have on the basis of the bigger quirk for these devices

class TuyaTempHumiditySensorVar03(CustomDevice): """Tuya temp and humidity sensor (variation 03).""" RH_MULTIPLIER = 10 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"),
        ("_TZE200_vvmbj46n", "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],
        }
    },
}
richardcc commented 7 months ago

Hi

I had a similar problem and from file ts0601_sensor.py I modified RH_MULTIPLIER, but I don't remember which one or where. The version I attached is working correctly for me.

BR Ricardo

El jue, 29 feb 2024 a las 14:59, Paul Sijben @.***>) escribió:

I have a quirk that now gives me values but the humidity is off by a factor of 10x . Does anyone have an idea to fix this?

This is the snippet of the quirk I have on the basis of the bigger quirk for these devices

class TuyaTempHumiditySensorVar03(CustomDevice): """Tuya temp and humidity sensor (variation 03).""" RH_MULTIPLIER = 10 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"), ("_TZE200_vvmbj46n", "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], } }, }

— Reply to this email directly, view it on GitHub https://github.com/zigpy/zha-device-handlers/issues/2732#issuecomment-1971205417, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABMEAJOITJNUMGTBQT6TYPLYV4Z5JAVCNFSM6AAAAAA7DP4T46VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNZRGIYDKNBRG4 . You are receiving this because you are subscribed to this thread.Message ID: @.***>

-- Un Saludo

paulsijben commented 7 months ago

Hi @richardcc , looks like the attachment did not make it. Could you please upload that one separately?

richardcc commented 7 months ago

Let me know if you can get it from here. Otherwise I'll try to upload the file.

"""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 TemperatureHumidityBatteryStatesManufCluster(TuyaMCUCluster): """Tuya Manufacturer Cluster with Temperature and Humidity data points. Battery states 25, 50 and 100%."""

dp_to_attribute: Dict[int, DPToAttributeMapping] = {
    1: TemperatureHumidityManufCluster.dp_to_attribute[1],
    2: TemperatureHumidityManufCluster.dp_to_attribute[2],
    3: DPToAttributeMapping(
        TuyaPowerConfigurationCluster2AAA.ep_attribute,
        "battery_percentage_remaining",
        converter=lambda x: {0: 25, 1: 50, 2: 100}[x],  # double

reported percentage ), }

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

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

# RelativeHumidity multiplier
RH_MULTIPLIER = 100

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"),
        ("_TZE200_vvmbj46n", "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_qyflbnbj", "TS0601"),
        ("_TZE200_utkemkbs", "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 TuyaTempHumiditySensorVar04(CustomDevice): """Tuya temp and humidity sensor (variation 04)."""

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"),
    ],
    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,
                TemperatureHumidityBatteryStatesManufCluster,
                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: [
        ("_TZE200_myd45weu", "TS0601"),
        ("_TZE200_ga1maeof", "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],
        }
    },
}

El jue, 29 feb 2024 a las 17:50, Paul Sijben @.***>) escribió:

Hi @richardcc https://github.com/richardcc , looks like the attachment did not make it. Could you please upload that one separately?

— Reply to this email directly, view it on GitHub https://github.com/zigpy/zha-device-handlers/issues/2732#issuecomment-1971552087, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABMEAJKANGQV5ZR4SKLYCPTYV5N5HAVCNFSM6AAAAAA7DP4T46VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNZRGU2TEMBYG4 . You are receiving this because you were mentioned.Message ID: @.***>

-- Un Saludo

richardcc commented 7 months ago

By the way my sensor is identified as: TS0601 / _TZE200_vvmbj46n

BR Ricardo

El jue, 29 feb 2024 a las 19:17, Ricardo Alba @.***>) escribió:

Let me know if you can get it from here. Otherwise I'll try to upload the file.

"""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 TemperatureHumidityBatteryStatesManufCluster(TuyaMCUCluster): """Tuya Manufacturer Cluster with Temperature and Humidity data points. Battery states 25, 50 and 100%."""

dp_to_attribute: Dict[int, DPToAttributeMapping] = {
    1: TemperatureHumidityManufCluster.dp_to_attribute[1],
    2: TemperatureHumidityManufCluster.dp_to_attribute[2],
    3: DPToAttributeMapping(
        TuyaPowerConfigurationCluster2AAA.ep_attribute,
        "battery_percentage_remaining",
        converter=lambda x: {0: 25, 1: 50, 2: 100}[x],  # double reported percentage
    ),
}

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

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

# RelativeHumidity multiplier
RH_MULTIPLIER = 100

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"),
        ("_TZE200_vvmbj46n", "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_qyflbnbj", "TS0601"),
        ("_TZE200_utkemkbs", "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 TuyaTempHumiditySensorVar04(CustomDevice): """Tuya temp and humidity sensor (variation 04)."""

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"),
    ],
    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,
                TemperatureHumidityBatteryStatesManufCluster,
                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: [
        ("_TZE200_myd45weu", "TS0601"),
        ("_TZE200_ga1maeof", "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],
        }
    },
}

El jue, 29 feb 2024 a las 17:50, Paul Sijben @.***>) escribió:

Hi @richardcc https://github.com/richardcc , looks like the attachment did not make it. Could you please upload that one separately?

— Reply to this email directly, view it on GitHub https://github.com/zigpy/zha-device-handlers/issues/2732#issuecomment-1971552087, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABMEAJKANGQV5ZR4SKLYCPTYV5N5HAVCNFSM6AAAAAA7DP4T46VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNZRGU2TEMBYG4 . You are receiving this because you were mentioned.Message ID: @.***>

-- Un Saludo

-- Un Saludo

paulsijben commented 7 months ago

@richardcc yes that solved it. Thank you!

Now for my education, I an going to try and find the differences between my (stolen and mutilated ;-) file and yours.

edgarveersel commented 5 months ago

Very happy with this custom quirk for my TS0601 / _TZE200_vvmbj46n. It's the first time I heard of quirks, and just tried it, and it works!

Would there be a way to set the time and the date on the device?

manny199 commented 4 months ago

Please, can you tell me where I must insert this custom quirk?

iTigere commented 4 months ago

How should it be inserted? ?

gillenoarbe commented 4 months ago

In my case, inserted the code by WLCD in one of the github available TS0601.py files at the end and worked after full restart of the docker container wih HA.

Please, can you tell me where I must insert this custom quirk?

How should it be inserted? ?

cecinitmortem commented 1 month ago

When I use the quirk the time does not get written to the device properly. It just starts counting from the moment the device is powered on. The date is also not set. I have tried to restart HA and rejoin the device multiple times but that does not help either. Did you also have the problem with date and time @richardcc @paulsijben ?