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
17 changes: 0 additions & 17 deletions homeassistant/components/light/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -977,20 +977,3 @@ def supported_color_modes(self) -> set[ColorMode] | set[str] | None:
def supported_features(self) -> int:
"""Flag supported features."""
return self._attr_supported_features


def legacy_supported_features(
supported_features: int, supported_color_modes: list[str] | None
) -> int:
"""Calculate supported features with backwards compatibility."""
# Backwards compatibility for supported_color_modes added in 2021.4
if supported_color_modes is None:
return supported_features
if any(mode in supported_color_modes for mode in COLOR_MODES_COLOR):
supported_features |= SUPPORT_COLOR
if any(mode in supported_color_modes for mode in COLOR_MODES_BRIGHTNESS):
supported_features |= SUPPORT_BRIGHTNESS
if ColorMode.COLOR_TEMP in supported_color_modes:
supported_features |= SUPPORT_COLOR_TEMP

return supported_features
67 changes: 46 additions & 21 deletions homeassistant/components/mqtt/light/schema_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@
ENTITY_ID_FORMAT,
FLASH_LONG,
FLASH_SHORT,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
VALID_COLOR_MODES,
ColorMode,
LightEntity,
LightEntityFeature,
legacy_supported_features,
brightness_supported,
color_supported,
filter_supported_color_modes,
valid_supported_color_modes,
)
from homeassistant.const import (
Expand Down Expand Up @@ -198,6 +197,7 @@ def __init__(self, hass, config, config_entry, discovery_data):
self._color_mode = None
self._color_temp = None
self._effect = None
self._fixed_color_mode = None
self._flash_times = None
self._hs = None
self._rgb = None
Expand Down Expand Up @@ -230,13 +230,20 @@ def _setup_from_config(self, config):
)
self._supported_features |= config[CONF_EFFECT] and LightEntityFeature.EFFECT
if not self._config[CONF_COLOR_MODE]:
self._supported_features |= config[CONF_BRIGHTNESS] and SUPPORT_BRIGHTNESS
self._supported_features |= config[CONF_COLOR_TEMP] and SUPPORT_COLOR_TEMP
self._supported_features |= config[CONF_HS] and SUPPORT_COLOR
self._supported_features |= config[CONF_RGB] and (
SUPPORT_COLOR | SUPPORT_BRIGHTNESS
)
self._supported_features |= config[CONF_XY] and SUPPORT_COLOR
color_modes = {ColorMode.ONOFF}
if config[CONF_BRIGHTNESS]:
color_modes.add(ColorMode.BRIGHTNESS)
if config[CONF_COLOR_TEMP]:
color_modes.add(ColorMode.COLOR_TEMP)
if config[CONF_HS] or config[CONF_RGB] or config[CONF_XY]:
color_modes.add(ColorMode.HS)
self._supported_color_modes = filter_supported_color_modes(color_modes)
if len(self._supported_color_modes) == 1:
self._fixed_color_mode = next(iter(self._supported_color_modes))
else:
self._supported_color_modes = self._config[CONF_SUPPORTED_COLOR_MODES]
if len(self._supported_color_modes) == 1:
self._color_mode = next(iter(self._supported_color_modes))

def _update_color(self, values):
if not self._config[CONF_COLOR_MODE]:
Expand Down Expand Up @@ -332,7 +339,12 @@ def state_received(msg):
elif values["state"] is None:
self._state = None

if self._supported_features and SUPPORT_COLOR and "color" in values:
if (
not self._config[CONF_COLOR_MODE]
and color_supported(self._supported_color_modes)
and "color" in values
):
# Deprecated color handling
if values["color"] is None:
self._hs = None
else:
Expand All @@ -341,7 +353,7 @@ def state_received(msg):
if self._config[CONF_COLOR_MODE] and "color_mode" in values:
self._update_color(values)

if self._supported_features and SUPPORT_BRIGHTNESS:
if brightness_supported(self._supported_color_modes):
try:
self._brightness = int(
values["brightness"]
Expand All @@ -354,10 +366,10 @@ def state_received(msg):
_LOGGER.warning("Invalid brightness value received")

if (
self._supported_features
and SUPPORT_COLOR_TEMP
ColorMode.COLOR_TEMP in self._supported_color_modes
and not self._config[CONF_COLOR_MODE]
):
# Deprecated color handling
try:
if values["color_temp"] is None:
self._color_temp = None
Expand Down Expand Up @@ -474,19 +486,25 @@ def assumed_state(self):
@property
def color_mode(self):
"""Return current color mode."""
return self._color_mode
if self._config[CONF_COLOR_MODE]:
return self._color_mode
if self._fixed_color_mode:
# Legacy light with support for a single color mode
return self._fixed_color_mode
# Legacy light with support for ct + hs, prioritize hs
if self._hs is not None:
return ColorMode.HS
return ColorMode.COLOR_TEMP

@property
def supported_color_modes(self):
"""Flag supported color modes."""
return self._config.get(CONF_SUPPORTED_COLOR_MODES)
return self._supported_color_modes

@property
def supported_features(self):
"""Flag supported features."""
return legacy_supported_features(
self._supported_features, self._config.get(CONF_SUPPORTED_COLOR_MODES)
)
return self._supported_features

def _set_flash_and_transition(self, message, **kwargs):
if ATTR_TRANSITION in kwargs:
Expand All @@ -510,7 +528,10 @@ def _scale_rgbxx(self, rgbxx, kwargs):
return tuple(round(i / 255 * brightness) for i in rgbxx)

def _supports_color_mode(self, color_mode):
return self.supported_color_modes and color_mode in self.supported_color_modes
"""Return True if the light natively supports a color mode."""
return (
self._config[CONF_COLOR_MODE] and color_mode in self.supported_color_modes
)

async def async_turn_on(self, **kwargs): # noqa: C901
"""Turn the device on.
Expand All @@ -524,6 +545,7 @@ async def async_turn_on(self, **kwargs): # noqa: C901
if ATTR_HS_COLOR in kwargs and (
self._config[CONF_HS] or self._config[CONF_RGB] or self._config[CONF_XY]
):
# Legacy color handling
hs_color = kwargs[ATTR_HS_COLOR]
message["color"] = {}
if self._config[CONF_RGB]:
Expand All @@ -548,6 +570,7 @@ async def async_turn_on(self, **kwargs): # noqa: C901
message["color"]["s"] = hs_color[1]

if self._optimistic:
self._color_temp = None
self._hs = kwargs[ATTR_HS_COLOR]
should_update = True

Expand Down Expand Up @@ -617,7 +640,9 @@ async def async_turn_on(self, **kwargs): # noqa: C901
message["color_temp"] = int(kwargs[ATTR_COLOR_TEMP])

if self._optimistic:
self._color_mode = ColorMode.COLOR_TEMP
self._color_temp = kwargs[ATTR_COLOR_TEMP]
self._hs = None
should_update = True

if ATTR_EFFECT in kwargs:
Expand Down
88 changes: 54 additions & 34 deletions homeassistant/components/mqtt/light/schema_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
ATTR_HS_COLOR,
ATTR_TRANSITION,
ENTITY_ID_FORMAT,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
ColorMode,
LightEntity,
LightEntityFeature,
filter_supported_color_modes,
)
from homeassistant.const import (
CONF_NAME,
Expand Down Expand Up @@ -129,6 +128,7 @@ def __init__(self, hass, config, config_entry, discovery_data):

# features
self._brightness = None
self._fixed_color_mode = None
self._color_temp = None
self._hs = None
self._effect = None
Expand Down Expand Up @@ -166,6 +166,21 @@ def _setup_from_config(self, config):
or self._templates[CONF_STATE_TEMPLATE] is None
)

color_modes = {ColorMode.ONOFF}
if self._templates[CONF_BRIGHTNESS_TEMPLATE] is not None:
color_modes.add(ColorMode.BRIGHTNESS)
if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None:
color_modes.add(ColorMode.COLOR_TEMP)
if (
self._templates[CONF_RED_TEMPLATE] is not None
and self._templates[CONF_GREEN_TEMPLATE] is not None
and self._templates[CONF_BLUE_TEMPLATE] is not None
):
color_modes.add(ColorMode.HS)
self._supported_color_modes = filter_supported_color_modes(color_modes)
if len(self._supported_color_modes) == 1:
self._fixed_color_mode = next(iter(self._supported_color_modes))

def _prepare_subscribe_topics(self):
"""(Re)Subscribe to topics."""
for tpl in self._templates.values():
Expand Down Expand Up @@ -200,11 +215,10 @@ def state_received(msg):

if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None:
try:
self._color_temp = int(
self._templates[
CONF_COLOR_TEMP_TEMPLATE
].async_render_with_possible_json_value(msg.payload)
)
color_temp = self._templates[
CONF_COLOR_TEMP_TEMPLATE
].async_render_with_possible_json_value(msg.payload)
self._color_temp = int(color_temp) if color_temp != "None" else None
except ValueError:
_LOGGER.warning("Invalid color temperature value received")

Expand All @@ -214,22 +228,21 @@ def state_received(msg):
and self._templates[CONF_BLUE_TEMPLATE] is not None
):
try:
red = int(
self._templates[
CONF_RED_TEMPLATE
].async_render_with_possible_json_value(msg.payload)
)
green = int(
self._templates[
CONF_GREEN_TEMPLATE
].async_render_with_possible_json_value(msg.payload)
)
blue = int(
self._templates[
CONF_BLUE_TEMPLATE
].async_render_with_possible_json_value(msg.payload)
)
self._hs = color_util.color_RGB_to_hs(red, green, blue)
red = self._templates[
CONF_RED_TEMPLATE
].async_render_with_possible_json_value(msg.payload)
green = self._templates[
CONF_GREEN_TEMPLATE
].async_render_with_possible_json_value(msg.payload)
blue = self._templates[
CONF_BLUE_TEMPLATE
].async_render_with_possible_json_value(msg.payload)
if red == "None" and green == "None" and blue == "None":
self._hs = None
else:
self._hs = color_util.color_RGB_to_hs(
int(red), int(green), int(blue)
)
except ValueError:
_LOGGER.warning("Invalid color value received")

Expand Down Expand Up @@ -340,6 +353,7 @@ async def async_turn_on(self, **kwargs):

if self._optimistic:
self._color_temp = kwargs[ATTR_COLOR_TEMP]
self._hs = None

if ATTR_HS_COLOR in kwargs:
hs_color = kwargs[ATTR_HS_COLOR]
Expand All @@ -363,6 +377,7 @@ async def async_turn_on(self, **kwargs):
values["sat"] = hs_color[1]

if self._optimistic:
self._color_temp = None
self._hs = kwargs[ATTR_HS_COLOR]

if ATTR_EFFECT in kwargs:
Expand Down Expand Up @@ -415,21 +430,26 @@ async def async_turn_off(self, **kwargs):
if self._optimistic:
self.async_write_ha_state()

@property
def color_mode(self):
"""Return current color mode."""
if self._fixed_color_mode:
return self._fixed_color_mode
# Support for ct + hs, prioritize hs
if self._hs is not None:
return ColorMode.HS
return ColorMode.COLOR_TEMP

@property
def supported_color_modes(self):
"""Flag supported color modes."""
return self._supported_color_modes

@property
def supported_features(self):
"""Flag supported features."""
features = LightEntityFeature.FLASH | LightEntityFeature.TRANSITION
if self._templates[CONF_BRIGHTNESS_TEMPLATE] is not None:
features = features | SUPPORT_BRIGHTNESS
if (
self._templates[CONF_RED_TEMPLATE] is not None
and self._templates[CONF_GREEN_TEMPLATE] is not None
and self._templates[CONF_BLUE_TEMPLATE] is not None
):
features = features | SUPPORT_COLOR | SUPPORT_BRIGHTNESS
if self._config.get(CONF_EFFECT_LIST) is not None:
features = features | LightEntityFeature.EFFECT
if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None:
features = features | SUPPORT_COLOR_TEMP

return features
Loading