Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Device Support Request] - TS0601 by _TZE200_v1jqz5cy - Chlorine Meter PH ORP EC TDS Salinity Temp CL Tester Swimming Pool Water Quality Analyzer #2565

Open
KirkKirk opened this issue Sep 4, 2023 · 88 comments
Labels
device support request This requests support for a new device in progress Issue is being worked on, near fix Tuya Request/PR regarding a Tuya device

Comments

@KirkKirk
Copy link

KirkKirk commented Sep 4, 2023

Problem description

Not supported device in ZHA

Smart WiFi Zigbee Chlorine Meter PH ORP EC TDS Salinity Temp CL Tester Swimming Pool Water Quality Analyzer

https://www.aliexpress.com/item/1005005575336871.html?spm=a2g0o.order_list.order_list_main.11.28e31802Wxn6Wl

Solution description

Add it to the supported device list in ZHA Please.

Screenshots/Video

Screenshots/Video

[image]

Device signature

Device signature
[{
"node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *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": "0x0051",
"input_clusters": [
"0x0000",
"0x0004",
"0x0005",
"0xef00"
],
"output_clusters": [
"0x000a",
"0x0019"
]
}
},
"manufacturer": "_TZE200_v1jqz5cy",
"model": "TS0601",
"class": "zigpy.device.Device"
}]

Diagnostic information

Diagnostic information
[{
  "home_assistant": {
    "installation_type": "Home Assistant OS",
    "version": "2023.8.4",
    "dev": false,
    "hassio": true,
    "virtualenv": false,
    "python_version": "3.11.4",
    "docker": true,
    "arch": "aarch64",
    "timezone": "Australia/Sydney",
    "os_name": "Linux",
    "os_version": "6.1.21-v8",
    "supervisor": "2023.08.1",
    "host_os": "Home Assistant OS 10.5",
    "docker_version": "23.0.6",
    "chassis": "embedded",
    "run_as_root": true
  },
  "custom_components": {
    "nodered": {
      "version": "2.2.0",
      "requirements": []
    },
    "smartthings": {
      "version": "1.0",
      "requirements": [
        "pysmartapp==0.3.3",
        "pysmartthings==0.7.6"
      ]
    },
    "dohome": {
      "version": "0.2.0",
      "requirements": []
    },
    "braviatv_psk": {
      "version": "0.4.2",
      "requirements": [
        "pySonyBraviaPSK==0.2.4"
      ]
    },
    "hacs": {
      "version": "1.32.1",
      "requirements": [
        "aiogithubapi>=22.10.1"
      ]
    },
    "truenas": {
      "version": "0.0.0",
      "requirements": []
    },
    "tesla_custom": {
      "version": "3.16.1",
      "requirements": [
        "teslajsonpy==3.9.3"
      ]
    },
    "sonoff": {
      "version": "3.5.2",
      "requirements": [
        "pycryptodome>=3.6.6"
      ]
    },
    "localtuya": {
      "version": "5.2.1",
      "requirements": []
    },
    "roborock": {
      "version": "1.0.11",
      "requirements": [
        "python-roborock==0.32.3",
        "ical==5.0.0"
      ]
    }
  },
  "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"
    ],
    "requirements": [
      "bellows==0.35.9",
      "pyserial==3.5",
      "pyserial-asyncio==0.6",
      "zha-quirks==0.0.102",
      "zigpy-deconz==0.21.0",
      "zigpy==0.56.4",
      "zigpy-xbee==0.18.1",
      "zigpy-zigate==0.11.0",
      "zigpy-znp==0.11.4"
    ],
    "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": "_slzb-06._tcp.local.",
        "name": "slzb-06*"
      }
    ],
    "is_built_in": true
  },
  "data": {
    "ieee": "**REDACTED**",
    "nwk": 7411,
    "manufacturer": "_TZE200_v1jqz5cy",
    "model": "TS0601",
    "name": "_TZE200_v1jqz5cy TS0601",
    "quirk_applied": false,
    "quirk_class": "zigpy.device.Device",
    "manufacturer_code": 4098,
    "power_source": "Battery or Unknown",
    "lqi": null,
    "rssi": null,
    "last_seen": "2023-09-04T18:58:59",
    "available": true,
    "device_type": "EndDevice",
    "signature": {
      "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *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": "0x0051",
          "input_clusters": [
            "0x0000",
            "0x0004",
            "0x0005",
            "0xef00"
          ],
          "output_clusters": [
            "0x000a",
            "0x0019"
          ]
        }
      },
      "manufacturer": "_TZE200_v1jqz5cy",
      "model": "TS0601"
    },
    "active_coordinator": false,
    "entities": [],
    "neighbors": [],
    "routes": [],
    "endpoint_names": [
      {
        "name": "SMART_PLUG"
      }
    ],
    "user_given_name": "Pool Sensor",
    "device_reg_id": "9de124bce188ac54b74bf7f2d0ac920a",
    "area_id": "backyard",
    "cluster_details": {
      "1": {
        "device_type": {
          "name": "SMART_PLUG",
          "id": 81
        },
        "profile_id": 260,
        "in_clusters": {
          "0x0000": {
            "endpoint_attribute": "basic",
            "attributes": {
              "0x0001": {
                "attribute_name": "app_version",
                "value": 65
              },
              "0x0004": {
                "attribute_name": "manufacturer",
                "value": "_TZE200_v1jqz5cy"
              },
              "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": null,
            "attributes": {},
            "unsupported_attributes": {}
          }
        },
        "out_clusters": {
          "0x0019": {
            "endpoint_attribute": "ota",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x000a": {
            "endpoint_attribute": "time",
            "attributes": {},
            "unsupported_attributes": {}
          }
        }
      }
    }
  }
}]

Logs

Logs
[Paste the logs here]

Custom quirk

Custom quirk
[Paste your custom quirk here]

Additional information

No response

@KirkKirk KirkKirk changed the title [Device Support Request] [Device Support Request] - Chlorine Meter PH ORP EC TDS Salinity Temp CL Tester Swimming Pool Water Quality Analyzer Sep 4, 2023
@KirkKirk KirkKirk changed the title [Device Support Request] - Chlorine Meter PH ORP EC TDS Salinity Temp CL Tester Swimming Pool Water Quality Analyzer [Device Support Request] - TS0601 by _TZE200_v1jqz5cy - Chlorine Meter PH ORP EC TDS Salinity Temp CL Tester Swimming Pool Water Quality Analyzer Sep 5, 2023
@tschiex
Copy link
Contributor

tschiex commented Sep 6, 2023

I would be happy to have the device supported too, but I cannot find it in the list of all the Zigbee2mqtt devices: looking for "_TZE200_v1jqz5cy" in the zigbee-herdsman-converters project returned nothing.

Instead, I found the very same request for the device to be supported in zigbee2mqtt here:

Koenkk/zigbee2mqtt#18704

Do you have the Tuya ZigBee gateway?
It is apparently needed to at least identify the Tuya Data Points and more.
I don't have one...

@KirkKirk
Copy link
Author

KirkKirk commented Sep 7, 2023

I find it strange that I can't seem to locate the source where I read about zigbee2mqtt supporting it. The threat you mentioned is the only information I came across this time. It's quite puzzling. I do have the Zigbee coordinator USB dongle, and I have successfully paired it with several other sensors.I didn't expect this expensive sensor to be unsupported. I hope someone can help as it looks promising for the purpose but it is now useless for me.

@tschiex
Copy link
Contributor

tschiex commented Sep 7, 2023 via email

@TheJulianJES TheJulianJES added the Tuya Request/PR regarding a Tuya device label Sep 8, 2023
@tschiex
Copy link
Contributor

tschiex commented Sep 9, 2023

I looked at this, this afternoon, but Zigbee and zha quirks are challenging to grab and the documentation for zha quirks looks minimal. Instead, the ZigBee specifications are precise but daunting to grab. I tried to get inspiration from existing tuya device quirks, but this device is challenging because it reports measurements (ORP and TDS) that do not exist in the ZCL Measurement cluster list. So I created new clusters with invented (unused) cluster numbers. I post below my current quirk version, but I haven't tried it. For sure, the measurement units/multipliers are likely incorrect.

I assume I'm close to the objective, and my hope is that one of the ZHA developers who knows about Tuya devices will have a look at it and make it work.

The Data point specification is taken from the still infant Z2M handler here: https://gist.github.com/Koenkk/7262a8a36bb84327ff563e0b8256024b

"""Tuya Pool sensor."""

from typing import Any, Dict

import zigpy.types as t
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 (
    pH,
    SodiumConcentration,
    ElectricalConductivity,
    ChlorineConcentration,
    TemperatureMeasurement,
)

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


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


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


class TuyaORP(TuyaLocalCluster):
    """Tuya local Oxydo-Reduction Potential cluster with a device RH_MULTIPLIER factor if required."""

    cluster_id = 0x042E
    name = "ORP Level"
    ep_attribute = "orp_level"

    attributes = {
        0x0000: ("measured_value", t.Single),  # fraction of 1 (one)
        0x0001: ("min_measured_value", t.Single),
        0x0002: ("max_measured_value", t.Single),
    }

    server_commands = {}
    client_commands = {}


class TuyaTDS(TuyaLocalCluster):
    """Tuya local Total Dissolved Solids cluster with a device RH_MULTIPLIER factor if required."""

    cluster_id = 0x042F
    name = "TDS Level"
    ep_attribute = "tds_level"

    attributes = {
        0x0000: ("measured_value", t.Single),  # fraction of 1 (one)
        0x0001: ("min_measured_value", t.Single),
        0x0002: ("max_measured_value", t.Single),
    }

    server_commands = {}
    client_commands = {}


class TuyaSodiumConcentration(SodiumConcentration, TuyaLocalCluster):
    """Tuya local NaCl cluster with a device RH_MULTIPLIER factor if required."""


class TuyaElectricalConductivity(ElectricalConductivity, TuyaLocalCluster):
    """Tuya local Electrical Conductivity cluster with a device RH_MULTIPLIER factor if required."""


class TuyaChlorineConcentration(ChlorineConcentration, TuyaLocalCluster):
    """Tuya local Chlorine Concentration 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 PoolManufCluster(TuyaMCUCluster):
    """Tuya Manufacturer Cluster with Pool data points."""

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaTDS.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
        2: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
        101: DPToAttributeMapping(
            TuyaORP.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
        102: DPToAttributeMapping(
            TuyaChlorineConcentration.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
        7: DPToAttributeMapping(
            TuyaPowerConfigurationCluster.ep_attribute,
            "battery_percentage_remaining",
            converter=lambda x: x * 2,  # double reported percentage
        ),
        105: DPToAttributeMapping(
            TuyaChlorineConcentration.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
        10: DPToAttributeMapping(
            TuyapH.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
        11: DPToAttributeMapping(
            TuyaElectricalConductivity.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
        108: DPToAttributeMapping(
            TuyaElectricalConductivity.ep_attribute,
            "max_measured_value",
            converter=lambda x: x * 100,
        ),
        109: DPToAttributeMapping(
            TuyaElectricalConductivity.ep_attribute,
            "min_measured_value",
            converter=lambda x: x * 100,
        ),
        110: DPToAttributeMapping(
            TuyaORP.ep_attribute,
            "max_measured_value",
            converter=lambda x: x * 100,
        ),
        111: DPToAttributeMapping(
            TuyaORP.ep_attribute,
            "min_measured_value",
            converter=lambda x: x * 100,
        ),
        112: DPToAttributeMapping(
            TuyaChlorineConcentration.ep_attribute,
            "max_measured_value",
            converter=lambda x: x * 100,
        ),
        113: DPToAttributeMapping(
            TuyaChlorineConcentration.ep_attribute,
            "min_easured_value",
            converter=lambda x: x * 100,
        ),
        117: DPToAttributeMapping(
            TuyaSodiumConcentration.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        101: "_dp_2_attr_update",
        102: "_dp_2_attr_update",
        7: "_dp_2_attr_update",
        105: "_dp_2_attr_update",
        10: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        108: "_dp_2_attr_update",
        109: "_dp_2_attr_update",
        110: "_dp_2_attr_update",
        111: "_dp_2_attr_update",
        112: "_dp_2_attr_update",
        113: "_dp_2_attr_update",
        117: "_dp_2_attr_update",
    }


class TuyaPoolSensor(CustomDevice):
    """Tuya Pool sensor."""

    signature = {
        # "profile_id": 260,
        # "device_type": "0x0051",
        # "in_clusters": ["0x0000","0x0004","0x0005","0xef00"],
        # "out_clusters": ["0x000a","0x0019"]
        MODELS_INFO: [
            ("_TZE200_v1jqz5cy", "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,
                    PoolManufCluster.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,
                    PoolManufCluster,
                    TuyaTemperatureMeasurement,
                    TuyapH,
                    TuyaORP,
                    TuyaChlorineConcentration,
                    TuyaTDS,
                    TuyaElectricalConductivity,
                    TuyaPowerConfigurationCluster,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

@tschiex
Copy link
Contributor

tschiex commented Sep 9, 2023

I do not have a Tuya gateway, so I cannot test it, but I also assume that 2 of the clusters (pH and ORP) should include commands to start calibration with the 3 pH and the single ORP buffers that is provided with the device.

@KirkKirk If you have such a gateway, could you try to find how these can be called? Apparently, Tuya devices can be reverse-engineered using the Tuya IoT cloud platform. See Find Tuya Data Points

@aberkeley
Copy link

aberkeley commented Sep 10, 2023

if it helps - seems it is working on deconz: https://forum.phoscon.de/t/tuya-tze200-v1jqz5cy-zigbee-chlorine-meter/3930

and here: dresden-elektronik/deconz-rest-plugin#7215

i have one too but no tuya gateway either - the deconz implementation seems to be working - maybe that was where you saw it working?

@KirkKirk
Copy link
Author

I do not have a Tuya gateway, so I cannot test it, but I also assume that 2 of the clusters (pH and ORP) should include commands to start calibration with the 3 pH and the single ORP buffer that is provided with the device.

@KirkKirk If you have such a gateway, could you try to find how these can be called? Apparently, Tuya devices can be reverse-engineered using the Tuya IoT cloud platform. See Find Tuya Data Points

I'm afraid that I don't have any other gateway apart from the SONOFF ZB Dongle-P Zigbee 3.0 USB Dongle Plus. I'm sorry, but my coding knowledge is limited, so I can't offer more assistance beyond what I have already provided. However, I am willing to help if you could guide me on what next steps to take.
Thank you !

@KirkKirk
Copy link
Author

KirkKirk commented Sep 11, 2023

if it helps - seems it is working on deconz: https://forum.phoscon.de/t/tuya-tze200-v1jqz5cy-zigbee-chlorine-meter/3930

and here: dresden-elektronik/deconz-rest-plugin#7215

i have one too but no tuya gateway either - the deconz implementation seems to be working - maybe that was where you saw it working?

Yes, that's what I saw! After considering my options, I have decided to stick with ZHA as it provides me with all the features I need.

@tschiex
Copy link
Contributor

tschiex commented Sep 11, 2023

I bought a Tuya gateway and followed the instructions on Z2M
https://www.zigbee2mqtt.io/advanced/support-new-devices/03_find_tuya_data_points.html

and found more DP Ids:

1: TDS
2: Temperature
101: ORP
102: Chlorine
7: Battery Percentage
103: pH Calibration
104: Backlight
105: Backlight Value
10: pH Value
11: EC Value
106: pH Max Value
107: pH Min Value
108: EC Max Value
109: EC Min Value
110: ORP Max Value
111: ORP Min Value
112: Chlorine Max Value
113: Chlorine Min Value
114: PH Calibration
115: EC Calibration
116: ORP Calibration
117: Salt

(Translation of Chinese by Google Translate camera from Tuya IoT platform website).

There are apparently 2 pH calibration DP Ids (and there are 2 pH calibration standards: Asia uses 6.86/4.00/9.18, EU uses 7/4/10). The two pH calibration DPs could be these 2 maybe?

My quirk proposal is therefore not only likely wrong but also incomplete...

@KirkKirk
Copy link
Author

@tschiex Have you attempted to retrieve the data from the Tuya integration? If you can integrate it with Home Assistant, I will also purchase the Tuya gateway.

@toulbar2
Copy link

toulbar2 commented Sep 14, 2023

@KirkKirk I will have a try but I'm not sure a failure will be informative: I already bought one Tuya device which is currently used. It's a Wifi device that I manage from HA, using "Local Tuya", not "Tuya" (I prefer to avoid cloud-dependencies, even if this device connects automatically to a Smart Life account).

Now, the Water quality device is ZigBee connected to a Wifi gateway which I registered to Tuya (not SmartLife) because I needed to use the Tuya IoT platform on the cloud to decipher the Data Points. I'm not sure the Tuya integration will cope with this situation, where there are 3 Tuya devices both Wifi and ZigBee and managed by 2 different Tuya cloud entities (Tuya and SmartLife). But I will try. :-)

@tschiex
Copy link
Contributor

tschiex commented Sep 15, 2023

Ok, I did reset the tuya gateway and the PhMeter, linked them to my Smartlife account (success), installed the Tuya extension on HA, linked it to the proper account on the cloud and.... I'm sorry to say that the gateway and the pHmeter show no entity. I'm quite confident that with some work, I could get the pghMeter to work on local Tuya. Will try that instead and keep you updated.

@tschiex
Copy link
Contributor

tschiex commented Sep 15, 2023

Well. I was wrong. The gateway can be integrated into local tuya (it shows in the list of devices that can be added) but the Zigbee device remains invisible under local Tuya. The only way is to go the ZHA/Z2M way apparently.

@KirkKirk
Copy link
Author

THANK YOU @tschiex.

@KirkKirk
Copy link
Author

Hi guys, is there any development on this?

@alexisml
Copy link

alexisml commented Sep 21, 2023

I bought a Tuya gateway and followed the instructions on Z2M https://www.zigbee2mqtt.io/advanced/support-new-devices/03_find_tuya_data_points.html

and found more DP Ids:

1: TDS 2: Temperature 101: ORP 102: Chlorine 7: Battery Percentage 103: pH Calibration 104: Backlight 105: Backlight Value 10: pH Value 11: EC Value 106: pH Max Value 107: pH Min Value 108: EC Max Value 109: EC Min Value 110: ORP Max Value 111: ORP Min Value 112: Chlorine Max Value 113: Chlorine Min Value 114: PH Calibration 115: EC Calibration 116: ORP Calibration 117: Salt

(Translation of Chinese by Google Translate camera from Tuya IoT platform website).

There are apparently 2 pH calibration DP Ids (and there are 2 pH calibration standards: Asia uses 6.86/4.00/9.18, EU uses 7/4/10). The two pH calibration DPs could be these 2 maybe?

My quirk proposal is therefore not only likely wrong but also incomplete...

I took your quirk proposal plus info from:

Fixed a typo in pH that made the imports not work, commented out battery info since it was failing ('TuyaPoolSensor' object has no attribute 'battery_bus'), and updated all cluster ids from the deconz comments. Still no luck, but a lot is going on in several places at the same time.

Here's my gist.

@tschiex
Copy link
Contributor

tschiex commented Sep 22, 2023

Thanks Alexis. I'm also following the Z2M thread on this device. Apparently, the device does not report any Data Point (Koenkk/zigbee2mqtt#18704 (comment)).
I will try to ZigBee-sniff the pairing process this week-end (never tried this).

@tschiex
Copy link
Contributor

tschiex commented Sep 24, 2023

Ok, I have done some Zigbee sniffing for Z2M/Koenkk and the device is now working on Z2M.
Apparently there are dataQuery from the device that are useful for starting data exchange.
See Z2M issue.
Alexis, any hint here?

@alexisml
Copy link

alexisml commented Sep 24, 2023

I believe we need to migrate this code.

However, I did a search by queryOnDeviceAnnounce and found some devices that use that thingy (as Koenkk mentioned):

  • _TZE200_yjjdcqsq
  • _TZE200_9yapgbuv
  • _TZE200_cirvgep4

Then looked for those in ZHA and found them here. BUT, I don't see any special things on ZHA side? I'll continue digging anyway, but if someone more proficient with ZHA is able to help, that would be awesome.

EDIT: I also suspect that the cluster ids change I did was not needed.

@tschiex
Copy link
Contributor

tschiex commented Sep 25, 2023

I was starting to do precisely the same analysis (finding the Z2M converters with the same specificity to find their mirror in ZHA) when I saw your message yesterday. Great!

For the quirk: I took your gist as is, I will compare it with my analysis of Tuya DPs and the other quirks you mention on ZHA. I also hope people knowledgeable in ZHA/zigpy will help. Not before this evening I'm afraid (local French time).

I'm wondering why only temperature shows as a sensor. The other virtual clusters (ORP,...) show in the "Manage your Zigbee device" menu but not beyond that. ANd they all return "None" when one query them.

@alexisml
Copy link

Just a few things / ideas to consider:

  • One of the differences I found is that the other devices use TuyaPowerConfigurationCluster2AAA instead of TuyaPowerConfigurationCluster (I updated that in my gist)
  • I have another Tuya device that needs special things, it uses zhaquirks.tuya.mcu.EnchantedDevice and implements a response to some command (see quirk and discussion here)
  • You can see if the quirk can be loaded by looking into Home Assistant logs (that's how I figured out why yours wasn't loading) (go to Settings -> System -> Logs)

@tschiex
Copy link
Contributor

tschiex commented Sep 25, 2023

Reacting quickly...

  • My understanding is that TuyaPowerConfigurationCluster2AAA is for devices using 2 AAA batteries (this is what I extracted from zigpy/zha code). The pH meter uses a Lithium battery, so I used the more flexible TuyaPowerConfigurationCluster. There may be a better choice, but 2AAA does not seem to fit :-)
  • zhaquirks.tuya.mcu.EnchantedDevice is definitely something worth looking. I will have a look.
  • the actual quirk (the version you put in the gist) seems to be properly loaded. This can be seen in the ZHA device page, under "Zigbee Info". It says: "Quirk: ts0601_pool_sensor.TuyaPoolSensor". It does expose a Temperature sensor and also clusters for ORP, EC... but they always return "none" as values when probed using "Manage the ZigBee device" in the device page.

@alexisml
Copy link

Based on last response in z2m, we need to actually make a call to cluster 0xEF00, attribute 0x03.

https://developer.tuya.com/en/docs/iot/tuya-zigbee-universal-docking-access-standard?id=K9ik6zvofpzql#subtitle-6-Private%20cluster

I'm trying to but failing. And for some reason now I don't even get the quirk loaded for the device either.

@MattWestb
Copy link
Contributor

If the device is OK paired and working you can re-powering the device and the MCU is sending current status for all DP is having / using to the host system = the same as command 0x3.

Put debug for quirks and look then the system is trying loading it and see what is complaining of but normally its some cluster that is not matching in the signature.

@alexisml
Copy link

2023-09-25 17:32:48.182 DEBUG (MainThread) [zigpy.zcl] [0x1708:1:0x0000] Decoded ZCL frame: Basic:Read_Attributes_rsp(status_records=[ReadAttributeRecord(attrid=0x0004, status=<Status.SUCCESS: 0>, value=TypeValue(type=CharacterString, value='_TZE200_v1jqz5cy')), ReadAttributeRecord(attrid=0x0005, status=<Status.SUCCESS: 0>, value=TypeValue(type=CharacterString, value='TS0601'))])
2023-09-25 17:32:48.182 INFO (MainThread) [zigpy.device] [0x1708] Read model 'TS0601' and manufacturer '_TZE200_v1jqz5cy' from <Endpoint id=1 in=[basic:0x0000, groups:0x0004, scenes:0x0005, None:0xEF00] out=[ota:0x0019, time:0x000A] status=<Status.ZDO_INIT: 1>>
2023-09-25 17:32:48.182 INFO (MainThread) [zigpy.device] [0x1708] Discovered basic device information for <Device model='TS0601' manuf='_TZE200_v1jqz5cy' nwk=0x1708 ieee=e0:79:8d:ff:fe:b8:76:2a is_initialized=True>
2023-09-25 17:32:48.182 DEBUG (MainThread) [zigpy.application] Device is initialized <Device model='TS0601' manuf='_TZE200_v1jqz5cy' nwk=0x1708 ieee=e0:79:8d:ff:fe:b8:76:2a is_initialized=True>
2023-09-25 17:32:48.183 DEBUG (MainThread) [zigpy.quirks.registry] Checking quirks for _TZE200_v1jqz5cy TS0601 (e0:79:8d:ff:fe:b8:76:2a)
2023-09-25 17:32:48.183 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'TS0601_TZE200_v1jqz5cy.TuyaPoolSensor'>
2023-09-25 17:32:48.183 DEBUG (MainThread) [zigpy.quirks.registry] Fail because input cluster mismatch on at least one endpoint
2023-09-25 17:32:48.183 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'TS0601_TZE200_v1jqz5cy.TuyaPoolSensor'>
2023-09-25 17:32:48.183 DEBUG (MainThread) [zigpy.quirks.registry] Fail because input cluster mismatch on at least one endpoint

:(

@tschiex
Copy link
Contributor

tschiex commented Jan 5, 2024

Thanks. Found a useful _divisor for this case.

@tschiex
Copy link
Contributor

tschiex commented Jan 6, 2024

Battery now working apparently.

Capture d’écran du 2024-01-06 13-36-41

@ofirda
Copy link

ofirda commented Jan 6, 2024

Thanks for the hard work!
Stepping in here for some help since I too bought the device and connected it via the Zigbee gateway (tuya mobile app sees it and I can see its values there)

I would also like to have it in HA - right now it shows in Tuya as unsupported = device is found without entities.

I am trying to add it via ZHA, but I don't know how to setup the connection to the gateway.

Under the "Select the serial port for your Zigbee radio" I select enter manually and get these options:

  • EZSP = Silicon Labs EmberZNet protocol: Elelabs, HUSBZB-1, Telegesis
  • ZNP = Texas Instruments Z-Stack ZNP protocol: CC253x, CC26x2, CC13x2
  • deCONZ = dresden elektronik deCONZ protocol: ConBee I/II, RaspBee I/II
  • ZiGate = ZiGate Zigbee radios: PiZiGate, ZiGate USB-TTL, ZiGate WiFi
  • XBee = Digi XBee Zigbee radios: Digi XBee Series 2, 2C, 3

Which one should I choose and what's the how do I find out the input paratmers of it?

@tschiex
Copy link
Contributor

tschiex commented Jan 7, 2024

The code to make the device functional under ZHA is not yet included in ZHA + ZHA-device-handlers. You will have to wait a bit. It's not just a quirk, it requires some new sensor classes in ZHA and even some new units in HA.

As for Zigbee controllers, I would say that the EZSP way is advised with ZHA. I have a SONOFF (ZBDONGLE-E model). See this link, in the "RECOMMENDED ZIGBEE RADIO ADAPTERS AND MODULES" section.

@tschiex
Copy link
Contributor

tschiex commented Jan 7, 2024

@TheJulianJES I understand you are busy. I will try two quickly answered questions. Short answers would help me move forward:

  1. The device has eight "AnalogOutput"-like datapoints. Should I use the number.py + "match quirk_id" approach here? Without any reaction, that's what I will try to do.
  2. I have no idea how to give access to a new service/entity that would allow users to emit a "data_query" packet and poll the device. A quick hint (anything similar that exists?) would be welcome.

Capture d’écran du 2024-01-07 11-31-16

@ofirda
Copy link

ofirda commented Jan 7, 2024

The code to make the device functional under ZHA is not yet included in ZHA + ZHA-device-handlers. You will have to wait a bit. It's not just a quirk, it requires some new sensor classes in ZHA and even some new units in HA.

As for Zigbee controllers, I would say that the EZSP way is advised with ZHA. I have a SONOFF (ZBDONGLE-E model). See this link, in the "RECOMMENDED ZIGBEE RADIO ADAPTERS AND MODULES" section.

Thanks Thomas

What would you say is the best way now for me to add this device to my HA and see its data in the dashboard?

@TheJulianJES
Copy link
Collaborator

  1. The device has eight "AnalogOutput"-like datapoints. Should I use the number.py + "match quirk_id" approach here? Without any reaction, that's what I will try to do.

Creating the number entities using number.py should be fine, yes.
And yeah, I'd recommend matching by quirk id. A quirk id needs to be set in the quirk using quirk_id = "something.quirk.id (example which links to a constant).
HA then needs to have the same string. Later, that string should be added to the quirk ids file in the quirks repo and then be imported from HA.

  1. I have no idea how to give access to a new service/entity that would allow users to emit a "data_query" packet and poll the device. A quick hint (anything similar that exists?) would be welcome.

Hmm, this is tricky. We could add a ZHA button entity maybe that sends that command.
Otherwise, the homeassistant.update_entity service call already exists and should call async_update in the "entity class" IIRC. Similar to this: homeassistant/components/zha/light.py#L974-L979

The command could then be sent when async_update is triggered, but that would have to be called on one entity, so maybe a custom button is a better idea?

@TheJulianJES TheJulianJES added the in progress Issue is being worked on, near fix label Jan 19, 2024
@tschiex
Copy link
Contributor

tschiex commented Jan 20, 2024

Ok. Thanks @TheJulianJES .

quirk_id and number.py entities created (and lit looks like they work, they have consistent initial values).
Here is how one block looks like:

@CONFIG_DIAGNOSTIC_MATCH(
    cluster_handler_names="tuya_manufacturer", quirk_ids=TUYA_POOL_SENSOR
)
# pylint: disable-next=hass-invalid-inheritance # needs fixing
class ChlorineMinValue(ZHANumberConfigurationEntity):
    """Representation of a ZHA Chlorine Concentration Maximum Value configuration entity."""

    _unique_id_suffix = "cl_min_value"
    _attr_entity_category = EntityCategory.CONFIG
    _attr_icon: str = ICONS[20]
    _attr_native_min_value: float = 0
    _attr_native_max_value: float = 15
    _attr_native_unit_of_measurement: str | None = UNITS[191]
    _attribute_name = "cl_min_value"
    _attr_translation_key: str = "cl_min_value"

But the created entities have not a "nice" name (_TZE200_v1jqz5cy TS0601 None).
How could this be changed?

Capture d’écran du 2024-01-20 15-15-22

@TheJulianJES
Copy link
Collaborator

I can give you more detailed instructions later, but you'll need to add the translation key to the translation files and run the "language" script once.

@tschiex
Copy link
Contributor

tschiex commented Jan 20, 2024

@TheJulianJES Ok. I have added a button:

@CONFIG_DIAGNOSTIC_MATCH(
    cluster_handler_names="tuya_manufacturer", quirk_ids=TUYA_POOL_SENSOR
)
class TuyaPoolSensorUpdateButton(ZHAAttributeButton):
    """Defines a ZHA update button for the Tuya pool sensor."""

    _unique_id_suffix = "update"
    _attribute_name = "update"
    _attribute_value = 1
    _attr_entity_category = EntityCategory.CONFIG
    _attr_translation_key = "update"

and modified the core/cluster_handlers/manufacturerspecific.py with:

    def __init__(self, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> None:
        """Initialize TuyaClusterHandler."""
        super().__init__(cluster, endpoint)
        if endpoint.device.quirk_id == TUYA_PLUG_MANUFACTURER:
            self.ZCL_INIT_ATTRS = {
                "backlight_mode": True,
                "power_on_state": True,
            }
        if endpoint.device.quirk_id == TUYA_POOL_SENSOR:
            self.ZCL_INIT_ATTRS = {
                "ph_min_value": True,
                "ph_max_value": True,
                "cl_min_value": True,
                "cl_max_value": True,
                "ec_min_value": True,
                "ec_max_value": True,
                "orp_min_value": True,
                "orp_max_value": True,
                "update": True,
            }

I also added this to the quirk:

class PoolManufCluster(TuyaMCUCluster):
    """Tuya Manufacturer Cluster with Pool data points."""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            # random attribute IDs
            0xEF01: ("ph_min_value", t.uint32_t, True),
            0xEF02: ("ph_max_value", t.uint32_t, True),
            0xEF03: ("cl_min_value", t.uint32_t, True),
            0xEF04: ("cl_max_value", t.uint32_t, True),
            0xEF05: ("ec_min_value", t.uint32_t, True),
            0xEF06: ("ec_max_value", t.uint32_t, True),
            0xEF07: ("orp_min_value", t.uint32_t, True),
            0xEF08: ("orp_max_value", t.uint32_t, True),
            0xEF09: ("update",t.Single, True),
        }
    )

    def _update_attribute(self, attrid: int, value: Any) -> None:
        """Catch button attribute to emit data_query."""
        super()._update_attribute(attrid, value)
        if attrid == "0xEF09":
            tuya_manuf_cluster = self.endpoint.device.endpoints[1].in_clusters[TuyaMCUCluster.cluster_id]
            tuya_manuf_cluster.command(0x03)

As this a chance to work?
I feel I'm working as a blind coder :-)

@tschiex
Copy link
Contributor

tschiex commented Jan 20, 2024

The button is there in the interface. It does something (as the Journal reports). Will prepare PRs. Information for translation is welcome. I tried to edit components/number/translation/en.json (adding translation for "cl_max_value") but it did no good.

Capture d’écran du 2024-01-20 16-46-30

@TheJulianJES
Copy link
Collaborator

@tschiex
Copy link
Contributor

tschiex commented Jan 20, 2024

@TheJulianJES Could you have a look at #2927 and more precisely https://github.com/zigpy/zha-device-handlers/actions/runs/7595993411/job/20689201853?pr=2927

The spell tests will not tolerate that a spell sends 2 messages. So my PR fails the tests, but I cannot do anything to pass them.

@Rudo1111
Copy link

Hello, there is currently a possibility - how to load sensors from TS0601 _TZE200_v1jqz5cy via SkyConnect v1.0 in Home Assistant (ZHA). well thank you
TS0601 _TZE200_v1jqz5cy

@ftardif
Copy link

ftardif commented Jul 24, 2024

Hi,
Amazing to see all the good work behind this development.
I understand this requires to synchronize ongoing changes in 3 components: quirk, ZHA and HA.
Could we summarize the tickets and/or PR's for each of these in order to track the progresses?

What would normally be the process to move this forward?
Is this currently blocked at a specific phase?
Can I help in any way (test, dev, code review)?
Again, thanks for the amazing work 👍

@alexandrezia
Copy link

To be honest, the thing is, I did all the merges from all PRs, commits, etc and build a local container, and got it running,
Only to find out the device is a complete useless cheap sensor.
I have two of them, just in case I got a defective one, and none of them is worth,
I compared the PH with manual tests (stripe and liquid) and the sensor measurements are way out ...
You have to recalibrate the PH sensor every week or less, the other sensors are kinda useless too, the only one that works well is the temperature, which I already have on my iAquaLink system.

@flety-emmanuel
Copy link

Greetings, I do have one of this sensor and integrated the latest version of the quirk to see what has been described, ie a single temperature sensor. I understand there's some core mods in HA to make it work under polling / request, how far are we to get it working natively (I run the latest version of the core) ? Thanks

@tschiex
Copy link
Contributor

tschiex commented Aug 26, 2024

My understanding is that this device requires too many changes in ZHA/HA to be accepted, even if my PR is Ok otherwise I think. This is understandable because most of the changes in HA/ZHA are related to creating sensor entities and this is now possible directly in the quirks, using quirks v2 (which did not exist when I started this). So, the best way to move forward here is probably to take my current quirk (v1) and rewrite it as a quirk v2. Very few changes in HA/ZHA would then be needed (only for introducing new units I think). I have spent (lost ?) quite some time on this and my availability is now low.

@danielp370
Copy link
Contributor

danielp370 commented Jan 4, 2025

@tschiex @TheJulianJES thanks for this initial implementation and guidance. I made the mistake of buying this sensor thinking it was already supported, so I guess I committed myself to trying to help out. I've followed this thread and have made an attempt to do a v2 version here: #3673

Using v2 quirks I tried to mimic the same functionality you had so far, however, it lacks some sensor units, and adds an auto refresh option. Aside from that it works great without requiring any other modifications.

Folks should be able to drop this quirk into /config/zha_quirks/ , reload and see a bunch more sensors.

@tschiex
Copy link
Contributor

tschiex commented Jan 5, 2025

Thanks a lot @danielp370 and kudos ! That's a nice example of unstructured team work :-)
I will install this very soon.

The main reason my PR didn't get into HA is probably that it introduced a lot of new stuff into HA/ZHA. With this v2, far less stuff is needed, still, yes, units... We should submit a PR for these.

@michaelt88
Copy link

michaelt88 commented Jan 14, 2025

Looks like some amazing work here!

To be honest, the thing is, I did all the merges from all PRs, commits, etc and build a local container, and got it running, Only to find out the device is a complete useless cheap sensor. I have two of them, just in case I got a defective one, and none of them is worth, I compared the PH with manual tests (stripe and liquid) and the sensor measurements are way out ... You have to recalibrate the PH sensor every week or less, the other sensors are kinda useless too, the only one that works well is the temperature, which I already have on my iAquaLink system.

I have been reading through this discussion here, and it seems that it is needed to continually poll the device for 1-2 minutes to allow the readings to stabilise
https://community.home-assistant.io/t/pool-monitoring-device-yieryi-ble-yl01-zigbee-ph-orp-free-chlorine-salinity-etc/659545/26

This is done by:

Setting “backlight_value” to “0” and back to “1” repeatedly in automation.

I'll be watching this ticket as keen to try this once it is integrated into HA using ZHA.

@tschiex
Copy link
Contributor

tschiex commented Jan 14, 2025

My quirk and a good fraction of its dependencies have been absorbed in a quirk v2. I installed it and it works fine.
There is a periodic refresh command sent. It's not doing anything with the "backlight_value", just sending the QUERY_DATA command I identified, but it should be easy to change/extend if useful.

See this link: https://github.com/zigpy/zha-device-handlers/blob/3a17d5db5cdc3af7040debc5022a9b4c8583409c/zhaquirks/tuya/ts0601_pool_sensor.py

@danielp370
Copy link
Contributor

danielp370 commented Jan 15, 2025 via email

@tschiex
Copy link
Contributor

tschiex commented Jan 15, 2025 via email

@TheJulianJES TheJulianJES added the device support request This requests support for a new device label Jan 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
device support request This requests support for a new device in progress Issue is being worked on, near fix Tuya Request/PR regarding a Tuya device
Projects
None yet
Development

Successfully merging a pull request may close this issue.