Open nicktrigger opened 1 week ago
Do you have the data points? That's the first step, either from the tuya developer console or a zigbee2mqtt converter.
I think that the diagnostics for Local Tuya should be useful for the DP IDs. I verified from Tuya Developer that these are correct.
{ "home_assistant": { "installation_type": "Home Assistant OS", "version": "2024.11.0", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.12.4", "docker": true, "arch": "x86_64", "timezone": "Asia/Makassar", "os_name": "Linux", "os_version": "6.6.54-haos", "supervisor": "2024.11.2", "host_os": "Home Assistant OS 13.2", "docker_version": "27.2.0", "chassis": "embedded", "run_as_root": true }, "custom_components": { "hacs": { "documentation": "https://hacs.xyz/docs/configuration/start", "version": "2.0.1", "requirements": [ "aiogithubapi>=22.10.1" ] }, "localtuya": { "documentation": "https://github.com/xZetsubou/hass-localtuya/", "version": "2024.9.0", "requirements": [] } }, "integration_manifest": { "domain": "localtuya", "name": "Local Tuya", "codeowners": [], "config_flow": true, "dependencies": [], "documentation": "https://github.com/xZetsubou/hass-localtuya/", "integration_type": "hub", "iot_class": "local_push", "issue_tracker": "https://github.com/xZetsubou/hass-localtuya/issues", "requirements": [], "version": "2024.9.0", "is_built_in": false, "overwrites_built_in": false }, "setup_times": { "null": { "setup": 0.0008731919806450605 }, "d3d7c7bd6a0d6eae5b514de424d066c4": { "wait_import_platforms": -0.7227834760560654, "wait_base_component": -0.0014464319683611393, "config_entry_setup": 0.7861054820241407 } }, "data": { "device_config": { "device_id": "eb4e6234a74b8fbb65dthm", "dps_strings": [ "1 ( code: switch , value: False )", "3 ( code: percent_state , value: 0, cloud pull )", "4 ( code: fault , value: 0, cloud pull )", "5 ( code: water_once , value: 0 )", "7 ( code: battery_percentage , value: 50 )", "8 ( code: battery_state , value: low, cloud pull )", "9 ( code: time_use , value: 0, cloud pull )", "10 ( code: weather_delay , value: cancel, cloud pull )", "11 ( code: countdown , value: 600 )", "12 ( code: work_state , value: idle )", "13 ( code: smart_weather , value: sunny, cloud pull )", "14 ( code: switch_weather , value: False, cloud pull )", "15 ( code: use_time_one , value: 0 )", "16 ( code: cycle_timing , value: AA== )", "17 ( code: timer , value: AA== )" ], "enable_debug": false, "entities": [ { "entity_category": "None", "friendly_name": "", "icon": "mdi:valve", "id": "1", "is_passive_entity": false, "platform": "switch", "restore_on_reconnect": false }, { "device_class": "problem", "entity_category": "diagnostic", "friendly_name": "Fault", "icon": "", "id": "4", "platform": "binary_sensor", "state_on": "1" }, { "device_class": "battery", "entity_category": "diagnostic", "friendly_name": "Battery", "icon": "", "id": "7", "platform": "sensor", "scaling": 1.0, "state_class": "measurement", "unit_of_measurement": "%" }, { "entity_category": "diagnostic", "friendly_name": "Battery Level", "icon": "mdi:battery", "id": "8", "platform": "sensor" }, { "entity_category": "diagnostic", "friendly_name": "Usage Time", "icon": "mdi:chart-arc", "id": "9", "platform": "sensor", "scaling": 1.0, "unit_of_measurement": "s" }, { "entity_category": "config", "friendly_name": "Switch Timer", "icon": "mdi:timer", "id": "11", "is_passive_entity": false, "max_value": 86400.0, "min_value": 0.0, "platform": "number", "restore_on_reconnect": false, "scaling": 1.0, "step_size": 1.0, "unit_of_measurement": "s" }, { "entity_category": "diagnostic", "friendly_name": "State", "icon": "mdi:state-machine", "id": "12", "platform": "sensor" }, { "entity_category": "config", "friendly_name": "Smart Weather Mode", "icon": "mdi:cog", "id": "13", "is_passive_entity": false, "platform": "select", "restore_on_reconnect": false, "select_options": { "clear": "clear", "cloud": "cloud", "cloudy": "Cloudy", "fog": "fog", "rainy": "Rainy", "snow": "snow", "sunny": "sunny" } }, { "entity_category": "config", "friendly_name": "Smart Weather", "icon": "mdi:auto-mode", "id": "14", "is_passive_entity": false, "platform": "switch", "restore_on_reconnect": false }, { "entity_category": "diagnostic", "friendly_name": "Single Usage Time", "icon": "mdi:chart-arc", "id": "15", "platform": "sensor", "scaling": 1.0, "unit_of_measurement": "s" } ], "friendly_name": "Irrigation Top Garden", "gateway_id": "****", "host": "192.168.50.100", "local_key": "**'", "model": "Flora", "node_id": "*", "protocol_version": "3.3" }, "device_cloud_info": { "active_time": 1730506468, "biz_type": 18, "category": "sfkzq", "create_time": 1715259144, "icon": "smart/icon/ay1537509218624daaHb/3d856d5bcc4d51a3ed733da21b8a884e.jpg", "id": "eb4e6234a74b8fbb65dthm", "ip": "", "lat": "", "local_key": "**'", "lon": "", "model": "Flora", "name": "top garden smart watering", "node_id": "*", "online": false, "owner_id": "**", "product_id": "akjefhj5", "product_name": "Smart Irrigator", "status": [ { "code": "switch", "value": false }, { "code": "percent_state", "value": 0 }, { "code": "water_once", "value": 0 }, { "code": "battery_percentage", "value": 50 }, { "code": "battery_state", "value": "low" }, { "code": "time_use", "value": 0 }, { "code": "weather_delay", "value": "cancel" }, { "code": "countdown", "value": 0 }, { "code": "work_state", "value": "idle" }, { "code": "smart_weather", "value": "sunny" }, { "code": "use_time_one", "value": 0 } ], "sub": true, "time_zone": "+08:00", "uid": "az1...8nF", "update_time": 1730727593, "uuid": "842712fffe7af5e1", "dps_data": { "1": { "code": "switch", "custom_name": "", "dp_id": 1, "time": 1730715900566, "type": "Boolean", "value": false, "values": "{\"type\": \"bool\"}", "id": 1, "accessMode": "rw" }, "3": { "code": "percent_state", "custom_name": "", "dp_id": 3, "time": 1730506468371, "type": "value", "value": 0, "id": 3, "accessMode": "ro", "values": "{\"type\": \"value\", \"max\": 100, \"min\": 0, \"scale\": 0, \"step\": 1, \"unit\": \"%\"}" }, "4": { "code": "fault", "custom_name": "", "dp_id": 4, "time": 1730506468371, "type": "bitmap", "value": 0, "id": 4, "accessMode": "ro", "values": "{\"type\": \"bitmap\", \"label\": [\"low_battery\", \"fault\", \"lack_water\", \"sensor_fault\", \"motor_fault\", \"low_temp\"], \"maxlen\": 6}" }, "5": { "code": "water_once", "custom_name": "", "dp_id": 5, "time": 1730543102347, "type": "value", "value": 0, "id": 5, "accessMode": "ro", "values": "{\"type\": \"value\", \"max\": 1000, \"min\": 0, \"scale\": 1, \"step\": 1, \"unit\": \"L\"}" }, "7": { "code": "battery_percentage", "custom_name": "", "dp_id": 7, "time": 1730727331354, "type": "value", "value": 50, "id": 7, "accessMode": "ro", "values": "{\"type\": \"value\", \"max\": 100, \"min\": 0, \"scale\": 0, \"step\": 1, \"unit\": \"%\"}" }, "8": { "code": "battery_state", "custom_name": "", "dp_id": 8, "time": 1730506468371, "type": "enum", "value": "low", "id": 8, "accessMode": "ro", "values": "{\"type\": \"enum\", \"range\": [\"low\", \"middle\", \"high\"]}" }, "9": { "code": "time_use", "custom_name": "", "dp_id": 9, "time": 1730506468371, "type": "value", "value": 0, "id": 9, "accessMode": "ro", "values": "{\"type\": \"value\", \"max\": 2592000, \"min\": 0, \"scale\": 0, \"step\": 1, \"unit\": \"s\"}" }, "10": { "code": "weather_delay", "custom_name": "", "dp_id": 10, "time": 1730506468371, "type": "Enum", "value": "cancel", "values": "{\"type\": \"enum\", \"range\": [\"cancel\", \"24h\", \"48h\", \"72h\"]}", "id": 10, "accessMode": "rw" }, "11": { "code": "countdown", "custom_name": "", "dp_id": 11, "time": 1730506468371, "type": "Integer", "value": 0, "values": "{\"type\": \"value\", \"max\": 86400, \"min\": 0, \"scale\": 0, \"step\": 1, \"unit\": \"s\"}", "id": 11, "accessMode": "rw" }, "12": { "code": "work_state", "custom_name": "", "dp_id": 12, "time": 1730506468975, "type": "enum", "value": "idle", "id": 12, "accessMode": "ro", "values": "{\"type\": \"enum\", \"range\": [\"auto\", \"manual\", \"idle\"]}" }, "13": { "code": "smart_weather", "custom_name": "", "dp_id": 13, "time": 1730506468371, "type": "Enum", "value": "sunny", "values": "{\"type\": \"enum\", \"range\": [\"sunny\", \"clear\", \"cloud\", \"cloudy\", \"rainy\", \"snow\", \"fog\"]}", "id": 13, "accessMode": "rw" }, "14": { "code": "switch_weather", "custom_name": "", "dp_id": 14, "time": 1730506468371, "type": "bool", "value": false, "id": 14, "accessMode": "rw", "values": "{\"type\": \"bool\"}" }, "15": { "code": "use_time_one", "custom_name": "", "dp_id": 15, "time": 1730715901150, "type": "value", "value": 0, "id": 15, "accessMode": "ro", "values": "{\"type\": \"value\", \"max\": 86400, \"min\": 0, \"scale\": 0, \"step\": 1, \"unit\": \"s\"}" }, "16": { "code": "cycle_timing", "custom_name": "", "dp_id": 16, "time": 1730506469089, "type": "raw", "value": "AA==", "id": 16, "accessMode": "rw", "values": "{\"type\": \"raw\", \"maxlen\": 128}" }, "17": { "code": "timer", "custom_name": "", "dp_id": 17, "time": 1730506469411, "type": "raw", "value": "AA==", "id": 17, "accessMode": "rw", "values": "{\"type\": \"raw\", \"maxlen\": 128}" } } } } }
Let's start simple, see if this works.
from zhaquirks.tuya import TuyaPowerConfigurationCluster4AA
from zhaquirks.tuya.builder import TuyaQuirkBuilder
(
TuyaQuirkBuilder("_TZE200_akjefhj5", "TS0601")
.tuya_onoff(dp_id=1)
.tuya_battery(dp_id=7, power_cfg=TuyaPowerConfigurationCluster4AA)
.skip_configuration()
.add_to_registry()
)
Thank you, I replaced the whole quirk with this code only, and the switch works fine, while the battery showed as 50%, which is what tuya reports too. However, I had another, older quirk still there which I deleted and now battery shows as unknown again. I tried reloading the other quirk too but no change to battery reporting after this. Still showing as unknown. I wonder if it's because this device has 2 batteries, not 4? I see the code refers to 4aa.
I deleted the old quirk again, rebooted home assistant and now I have battery too!
Nice, OK what other DPs do you want to add?
You can see here https://github.com/prairiesnpr/zha-device-handlers/blob/Tuya-Quirk-Builder-Docs/tuya.md for examples if you want to try adding some yourself.
Feel free to swap the 4aa for 2aa, I assumed it would as 4 since most of the other valves were.
I would remove your nabu casa link in the screenshots also, but I'm paranoid.
Very cool! Thanks so much
Ideally I'd like to have access to these dpids
"4 ( code: fault , value: 0, cloud pull )", "5 ( code: water_once , value: 0 )", "8 ( code: battery_state , value: low, cloud pull )", "9 ( code: time_use , value: 0, cloud pull )", "11 ( code: countdown , value: 600 )", "12 ( code: work_state , value: idle )", "15 ( code: use_time_one , value: 0 )", "17 ( code: timer , value: AA== )"
If you don't have time, I'll have a play with the instructions on the link you sent!
And thanks for the advice re nabu casa, I have removed the screenshots.
It would be great to get the setting to determine what volume to water or report the volume but I'm not convinced this device ever had that even with Tuya.
Nice, OK what other DPs do you want to add?
You can see here https://github.com/prairiesnpr/zha-device-handlers/blob/Tuya-Quirk-Builder-Docs/tuya.md for examples if you want to try adding some yourself.
Feel free to swap the 4aa for 2aa, I assumed it would as 4 since most of the other valves were.
Regarding the 2aa batteries. If I change to 2aa, it fails to load the quirk, I assume as this is not in the quirk currently? Is there a way to add this to the custom quirk ans base it off 1.2volts for rechargeable batteries?
Regarding the 2aa batteries. If I change to 2aa, it fails to load the quirk, I assume as this is not in the quirk currently? Is there a way to add this to the custom quirk ans base it off 1.2volts for rechargeable batteries?
Updating the scaling wouldn't be that simple, Tuya is reporting as a percentage, so we would have to apply a convertor to that percentage to get to the desired value, possible, but no idea what the conversion would be and it's probably not going to be very accurate anyways. These battery reports are seldom accurate using stock batteries.
I'm assuming you are importing this as a custom quirk as detailed in the ZHA documentation. Where we have a single file per custom quirk. So, you need to import the 2AA config. Which looks like so.
from zhaquirks.tuya import TuyaPowerConfigurationCluster2AA
from zhaquirks.tuya.builder import TuyaQuirkBuilder
(
TuyaQuirkBuilder("_TZE200_akjefhj5", "TS0601")
.tuya_onoff(dp_id=1)
.tuya_battery(dp_id=7, power_cfg=TuyaPowerConfigurationCluster2AA)
.skip_configuration()
.add_to_registry()
)
You can try this, DP 9 and 15 seem the same, I might be missing something and 17 would need more details. It's rw, but I don't know what it actually does and how to translate the value.
from zhaquirks.tuya import TuyaPowerConfigurationCluster2AA
from zhaquirks.tuya.builder import TuyaQuirkBuilder
from zigpy.quirks.v2 import EntityPlatform, EntityType
from zigpy.quirks.v2.homeassistant import UnitOfTime
from zigpy.quirks.v2.homeassistant.sensor import SensorDeviceClass, SensorStateClass
import zigpy.types as t
class IrrigationStatus(t.enum8):
"""Irrigation Status Enum."""
Auto = 0x00
Manual = 0x01
Idle = 0x02
(
TuyaQuirkBuilder("_TZE200_akjefhj5", "TS0601")
.tuya_onoff(dp_id=1)
.tuya_binary_sensor(dp_id=4,
attribute_name="is_faulted",
translation_key="device_fault",
fallback_name="Fault"
)
.tuya_metering(dp_id=5)
.tuya_battery(dp_id=7, power_cfg=TuyaPowerConfigurationCluster2AA)
.tuya_sensor(
dp_id=9,
attribute_name="valve_duration",
type=t.uint32_t,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DURATION,
unit=UnitOfTime.SECONDS,
entity_type=EntityType.STANDARD,
translation_key="irriation_duration",
fallback_name="Irrigation duration",
)
.tuya_number(
dp_id=11,
attribute_name="valve_countdown",
type=t.uint16_t,
device_class=SensorDeviceClass.DURATION,
unit=UnitOfTime.SECONDS,
min_value=0,
max_value=86400,
step=1,
translation_key="valve_countdown",
fallback_name="Irrigation time",
)
.tuya_enum(
dp_id=12,
attribute_name="valve_state",
enum_class=IrrigationStatus,
entity_platform=EntityPlatform.SENSOR,
entity_type=EntityType.STANDARD,
translation_key="valve_state",
fallback_name="Work State",
)
.tuya_sensor(
dp_id=15,
attribute_name="valve_use_time",
type=t.uint32_t,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DURATION,
unit=UnitOfTime.SECONDS,
entity_type=EntityType.STANDARD,
translation_key="valve_use_time",
fallback_name="Valve use time on",
)
.skip_configuration()
.add_to_registry()
)
This is mostly working! I changed back to 4aa for the power settings as zha fails to load completely when 2aa is in the quirk. I will post the error message below.
For dp4 it currently shows as fault "off". Is this the default state? Then it gives these values if there is a fault? ["low_battery", "fault", "lack_water", "sensor_fault", "motor_fault", "low_temp"]
The valve time doesn't seem to do anything. I'd expect that you set a time and then when activating the switch it runs for this amount of time. Is this possible?
Duration shows twice, the 2nd entry being accurate.
The instantaneous demand section doesn't seem to show anything, expect for an error saying its not being provided by the zha integration any more.
If its possible to remove the extra duration entry and the instantaneous demand, and fix the valve time section (and the 2aa power issue) I think this is done!
2aa power error from the logs:
Logger: homeassistant.config_entries Source: config_entries.py:635 First occurred: 20:12:28 (1 occurrences) Last logged: 20:12:28
Error setting up entry for zha Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/config_entries.py", line 635, in async_setup_with_context result = await component.async_setup_entry(hass, self) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/zha/init.py", line 132, in async_setup_entry async with radio_mgr.connect_zigpy_app() as app: File "/usr/local/lib/python3.12/contextlib.py", line 210, in aenter return await anext(self.gen) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/src/homeassistant/homeassistant/components/zha/radio_manager.py", line 182, in connect_zigpy_app app = await self.radio_type.controller.new( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/zigpy/application.py", line 254, in new await app._load_db() File "/usr/local/lib/python3.12/site-packages/zigpy/application.py", line 110, in _load_db await self._dblistener.load() File "/usr/local/lib/python3.12/site-packages/zigpy/appdb.py", line 684, in load device = zigpy.quirks.get_device(device) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/zigpy/quirks/init.py", line 43, in get_device return _DEVICE_REGISTRY.get_device(device) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/zigpy/quirks/registry.py", line 130, in get_device return quirk_entry.create_device(device) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/zigpy/quirks/v2/init.py", line 429, in create_device return CustomDeviceV2(device.application, device.ieee, device.nwk, device, self) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/zigpy/quirks/v2/init.py", line 90, in init add_meta(self) File "/usr/local/lib/python3.12/site-packages/zigpy/quirks/v2/init.py", line 173, in call cluster = self.cluster(endpoint, is_server=True) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/zhaquirks/tuya/init.py", line 895, in init__ self.endpoint.device.battery_bus.add_listener(self) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'CustomDeviceV2' object has no attribute 'battery_bus'
Looks like there is a difference in the power configuration clusters, 2AA and 3AA don't match the others, hence the error. We would need to look into why that is. It's not a huge deal, it only sets the UI to tell you the correct battery size and quantity.
Just delete instantaneous demand, it's left over from the old quirk.
Delete the demand sensor that's not working, common for Tuya to have a ton of extra DPs that don't actually do anything.
Fault is interesting, you show it as a binary sensor above, but it's actually a bitmap. If you want all the values, you will need to extract the correct bit corresponding to the fault, then add a binary sensor for each possible fault. That would require adding multiple entries for a single DP though, which isn't currently supported.
On valve time, that's the behavior I would expect, is that how it responds in local Tuya? If so, then it will just take some more digging. You unfortunately are getting to the point, you will either need to compare to a zigbee2mqtt convertor or sniff the traffic between local Tuya to determine what local Tuya sends, to which DP and compare it to what the quirk sends.
I can't delete the entries, but have hidden them anyway for the unnecessary DPs
For fault, its not the end of the world, maybe 1 day!
For Valve time, I suspect this is related to the difference between the 2 different watering states, manual & automatic. Automatic takes the time setting, manual does not. When I have a moment I will add back to Local Tuya & see what it is sending to start the watering for pre-determined time periods vs. manual watering.
Problem description
I have been searching for a custom quirk to support this valve. I found this request, now closed. The custom quirk there does not support anything other than simple on/off operation.
https://github.com/zigpy/zha-device-handlers/issues/1668
I added my product to tuya valve in the latest release as a custom quirk but unfortunately it gives the exact same functionality.
Currently this valve works perfectly with local tuya, but i want to get rid of my tuya zigbee hub and go native zha for all my devices.
Solution description
I would like further support for this valve including timers, status updates and battery. If love is someone could give me guidance on how to build up a custom quirk to add more support.
Screenshots/Video
Screenshots/Video
[Paste/up ![Screenshot_20241108_071546_Edge](https://github.com/user-attachments/assets/12bb1b54-0b58-4fe3-9a83-93f678882d47) ![Screenshot_20241108_071537_Edge](https://github.com/user-attachments/assets/42655c2c-7acb-4f68-bdd4-de1e9a70f9e2) ![Screenshot_20241108_071530_Edge](https://github.com/user-attachments/assets/ad654c08-3d91-4eb6-b491-af6543778f6f) ![Screenshot_20241108_071453_Edge](https://github.com/user-attachments/assets/63f25f0c-6b8b-41b0-a2c8-f7c0be1741f8) ![Screenshot_20241108_071442_Edge](https://github.com/user-attachments/assets/aaa15b74-195f-41a9-8475-2249f933df90) load your media here]Device signature
Device signature
```json [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 ⌄ ⌄ ⌄ ⌄ ⌄ ⌄ { "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": 4098, "maximum_buffer_size": 82, "maximum_incoming_transfer_size": 82, "server_mask": 11264, "maximum_outgoing_transfer_size": 82, "descriptor_capability_field": 0 }, "endpoints": { "1": { "profile_id": "0x0104", "device_type": "0x0051", "input_clusters": [ "0x0000", "0x0001", "0x0004", "0x0005", "0x0006", "0x0702", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE200_akjefhj5", "model": "TS0601", "class": "ts0601_TZE200_akjefhj5_valve.TuyaValve" } ] ```Diagnostic information
Diagnostic information
```json [zha-01J9XY83W61D8PMQM9FG20JYW5-_TZE200_akjefhj5 TS0601-acf53a2be849a1a19f5c194772a61da4 (1).json](https://github.com/user-attachments/files/17670698/zha-01J9XY83W61D8PMQM9FG20JYW5-_TZE200_akjefhj5.TS0601-acf53a2be849a1a19f5c194772a61da4.1.json) ```Logs
No response
Custom quirk
Custom quirk
```python class TuyaValve(CustomDevice): """Tuya valve device.""" signature = { MODELS_INFO: [("_TZE200_akjefhj5", "TS0601")], ```Additional information
No response