diff --git a/homeassistant/components/smartthings/light.py b/homeassistant/components/smartthings/light.py index 54e8ad18a7c..aa3a8d35859 100644 --- a/homeassistant/components/smartthings/light.py +++ b/homeassistant/components/smartthings/light.py @@ -3,12 +3,13 @@ from __future__ import annotations 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 ( ATTR_BRIGHTNESS, + ATTR_COLOR_MODE, ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, ATTR_TRANSITION, @@ -19,6 +20,7 @@ from homeassistant.components.light import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback +from homeassistant.helpers.restore_state import RestoreEntity from . import FullDevice, SmartThingsConfigEntry from .const import MAIN @@ -53,7 +55,7 @@ def convert_scale( return round(value * target_scale / value_scale, round_digits) -class SmartThingsLight(SmartThingsEntity, LightEntity): +class SmartThingsLight(SmartThingsEntity, LightEntity, RestoreEntity): """Define a SmartThings Light.""" _attr_name = None @@ -84,18 +86,28 @@ class SmartThingsLight(SmartThingsEntity, LightEntity): color_modes = set() if self.supports_capability(Capability.COLOR_TEMPERATURE): color_modes.add(ColorMode.COLOR_TEMP) + self._attr_color_mode = ColorMode.COLOR_TEMP if self.supports_capability(Capability.COLOR_CONTROL): color_modes.add(ColorMode.HS) + self._attr_color_mode = ColorMode.HS if not color_modes and self.supports_capability(Capability.SWITCH_LEVEL): color_modes.add(ColorMode.BRIGHTNESS) if not color_modes: 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 features = LightEntityFeature(0) if self.supports_capability(Capability.SWITCH_LEVEL): features |= LightEntityFeature.TRANSITION 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: """Turn the light on.""" tasks = [] @@ -195,17 +207,14 @@ class SmartThingsLight(SmartThingsEntity, LightEntity): argument=[level, duration], ) - @property - def color_mode(self) -> ColorMode: - """Return the color mode of the light.""" - if len(self._attr_supported_color_modes) == 1: - # The light supports only a single color mode - return list(self._attr_supported_color_modes)[0] - - # The light supports hs + color temp, determine which one it is - if self._attr_hs_color and self._attr_hs_color[1]: - return ColorMode.HS - return ColorMode.COLOR_TEMP + def _update_handler(self, event: DeviceEvent) -> None: + """Handle device updates.""" + if event.capability in (Capability.COLOR_CONTROL, Capability.COLOR_TEMPERATURE): + self._attr_color_mode = { + Capability.COLOR_CONTROL: ColorMode.HS, + Capability.COLOR_TEMPERATURE: ColorMode.COLOR_TEMP, + }[cast(Capability, event.capability)] + super()._update_handler(event) @property def is_on(self) -> bool: diff --git a/tests/components/smartthings/test_light.py b/tests/components/smartthings/test_light.py index 8d47e90c9f5..56eadde748b 100644 --- a/tests/components/smartthings/test_light.py +++ b/tests/components/smartthings/test_light.py @@ -12,7 +12,12 @@ from homeassistant.components.light import ( ATTR_COLOR_MODE, ATTR_COLOR_TEMP_KELVIN, ATTR_HS_COLOR, + ATTR_MAX_COLOR_TEMP_KELVIN, + ATTR_MIN_COLOR_TEMP_KELVIN, + ATTR_RGB_COLOR, + ATTR_SUPPORTED_COLOR_MODES, ATTR_TRANSITION, + ATTR_XY_COLOR, DOMAIN as LIGHT_DOMAIN, ColorMode, ) @@ -25,7 +30,7 @@ from homeassistant.const import ( STATE_ON, Platform, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, State from homeassistant.helpers import entity_registry as er from . import ( @@ -35,7 +40,7 @@ from . import ( trigger_update, ) -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, mock_restore_cache_with_extra_data async def test_all_entities( @@ -228,6 +233,15 @@ async def test_updating_brightness( set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on") 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 await trigger_update( @@ -252,8 +266,17 @@ async def test_updating_hs( set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on") 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] == ( - 218.906, + 144.0, 60, ) @@ -280,9 +303,17 @@ async def test_updating_color_temp( ) -> None: """Test color temperature update.""" 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 trigger_update( + hass, + devices, + "cb958955-b015-498c-9e62-fc0c51abd054", + Capability.COLOR_TEMPERATURE, + Attribute.COLOR_TEMPERATURE, + 3000, + ) + assert ( hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE] 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] == 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 + )