fix(light): kein Farbtemperaturmodus ohne echten Bereich

Das Montana-Black-Shield (neues Ziel ledshield_rgb in der Firmware) steuert
analoge RGB-Streifen und hat keinen eigenen Weisskanal. Sein Board meldet
deshalb ct_min == ct_max (nl_board.c: 4000/4000) - die Konvention der Firmware
fuer "weiss entsteht als RGB-Mischung".

supported_color_modes bot COLOR_TEMP bisher IMMER an. Beim Shield bekaeme Home
Assistant damit einen Farbtemperaturregler, dessen Grenzen zusammenfallen.
Jetzt entscheidet der gemeldete Bereich: nur wenn oben > unten, gibt es
COLOR_TEMP. Dieselbe Regel benutzt ninlight_core schon (device_state.dart,
hasColorTemp), dort faellt der Regler beim Shield von selbst weg.

Die Menge darf nie leer werden - HA verlangt mindestens einen Modus -, deshalb
faellt sie im Grenzfall auf BRIGHTNESS zurueck: ein Geraet ohne Farbe und ohne
Bereich kann immer noch dimmen.
This commit is contained in:
Michael Sauer
2026-09-26 18:13:03 +02:00
parent 83618b19fd
commit b482f84131
2 changed files with 57 additions and 4 deletions
+22 -3
View File
@@ -68,11 +68,30 @@ class NinlightLight(NinlightEntity, LightEntity):
def brightness(self) -> int: def brightness(self) -> int:
return lamp_to_ha_brightness(int(self._state["brightness"])) return lamp_to_ha_brightness(int(self._state["brightness"]))
def _has_ct_range(self) -> bool:
"""Ob das Geraet einen echten Farbtemperaturbereich hat.
Das Montana-Black-Shield steuert analoge RGB-Streifen ohne eigenen
Weisskanal und meldet deshalb ct_min == ct_max (nl_board.c,
"ledshield_rgb": 4000/4000) - die Konvention der Firmware fuer "weiss
entsteht als RGB-Mischung". Ein Bereich ohne Spielraum ist keiner;
wuerde er hier als COLOR_TEMP durchgehen, bekaeme Home Assistant einen
Regler mit min == max.
"""
unten, oben = self._kelvin_range()
return oben > unten
@property @property
def supported_color_modes(self) -> set[ColorMode]: def supported_color_modes(self) -> set[ColorMode]:
if self._state["capabilities"].get("color"): farbe = bool(self._state["capabilities"].get("color"))
return {ColorMode.RGB, ColorMode.COLOR_TEMP} modi: set[ColorMode] = set()
return {ColorMode.COLOR_TEMP} if farbe:
modi.add(ColorMode.RGB)
if self._has_ct_range():
modi.add(ColorMode.COLOR_TEMP)
# Leer darf die Menge nie sein - HA verlangt mindestens einen Modus.
# Ein Geraet ohne Farbe und ohne Bereich kann immer noch dimmen.
return modi or {ColorMode.BRIGHTNESS}
@property @property
def color_mode(self) -> ColorMode: def color_mode(self) -> ColorMode:
+35 -1
View File
@@ -44,7 +44,16 @@ from custom_components.ninlight.light import (
transition_ms, transition_ms,
) )
from tests.helpers import entity_id_for from tests.helpers import entity_id_for
from tests_unit.samples import HOST_CW, INFO_CW, MAC_CW, STATE_CW, STATE_RGB, TOKEN from tests_unit.samples import (
HOST_CW,
HOST_RGB,
INFO_CW,
MAC_CW,
MAC_RGB,
STATE_CW,
STATE_RGB,
TOKEN,
)
def test_brightness_never_zero_and_round_trips() -> None: def test_brightness_never_zero_and_round_trips() -> None:
@@ -99,6 +108,31 @@ async def test_white_lamp_offers_only_color_temp(
assert state.attributes[ATTR_MAX_COLOR_TEMP_KELVIN] == 5000 assert state.attributes[ATTR_MAX_COLOR_TEMP_KELVIN] == 5000
async def test_shield_without_ct_range_offers_only_rgb(
hass: HomeAssistant,
mock_client: MagicMock,
lamp_state: dict[str, Any],
) -> None:
"""Das Montana-Black-Shield steuert analoge RGB-Streifen und hat keinen
eigenen Weisskanal: sein Board meldet ct_min == ct_max (nl_board.c,
"ledshield_rgb", 4000/4000) - die Konvention fuer "weiss entsteht als
RGB-Mischung". Ein Bereich ohne Spielraum ist kein Farbtemperaturbereich;
HA bekaeme sonst einen Regler mit min == max.
"""
lamp_state.clear()
lamp_state.update(deepcopy(STATE_RGB))
lamp_state["capabilities"] = {"color": True, "color_temp": [4000, 4000]}
entry = MockConfigEntry(
domain=DOMAIN, unique_id=MAC_RGB, data={CONF_HOST: HOST_RGB, CONF_TOKEN: TOKEN}
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get(entity_id_for(hass, "light", "light"))
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.RGB]
async def test_turn_on_sends_everything_in_one_request( async def test_turn_on_sends_everything_in_one_request(
hass: HomeAssistant, init_integration, mock_client: MagicMock hass: HomeAssistant, init_integration, mock_client: MagicMock
) -> None: ) -> None: