Skip to content

Do not bind clusters whose entities are not created#783

Open
puddly wants to merge 1 commit into
zigpy:devfrom
puddly:puddly/fix-ignored-entities-binding
Open

Do not bind clusters whose entities are not created#783
puddly wants to merge 1 commit into
zigpy:devfrom
puddly:puddly/fix-ignored-entities-binding

Conversation

@puddly
Copy link
Copy Markdown
Contributor

@puddly puddly commented Jun 2, 2026

See #781.

Copilot AI review requested due to automatic review settings June 2, 2026 22:26
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.41%. Comparing base (3a884dc) to head (2129f1c).

Additional details and impacted files
@@           Coverage Diff           @@
##              dev     #783   +/-   ##
=======================================
  Coverage   97.41%   97.41%           
=======================================
  Files          50       50           
  Lines       10419    10419           
=======================================
  Hits        10150    10150           
  Misses        269      269           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes ZHA device entity discovery so that entities prevented via quirks v2 no longer contribute to aggregated cluster configuration, with the goal of avoiding binding/reporting setup for clusters tied only to those prevented entities.

Changes:

  • Skip adding quirk-prevented entities to _discovered_entities so they no longer drive aggregate_cluster_configs (binding/reporting/cluster hooks).
  • Add a regression test asserting a prevented entity no longer triggers a cluster bind while a non-prevented sibling entity still does.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
zha/zigbee/device.py Alters discovery flow so quirk-prevented entities don’t contribute to cluster binding/reporting aggregation.
tests/test_device.py Adds a test to verify quirk-prevented entities no longer trigger cluster binding.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread zha/zigbee/device.py
Comment on lines +1161 to +1168
# A quirk-prevented entity must not drive cluster binding or
# reporting either, so it's kept out of `_discovered_entities`
# (the source for `aggregate_cluster_configs`) entirely.
if self._is_entity_removed_by_quirk(entity):
continue

self._discovered_entities.append(entity)

Comment thread tests/test_device.py
Comment on lines +1205 to +1217
zigpy_dev = await zigpy_device_from_json(
zha_gateway.application_controller,
"tests/data/devices/innr-sp-240.json",
)

level = zigpy_dev.endpoints[1].level
on_off = zigpy_dev.endpoints[1].on_off

with (
patch.object(level, "bind", wraps=level.bind) as mock_level_bind,
patch.object(on_off, "bind", wraps=on_off.bind) as mock_on_off_bind,
):
zha_device = await join_zigpy_device(zha_gateway, zigpy_dev)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, my docstring comment below is related to this. I do kind of agree that it's not perfect to have ZHA rely on zha-quirks for its tests (that do not test the quirk but some ZHA feature). Realistically, the quirk for this device should never change, so I think updating the docstring a bit is probably enough.

Copy link
Copy Markdown
Contributor

@TheJulianJES TheJulianJES left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading #781, I wasn't sure if quirks preventing entity creation but not affecting binding/reporting was actually a bug or not. With the cluster handlers, we had the same behavior: ZHA entities didn't actually affect the default binding/reporting config.

But I do think we can go this route now, since we should rarely/never incorrectly prevent default entity creation from quirks. If we do, users will just have to reconfigure/reinterview devices, until we have some way of doing this automatically (or suggesting the user to do so, e.g. with repairs).


I guess this is nice for the Frient devices that also expose the BinaryInput cluster, which we (currently) do not use for any entities.

We do have to be careful with quirks that, for example, remove the default IasZone entity, only to introduce two new ones with quirks v2 for the Alarm0 and Alarm1 bit. Technically, we can't really remove an existing entity like that anyway, as it would be a breaking change for existing users (unrelated to this PR), but it's something to remember.

As a related example, the Frient vibration sensor just disables the ZHA IasZone entity by default (and introduces quirks v2 sensors above, with no attribute reporting config currently): https://github.com/zigpy/zha-device-handlers/blob/386e5ec3563fdbfce2ed370c64a338e9d92d2a4e/zhaquirks/develco/vibration.py#L133-L142


It might be nice to have a list of all affected devices from the diagnostics database, just to see which ones are affected.

Additional instructions for zigpy-review-bot when reviewing this PR

PR #657 had intermediate commits to have diagnostics include which clusters ZHA tried to bind. See commits 4e15e88 (#657) and the revert: 21b3755 (#657).

Reintroduce something like this locally and generate a diff comparing the diagnostics with the changes from this PR (783) and without. Include all affected devices/clusters in the review body, possibly in a /details block.

Comment thread tests/test_device.py
Comment on lines +1204 to +1208
"""A quirk-prevented entity must not drive cluster binding."""
zigpy_dev = await zigpy_device_from_json(
zha_gateway.application_controller,
"tests/data/devices/innr-sp-240.json",
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, the docstring would describe a bit better what the quirk does here, since you have to guess a bit, or know that it prevents default entity creation for all entities on the LevelControl cluster.

Copy link
Copy Markdown
Collaborator

@zigpy-review-bot zigpy-review-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core change is correct and well-targeted — I traced both consumers of _discovered_entities (device.py:1009 initialize, device.py:1289 configure), and they're the only readers, so excluding quirk-prevented entities cleanly removes them from binding/reporting aggregation. A Codex review found no bugs and the new test passes. A few notes below; the big one is the affected-device audit requested in the review thread.

Please update the now-stale docstring. The comment on self._discovered_entities at device.py:403-406 still says the list includes "ones removed by a quirk" so that "binding/reporting matches the legacy claim-during-discovery flow" — this PR deliberately reverses exactly that, so the comment now describes the opposite of the code.

This is a partial fix for #781. It covers the non-virtual half (point 2 of the issue); virtual-entity-driven binds like IAS Zone enrollment remain non-disableable since _is_entity_removed_by_quirk still returns early for Platform.VIRTUAL. Worth a line in the PR body so #781 isn't auto-closed as fully resolved.

Re: the IasZone concern raised in the thread — the audit below confirms it's safe in practice: across all 810 snapshots, no IasZone (0x0500) bind is ever dropped, because the IasZoneEnrollment virtual entity (exempt from the filter) keeps the bind even when a quirk prevents the IasZone binary-sensor entity. So the Develco-vibration-style "disable IasZone, add v2 sensors" quirks keep their enrollment/binding.

One case theoretically touches the dropped reporting: lumi.curtain.acn002 (Aqara E1 roller). Its custom AnalogOutputRollerE1 cluster redirects analog_output present_value reports to cover position (roller_curtain_e1.py:113-128), and this PR drops that cluster's bind+reporting. I checked the rest of zha-device-handlers: it's the only prevent_default_entity_creation usage whose prevented cluster feeds a custom report handler (innr SP240, Sonoff S60/ZBM5 merely hide theirs). It's likely fine in practice, though — Z2M doesn't bind/configure AnalogOutput reporting for this device either (position comes from unsolicited genMultistateOutput reports + an on-demand AnalogOutput read), and Aqara devices commonly report without a configured reporting binding. Worth a sanity check on hardware, but not a blocker.

On the test — agree with the request for a clearer docstring on what the innr SP 240 quirk does here (it prevents all LevelControl entities). Copilot's "use a test-local DeviceRegistry" suggestion is reasonable in principle, but loading a real zhaquirks device is consistent with the existing tests in this file, so I'd treat it as optional.

Affected devices (full diagnostics-database audit — 11 of 810)

Computed by diffing aggregate_cluster_configs over the discovered-entity set with vs without the prevent-filter, across every snapshot in tests/data/devices/ (the PR only reorders _discover_new_entities; the aggregation function itself is unchanged, so this reproduces the with/without-PR bind state). Each row = a cluster that was bound/reported only because of a now-prevented entity, so it loses binding + reporting under this PR.

Device Endpoint → cluster Effect
frient EMIZB-151 ep64,65,66,67 → 0x0702 smartenergy_metering bind→off, reporting lost (current_summ_delivered/received, tiers 1-6, instantaneous_demand, status)
frient FLSZB-110 ep35 → 0x000f binary_input bind→off, present_value reporting lost
frient MOSZB-140 ep35 → 0x000f binary_input; ep40,41 → 0x0406 occupancy bind→off, reporting lost
frient MOSZB-153 ep35 → 0x000f binary_input; ep40,41 → 0x0406 occupancy bind→off, reporting lost
frient SCAZB-141 ep35,46 → 0x000f binary_input bind→off, present_value reporting lost
frient SMSZB-120 ep35 → 0x000f binary_input bind→off, present_value reporting lost
frient SPLZB-141 ep2 → 0x0002 device_temperature bind→off, current_temperature reporting lost
frient WISZB-131 ep35 → 0x000f binary_input bind→off, present_value reporting lost
innr SP 240 ep1 → 0x0008 level bind→off, current_level reporting lost (test case; non-dimmable plug)
Legrand Double gangs remote switch ep1 → 0x000f binary_input bind→off, present_value reporting lost
LUMI lumi.curtain.acn002 ep1 → 0x0006 on_off; ep1 → 0x000d analog_output bind→off, reporting lost — analog_output present_value feeds cover position; only theoretical impact, see note above

The frient binary_input/occupancy/metering and innr level cases are clusters ZHA exposes no real entity for (or a non-dimmable plug), so dropping the bind matches intent. No IasZone (0x0500) bind changes anywhere. Existing users keep stale bindings until they reconfigure/re-interview.

Comment thread zha/zigbee/device.py
if self._is_entity_removed_by_quirk(entity):
continue

self._discovered_entities.append(entity)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring on this list at device.py:403-406 is now stale — it states _discovered_entities includes "ones removed by a quirk" so binding/reporting matches the legacy claim-during-discovery flow, which is precisely what this PR reverses. Please update it to reflect that quirk-prevented entities are now excluded.

@TheJulianJES
Copy link
Copy Markdown
Contributor

Ah, it's good that the Aqara curtain was brought up. That might be valid – I'll have to take a look at that later.

I do wonder if we should maybe add an additional keep_config keyword argument for prevent_default_entity_creation. If that's explicitly provided by quirks, ZHA would still bind the cluster and set up reporting as before.

That might be useful for cases like the IasZone example or the AnalogOutput stuff, so we don't need to explicitly redefine both binding and attribute report configurations in the quirk for those attributes (which we can't do yet anyway). I'm not fully sure about this yet. Sometimes, making it more explicit in the quirk might be good, but I think this additional argument could still be useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants