Skip to content

fix(light): reserve zero brightness as off#5218

Open
Zuz666 wants to merge 4 commits into
make-all:mainfrom
Zuz666:fix/brightness-zero-range-off
Open

fix(light): reserve zero brightness as off#5218
Zuz666 wants to merge 4 commits into
make-all:mainfrom
Zuz666:fix/brightness-zero-range-off

Conversation

@Zuz666

@Zuz666 Zuz666 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Problem

Brightness-only light entities use the brightness DP as their on/off state. For zero-based ranges, DP 0 is the off value. Very low non-zero Home Assistant brightness can otherwise be converted or rounded to DP 0, so light.turn_on(brightness=1) can turn the light off instead of setting the first visible level (related analysis: #5197).

Example

Kogan KAWHTNOSLPA kogan_noise_machine.yaml defines the light entity as DP 104 brightness with range 0..255 and no switch DP on the light entity. In this configured entity shape, tuya-local uses DP 104 for brightness and infers the on/off state from the same value.

This is a code-path example derived from the config only. I found neither a test on actual Kogan hardware nor a device-owner report confirming the physical behavior of DP 104.

Change

For brightness-only lights with zero-based ranges, non-zero HA brightness is converted over the first-on value to max range, keeping DP 0 reserved for off. This also accounts for configured step, so low brightness does not round back to 0.

Regression tests were added for low-brightness writes and for reading the first on value.

Impact / risk

A config scan found 36 light entities across 29 device configs matching the condition changed here: no switch DP on the light entity and a brightness range starting at 0.

For this entity shape, tuya-local already treats DP 0 as off: async_turn_off() writes 0, and is_on requires brightness greater than 0. Device-specific evidence for that convention exists for other matching devices (#2589, #2767, #1173), but it does not establish the behavior of Kogan DP 104. Some devices with the same config shape may still use DP 0 differently, so side effects are possible.

Test plan

The integration code path was exercised with Home Assistant 2026.5.4 using a local fake Tuya device matching Kogan KAWHTNOSLPA / kogan_noise_machine.yaml (fake_kogan_noise_machine.py), where DP 104 controlled the simulated light entity state, and with the regression tests.

The fake-device result validates the integration code path only; it is not hardware validation for the Kogan device.

@github-project-automation github-project-automation Bot moved this to 👀 In review in Tuya Local Jun 2, 2026
@make-all make-all moved this from 👀 In review to 🏗 Stalled in Tuya Local Jun 2, 2026
@Zuz666
Zuz666 marked this pull request as ready for review June 2, 2026 15:31
@make-all

Copy link
Copy Markdown
Owner

I'm still not sure what you are actually trying to fix here, as you seem to be testing with a mock device based on the above explanation, and there have never been reports from users of actual devices.

@Zuz666
Zuz666 force-pushed the fix/brightness-zero-range-off branch from 12ea6e1 to 691bf7b Compare July 20, 2026 22:53
@Zuz666

Zuz666 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

You're right that my original Kogan test used a mock, not real hardware. I should have separated that from hardware evidence more clearly, so I investigated the affected configurations.

The issue is a mismatch between two existing contracts:

  • Without a switch DP, tuya-local already treats brightness DP 0 as off: async_turn_off() writes 0, and is_on requires brightness greater than 0.
  • Home Assistant's brightness_to_value() says: “Do not include 0 if the light is off for value 0.”

We currently pass the full zero-based range to that helper. With Kogan's configured 0..255 range:

brightness_to_value((0, 255), 1) == 0.003921...

tuya-local's integer conversion then writes:

int(round(0.003921...)) == 0

Therefore turn_on(brightness=1) can write the same DP value as turn_off. This PR excludes the reserved off value from non-zero brightness conversion.

There is real-device evidence for the underlying 0 = off, positive = on contract. In #2589, the PlantsIO Ivy owner reported: “A value of 0 is off, with 1-100 being brightness values” and “The datapoint is correctly updated and the screen turns off.” HA nevertheless displayed brightness 3 (about 1%) and reported the physically-off display as on. c6c0a76a fixed that read-side zero-boundary collision by not scaling the off value. This PR fixes the symmetric write path. The reverse service call was not captured in #2589; its result follows from the conversion above, not from another hardware observation.

The Rohnson R-9530 owner also stated “0 = turned off, 1 = low, 2 = high,” and the Petrust TK-WF002 data model describes brightness 0 as turning the indicator off.

Across 36 matching entities in 29 configs, I found direct evidence for six entities in five device families and no confirmed counterexample where DP 0 is a visible minimum. Kogan itself remains unverified, so I have updated the PR description to make clear that its fake-device test only reproduces the code path and is not hardware validation.

The PR does not introduce 0 = off; it makes conversion consistent with the existing is_on and turn_off behavior. Would you consider this evidence sufficient for the shared behavior, or would you prefer it to be opt-in per config?

Thank you for maintaining this integration, and for your patience while I investigated this properly.

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

Projects

Status: 🏗 Stalled

Development

Successfully merging this pull request may close these issues.

2 participants