diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index 1d547e895bf..204f8613883 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -14,6 +14,7 @@ from xknx.io import ( ConnectionType, ) from xknx.telegram import AddressFilter, GroupAddress, Telegram +from xknx.telegram.apci import GroupValueResponse, GroupValueWrite from homeassistant.const import ( CONF_ENTITY_ID, @@ -322,9 +323,21 @@ class KNXModule: async def telegram_received_cb(self, telegram): """Call invoked after a KNX telegram was received.""" + data = None + + # Not all telegrams have serializable data. + if isinstance(telegram.payload, (GroupValueWrite, GroupValueResponse)): + data = telegram.payload.value.value + self.hass.bus.async_fire( "knx_event", - {"address": str(telegram.group_address), "data": telegram.payload.value}, + { + "data": data, + "destination": str(telegram.destination_address), + "direction": telegram.direction.value, + "source": str(telegram.source_address), + "telegramtype": telegram.payload.__class__.__name__, + }, ) async def service_send_to_knx_bus(self, call): @@ -344,10 +357,10 @@ class KNXModule: return DPTBinary(attr_payload) return DPTArray(attr_payload) - payload = calculate_payload(attr_payload) - address = GroupAddress(attr_address) - - telegram = Telegram(group_address=address, payload=payload) + telegram = Telegram( + destination_address=GroupAddress(attr_address), + payload=GroupValueWrite(calculate_payload(attr_payload)), + ) await self.xknx.telegrams.put(telegram) diff --git a/homeassistant/components/knx/factory.py b/homeassistant/components/knx/factory.py index 385b7c009ed..c1e73733b22 100644 --- a/homeassistant/components/knx/factory.py +++ b/homeassistant/components/knx/factory.py @@ -1,4 +1,6 @@ """Factory function to initialize KNX devices from config.""" +from typing import Optional, Tuple + from xknx import XKNX from xknx.devices import ( BinarySensor as XknxBinarySensor, @@ -86,8 +88,30 @@ def _create_cover(knx_module: XKNX, config: ConfigType) -> XknxCover: ) +def _create_light_color( + color: str, config: ConfigType +) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str]]: + """Load color configuration from configuration structure.""" + if "individual_colors" in config and color in config["individual_colors"]: + sub_config = config["individual_colors"][color] + group_address_switch = sub_config.get(CONF_ADDRESS) + group_address_switch_state = sub_config.get(LightSchema.CONF_STATE_ADDRESS) + group_address_brightness = sub_config.get(LightSchema.CONF_BRIGHTNESS_ADDRESS) + group_address_brightness_state = sub_config.get( + LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS + ) + return ( + group_address_switch, + group_address_switch_state, + group_address_brightness, + group_address_brightness_state, + ) + return None, None, None, None + + def _create_light(knx_module: XKNX, config: ConfigType) -> XknxLight: """Return a KNX Light device to be used within XKNX.""" + group_address_tunable_white = None group_address_tunable_white_state = None group_address_color_temp = None @@ -103,10 +127,35 @@ def _create_light(knx_module: XKNX, config: ConfigType) -> XknxLight: LightSchema.CONF_COLOR_TEMP_STATE_ADDRESS ) + ( + red_switch, + red_switch_state, + red_brightness, + red_brightness_state, + ) = _create_light_color(LightSchema.CONF_RED, config) + ( + green_switch, + green_switch_state, + green_brightness, + green_brightness_state, + ) = _create_light_color(LightSchema.CONF_GREEN, config) + ( + blue_switch, + blue_switch_state, + blue_brightness, + blue_brightness_state, + ) = _create_light_color(LightSchema.CONF_BLUE, config) + ( + white_switch, + white_switch_state, + white_brightness, + white_brightness_state, + ) = _create_light_color(LightSchema.CONF_WHITE, config) + return XknxLight( knx_module, name=config[CONF_NAME], - group_address_switch=config[CONF_ADDRESS], + group_address_switch=config.get(CONF_ADDRESS), group_address_switch_state=config.get(LightSchema.CONF_STATE_ADDRESS), group_address_brightness=config.get(LightSchema.CONF_BRIGHTNESS_ADDRESS), group_address_brightness_state=config.get( @@ -120,6 +169,22 @@ def _create_light(knx_module: XKNX, config: ConfigType) -> XknxLight: group_address_tunable_white_state=group_address_tunable_white_state, group_address_color_temperature=group_address_color_temp, group_address_color_temperature_state=group_address_color_temp_state, + group_address_switch_red=red_switch, + group_address_switch_red_state=red_switch_state, + group_address_brightness_red=red_brightness, + group_address_brightness_red_state=red_brightness_state, + group_address_switch_green=green_switch, + group_address_switch_green_state=green_switch_state, + group_address_brightness_green=green_brightness, + group_address_brightness_green_state=green_brightness_state, + group_address_switch_blue=blue_switch, + group_address_switch_blue_state=blue_switch_state, + group_address_brightness_blue=blue_brightness, + group_address_brightness_blue_state=blue_brightness_state, + group_address_switch_white=white_switch, + group_address_switch_white_state=white_switch_state, + group_address_brightness_white=white_brightness, + group_address_brightness_white_state=white_brightness_state, min_kelvin=config[LightSchema.CONF_MIN_KELVIN], max_kelvin=config[LightSchema.CONF_MAX_KELVIN], ) diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index 631a6329c8c..eb084b5ebd4 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -2,7 +2,7 @@ "domain": "knx", "name": "KNX", "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==0.15.6"], + "requirements": ["xknx==0.16.0"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "silver" } diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index b1d791e3284..4243dcc24e4 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -139,31 +139,72 @@ class LightSchema: DEFAULT_MIN_KELVIN = 2700 # 370 mireds DEFAULT_MAX_KELVIN = 6000 # 166 mireds - SCHEMA = vol.Schema( + CONF_INDIVIDUAL_COLORS = "individual_colors" + CONF_RED = "red" + CONF_GREEN = "green" + CONF_BLUE = "blue" + CONF_WHITE = "white" + + COLOR_SCHEMA = vol.Schema( { - vol.Required(CONF_ADDRESS): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_ADDRESS): cv.string, vol.Optional(CONF_STATE_ADDRESS): cv.string, - vol.Optional(CONF_BRIGHTNESS_ADDRESS): cv.string, + vol.Required(CONF_BRIGHTNESS_ADDRESS): cv.string, vol.Optional(CONF_BRIGHTNESS_STATE_ADDRESS): cv.string, - vol.Optional(CONF_COLOR_ADDRESS): cv.string, - vol.Optional(CONF_COLOR_STATE_ADDRESS): cv.string, - vol.Optional(CONF_COLOR_TEMP_ADDRESS): cv.string, - vol.Optional(CONF_COLOR_TEMP_STATE_ADDRESS): cv.string, - vol.Optional( - CONF_COLOR_TEMP_MODE, default=DEFAULT_COLOR_TEMP_MODE - ): cv.enum(ColorTempModes), - vol.Optional(CONF_RGBW_ADDRESS): cv.string, - vol.Optional(CONF_RGBW_STATE_ADDRESS): cv.string, - vol.Optional(CONF_MIN_KELVIN, default=DEFAULT_MIN_KELVIN): vol.All( - vol.Coerce(int), vol.Range(min=1) - ), - vol.Optional(CONF_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All( - vol.Coerce(int), vol.Range(min=1) - ), } ) + SCHEMA = vol.All( + vol.Schema( + { + vol.Optional(CONF_ADDRESS): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_STATE_ADDRESS): cv.string, + vol.Optional(CONF_BRIGHTNESS_ADDRESS): cv.string, + vol.Optional(CONF_BRIGHTNESS_STATE_ADDRESS): cv.string, + vol.Exclusive(CONF_INDIVIDUAL_COLORS, "color"): { + vol.Inclusive(CONF_RED, "colors"): COLOR_SCHEMA, + vol.Inclusive(CONF_GREEN, "colors"): COLOR_SCHEMA, + vol.Inclusive(CONF_BLUE, "colors"): COLOR_SCHEMA, + vol.Optional(CONF_WHITE): COLOR_SCHEMA, + }, + vol.Exclusive(CONF_COLOR_ADDRESS, "color"): cv.string, + vol.Optional(CONF_COLOR_STATE_ADDRESS): cv.string, + vol.Optional(CONF_COLOR_TEMP_ADDRESS): cv.string, + vol.Optional(CONF_COLOR_TEMP_STATE_ADDRESS): cv.string, + vol.Optional( + CONF_COLOR_TEMP_MODE, default=DEFAULT_COLOR_TEMP_MODE + ): cv.enum(ColorTempModes), + vol.Exclusive(CONF_RGBW_ADDRESS, "color"): cv.string, + vol.Optional(CONF_RGBW_STATE_ADDRESS): cv.string, + vol.Optional(CONF_MIN_KELVIN, default=DEFAULT_MIN_KELVIN): vol.All( + vol.Coerce(int), vol.Range(min=1) + ), + vol.Optional(CONF_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All( + vol.Coerce(int), vol.Range(min=1) + ), + } + ), + vol.Any( + vol.Schema( + { + vol.Required(CONF_INDIVIDUAL_COLORS): { + vol.Required(CONF_RED): {vol.Required(CONF_ADDRESS): object}, + vol.Required(CONF_GREEN): {vol.Required(CONF_ADDRESS): object}, + vol.Required(CONF_BLUE): {vol.Required(CONF_ADDRESS): object}, + }, + }, + extra=vol.ALLOW_EXTRA, + ), + vol.Schema( + { + vol.Required(CONF_ADDRESS): object, + }, + extra=vol.ALLOW_EXTRA, + ), + ), + ) + class ClimateSchema: """Voluptuous schema for KNX climate devices.""" diff --git a/requirements_all.txt b/requirements_all.txt index 830a5e4f456..7d24c803a31 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2305,7 +2305,7 @@ xboxapi==2.0.1 xfinity-gateway==0.0.4 # homeassistant.components.knx -xknx==0.15.6 +xknx==0.16.0 # homeassistant.components.bluesound # homeassistant.components.rest