Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Include device_on in set_brightness, set_color_temp, and set_hsv#1680

Open
fluffyspace wants to merge 5 commits into
python-kasa:masterpython-kasa/python-kasa:masterfrom
fluffyspace:fix-device-on-light-commandsfluffyspace/python-kasa:fix-device-on-light-commandsCopy head branch name to clipboard
Open

Include device_on in set_brightness, set_color_temp, and set_hsv#1680
fluffyspace wants to merge 5 commits into
python-kasa:masterpython-kasa/python-kasa:masterfrom
fluffyspace:fix-device-on-light-commandsfluffyspace/python-kasa:fix-device-on-light-commandsCopy head branch name to clipboard

Conversation

@fluffyspace
Copy link
Copy Markdown

@fluffyspace fluffyspace commented Apr 6, 2026

Summary

  • Conditionally include device_on: True in the set_device_info payload from set_brightness(), set_color_temp(), and set_hsv() — only when self._device.is_on is already true.
  • Fixes silent command failure on devices that treat power state and brightness/color as independent parameters (confirmed on Tapo L900-5 LED strip), without regressing Regression? Latest kasa cli turns light on when setting brightness. #1532 (changing brightness on an already-off device must not turn it on).

Problem

Some Tapo devices (confirmed on the L900-5 LED light strip) do not implicitly turn on when receiving brightness or color commands via set_device_info. The device:

  1. Accepts the KLAP command and returns success (error_code: 0)
  2. Updates its internal state (reports device_on: true, brightness: 100 when queried)
  3. But does not physically turn on the LEDs

This creates a "zombie state" where the device reports it's on but the LEDs are off. The state persists until the device is power-cycled. Since every protocol response indicates success, python-kasa (and Home Assistant) have no way to detect the failure.

Root cause

  • Brightness.set_brightness() sent {"brightness": N} — no device_on
  • ColorTemperature.set_color_temp() sent {"color_temp": N} — no device_on
  • Color.set_hsv() sent {"hue": N, "saturation": N} — no device_on

Meanwhile, Light.set_state() always includes device_on in the payload, and works correctly.

Fix

Include "device_on": True in all three methods' set_device_info payloads, only when the device is currently on. This re-asserts the power state for the zombie-state case (the device thinks it's on, so the flag is still sent and the LEDs physically come up) while leaving a deliberately-off device alone.

Scenario is_on device_on sent? Result
L900-5 zombie state (reports on, LEDs off) True yes LEDs come on
Normal "device on, change brightness" True yes Same as Tapo app
Pre-set brightness while off (#1532) False no Device stays off

This matches the spirit of set_state() (which sends device_on explicitly) without contradicting the "set brightness without turning on" use case from #1532 / #1066.

Testing

Tested on a Tapo L900-5 (firmware current, KLAP v2 transport) controlled via Home Assistant's tplink integration:

  • Before fix: light.turn_on with brightness_pct=100 → device reports on, LEDs stay off
  • After fix: light.turn_on with brightness_pct=100 → device reports on, LEDs turn on
  • Stress test: 28 rapid-fire on/off commands (delays from 10s down to 0.1s) — all 28 physically executed correctly with the fix

Unit tests

New tests/smart/modules/test_light_device_on_payload.py parametrizes set_brightness, set_color_temp, and set_hsv over is_on=[True, False] and locks in:

Updated existing test_light_effect_brightness cases to force dev._info["device_on"] = True so the asserted payload is deterministic across fixtures (some have device_on: false baked in).

Closes the regression risk for #1532 / #1066.

🤖 Generated with Claude Code

fluffyspace and others added 3 commits April 6, 2026 12:03
Some Tapo devices (confirmed on L900-5 LED strip) treat power state
and brightness/color as independent parameters. When set_device_info
is called with only brightness or color parameters (without device_on),
the device accepts the command, updates its internal state, and reports
success — but the LEDs do not physically turn on.

This causes a particularly bad failure mode: the device enters a
"zombie state" where it reports on/brightness=100 to all queries
while the LEDs remain off. The device stays in this state until
power cycled, and python-kasa has no way to detect the discrepancy
since every protocol response indicates success.

Adding device_on: True to set_brightness(), set_color_temp(), and
set_hsv() ensures the device always receives an explicit power state
with light property changes. This matches the behavior of set_state()
which already includes device_on, and is consistent with how the
official Tapo app sends commands.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update test_light_effect_brightness assertions in both
test_light_effect.py and test_light_strip_effect.py to match the
new set_brightness payload that includes device_on: True.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.24%. Comparing base (76d9f68) to head (f04f085).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1680      +/-   ##
==========================================
+ Coverage   93.22%   93.24%   +0.02%     
==========================================
  Files         157      157              
  Lines        9815     9822       +7     
  Branches     1003     1006       +3     
==========================================
+ Hits         9150     9159       +9     
  Misses        472      472              
+ Partials      193      191       -2     

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

Forcing device_on: True unconditionally in set_brightness, set_color_temp,
and set_hsv fixed the L900-5 zombie state but reintroduced the python-kasa#1532
regression: users who want to change brightness on an off device without
turning it on as a side-effect.

Now device_on: True is injected only when self._device.is_on is already
true. That still re-asserts the power state for devices in the zombie
state (they report on, so the flag is still sent and the LEDs physically
come up), while leaving a deliberately-off device alone.

Tests parametrize all three setters over is_on=[True, False] to lock in
both branches.
@fluffyspace
Copy link
Copy Markdown
Author

Updated in 85d8d38 to address the regression risk against #1532 / #1066.

Original approach forced device_on: True unconditionally, which would have brought back the "setting brightness turns on the light" behavior that those issues are explicitly asking to preserve.

New approach: device_on: True is injected only when self._device.is_on is already true. That still re-asserts power state for the L900-5 zombie case (device reports on but LEDs are off → flag is still sent → LEDs come up) while leaving a deliberately-off device alone.

PR description updated with the full matrix; new parametrized tests in tests/smart/modules/test_light_device_on_payload.py lock in both branches.

Fixes mypy: `dev` is typed as `Device` (the abstract base) which has
no `_info` attribute. Patching the public `is_on` property has the
same effect on the brightness module's logic and is type-clean.
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.

2 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.