Skip to content

Fix light polling trusting unsupported color_mode#707

Merged
TheJulianJES merged 8 commits intozigpy:devfrom
TheJulianJES:tjj/light_color_mode_polling
Mar 12, 2026
Merged

Fix light polling trusting unsupported color_mode#707
TheJulianJES merged 8 commits intozigpy:devfrom
TheJulianJES:tjj/light_color_mode_polling

Conversation

@TheJulianJES
Copy link
Contributor

Proposed change

This works around an issue in lights that report color_mode=XY during polling, even though they only support color temperature. This PR effectively clones the logic used to determine the color mode in recompute_capabilities:

if len(supported_color_modes) == 1:
self._color_mode = next(iter(supported_color_modes))
else: # Light supports color_temp + xy, determine which mode the light is in
assert self._color_cluster_handler
if (
self._color_cluster_handler.color_mode
== Color.ColorMode.Color_temperature
):
self._color_mode = ColorMode.COLOR_TEMP
else:
self._color_mode = ColorMode.XY

This has the advantage that even if a color temperature light reports color_mode=XY, we still update the color temp values during polling, as _supported_color_modes only contains COLOR_TEMP.

We can also consider polling out that logic into a _determine_color_mode method, for example.

Additional information

HA Core recently introduced stricter validation of the set color_mode, which seems to have exposed this issue. See:

Related PR

A bigger change is tried in the PR linked above, where the endpoint's device_type is used and trusted to determine the color capabilities of a light. However, it seems like we already have one device that doesn't follow those rules either...

There's also a difference in how that PR handles color_mode when polling: If the device responds with an incorrect color_mode (e.g. XY), even though we know from _supported_color_modes that it only supports COLOR_TEMP, that PR ignores the updates it got from polling. With this PR, we update color temper values, even if the light reports color_mode=XY when our _supported_color_modes only include COLOR_TEMP.

Future

We do need to have a more robust solution for this in the future. For a patch release, I think this is the easiest solution that should fix the issue and not introduce new ones.

Notes for me later (for a proper long-term solution):

  • Should we also check if current_x and current_y are unsupported?
  • Fallback logic to supporting color temperature if it just has a value, but not in color capabilities.
    • Min/max mireds sometime set to 0 then, causing log warnings, as device shouldn't show color temp in first place

@codecov
Copy link

codecov bot commented Mar 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.62%. Comparing base (c51efa5) to head (43a1a51).
⚠️ Report is 1 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #707      +/-   ##
==========================================
+ Coverage   97.51%   97.62%   +0.11%     
==========================================
  Files          62       62              
  Lines       10946    10951       +5     
==========================================
+ Hits        10674    10691      +17     
+ Misses        272      260      -12     

☔ 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
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 hardens ZHA light polling and state restoration against devices that incorrectly report an unsupported Zigbee color_mode (e.g., reporting XY even though the light only supports color temperature), aligning runtime updates with the already-established capability recomputation logic.

Changes:

  • Ignore restored/persisted color_mode values that are not present in the entity’s _supported_color_modes.
  • During polling (async_update), derive an “effective” color_mode from _supported_color_modes (especially for single-mode lights), so color temperature updates aren’t dropped when devices misreport color_mode.
  • Add tests covering restoration and polling behavior for color-temp-only lights and dual-mode lights.

Reviewed changes

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

File Description
zha/application/platforms/light/__init__.py Validates restored color_mode against supported modes; derives an effective polled color mode to keep updates consistent with capabilities.
tests/test_light.py Adds regression tests for restoring unsupported color_mode and for polling behavior when devices misreport color_mode.

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

Comment on lines 1088 to +1092
if (color_mode := results.get("color_mode")) is not None:
if color_mode == Color.ColorMode.Color_temperature:
self._color_mode = ColorMode.COLOR_TEMP
# Determine the effective color mode: if only one mode is
# supported, use it regardless of what the device reports
if len(self._supported_color_modes) == 1:
effective_mode = next(iter(self._supported_color_modes))
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The new comment says to use the only supported mode “regardless of what the device reports”, but this logic only runs when results.get("color_mode") is not None. If the color_mode read fails/returns no value while other attributes (e.g., color_temperature) do, polling will currently skip updating state entirely; either adjust the comment to reflect the guard, or consider deriving effective_mode from _supported_color_modes even when color_mode is missing so polling can still apply updates.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

@TheJulianJES TheJulianJES Mar 12, 2026

Choose a reason for hiding this comment

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

Hmm, I guess this is valid somewhat...

I guess this also brings into question if there's a non-functional Color cluster on the device. Instead of having XY and/or COLOR_TEMP as supported modes, we'd just have ONOFF or BRIGHTNESS.
This would still work with the current logic, we would just re-set the color_mode to ONOFF or BRIGHTNESS during every poll. Shouldn't matter..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Honestly, I think current behavior is fine. A light with color_mode being unsupported somehow still supporting color/temp is something we don't need to care about when polling IMO.

@TheJulianJES TheJulianJES marked this pull request as ready for review March 12, 2026 21:19
@TheJulianJES
Copy link
Contributor Author

@puddly What do you think of this for the patch release tomorrow?

@puddly
Copy link
Contributor

puddly commented Mar 12, 2026

I think it's fine. I played around more with #703 but it's a dead end for now and your PR here fixes the actual runtime issue, since we can't assume these devices aren't sending bad data anyways.

@TheJulianJES
Copy link
Contributor Author

#703 is still something to look at (or a different but kinda similar approach?), as we do seem to incorrectly identify some lights as supporting XY or color temperature. #572 is also still open to fix the color temperature issue.

With both of those PRs, I worry that there's more lights out in the real world that "work fine" now, without any quirks, but later break when we fix our behavior. I think we'll just need to add logging at some point (with requests to report these devices), then implement quirks for them, and merge ZHA change(s).

@TheJulianJES TheJulianJES merged commit 106c82e into zigpy:dev Mar 12, 2026
19 checks passed
@puddly
Copy link
Contributor

puddly commented Mar 12, 2026

Yeah, fundamentally there is no way to handle all of these weird devices with a generic ZCL Light entity. We maybe just need to have quirks provide hints to the entity object to override this behavior per-device, as annoying as that is 😓

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