Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

KNX: Support for HS-color lights #53538

Merged
merged 1 commit into from Jul 27, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions homeassistant/components/knx/light.py
Expand Up @@ -10,11 +10,13 @@
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP,
ATTR_HS_COLOR,
ATTR_RGB_COLOR,
ATTR_RGBW_COLOR,
ATTR_XY_COLOR,
COLOR_MODE_BRIGHTNESS,
COLOR_MODE_COLOR_TEMP,
COLOR_MODE_HS,
COLOR_MODE_ONOFF,
COLOR_MODE_RGB,
COLOR_MODE_RGBW,
Expand Down Expand Up @@ -158,6 +160,12 @@ def individual_color_addresses(color: str, feature: str) -> Any | None:
group_address_color_state=config.get(LightSchema.CONF_COLOR_STATE_ADDRESS),
group_address_rgbw=config.get(LightSchema.CONF_RGBW_ADDRESS),
group_address_rgbw_state=config.get(LightSchema.CONF_RGBW_STATE_ADDRESS),
group_address_hue=config.get(LightSchema.CONF_HUE_ADDRESS),
group_address_hue_state=config.get(LightSchema.CONF_HUE_STATE_ADDRESS),
group_address_saturation=config.get(LightSchema.CONF_SATURATION_ADDRESS),
group_address_saturation_state=config.get(
LightSchema.CONF_SATURATION_STATE_ADDRESS
),
group_address_xyy_color=config.get(LightSchema.CONF_XYY_ADDRESS),
group_address_xyy_color_state=config.get(LightSchema.CONF_XYY_STATE_ADDRESS),
group_address_tunable_white=group_address_tunable_white,
Expand Down Expand Up @@ -283,6 +291,13 @@ def rgbw_color(self) -> tuple[int, int, int, int] | None:
return (*rgb, white)
return None

@property
def hs_color(self) -> tuple[float, float] | None:
"""Return the hue and saturation color value [float, float]."""
# Hue is scaled 0..360 int encoded in 1 byte in KNX (-> only 256 possible values)
# Saturation is scaled 0..100 int
return self._device.current_hs_color

@property
def xy_color(self) -> tuple[float, float] | None:
"""Return the xy color value [float, float]."""
Expand Down Expand Up @@ -315,6 +330,8 @@ def color_mode(self) -> str | None:
"""Return the color mode of the light."""
if self._device.supports_xyy_color:
return COLOR_MODE_XY
if self._device.supports_hs_color:
return COLOR_MODE_HS
if self._device.supports_rgbw:
return COLOR_MODE_RGBW
if self._device.supports_color:
Expand All @@ -339,6 +356,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
mireds = kwargs.get(ATTR_COLOR_TEMP)
rgb = kwargs.get(ATTR_RGB_COLOR)
rgbw = kwargs.get(ATTR_RGBW_COLOR)
hs_color = kwargs.get(ATTR_HS_COLOR)
xy_color = kwargs.get(ATTR_XY_COLOR)

if (
Expand All @@ -347,6 +365,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
and mireds is None
and rgb is None
and rgbw is None
and hs_color is None
and xy_color is None
):
await self._device.set_on()
Expand Down Expand Up @@ -396,6 +415,12 @@ async def set_color(
)
return

if hs_color is not None:
# round so only one telegram will be sent if the other matches state
hue = round(hs_color[0])
sat = round(hs_color[1])
await self._device.set_hs_color((hue, sat))

if brightness is not None:
# brightness: 1..255; 0 brightness will call async_turn_off()
if self._device.brightness.writable:
Expand Down
55 changes: 45 additions & 10 deletions homeassistant/components/knx/schema.py
Expand Up @@ -496,8 +496,12 @@ class LightSchema(KNXPlatformSchema):
CONF_COLOR_TEMP_ADDRESS = "color_temperature_address"
CONF_COLOR_TEMP_STATE_ADDRESS = "color_temperature_state_address"
CONF_COLOR_TEMP_MODE = "color_temperature_mode"
CONF_HUE_ADDRESS = "hue_address"
CONF_HUE_STATE_ADDRESS = "hue_state_address"
CONF_RGBW_ADDRESS = "rgbw_address"
CONF_RGBW_STATE_ADDRESS = "rgbw_state_address"
CONF_SATURATION_ADDRESS = "saturation_address"
CONF_SATURATION_STATE_ADDRESS = "saturation_state_address"
CONF_XYY_ADDRESS = "xyy_address"
CONF_XYY_STATE_ADDRESS = "xyy_state_address"
CONF_MIN_KELVIN = "min_kelvin"
Expand All @@ -514,7 +518,18 @@ class LightSchema(KNXPlatformSchema):
CONF_BLUE = "blue"
CONF_WHITE = "white"

COLOR_SCHEMA = vol.Schema(
_hs_color_inclusion_msg = (
"'hue_address', 'saturation_address' and 'brightness_address'"
" are required for hs_color configuration"
)
HS_COLOR_SCHEMA = {
vol.Optional(CONF_HUE_ADDRESS): ga_list_validator,
vol.Optional(CONF_HUE_STATE_ADDRESS): ga_list_validator,
vol.Optional(CONF_SATURATION_ADDRESS): ga_list_validator,
vol.Optional(CONF_SATURATION_STATE_ADDRESS): ga_list_validator,
}

INDIVIDUAL_COLOR_SCHEMA = vol.Schema(
{
vol.Optional(KNX_ADDRESS): ga_list_validator,
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
Expand All @@ -536,18 +551,18 @@ class LightSchema(KNXPlatformSchema):
CONF_RED,
"individual_colors",
msg="'red', 'green' and 'blue' are required for individual colors configuration",
): COLOR_SCHEMA,
): INDIVIDUAL_COLOR_SCHEMA,
vol.Inclusive(
CONF_GREEN,
"individual_colors",
msg="'red', 'green' and 'blue' are required for individual colors configuration",
): COLOR_SCHEMA,
): INDIVIDUAL_COLOR_SCHEMA,
vol.Inclusive(
CONF_BLUE,
"individual_colors",
msg="'red', 'green' and 'blue' are required for individual colors configuration",
): COLOR_SCHEMA,
vol.Optional(CONF_WHITE): COLOR_SCHEMA,
): INDIVIDUAL_COLOR_SCHEMA,
vol.Optional(CONF_WHITE): INDIVIDUAL_COLOR_SCHEMA,
},
vol.Exclusive(CONF_COLOR_ADDRESS, "color"): ga_list_validator,
vol.Optional(CONF_COLOR_STATE_ADDRESS): ga_list_validator,
Expand All @@ -556,6 +571,7 @@ class LightSchema(KNXPlatformSchema):
vol.Optional(
CONF_COLOR_TEMP_MODE, default=DEFAULT_COLOR_TEMP_MODE
): vol.All(vol.Upper, cv.enum(ColorTempModes)),
**HS_COLOR_SCHEMA,
vol.Exclusive(CONF_RGBW_ADDRESS, "color"): ga_list_validator,
vol.Optional(CONF_RGBW_STATE_ADDRESS): ga_list_validator,
vol.Exclusive(CONF_XYY_ADDRESS, "color"): ga_list_validator,
Expand All @@ -569,20 +585,39 @@ class LightSchema(KNXPlatformSchema):
}
),
vol.Any(
# either global "address" or "individual_colors" is required
vol.Schema(
{vol.Required(KNX_ADDRESS): object},
extra=vol.ALLOW_EXTRA,
),
vol.Schema( # brightness addresses are required in INDIVIDUAL_COLOR_SCHEMA
{vol.Required(CONF_INDIVIDUAL_COLORS): object},
extra=vol.ALLOW_EXTRA,
),
msg="either 'address' or 'individual_colors' is required",
),
vol.Any(
vol.Schema( # 'brightness' is non-optional for hs-color
{
# brightness addresses are required in COLOR_SCHEMA
vol.Required(CONF_INDIVIDUAL_COLORS): object,
vol.Inclusive(
CONF_BRIGHTNESS_ADDRESS, "hs_color", msg=_hs_color_inclusion_msg
): object,
vol.Inclusive(
CONF_HUE_ADDRESS, "hs_color", msg=_hs_color_inclusion_msg
): object,
vol.Inclusive(
CONF_SATURATION_ADDRESS, "hs_color", msg=_hs_color_inclusion_msg
): object,
},
extra=vol.ALLOW_EXTRA,
),
vol.Schema(
vol.Schema( # hs-colors not used
{
vol.Required(KNX_ADDRESS): object,
vol.Optional(CONF_HUE_ADDRESS): None,
vol.Optional(CONF_SATURATION_ADDRESS): None,
},
extra=vol.ALLOW_EXTRA,
),
msg=_hs_color_inclusion_msg,
),
)

Expand Down