#Building a V2 quirk for ZHA

1 messages · Page 1 of 1 (latest)

solid owl
#

I'm trying to build a V2 quirk for ZHA but can't match the device signature with V2. I'm trying to match : "Signify Netherlands B.V.", "SOC001". How would you write the QuirkBuilder to do so?

solid owl
#

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?

safe crypt
#

You can ignore the signature completely in v2

safe crypt
#

Then replace it with the replaces method

solid owl
#

So I don't need to provide parameters to the quirk builder like this? QuirkBuilder("Signify Netherlands B.V.", "SOC001")

thorny needle
#

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.

solid owl
#

Wait it actually does work.... that is so weird. But the binary entity only update upon reading the attribute manually.

thorny needle
#

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.

thorny needle
solid owl
#

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.

thorny needle
#

Does the sensor not expose open/closed via the OnOff cluster or IasZone by default?

solid owl
#

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.

solid owl
inner patio
#

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

solid owl
inner patio
#

One of the devs will address. I just have minor insight into things and knew enough to point you awy from ZHAWS

solid owl
#

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 :

thorny needle
#

.. should probably be done for everything that currently hardcodes entity_type.

solid owl
#

Yes

#

Ah that explains.

thorny needle
#

Feel free to PR the changes to zigpy. Otherwise, I can also do it in a few minutes or so

solid owl
#

I'll let you do it if you don't mind i'm not terribly good with code.

thorny needle
solid owl
#

Wait that's it?

#

Oh, maybe I should trust myself haha

solid owl
#

@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.

thorny needle
#

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.

safe crypt
#

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

thorny needle
#

I thought the sensor does that since the binary sensor refactor?

safe crypt
#

I don’t remember 😄

thorny needle
#

I'll check

safe crypt
#

I wish there was a dynamic way to see what is what

solid owl
#

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.

safe crypt
#

Would make this easier

thorny needle
#

Like, we do not poll battery-powered devices on startup.

solid owl
#

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.

thorny needle
#

Yeah, ok.

#

(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).

solid owl
thorny needle
#

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.

solid owl
thorny needle
#

Or was that a misunderstanding already?

solid owl
#

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.

thorny needle
#

Ah, ok, and the OnOff one

solid owl
#

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

thorny needle
#

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.

solid owl
#

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.

thorny needle
#

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.

solid owl
#

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.

thorny needle
#

By default, it’s the only entity shown for the open/closed state, right? And since it works, people will be using it already.

solid owl
#

Yes you are right.

#

Just saw your last comment. I think you nailed it. I wasn't clear after all.

thorny needle
#

Thanks for the PR. Should already help with visiblity around the device and so on. Also added some small comments to it.

solid owl
#

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?

thorny needle
#

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.

solid owl
#

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()]>>)

thorny needle
#

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.

solid owl
#

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..

thorny needle
#

What was the issue with the quirk? I wonder if we should improve validation on whatever it was.

solid owl
#

I put a string instead of a cluster in v2.

safe crypt
#

We can add explicit type checks to the builders…

solid owl
#

I just had an idea @thorny needle could we apply the quirk to only new installations?

thorny needle
#

Not in a clean way. It would also make troubleshooting a bit weird

solid owl
#

Oh ok. ☹️

#

Sorry for mentioning you. 😅

thorny needle
#

All good

grizzled juniper
#

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'

grizzled juniper
#

I think I understand now... It's not included in my HA core yet

grizzled juniper
#

Nice, works with 2024.11. beta. Now I just have to understand how to make 1 switch and 1 sensor to 12+4 🤨

grizzled juniper
#

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()
)
grizzled juniper
real zodiac
#

On the temp and humidity, I think you will need to add those to separate end points

real zodiac
grizzled juniper
#

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.

solar linden
#

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

thorny needle
#

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?

solar linden
#
(
    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.

solar linden
#

oh, the logs are on TuyaManufCluster, but TuyaNewManufCluster has no logs, so that probably explains that part

real zodiac
solar linden
real zodiac
#

I think they should be in the 100 range

solar linden
# real zodiac Try using 0x65 and 0x6f

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? 🤔

real zodiac
#

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.

solar linden
solar linden
#
    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>}],

real zodiac
# solar linden I've added that as a simple test, but get this error when I try to read it: `Rea...

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()
)
solar linden
#

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 name from the AttributeDef, which is not defined? 🤔 it is defined on post_init

real zodiac
solar linden
#

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)

real zodiac
#

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.

solar linden
#

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

real zodiac
#

Not currently, we need to add it though

solar linden
#

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

solar linden