Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 43 additions & 26 deletions homeassistant/components/smartthings/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
ATTR_COLOR_TEMP,
ATTR_HS_COLOR,
ATTR_TRANSITION,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
ColorMode,
LightEntity,
LightEntityFeature,
brightness_supported,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
Expand Down Expand Up @@ -73,44 +72,58 @@ def convert_scale(value, value_scale, target_scale, round_digits=4):
class SmartThingsLight(SmartThingsEntity, LightEntity):
"""Define a SmartThings Light."""

_attr_supported_color_modes: set[ColorMode]

def __init__(self, device):
"""Initialize a SmartThingsLight."""
super().__init__(device)
self._brightness = None
self._color_temp = None
self._hs_color = None
self._supported_features = self._determine_features()
self._attr_supported_color_modes = self._determine_color_modes()
self._attr_supported_features = self._determine_features()

def _determine_features(self):
def _determine_color_modes(self):
"""Get features supported by the device."""
features = 0
# Brightness and transition
if Capability.switch_level in self._device.capabilities:
features |= SUPPORT_BRIGHTNESS | LightEntityFeature.TRANSITION
color_modes = set()
# Color Temperature
if Capability.color_temperature in self._device.capabilities:
features |= SUPPORT_COLOR_TEMP
color_modes.add(ColorMode.COLOR_TEMP)
# Color
if Capability.color_control in self._device.capabilities:
features |= SUPPORT_COLOR
color_modes.add(ColorMode.HS)
# Brightness
if not color_modes and Capability.switch_level in self._device.capabilities:
color_modes.add(ColorMode.BRIGHTNESS)
if not color_modes:
color_modes.add(ColorMode.ONOFF)

return color_modes

def _determine_features(self):
"""Get features supported by the device."""
features = 0
# Transition
if Capability.switch_level in self._device.capabilities:
features |= LightEntityFeature.TRANSITION

return features

async def async_turn_on(self, **kwargs) -> None:
"""Turn the light on."""
tasks = []
# Color temperature
if self._supported_features & SUPPORT_COLOR_TEMP and ATTR_COLOR_TEMP in kwargs:
if ATTR_COLOR_TEMP in kwargs:
tasks.append(self.async_set_color_temp(kwargs[ATTR_COLOR_TEMP]))
# Color
if self._supported_features & SUPPORT_COLOR and ATTR_HS_COLOR in kwargs:
if ATTR_HS_COLOR in kwargs:
tasks.append(self.async_set_color(kwargs[ATTR_HS_COLOR]))
if tasks:
# Set temp/color first
await asyncio.gather(*tasks)

# Switch/brightness/transition
if self._supported_features & SUPPORT_BRIGHTNESS and ATTR_BRIGHTNESS in kwargs:
if ATTR_BRIGHTNESS in kwargs:
await self.async_set_level(
kwargs[ATTR_BRIGHTNESS], kwargs.get(ATTR_TRANSITION, 0)
)
Expand All @@ -124,10 +137,7 @@ async def async_turn_on(self, **kwargs) -> None:
async def async_turn_off(self, **kwargs) -> None:
"""Turn the light off."""
# Switch/transition
if (
self._supported_features & LightEntityFeature.TRANSITION
and ATTR_TRANSITION in kwargs
):
if ATTR_TRANSITION in kwargs:
await self.async_set_level(0, int(kwargs[ATTR_TRANSITION]))
else:
await self._device.switch_off(set_status=True)
Expand All @@ -139,17 +149,17 @@ async def async_turn_off(self, **kwargs) -> None:
async def async_update(self):
"""Update entity attributes when the device status has changed."""
# Brightness and transition
if self._supported_features & SUPPORT_BRIGHTNESS:
if brightness_supported(self._attr_supported_color_modes):
self._brightness = int(
convert_scale(self._device.status.level, 100, 255, 0)
)
# Color Temperature
if self._supported_features & SUPPORT_COLOR_TEMP:
if ColorMode.COLOR_TEMP in self._attr_supported_color_modes:
self._color_temp = color_util.color_temperature_kelvin_to_mired(
self._device.status.color_temperature
)
# Color
if self._supported_features & SUPPORT_COLOR:
if ColorMode.HS in self._attr_supported_color_modes:
self._hs_color = (
convert_scale(self._device.status.hue, 100, 360),
self._device.status.saturation,
Expand Down Expand Up @@ -178,6 +188,18 @@ async def async_set_level(self, brightness: int, transition: int):
duration = int(transition)
await self._device.set_level(level, duration, set_status=True)

@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._hs_color and self._hs_color[1]:
return ColorMode.HS
return ColorMode.COLOR_TEMP
Comment on lines +198 to +201
Copy link
Copy Markdown
Contributor Author

@emontnemery emontnemery Apr 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not find anything in the pysmartthings which helps determine which mode a light is in. This code may not be wanted, because setting the light to a hs-color with 0 saturation will make the light report itself being in CT mode.

@andrewsayre could you confirm what value the library reports in the status updates when setting the light to a color or a color temperature?


@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
Expand Down Expand Up @@ -213,8 +235,3 @@ def min_mireds(self):
# implemented within each device-type handler. This value is the
# highest kelvin found supported across 20+ handlers.
return 111 # 9000K

@property
def supported_features(self) -> int:
"""Flag supported features."""
return self._supported_features
33 changes: 13 additions & 20 deletions tests/components/smartthings/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP,
ATTR_HS_COLOR,
ATTR_SUPPORTED_COLOR_MODES,
ATTR_TRANSITION,
DOMAIN as LIGHT_DOMAIN,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
ColorMode,
LightEntityFeature,
)
from homeassistant.components.smartthings.const import DOMAIN, SIGNAL_SMARTTHINGS_UPDATE
Expand Down Expand Up @@ -66,7 +65,7 @@ def light_devices_fixture(device_factory):
Attribute.switch: "on",
Attribute.level: 100,
Attribute.hue: 76.0,
Attribute.saturation: 55.0,
Attribute.saturation: 0.0,
Attribute.color_temperature: 4500,
},
),
Expand All @@ -80,33 +79,27 @@ async def test_entity_state(hass, light_devices):
# Dimmer 1
state = hass.states.get("light.dimmer_1")
assert state.state == "on"
assert (
state.attributes[ATTR_SUPPORTED_FEATURES]
== SUPPORT_BRIGHTNESS | LightEntityFeature.TRANSITION
)
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.BRIGHTNESS]
assert state.attributes[ATTR_SUPPORTED_FEATURES] == LightEntityFeature.TRANSITION
assert isinstance(state.attributes[ATTR_BRIGHTNESS], int)
assert state.attributes[ATTR_BRIGHTNESS] == 255

# Color Dimmer 1
state = hass.states.get("light.color_dimmer_1")
assert state.state == "off"
assert (
state.attributes[ATTR_SUPPORTED_FEATURES]
== SUPPORT_BRIGHTNESS | LightEntityFeature.TRANSITION | SUPPORT_COLOR
)
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [ColorMode.HS]
assert state.attributes[ATTR_SUPPORTED_FEATURES] == LightEntityFeature.TRANSITION

# Color Dimmer 2
state = hass.states.get("light.color_dimmer_2")
assert state.state == "on"
assert (
state.attributes[ATTR_SUPPORTED_FEATURES]
== SUPPORT_BRIGHTNESS
| LightEntityFeature.TRANSITION
| SUPPORT_COLOR
| SUPPORT_COLOR_TEMP
)
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [
ColorMode.COLOR_TEMP,
ColorMode.HS,
]
assert state.attributes[ATTR_SUPPORTED_FEATURES] == LightEntityFeature.TRANSITION
assert state.attributes[ATTR_BRIGHTNESS] == 255
assert state.attributes[ATTR_HS_COLOR] == (273.6, 55.0)
assert ATTR_HS_COLOR not in state.attributes[ATTR_HS_COLOR]
assert isinstance(state.attributes[ATTR_COLOR_TEMP], int)
assert state.attributes[ATTR_COLOR_TEMP] == 222

Expand Down