Use last event as color mode in SmartThings (#139473)

* Use last event as color mode in SmartThings

* Use last event as color mode in SmartThings

* Fix
pull/125870/merge
Joost Lekkerkerker 2025-02-28 20:39:49 +01:00 committed by GitHub
parent 39bc37d225
commit 455363871f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 135 additions and 18 deletions

View File

@ -3,12 +3,13 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from typing import Any from typing import Any, cast
from pysmartthings import Attribute, Capability, Command, SmartThings from pysmartthings import Attribute, Capability, Command, DeviceEvent, SmartThings
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_COLOR_TEMP_KELVIN, ATTR_COLOR_TEMP_KELVIN,
ATTR_HS_COLOR, ATTR_HS_COLOR,
ATTR_TRANSITION, ATTR_TRANSITION,
@ -19,6 +20,7 @@ from homeassistant.components.light import (
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from . import FullDevice, SmartThingsConfigEntry from . import FullDevice, SmartThingsConfigEntry
from .const import MAIN from .const import MAIN
@ -53,7 +55,7 @@ def convert_scale(
return round(value * target_scale / value_scale, round_digits) return round(value * target_scale / value_scale, round_digits)
class SmartThingsLight(SmartThingsEntity, LightEntity): class SmartThingsLight(SmartThingsEntity, LightEntity, RestoreEntity):
"""Define a SmartThings Light.""" """Define a SmartThings Light."""
_attr_name = None _attr_name = None
@ -84,18 +86,28 @@ class SmartThingsLight(SmartThingsEntity, LightEntity):
color_modes = set() color_modes = set()
if self.supports_capability(Capability.COLOR_TEMPERATURE): if self.supports_capability(Capability.COLOR_TEMPERATURE):
color_modes.add(ColorMode.COLOR_TEMP) color_modes.add(ColorMode.COLOR_TEMP)
self._attr_color_mode = ColorMode.COLOR_TEMP
if self.supports_capability(Capability.COLOR_CONTROL): if self.supports_capability(Capability.COLOR_CONTROL):
color_modes.add(ColorMode.HS) color_modes.add(ColorMode.HS)
self._attr_color_mode = ColorMode.HS
if not color_modes and self.supports_capability(Capability.SWITCH_LEVEL): if not color_modes and self.supports_capability(Capability.SWITCH_LEVEL):
color_modes.add(ColorMode.BRIGHTNESS) color_modes.add(ColorMode.BRIGHTNESS)
if not color_modes: if not color_modes:
color_modes.add(ColorMode.ONOFF) color_modes.add(ColorMode.ONOFF)
if len(color_modes) == 1:
self._attr_color_mode = list(color_modes)[0]
self._attr_supported_color_modes = color_modes self._attr_supported_color_modes = color_modes
features = LightEntityFeature(0) features = LightEntityFeature(0)
if self.supports_capability(Capability.SWITCH_LEVEL): if self.supports_capability(Capability.SWITCH_LEVEL):
features |= LightEntityFeature.TRANSITION features |= LightEntityFeature.TRANSITION
self._attr_supported_features = features self._attr_supported_features = features
async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
await super().async_added_to_hass()
if (last_state := await self.async_get_last_extra_data()) is not None:
self._attr_color_mode = last_state.as_dict()[ATTR_COLOR_MODE]
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on.""" """Turn the light on."""
tasks = [] tasks = []
@ -195,17 +207,14 @@ class SmartThingsLight(SmartThingsEntity, LightEntity):
argument=[level, duration], argument=[level, duration],
) )
@property def _update_handler(self, event: DeviceEvent) -> None:
def color_mode(self) -> ColorMode: """Handle device updates."""
"""Return the color mode of the light.""" if event.capability in (Capability.COLOR_CONTROL, Capability.COLOR_TEMPERATURE):
if len(self._attr_supported_color_modes) == 1: self._attr_color_mode = {
# The light supports only a single color mode Capability.COLOR_CONTROL: ColorMode.HS,
return list(self._attr_supported_color_modes)[0] Capability.COLOR_TEMPERATURE: ColorMode.COLOR_TEMP,
}[cast(Capability, event.capability)]
# The light supports hs + color temp, determine which one it is super()._update_handler(event)
if self._attr_hs_color and self._attr_hs_color[1]:
return ColorMode.HS
return ColorMode.COLOR_TEMP
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:

View File

@ -12,7 +12,12 @@ from homeassistant.components.light import (
ATTR_COLOR_MODE, ATTR_COLOR_MODE,
ATTR_COLOR_TEMP_KELVIN, ATTR_COLOR_TEMP_KELVIN,
ATTR_HS_COLOR, ATTR_HS_COLOR,
ATTR_MAX_COLOR_TEMP_KELVIN,
ATTR_MIN_COLOR_TEMP_KELVIN,
ATTR_RGB_COLOR,
ATTR_SUPPORTED_COLOR_MODES,
ATTR_TRANSITION, ATTR_TRANSITION,
ATTR_XY_COLOR,
DOMAIN as LIGHT_DOMAIN, DOMAIN as LIGHT_DOMAIN,
ColorMode, ColorMode,
) )
@ -25,7 +30,7 @@ from homeassistant.const import (
STATE_ON, STATE_ON,
Platform, Platform,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from . import ( from . import (
@ -35,7 +40,7 @@ from . import (
trigger_update, trigger_update,
) )
from tests.common import MockConfigEntry from tests.common import MockConfigEntry, mock_restore_cache_with_extra_data
async def test_all_entities( async def test_all_entities(
@ -228,6 +233,15 @@ async def test_updating_brightness(
set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on") set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)
await trigger_update(
hass,
devices,
"cb958955-b015-498c-9e62-fc0c51abd054",
Capability.COLOR_CONTROL,
Attribute.HUE,
40,
)
assert hass.states.get("light.standing_light").attributes[ATTR_BRIGHTNESS] == 178 assert hass.states.get("light.standing_light").attributes[ATTR_BRIGHTNESS] == 178
await trigger_update( await trigger_update(
@ -252,8 +266,17 @@ async def test_updating_hs(
set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on") set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)
await trigger_update(
hass,
devices,
"cb958955-b015-498c-9e62-fc0c51abd054",
Capability.COLOR_CONTROL,
Attribute.HUE,
40,
)
assert hass.states.get("light.standing_light").attributes[ATTR_HS_COLOR] == ( assert hass.states.get("light.standing_light").attributes[ATTR_HS_COLOR] == (
218.906, 144.0,
60, 60,
) )
@ -280,9 +303,17 @@ async def test_updating_color_temp(
) -> None: ) -> None:
"""Test color temperature update.""" """Test color temperature update."""
set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on") set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")
set_attribute_value(devices, Capability.COLOR_CONTROL, Attribute.SATURATION, 0)
await setup_integration(hass, mock_config_entry) await setup_integration(hass, mock_config_entry)
await trigger_update(
hass,
devices,
"cb958955-b015-498c-9e62-fc0c51abd054",
Capability.COLOR_TEMPERATURE,
Attribute.COLOR_TEMPERATURE,
3000,
)
assert ( assert (
hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE] hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
is ColorMode.COLOR_TEMP is ColorMode.COLOR_TEMP
@ -305,3 +336,80 @@ async def test_updating_color_temp(
hass.states.get("light.standing_light").attributes[ATTR_COLOR_TEMP_KELVIN] hass.states.get("light.standing_light").attributes[ATTR_COLOR_TEMP_KELVIN]
== 2000 == 2000
) )
@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_color_modes(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test color mode changes."""
set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")
set_attribute_value(devices, Capability.COLOR_CONTROL, Attribute.SATURATION, 50)
await setup_integration(hass, mock_config_entry)
assert (
hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
is ColorMode.HS
)
await trigger_update(
hass,
devices,
"cb958955-b015-498c-9e62-fc0c51abd054",
Capability.COLOR_TEMPERATURE,
Attribute.COLOR_TEMPERATURE,
2000,
)
assert (
hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
is ColorMode.COLOR_TEMP
)
await trigger_update(
hass,
devices,
"cb958955-b015-498c-9e62-fc0c51abd054",
Capability.COLOR_CONTROL,
Attribute.HUE,
20,
)
assert (
hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
is ColorMode.HS
)
@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_color_mode_after_startup(
hass: HomeAssistant,
devices: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test color mode after startup."""
set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")
RESTORE_DATA = {
ATTR_BRIGHTNESS: 178,
ATTR_COLOR_MODE: ColorMode.COLOR_TEMP,
ATTR_COLOR_TEMP_KELVIN: 3000,
ATTR_HS_COLOR: (144.0, 60),
ATTR_MAX_COLOR_TEMP_KELVIN: 9000,
ATTR_MIN_COLOR_TEMP_KELVIN: 2000,
ATTR_RGB_COLOR: (255, 128, 0),
ATTR_SUPPORTED_COLOR_MODES: [ColorMode.COLOR_TEMP, ColorMode.HS],
ATTR_XY_COLOR: (0.61, 0.35),
}
mock_restore_cache_with_extra_data(
hass, ((State("light.standing_light", STATE_ON), RESTORE_DATA),)
)
await setup_integration(hass, mock_config_entry)
assert (
hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
is ColorMode.COLOR_TEMP
)