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] Tuya water timer (TS0049 by _TZ3000_gjpgagal) #3316

Open
gunarser opened this issue Aug 25, 2024 · 31 comments
Open
Labels
device support request This requests support for a new device Tuya Request/PR regarding a Tuya device

Comments

@gunarser
Copy link

Problem description

Please can a handler be created to support a Tuya water timer (TS0049). The device can be added to home assistant via ZHA but not all entities (like timer which by default is set to 10 minutes) are available to control the valve.

Solution description

Creation of a new handler that supports the device

Screenshots/Video

image

Device signature

Device signature
{
  "node_descriptor": {
    "logical_type": 2,
    "complex_descriptor_available": 0,
    "user_descriptor_available": 0,
    "reserved": 0,
    "aps_flags": 0,
    "frequency_band": 8,
    "mac_capability_flags": 128,
    "manufacturer_code": 4417,
    "maximum_buffer_size": 66,
    "maximum_incoming_transfer_size": 66,
    "server_mask": 10752,
    "maximum_outgoing_transfer_size": 66,
    "descriptor_capability_field": 0
  },
  "endpoints": {
    "1": {
      "profile_id": "0x0104",
      "device_type": "0x0000",
      "input_clusters": [
        "0x0000",
        "0x0001",
        "0x0003",
        "0x0004",
        "0x0005",
        "0x0006",
        "0xe001"
      ],
      "output_clusters": [
        "0x000a",
        "0x0019"
      ]
    }
  },
  "manufacturer": "_TZ3000_gjpgagal",
  "model": "TS0049",
  "class": "zigpy.device.Device"
}

Diagnostic information

Diagnostic information
{
  "home_assistant": {
    "installation_type": "Home Assistant OS",
    "version": "2024.8.2",
    "dev": false,
    "hassio": true,
    "virtualenv": false,
    "python_version": "3.12.4",
    "docker": true,
    "arch": "aarch64",
    "timezone": "Europe/Riga",
    "os_name": "Linux",
    "os_version": "6.6.31-haos-raspi",
    "supervisor": "2024.08.0",
    "host_os": "Home Assistant OS 13.1",
    "docker_version": "26.1.4",
    "chassis": "embedded",
    "run_as_root": true
  },
  "custom_components": {
    "attributes": {
      "documentation": "https://github.com/pilotak/homeassistant-attributes",
      "version": "1.2.1",
      "requirements": []
    },
    "mikrotik_router": {
      "documentation": "https://github.com/tomaae/homeassistant-mikrotik_router",
      "version": "0.0.0",
      "requirements": [
        "librouteros>=3.2.0",
        "mac-vendor-lookup>=0.1.12"
      ]
    },
    "salusfy": {
      "documentation": "https://github.com/floringhimie/salusfy",
      "version": "0.0.1",
      "requirements": []
    },
    "sunspec": {
      "documentation": "https://github.com/cjne/ha-sunspec",
      "version": "0.0.26",
      "requirements": [
        "pysunspec2==1.1.5"
      ]
    },
    "home_connect_alt": {
      "documentation": "https://github.com/ekutner/home-connect-hass",
      "version": "1.1.7",
      "requirements": [
        "home-connect-async==0.8.0"
      ]
    },
    "frigate": {
      "documentation": "https://github.com/blakeblackshear/frigate",
      "version": "5.3.0",
      "requirements": [
        "pytz"
      ]
    },
    "uptime_kuma": {
      "documentation": "https://github.com/meichthys/uptime_kuma/blob/main/README.md",
      "version": "2.1.0",
      "requirements": [
        "pyuptimekuma-hass"
      ]
    },
    "localtuya": {
      "documentation": "https://github.com/xZetsubou/hass-localtuya/",
      "version": "2024.7.0",
      "requirements": []
    },
    "nordpool": {
      "documentation": "https://github.com/custom-components/nordpool/",
      "version": "0.0.14",
      "requirements": [
        "nordpool>=0.2",
        "backoff"
      ]
    },
    "hacs": {
      "documentation": "https://hacs.xyz/docs/configuration/start",
      "version": "2.0.0",
      "requirements": [
        "aiogithubapi>=22.10.1"
      ]
    },
    "ssh_command": {
      "documentation": "https://github.com/AlexxIT/SSHCommand",
      "version": "1.1.0",
      "requirements": [
        "paramiko"
      ]
    },
    "watchman": {
      "documentation": "https://github.com/dummylabs/thewatchman",
      "version": "0.6.3",
      "requirements": [
        "prettytable==3.10.0"
      ]
    }
  },
  "integration_manifest": {
    "domain": "zha",
    "name": "Zigbee Home Automation",
    "after_dependencies": [
      "onboarding",
      "usb"
    ],
    "codeowners": [
      "dmulcahey",
      "adminiuga",
      "puddly",
      "TheJulianJES"
    ],
    "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",
      "zha",
      "universal_silabs_flasher"
    ],
    "requirements": [
      "universal-silabs-flasher==0.0.22",
      "zha==0.0.31"
    ],
    "usb": [
      {
        "vid": "10C4",
        "pid": "EA60",
        "description": "*2652*",
        "known_devices": [
          "slae.sh cc2652rb stick"
        ]
      },
      {
        "vid": "10C4",
        "pid": "EA60",
        "description": "*slzb-07*",
        "known_devices": [
          "smlight slzb-07"
        ]
      },
      {
        "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": "0403",
        "pid": "6015",
        "description": "*conbee*",
        "known_devices": [
          "Conbee III"
        ]
      },
      {
        "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*"
      },
      {
        "type": "_xzg._tcp.local.",
        "name": "xzg*"
      },
      {
        "type": "_czc._tcp.local.",
        "name": "czc*"
      }
    ],
    "is_built_in": true
  },
  "setup_times": {
    "null": {
      "setup": 7.906100000809602e-05
    },
    "9a224a950316e4e06044c03d77858d2c": {
      "wait_import_platforms": -0.011965733000010914,
      "config_entry_setup": 13.179298865000021
    }
  },
  "data": {
    "ieee": "**REDACTED**",
    "nwk": 49933,
    "manufacturer": "_TZ3000_gjpgagal",
    "model": "TS0049",
    "name": "_TZ3000_gjpgagal TS0049",
    "quirk_applied": false,
    "quirk_class": "zigpy.device.Device",
    "quirk_id": null,
    "manufacturer_code": 4417,
    "power_source": "Battery or Unknown",
    "lqi": 56,
    "rssi": -86,
    "last_seen": "2024-08-25T16:35:05",
    "available": true,
    "device_type": "EndDevice",
    "signature": {
      "node_descriptor": {
        "logical_type": 2,
        "complex_descriptor_available": 0,
        "user_descriptor_available": 0,
        "reserved": 0,
        "aps_flags": 0,
        "frequency_band": 8,
        "mac_capability_flags": 128,
        "manufacturer_code": 4417,
        "maximum_buffer_size": 66,
        "maximum_incoming_transfer_size": 66,
        "server_mask": 10752,
        "maximum_outgoing_transfer_size": 66,
        "descriptor_capability_field": 0
      },
      "endpoints": {
        "1": {
          "profile_id": "0x0104",
          "device_type": "0x0000",
          "input_clusters": [
            "0x0000",
            "0x0001",
            "0x0003",
            "0x0004",
            "0x0005",
            "0x0006",
            "0xe001"
          ],
          "output_clusters": [
            "0x000a",
            "0x0019"
          ]
        }
      },
      "manufacturer": "_TZ3000_gjpgagal",
      "model": "TS0049"
    },
    "active_coordinator": false,
    "entities": [
      {
        "entity_id": "button.tz3000_gjpgagal_ts0049_identify",
        "name": "_TZ3000_gjpgagal TS0049"
      },
      {
        "entity_id": "sensor.tz3000_gjpgagal_ts0049_battery",
        "name": "_TZ3000_gjpgagal TS0049"
      },
      {
        "entity_id": "switch.tz3000_gjpgagal_ts0049_switch",
        "name": "_TZ3000_gjpgagal TS0049"
      },
      {
        "entity_id": "update.tz3000_gjpgagal_ts0049_firmware",
        "name": "_TZ3000_gjpgagal TS0049"
      },
      {
        "entity_id": "sensor.water_timer_battery",
        "name": "_TZ3000_gjpgagal TS0049"
      },
      {
        "entity_id": "update.water_timer_firmware",
        "name": "_TZ3000_gjpgagal TS0049"
      },
      {
        "entity_id": "button.water_timer_identify",
        "name": "_TZ3000_gjpgagal TS0049"
      },
      {
        "entity_id": "switch.water_timer_switch",
        "name": "_TZ3000_gjpgagal TS0049"
      }
    ],
    "neighbors": [],
    "routes": [],
    "endpoint_names": [
      {
        "name": "ON_OFF_SWITCH"
      }
    ],
    "user_given_name": "Water timer",
    "device_reg_id": "2aa680f6ef3d23419a787e148e5310a1",
    "area_id": "darzs",
    "cluster_details": {
      "1": {
        "device_type": {
          "name": "ON_OFF_SWITCH",
          "id": 0
        },
        "profile_id": 260,
        "in_clusters": {
          "0x0003": {
            "endpoint_attribute": "identify",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0004": {
            "endpoint_attribute": "groups",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0005": {
            "endpoint_attribute": "scenes",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0001": {
            "endpoint_attribute": "power",
            "attributes": {
              "0x0021": {
                "attribute_name": "battery_percentage_remaining",
                "value": 200
              },
              "0x0020": {
                "attribute_name": "battery_voltage",
                "value": 30
              }
            },
            "unsupported_attributes": {
              "0x0031": {
                "attribute_name": "battery_size"
              },
              "0x0033": {
                "attribute_name": "battery_quantity"
              }
            }
          },
          "0x0006": {
            "endpoint_attribute": "on_off",
            "attributes": {
              "0x4002": {
                "attribute_name": "off_wait_time",
                "value": 0
              },
              "0x0000": {
                "attribute_name": "on_off",
                "value": 0
              },
              "0x4001": {
                "attribute_name": "on_time",
                "value": 0
              }
            },
            "unsupported_attributes": {
              "0x4003": {
                "attribute_name": "start_up_on_off"
              }
            }
          },
          "0xe001": {
            "endpoint_attribute": null,
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0000": {
            "endpoint_attribute": "basic",
            "attributes": {
              "0x0004": {
                "attribute_name": "manufacturer",
                "value": "_TZ3000_gjpgagal"
              },
              "0x0005": {
                "attribute_name": "model",
                "value": "TS0049"
              }
            },
            "unsupported_attributes": {}
          }
        },
        "out_clusters": {
          "0x0019": {
            "endpoint_attribute": "ota",
            "attributes": {
              "0x0002": {
                "attribute_name": "current_file_version",
                "value": 73
              }
            },
            "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

I have a Tuya Zigbee gateway at my disposal that could be used for further troubleshooting if necessary.

@TheJulianJES TheJulianJES added the Tuya Request/PR regarding a Tuya device label Aug 28, 2024
@danpeig
Copy link
Contributor

danpeig commented Sep 1, 2024

Look at issue #2377

@gunarser
Copy link
Author

gunarser commented Sep 2, 2024

Look at issue #2377

If that was meant for me then I already tried the quirk and commented on the results here: #2377 (comment)
It looks like there are some incompatibility issues, i.e. the device is quite different from the one the quirk was made for.

@danpeig
Copy link
Contributor

danpeig commented Sep 2, 2024

#2377 is the thread for TS0049. Developers that work with this type of device are subscribed to that issue.

As far as I am aware of, Tuya has only one specification for TS0049.

Most user problems are related with binding the quirk to the device because different manufacturers have different signatures.

@gunarser
Copy link
Author

gunarser commented Sep 2, 2024

OK. Perhaps I was doing something wrong. I changed the manufacturer as per my comment but the quirk does not get loaded. What is the best way to troubleshoot such cases?

@danpeig
Copy link
Contributor

danpeig commented Sep 2, 2024

Do you have other custom quirks installed and working? I think the first step is ensure the system is actually loading the code.

My recommendation is use a quirk for a ZigBee device you already have working like the TS0001 or TS0011 (wall plugs and light switches) and see if it loads properly. The custom quirk will appear in the device information page.

Once the quirk are loading, then you can force the 0049 to bind to your specific device.

@gunarser
Copy link
Author

gunarser commented Sep 2, 2024

I have Nous light switches that require quirk and some time back I installed it manually and it was correctly loaded. But for some time the quirk is included in HA and added automatically. So I removed the custom quirk.
I don't have any other device that need custom quirk. Can I use those light switches for a test even though the quirk is already in HA?

This is how swithes look like in HA:

image

@danpeig
Copy link
Contributor

danpeig commented Sep 2, 2024

If you have a custom quirk it will be loaded instead of the built in. This is just to test the environment.

If it loads, then it's just about fingerprinting your device properly.

You can change the TS001x quirk (known to be working) to see if you manage to attach it to the water valve. The valve will obviously no work, but you will know the binding code.

Regards

@gunarser
Copy link
Author

gunarser commented Sep 3, 2024

I added the ts001x.py file to the config/custom_zha_quirks/ directory and it looks like the custom quirk is indeed loaded for light switches instead of the built-in one:
image

I modified the ts001x.py file by changing model ID of 3-gang switch from TS0013 to TS0049 but the quirk does not get loaded after HA restart. Should I re-pair the water timer to get the quirk loaded or is there something else that needs to be done in order to get the ts001x loaded?

@danpeig
Copy link
Contributor

danpeig commented Sep 3, 2024

The quirk should still be loading for the other devices. If it is not, you have a syntax problem with the code.

If it loads but not binds yo the valve, you have a problem with the "signature". The signature must match your valve device in order for it to bind. The device name is only one parameter of the signature (see https://github.com/zigpy/zha-device-handlers).

@gunarser
Copy link
Author

gunarser commented Sep 4, 2024

I changed almost all the parameters in the signature both in TS0049.py and ts001x.py but none of the quirks gets loaded (at least they do not appear under Zigbee info tab).
How much of the signature must match in order to get quirk loaded?

This is the signature section of the modified TS0049 quirk:

class TuyaIrrigationValve(EnchantedDevice):
    """Tuya green irrigation valve device."""
    signature = {
        MODELS_INFO: [("_TZ3000_gjpgagal", "TS0049")],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0
            # device_version=1
            # input_clusters=[0, 1, 3, 4, 5, 6, 57345]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Power.cluster_id,
                    Identity.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    Null.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Power.cluster_id,
                    Identity.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaOnOff,
                    Null.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
        },
    }`

This is how the device signature looks like in 'Manage Zigbee device' window:

{
  "node_descriptor": {
    "logical_type": 2,
    "complex_descriptor_available": 0,
    "user_descriptor_available": 0,
    "reserved": 0,
    "aps_flags": 0,
    "frequency_band": 8,
    "mac_capability_flags": 128,
    "manufacturer_code": 4417,
    "maximum_buffer_size": 66,
    "maximum_incoming_transfer_size": 66,
    "server_mask": 10752,
    "maximum_outgoing_transfer_size": 66,
    "descriptor_capability_field": 0
  },
  "endpoints": {
    "1": {
      "profile_id": "0x0104",
      "device_type": "0x0000",
      "input_clusters": [
        "0x0000",
        "0x0001",
        "0x0003",
        "0x0004",
        "0x0005",
        "0x0006",
        "0xe001"
      ],
      "output_clusters": [
        "0x000a",
        "0x0019"
      ]
    }
  },
  "manufacturer": "_TZ3000_gjpgagal",
  "model": "TS0049",
  "class": "zigpy.device.Device"
}

@danpeig
Copy link
Contributor

danpeig commented Sep 4, 2024

If you use the text below in the signature it should bind:

"profile_id": "0x0104", "device_type": "0x0000", "input_clusters": [ "0x0000", "0x0001", "0x0003", "0x0004", "0x0005", "0x0006", "0xe001" ], "output_clusters": [ "0x000a", "0x0019" ]

@gunarser
Copy link
Author

gunarser commented Sep 4, 2024

I finally managed to load and attach a custom quirk. I used this quirk-generator
For beginners it is a great tool to get started.
Now the device info looks like this:

image

Now the question is how to add the missing attributes like timer setting to the quirk?

@danpeig
Copy link
Contributor

danpeig commented Sep 4, 2024

Great! Now you just need to get the replacements to work correctly. The timer will be set using the "Manage Zigbee Device" -> "Valve family cluster" -> irrigation_time. The On-Off switch will be exposed in the interface.

@gunarser
Copy link
Author

gunarser commented Sep 5, 2024

Should the TS0049 quirk provide the needed "Valve family cluster" or there is other quirk that should be used as an example? This is what I see under clusters (no value is read from the irrigation_time attribute):

image
image
image

@danpeig
Copy link
Contributor

danpeig commented Sep 5, 2024

Yes. It is probably not mapped correctly.

@gunarser
Copy link
Author

gunarser commented Sep 5, 2024

What would be the procedure to get the right mappings? Do I need Tuya Zigbee gateway for that?

@danpeig
Copy link
Contributor

danpeig commented Sep 5, 2024

The attribute mapping is the final step to get the valve working. There is no need to use a Tuya brand ZigBee router.

This link has the attribute map specs for the valve: https://developer.tuya.com/en/docs/connect-subdevices-to-gateways/Zigbee-Water-valve-controller-access-standard?id=Kbahvojbqzvgl

@gunarser
Copy link
Author

gunarser commented Sep 8, 2024

I understand that I can additionally map/read/control the following attributes:
DP4 Fault report
DP11 Irrigation time
DP12 Work state
and perhaps DP15 Once using time

But I don't quite understand how that mapping is done via the quirk. Which lines variables need to be adjusted?

@danpeig
Copy link
Contributor

danpeig commented Sep 8, 2024

The data points are numbers, in the original code: 26, 101, 110, 111...etc.

Clusters are hexadecimal: 0xEF01, 0xEF02...

First you must set the clusters, then the DP.

@gunarser
Copy link
Author

gunarser commented Sep 8, 2024

I modified the following lines in the quirk:

class TuyaValveFamilyCluster(TuyaMCUCluster):
    """On/Off Tuya family cluster with extra device attributes"""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            0xEF01: ("countdown", t.uint32_t, True),
            0xEF02: ("work_state", t.uint32_t, True),
            0xEF03: ("fault", t.uint32_t, True),
        }
    )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        4: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "fault",
        ),
        11: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "countdown",
        ),
        12: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "work_state",
        ),
    }

    data_point_handlers = {
        4: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        12: "_dp_2_attr_update",
    }

But I get no values in the corresponding attributes:

image

Value is none for all.

@gunarser
Copy link
Author

What am I doing wrong? Perhaps cluster numbers are defined wrong? I am total newbie to quirks, don't know how these mappings work.

I modified the following lines in the quirk:

class TuyaValveFamilyCluster(TuyaMCUCluster):
    """On/Off Tuya family cluster with extra device attributes"""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            0xEF01: ("countdown", t.uint32_t, True),
            0xEF02: ("work_state", t.uint32_t, True),
            0xEF03: ("fault", t.uint32_t, True),
        }
    )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        4: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "fault",
        ),
        11: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "countdown",
        ),
        12: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "work_state",
        ),
    }

    data_point_handlers = {
        4: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        12: "_dp_2_attr_update",
    }

But I get no values in the corresponding attributes:

image

Value is none for all.

@danpeig
Copy link
Contributor

danpeig commented Sep 11, 2024

Is your valve supported by ZigBee 2MQTT? For the other model, we used the mapping information from this tool. Otherwise it is trial and error.

@gunarser
Copy link
Author

gunarser commented Sep 12, 2024

I don't know. I found the following Github issues that might relate to my valve:
Koenkk/zigbee2mqtt#23191
Koenkk/zigbee2mqtt#16671
Koenkk/zigbee2mqtt#22950
Koenkk/zigbee2mqtt#21788

Should I install Z2M in order to understand whether my specific device is supported?

Is your valve supported by ZigBee 2MQTT? For the other model, we used the mapping information from this tool. Otherwise it is trial and error.

@danpeig
Copy link
Contributor

danpeig commented Sep 12, 2024

I just got the Z2M source code.

@gunarser
Copy link
Author

Good! Tell if anything is needed from me. As I mentioned before I have a spare Tuya Zigbee gateway - could do some additional troubleshooting if necessary.

I just got the Z2M source code.

@gunarser
Copy link
Author

In the meantime, I installed Z2M Docker container and paired the valve to it:

image

image

image

Should I collect some more info from Z2M in order to create ZHA quirk?

@danpeig
Copy link
Contributor

danpeig commented Sep 14, 2024

Z2M has this device properly mapped. You need to translate it to ZHA. This is what we did with the other valve. Unfortunately I don't remember how exactly that worked.

@gunarser
Copy link
Author

gunarser commented Sep 14, 2024

OK. Who might remember?

@danpeig
Copy link
Contributor

danpeig commented Sep 14, 2024

Whoever committed this valve map to Z2M.

@gunarser
Copy link
Author

I connected the valve to Tuya Zigbee gateway and retrieved DP IDs as described in the following document: https://www.zigbee2mqtt.io/advanced/support-new-devices/03_find_tuya_data_points.html

They were:

{"1":"Switch",
"4":"Failure to report",
"7":"Battery",
"9":"Accumulated usage time",
"10":"Weather Delay",
"11":"Irrigation time",
"12":"work state",
"15":"Once using time",
"16":"Cycle irrigation",
"17":"Normal  timer",
"29":"Next irrigation time"}

Basically they are the same as described in Tuya document: https://developer.tuya.com/en/docs/connect-subdevices-to-gateways/Zigbee-Water-valve-controller-access-standard?id=Kbahvojbqzvgl

So the DPs are correct. The question is why the quirk does not map them correctly. What am I missing?!

@gunarser
Copy link
Author

gunarser commented Sep 14, 2024

Here is the complete modified quirk:

from typing import Any, Dict, Optional, Union

import zigpy.types as t
#from zhaquirks import DoublingPowerConfigurationCluster
from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks.tuya import (
    TUYA_SEND_DATA, TuyaLocalCluster,
)
from zhaquirks.tuya.mcu import (
    DPToAttributeMapping,
    EnchantedDevice,
    TuyaMCUCluster,
#    TuyaOnOff,
)
from zigpy.profiles import zha
from zigpy.zcl import foundation
from zigpy.zcl.clusters.general import Basic, Groups, Identify, OnOff, Ota, Time, PowerConfiguration, Scenes

#class TuyaValveFamilyBattery(TuyaLocalCluster, DoublingPowerConfigurationCluster):
#    _values = [10, 50, 90]
#    _CONSTANT_ATTRIBUTES = {
#        PowerConfiguration.attributes_by_name["battery_quantity"].id: 4,
#        PowerConfiguration.attributes_by_name["battery_size"].id: PowerConfiguration.BatterySize.AAA
#    }
#
#    def _update_attribute(self, attrid, value):
#        if attrid == self.BATTERY_PERCENTAGE_REMAINING:
#            value = self._values[value]
#        super()._update_attribute(attrid, value)

class TuyaValveFamilyCluster(TuyaMCUCluster):
    """On/Off Tuya family cluster with extra device attributes"""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            0xEF01: ("countdown", t.uint32_t, True),
            0xEF02: ("work_state", t.enum8, True),
            0xEF03: ("fault", t.uint32_t, True),
        }
    )

    async def command(
            self,
            command_id: Union[foundation.GeneralCommand, int, t.uint8_t],
            *args,
            manufacturer: Optional[Union[int, t.uint16_t]] = None,
            expect_reply: bool = True,
            tsn: Optional[Union[int, t.uint8_t]] = None,
            **kwargs: Any,
    ):
        """Override the default Cluster command."""
        self.debug("Setting the NO manufacturer id in command: %s", command_id)
        return await super().command(
            TUYA_SEND_DATA,
            *args,
            manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID,
            expect_reply=expect_reply,
            tsn=tsn,
            **kwargs,
        )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        4: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "fault",
        ),
        11: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "countdown",
        ),
        12: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "work_state",
        ),
    }

    data_point_handlers = {
        4: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        12: "_dp_2_attr_update",
    }

class TuyaIrrigationValve(EnchantedDevice):
    """Tuya green irrigation valve device."""
    signature = {
        MODELS_INFO: [("_TZ3000_gjpgagal", "TS0049")],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0
            # device_version=1
            # input_clusters=[0, 61184]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    0xe001,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    TuyaValveFamilyCluster,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
        },
    }

I commented out attribute definitions that are there already out-of-box like on/off switch.
Perhaps the issue is with this section:

    async def command(
            self,
            command_id: Union[foundation.GeneralCommand, int, t.uint8_t],
            *args,
            manufacturer: Optional[Union[int, t.uint16_t]] = None,
            expect_reply: bool = True,
            tsn: Optional[Union[int, t.uint8_t]] = None,
            **kwargs: Any,
    ):
        """Override the default Cluster command."""
        self.debug("Setting the NO manufacturer id in command: %s", command_id)
        return await super().command(
            TUYA_SEND_DATA,
            *args,
            manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID,
            expect_reply=expect_reply,
            tsn=tsn,
            **kwargs,
        )

@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 Tuya Request/PR regarding a Tuya device
Projects
None yet
Development

No branches or pull requests

3 participants