diff --git a/custom_components/ninlight/light.py b/custom_components/ninlight/light.py index d63110a..f442eec 100644 --- a/custom_components/ninlight/light.py +++ b/custom_components/ninlight/light.py @@ -68,11 +68,30 @@ class NinlightLight(NinlightEntity, LightEntity): def brightness(self) -> int: 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 def supported_color_modes(self) -> set[ColorMode]: - if self._state["capabilities"].get("color"): - return {ColorMode.RGB, ColorMode.COLOR_TEMP} - return {ColorMode.COLOR_TEMP} + farbe = bool(self._state["capabilities"].get("color")) + modi: set[ColorMode] = set() + 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 def color_mode(self) -> ColorMode: diff --git a/tests/test_light.py b/tests/test_light.py index 1952474..3075533 100644 --- a/tests/test_light.py +++ b/tests/test_light.py @@ -44,7 +44,16 @@ from custom_components.ninlight.light import ( transition_ms, ) 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: @@ -99,6 +108,31 @@ async def test_white_lamp_offers_only_color_temp( 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( hass: HomeAssistant, init_integration, mock_client: MagicMock ) -> None: