#Building a V2 quirk for ZHA
1 messages · Page 1 of 1 (latest)
I was able to match it I think, not sure why it didn't work but when I try to match the attribute name it can't find it since the device does not report it. How do you use quirks v2 when a device does not report it's attributes?
You can ignore the signature completely in v2
Make a custom cluster
Then replace it with the replaces method
So I don't need to provide parameters to the quirk builder like this? QuirkBuilder("Signify Netherlands B.V.", "SOC001")
Manufacturer and model are the only part of the signature that's required. So yeah, that's enough.
You can just use .replaces(customCluster).add_to_registry() then.
I came with this until now and was wondering if I understood the basics. I know I can't use enums in a binary, I would need a kind of converter class? https://pastebin.com/zEzsBdpu
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
Wait it actually does work.... that is so weird. But the binary entity only update upon reading the attribute manually.
The .replaces part for the custom cluster can be done like this:
(
QuirkBuilder("Signify Netherlands B.V.", "SOC001")
.replaces(PhilipsContactCluster, endpoint_id=2)
.binary_sensor(
"contact", # ideally you'd want to use new style zigpy AttributeDefs and use `.name` here
PhilipsContactCluster.cluster_id,
endpoint_id=2
)
.add_to_registry()
)
No need to specify which cluster ID to replace, as it's the same of the custom cluster.
Attribute reporting might need to be set up via ZHA.
Woah it's even more clean now! 😄
I'll look at the .name when it all works.
Unfortunately i'm not sure how to go for the attribute reporting. I've been stuck for a couple of hours. 😦
I actually taught ZHA configured attributes reporting for all clusters by default and would pick up my custom one since I declared it.
In order for ZHA to automatically bind the cluster upon pairing/reconfiguration, it needs to be added here similar to this: https://github.com/zigpy/zha/blob/61ec3039f1323190b405f9bbb100c7f2a7520c19/zha/zigbee/cluster_handlers/manufacturerspecific.py#L81-L86
If that's not enough, then attribute reporting needs to be set up in REPORT_CONFIG.
Does the sensor not expose open/closed via the OnOff cluster or IasZone by default?
Yes the open/closed is exposed in on/off but it's only meant for binding, Philips made it stateless. The statefull one is in the manufacturer specific cluster and they also made the tamper attribute there.
ok nice so I was able to test for the bindings with manufacturerspecific.py and it seem to work. Now I need to setup reporting in REPORT_CONFIG but there are several of them it should now be in websocket? https://github.com/zigpy/zha-websocket-server/blob/d6124915a64d51942115afcde459d2dd59c87aed/zhaws/server/zigbee/cluster/manufacturerspecific.py
zha-websocket-sever is in early development (despite its age)😂) it will use the zha lib just as the internal zha integration does. I think you should stick with the ha zha integration for anything right now
ahhh ok ok haha then i'm believing it's gonna be here then : https://github.com/zigpy/zha/blob/4c5fc061374aeaae5a80769040d552177dc8c6d0/zha/zigbee/cluster_handlers/manufacturerspecific.py#L4
One of the devs will address. I just have minor insight into things and knew enough to point you awy from ZHAWS
Nice so everythign works. The remaining thing I was wondering is how can I change the entity type since what I configured is not diagnostic territory :
The entities are still created through quirks v2, right? (not directly in ZHA)
https://github.com/zigpy/zigpy/blob/221da72a06ffb56106aecabe9c146dc433e7caf4/zigpy/quirks/v2/__init__.py#L739-L770 needs to have an entity_type parameter added.
.. should probably be done for everything that currently hardcodes entity_type.
Feel free to PR the changes to zigpy. Otherwise, I can also do it in a few minutes or so
I'll let you do it if you don't mind i'm not terribly good with code.
You've been doing good so far! 😄
PR is: https://github.com/zigpy/zigpy/pull/1487
@thorny needle thanks for saving me from crying in an infinite loop of confusion. 😄 I didn't understood that the prerequisites we're in reverse.
I might've already asked this, but what's the differences between the IAS bits (for the sensor and the one for tamper) vs the attributes on the Hue cluster?
We could also add code in ZHA to also expose the IAS tamper bit as an entity, just for the Hue contact sensor.
We need to update the cluster handler for IAS too
Right now I think it just looks to see if either 1 or 2 is set
I thought the sensor does that since the binary sensor refactor?
I don’t remember 😄
I'll check
I wish there was a dynamic way to see what is what
Yeah don't worry I never actually explained my reasoning. The on_off attribute on this device is stateless and I believed in case of a network failure, when it would come back-up, any new state happening during the network being offline, we would never see the new state. For example, here when I have a power outage, and I open a door or a window, I believe I would never know.
Would make this easier
Hmm, is the sensor similar to the Hue motion sensors, i.e. that you can always read the attributes and it wakes up?
Like, we do not poll battery-powered devices on startup.
Yes that's why I like the device. It does not really sleep.. But the catch with the on_off attribute on this one is that it will report events but will not write it to the attribute. So when you read the attribute there's nothing.
Yeah, ok.
So, for ZHA to expose the IAS tamper bit as an entity, something like this might work: https://github.com/zigpy/zha/compare/dev...TheJulianJES:zha:tjj/ias_tamper_test
(If this were to actually be implemented, there should be an IasBase class, so the device class + translation key don't need to be overridden like that)
(There should be no need to modify the cluster handler anymore, as the binary sensor code was refactored at one point)
The thing with going with the quirks v2 route is that if we were to add that quirk, we'd at least have duplicate entities for the open/closed state. The quirks custom one and the standard OnOff one.
The custom attribute could be mapped to the OnOff cluster (and have it be readable), but that's somewhat hacky.
But we can't really remove the OnOff one at this point, as quite a few people already use that. As of yet, we also don't have a way of migrating unique IDs easily (HA has a helper for that, but it's somewhat complicated with everything we have set up).
The binary sensor of the v2 device handler? So you are telling me that at some point I won't need to modify the cluster handler but not yet?
^ I was referring to that. It should no longer be needed to modify the cluster handler if a native IAS tamper entity were to be added to ZHA.
The cluster handler you’re adding for quirks v2 (to have the custom attribute bound + set up reporting) is manufacturer specific, not the current IAS one.
Just to make sure we are on the same page, the manufacturer cluster is not a IAS cluster. It's just 2 enums and 2 int. So if I go the native way, I need to translate. I agree it's better to do that to avoid modifying the cluster handler but I don't have the knowledge to do it or at least, I would need heavy assistance to achieve it.
I understood that the manufacturer cluster is a custom one, but you mentioned that it also has the IAS cluster (https://github.com/zigpy/zha-device-handlers/issues/3314#issuecomment-2415184115)
Or was that a misunderstanding already?
Ahhh so sorry. No. I wish so much that device had IAS. It only has the custom cluster.
I was just refer to the ZCL standard.
Ah, ok, and the OnOff one
Yep
God, would have loved it they would use IAS.
worse, they don't report attributes, I had to loop a script that read every addresses of the cluster to discover them.
It lasted 3 days
Ok, so we at least need to go the quirks v2 route for the tamper entity. We have to see what to do with the open/close entity. In an initial quirks v2 version (PR), I would not implement the open/close custom entity.
For the on_off I agree that it's less than ideal disabling it since it breaks binding to other devices.
I guess we could leave it?
Or could we just not create an entity for it but leave the possibility to bind it?
By leave it I mean not remove it.
There are ways to still have the real OnOff cluster to be bindable and not show the entity in HA, but it’s not easy if we want to migrate everyone over to the new entity. It might be possible to forward reads to the custom cluster and then translate answers back to the OnOff one, but I’m not entirely sure it would work like that.
Like, having both entities in HA is really not ideal.
But we can’t remove the existing OnOff entity because some people are already using that.
Oh other people bought this absurdly priced device already?
We want to avoid breaking change I guess? The thing is that on_off for this device was never meant to be used for other things then binding to other devices.
By default, it’s the only entity shown for the open/closed state, right? And since it works, people will be using it already.
Yes you are right.
Just saw your last comment. I think you nailed it. I wasn't clear after all.
Thanks for the PR. Should already help with visiblity around the device and so on. Also added some small comments to it.
I will address what you recommended. Really appreciate.
For the on_off I think I would go for not creating an entity for it but still expose it for binding and propose it as a breaking change. Would that be possible in v2?
Could that be some sort of replace instead of remove?
The unique ID in HA still changes. We define it as the IEEE + cluster + attribute id + potential suffix (or something along those lines). It would need to be the same to keep the same "entity id" in HA (or old UID migrated via a HA helper to the new UID).
The OnOff sensor in ZHA could be changed to explicitly not create for the Hue sensor, but I don't really like that. (would be a breaking change)
Tbh, I would just avoid creating the v2 open/close entity in the initial PR. We can look into replacing the OnOff entity (or "rewiring" the clusters behind the scenes in the future).
Users already need to reconfigure the device to set up binding and get attribute reports from the manufacturer specific clusters (for the v2 entities).
The benefit of the manufacturer specific open/closed attribute is minimal. ZHA does not poll battery-powered end-devices on startup. So, to most users, it won't make a difference what entity we use (OnOff vs v2 quirk one) when HA is powered off and the state changes during that time. They'd need to set up an automation that calls homeassistant.update_entity on startup for that sensor to see any change between those entities (with a delay to avoid the network storm on startup).
And just for reference, Z2M also uses the OnOff cluster for the opened/closed state and doesn't expose the tamper entity at the moment.
Oh no. I think I broke my DB while re-adding the device. Can I clean it from the databasse? 😦 Enregistreur: homeassistant
Source: runner.py:147
S'est produit pour la première fois: 13:40:41 (1 occurrences)
Dernier enregistrement: 13:40:41
Error doing job: Task was destroyed but it is pending! (<Task pending name='Task-304' coro=<PersistingListener._worker() running at /usr/local/lib/python3.12/site-packages/zigpy/appdb.py:161> wait_for=<Future pending cb=[Task.task_wakeup()]>>)
That message alone doesn't mean a lot. Is there more in the logs? What's happening when you re-add the device?
It should be rare to actually break a zigpy db
Removing the device from HA should remove references in the zigpy db too.
Let me see. In the GUI there's not much more than that. The rest is unable to get ZHA device. Here's my log. I can make it more verbose if need be.
I don't see the device in the device list. Basically I was trying to add it again but never appeared when I made the join network open. I decided to restart HA and that's where it blocked.
Oh wait i'm dumb, it was my quirk.
I just want to apologies.
I got so nervous when I saw something about the databse that I didn't really think..
I should not test in production as well..
What was the issue with the quirk? I wonder if we should improve validation on whatever it was.
I put a string instead of a cluster in v2.
We can add explicit type checks to the builders…
I just had an idea @thorny needle could we apply the quirk to only new installations?
Not in a clean way. It would also make troubleshooting a bit weird
All good
Hello guys, I'm total beginner but can I ask you a quick one. I'm trying to build a quirk v2 but I just don't understand why it's not importing the builder module? Do I have to somehow use the dev branch? @thorny needle
from zhaquirks.tuya.builder import TuyaQuirkBuilder
ModuleNotFoundError: No module named 'zhaquirks.tuya.builder'
I think I understand now... It's not included in my HA core yet
Nice, works with 2024.11. beta. Now I just have to understand how to make 1 switch and 1 sensor to 12+4 🤨
Well this is where I'm stuck at. This only creates 1 switch, 1 temp and 1 humidity that include all of these. Not sure where to go from here... Any help appreciated!
from zhaquirks.tuya.builder import TuyaQuirkBuilder
(
TuyaQuirkBuilder("_TZE204_dqolcpcp", "TS0601")
.tuya_onoff(dp_id=1)
.tuya_onoff(dp_id=2)
.tuya_onoff(dp_id=3)
.tuya_onoff(dp_id=4)
.tuya_onoff(dp_id=5)
.tuya_onoff(dp_id=6)
.tuya_onoff(dp_id=101)
.tuya_onoff(dp_id=102)
.tuya_onoff(dp_id=103)
.tuya_onoff(dp_id=104)
.tuya_onoff(dp_id=105)
.tuya_onoff(dp_id=106)
.tuya_temperature(dp_id=183, scale=10)
.tuya_humidity(dp_id=182, scale=10)
.tuya_temperature(dp_id=185, scale=10)
.tuya_humidity(dp_id=184, scale=10)
.tuya_temperature(dp_id=187, scale=10)
.tuya_humidity(dp_id=186, scale=10)
.tuya_temperature(dp_id=189, scale=10)
.tuya_humidity(dp_id=188, scale=10)
.skip_configuration()
.add_to_registry()
)
For the onoff, try using tuya_switch, https://github.com/zigpy/zha-device-handlers/blob/728ee427263d89a9c9186eacefc90e4994d81b82/zhaquirks/tuya/ts0601_valve.py#L497
On the temp and humidity, I think you will need to add those to separate end points
For the temp and humidity, try calling tuya_dp, specifying a different endpoint for each temp and humidity cluster, then call adds with the same end point.
Okay, thank you. I'll see what I can do when I have time to play again.... who knows when. Too bad the V1 quirk works fine with the switches but it's just missing the 8 measurements.
I tried working on that as well but it was even deeper pit for me.
Is there anything needed to a device update data when using TuyaQuirkBuilder? When I define it using quirksV1, it updates (and I see the tuya attribute logs), but with V2 I see no logs and the values do not change
Not really, it should work the same. Tuya sends those messages on datapoints without us needing to subscribe or anything.
Can you post the v2 quirk and/or the v1 quirk?
(
TuyaQuirkBuilder("_TZE204_ugekduaj", "TS0601")
.tuya_dp(
dp_id=0x0265,
ep_attribute=ElectricalMeasurement.ep_attribute,
attribute_name="ac_frequency",
)
.tuya_dp(
dp_id=0x026f,
ep_attribute=ElectricalMeasurement.ep_attribute,
attribute_name="total_active_power",
)
.adds(ElectricalMeasurement)
.add_to_registry()
)
I only added that for now, to try it out
the v1 I tried was the HikingPowerMeter. I just added the new model to it to check it out, but most datapoints are different, and that is why I was trying to add the new one using v2
but when I used that v1 quirk I could see the logs from zha-device-handlers with all the datapoints every few secconds.
oh, the logs are on TuyaManufCluster, but TuyaNewManufCluster has no logs, so that probably explains that part
Are those the correct DP id's? They seem very high.
I think so, at least the first one worked when I just hacked it into the HikingPowerMeter (which has higher ids: https://github.com/zigpy/zha-device-handlers/blob/dev/zhaquirks/tuya/ts0601_din_power.py#L33 )
Try using 0x65 and 0x6f
I think they should be in the 100 range
thats what I tried initially using the v1 quirk (since I was copying them from Z2M) and it wasn't working, but 0x265 did. Are quirks v1 flipping the 9th bit on some ids for some reason? 🤔
I'll have to look at the logic on that specific v1 quirk, if you are copying from z2m, the v2 quirk should match what they have.
yeah, I've added a log to TuyaNewManufCluster similar to the one on TuyaManufCluster and the values are lower, like Z2M ones
TuyaQuirkBuilder("_TZE204_ugekduaj", "TS0601")
.adds(ElectricalMeasurement,
constant_attributes = {
ElectricalMeasurement.AttributeDefs.ac_frequency_multiplier: 1,
ElectricalMeasurement.AttributeDefs.ac_frequency_divisor: 1,
ElectricalMeasurement.AttributeDefs.ac_frequency: 6,
ElectricalMeasurement.AttributeDefs.ac_frequency_min: 0,
ElectricalMeasurement.AttributeDefs.ac_frequency_max: 10,
}
)
.add_to_registry()
I've added that as a simple test, but get this error when I try to read it: Read attribute for: cluster_id: [2820] cluster_type: [in] endpoint_id: [1] attribute: [768] manufacturer: [None] response: [None] failure: [{768: <Status.UNSUPPORTED_ATTRIBUTE: 134>}],
I think you need something like this
class TuyaElectricalMeasurement(ElectricalMeasurement, TuyaLocalCluster):
"""Tuya Electrical Measurement cluster."""
_CONSTANT_ATTRIBUTES = {
ElectricalMeasurement.AttributeDefs.ac_frequency_multiplier: 1,
ElectricalMeasurement.AttributeDefs.ac_frequency_divisor: 1,
ElectricalMeasurement.AttributeDefs.ac_frequency: 6,
ElectricalMeasurement.AttributeDefs.ac_frequency_min: 0,
ElectricalMeasurement.AttributeDefs.ac_frequency_max: 10,
}
Then your quirk should look like so.
(
TuyaQuirkBuilder("_TZE204_ugekduaj", "TS0601")
.tuya_dp(
dp_id=0x65,
ep_attribute=ElectricalMeasurement.ep_attribute,
attribute_name="ac_frequency",
)
.tuya_dp(
dp_id=0x6f,
ep_attribute=ElectricalMeasurement.ep_attribute,
attribute_name="total_active_power",
)
.adds(TuyaElectricalMeasurement)
.skip_configuration()
.add_to_registry()
)
that seems to work (but with the id from the attribute def: ElectricalMeasurement.AttributeDefs.ac_frequency_divisor.id: 1
but I think there's some bug on the builder, because it should set CONSTANT_ATTRIBUTES on the cluster using the method arg dict:
but it uses the it is defined on post_initname from the AttributeDef, which is not defined? 🤔
Not following here, do you have the start of a working quirk or did you still need help?
sorry
class TuyaElectricalMeasurement(ElectricalMeasurement, TuyaLocalCluster):
"""Tuya Electrical Measurement cluster."""
_CONSTANT_ATTRIBUTES = {
ElectricalMeasurement.AttributeDefs.ac_frequency_divisor.id: 100,
}
(
TuyaQuirkBuilder("_TZE204_ugekduaj", "TS0601")
.tuya_dp(
dp_id=0x65,
ep_attribute=TuyaElectricalMeasurement.ep_attribute,
attribute_name="ac_frequency",
)
.adds(TuyaElectricalMeasurement)
.skip_configuration()
.add_to_registry()
)
this works ⏫
class TuyaElectricalMeasurement(ElectricalMeasurement, TuyaLocalCluster):
"""Tuya Electrical Measurement cluster."""
(
TuyaQuirkBuilder("_TZE204_ugekduaj", "TS0601")
.tuya_dp(
dp_id=0x65,
ep_attribute=TuyaElectricalMeasurement.ep_attribute,
attribute_name="ac_frequency",
)
.adds(TuyaElectricalMeasurement,
constant_attributes = {
TuyaElectricalMeasurement.AttributeDefs.ac_frequency_divisor: 100,
}
)
.skip_configuration()
.add_to_registry()
)
but that should also work for the const attributes (according to zigpy tests)
No idea on that one, didn't test adding constant attributes like that when I built TuyaQuirkBuilder, it may be an issue with the order since we dynamically build the replacement. But I agree, would expect it to work.
The TuyaQuirkBuilder has no way atm to map 1 DP to multiple attributes, right? I have a device where current+voltage+power come from the same DP
Not currently, we need to add it though
I've refactored the dp_to_attr map so it can support multiple attrs
still need to test it a bit more, but seems to work without major changes