Skip to content

Add bitmap-bit mask support to ConfigurableAttributeSwitch#814

Draft
TheJulianJES wants to merge 2 commits into
devfrom
zigpy-bot/configurable-switch-bitmap-mask
Draft

Add bitmap-bit mask support to ConfigurableAttributeSwitch#814
TheJulianJES wants to merge 2 commits into
devfrom
zigpy-bot/configurable-switch-bitmap-mask

Conversation

@TheJulianJES

@TheJulianJES TheJulianJES commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Related quirks PR: zigpy/zha-device-handlers#5141

Summary

Adds an optional mask to ConfigurableAttributeSwitch so a config switch can target individual bit(s) of a bitmap attribute instead of the whole attribute.

When mask is set:

  • is_on is bool(cluster.get(attribute) & mask)
  • turning on writes current | mask, turning off writes current & ~mask

The current value is read from the (shared) cluster attribute cache before each write, so only the masked bit(s) change and the other bits are preserved. Sibling switches that target different bits of the same attribute share that cluster cache, so their bits accumulate correctly across sequential writes.

This lets quirks v2 expose bitmap bits as switches without synthesizing per-bit shadow attributes on a local cluster (see the quirks PR, which uses it to delete the Sonoff ZBM5 shadow cluster). It also generalizes a pattern that previously had to be hand-rolled per device.

Changes

  • ConfigurableAttributeSwitch gains a mask init argument / _mask class attribute and the read-modify-write behavior above.
  • DanfossAdaptationRunSettings is migrated onto _mask = 0x01 as the first in-tree consumer — its docstring already noted it is "a bitmap, but only the first bit is used". Single-bit behavior is unchanged, but other bits are no longer clobbered on write.
Why mask is not on ConfigurableAttributeSwitchInfo

mask is intentionally kept as instance state and not added to the serialized info dataclass. Adding a field there would change the diagnostics JSON for every config switch, forcing a regeneration of all ~87 device snapshots (the test_devices_from_files test asserts full diagnostics equality) — pure noise for a behavior-only change. The behavior is driven entirely by _mask.

Relationship to existing bitmap-bit switches

WindowCoveringInversionSwitch and DanfossAdaptationRunSettings already open-coded single-bit bitmap handling. This adds a reusable, declarative way to do it. WindowCoveringInversionSwitch is left alone — it reads one attribute (config_status) but writes a different one (window_covering_mode), which the single-attribute mask doesn't model. DanfossAdaptationRunSettings fits the mask exactly and is migrated.

Tests

  • New test_switch_configurable_bitmap_mask verifies that toggling a masked switch sets/clears only its bit and preserves the others (via the Danfoss adaptation-run entity).
  • Existing switch tests and the Danfoss device-file diagnostics test pass unchanged (the migration is snapshot-safe).

Coupled release

The quirks-side counterpart (zigpy/zha-device-handlers#5141) depends on this change, so this should land/release first.

A masked configurable switch toggles only the bit(s) in `mask` of a bitmap
attribute, reading the current (shared) cluster cache value to preserve the
other bits. This lets quirks expose individual bits of a real ZCL bitmap as
switches without synthesizing per-bit shadow attributes on a local cluster.

Migrate the existing `DanfossAdaptationRunSettings` (a bitmap using only its
first bit) onto `_mask` as the first in-tree consumer.

Copilot AI left a comment

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.

Pull request overview

This PR adds optional bitmap bitmask behavior to ConfigurableAttributeSwitch, enabling a switch entity to toggle only specific bit(s) of a bitmap attribute while preserving other bits via the cluster cache. It also migrates the in-tree DanfossAdaptationRunSettings switch to use the new _mask capability and adds a regression test around the new behavior.

Changes:

  • Add _mask / mask support to ConfigurableAttributeSwitch for read-modify-write updates of bitmap attributes.
  • Update DanfossAdaptationRunSettings to use _mask = 0x01 instead of treating the bitmap as a plain boolean.
  • Add a unit test validating that masked toggling preserves other bits.

Reviewed changes

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

File Description
zha/application/platforms/switch.py Introduces optional bitmap mask support in ConfigurableAttributeSwitch and updates Danfoss adaptation switch to toggle only bit 0x01.
tests/test_switch.py Adds coverage for masked bitmap toggling to ensure non-masked bits are preserved across on/off operations.

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

Comment on lines +442 to +443
if mask is not None:
self._mask = mask
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.29%. Comparing base (a0e3195) to head (c8899b2).
⚠️ Report is 2 commits behind head on dev.

Additional details and impacted files
@@           Coverage Diff           @@
##              dev     #814   +/-   ##
=======================================
  Coverage   97.29%   97.29%           
=======================================
  Files          55       55           
  Lines       10933    10945   +12     
=======================================
+ Hits        10637    10649   +12     
  Misses        296      296           

☔ View full report in Codecov by Harness.
📢 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.

Reject a zero `mask` (it would make the switch a permanent no-op), and extend
the test to build a switch via the `mask=` constructor argument — the path
quirks v2 discovery uses — so that path and the zero-mask guard are covered.
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.

3 participants