Update xknx to 0.14.2 (#40304)

* Updates xknx to 0.14.0

* Review: Explicity add state attributes to weather device

* Review: Remove state attributes from weather device

* Review: Add `counter` as a state attribute to binary_sensors
This commit is contained in:
Marvin Wichmann 2020-09-20 23:40:36 +02:00 committed by GitHub
parent 0c077685b6
commit 45288431f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 61 additions and 63 deletions

View file

@ -6,7 +6,12 @@ from xknx import XKNX
from xknx.devices import DateTime, ExposeSensor from xknx.devices import DateTime, ExposeSensor
from xknx.dpt import DPTArray, DPTBase, DPTBinary from xknx.dpt import DPTArray, DPTBase, DPTBinary
from xknx.exceptions import XKNXException from xknx.exceptions import XKNXException
from xknx.io import DEFAULT_MCAST_PORT, ConnectionConfig, ConnectionType from xknx.io import (
DEFAULT_MCAST_GRP,
DEFAULT_MCAST_PORT,
ConnectionConfig,
ConnectionType,
)
from xknx.telegram import AddressFilter, GroupAddress, Telegram from xknx.telegram import AddressFilter, GroupAddress, Telegram
from homeassistant.const import ( from homeassistant.const import (
@ -48,6 +53,9 @@ CONF_KNX_ROUTING = "routing"
CONF_KNX_TUNNELING = "tunneling" CONF_KNX_TUNNELING = "tunneling"
CONF_KNX_FIRE_EVENT = "fire_event" CONF_KNX_FIRE_EVENT = "fire_event"
CONF_KNX_FIRE_EVENT_FILTER = "fire_event_filter" CONF_KNX_FIRE_EVENT_FILTER = "fire_event_filter"
CONF_KNX_INDIVIDUAL_ADDRESS = "individual_address"
CONF_KNX_MCAST_GRP = "multicast_group"
CONF_KNX_MCAST_PORT = "multicast_port"
CONF_KNX_STATE_UPDATER = "state_updater" CONF_KNX_STATE_UPDATER = "state_updater"
CONF_KNX_RATE_LIMIT = "rate_limit" CONF_KNX_RATE_LIMIT = "rate_limit"
CONF_KNX_EXPOSE = "expose" CONF_KNX_EXPOSE = "expose"
@ -72,6 +80,11 @@ CONFIG_SCHEMA = vol.Schema(
vol.Inclusive(CONF_KNX_FIRE_EVENT_FILTER, "fire_ev"): vol.All( vol.Inclusive(CONF_KNX_FIRE_EVENT_FILTER, "fire_ev"): vol.All(
cv.ensure_list, [cv.string] cv.ensure_list, [cv.string]
), ),
vol.Optional(
CONF_KNX_INDIVIDUAL_ADDRESS, default=XKNX.DEFAULT_ADDRESS
): cv.string,
vol.Optional(CONF_KNX_MCAST_GRP, default=DEFAULT_MCAST_GRP): cv.string,
vol.Optional(CONF_KNX_MCAST_PORT, default=DEFAULT_MCAST_PORT): cv.port,
vol.Optional(CONF_KNX_STATE_UPDATER, default=True): cv.boolean, vol.Optional(CONF_KNX_STATE_UPDATER, default=True): cv.boolean,
vol.Optional(CONF_KNX_RATE_LIMIT, default=20): vol.All( vol.Optional(CONF_KNX_RATE_LIMIT, default=20): vol.All(
vol.Coerce(int), vol.Range(min=1, max=100) vol.Coerce(int), vol.Range(min=1, max=100)
@ -130,17 +143,15 @@ async def async_setup(hass, config):
hass.data[DATA_KNX].async_create_exposures() hass.data[DATA_KNX].async_create_exposures()
await hass.data[DATA_KNX].start() await hass.data[DATA_KNX].start()
except XKNXException as ex: except XKNXException as ex:
_LOGGER.warning("Can't connect to KNX interface: %s", ex) _LOGGER.warning("Could not connect to KNX interface: %s", ex)
hass.components.persistent_notification.async_create( hass.components.persistent_notification.async_create(
f"Can't connect to KNX interface: <br><b>{ex}</b>", title="KNX" f"Could not connect to KNX interface: <br><b>{ex}</b>", title="KNX"
) )
for platform in SupportedPlatforms: for platform in SupportedPlatforms:
if platform.value in config[DOMAIN]: if platform.value in config[DOMAIN]:
for device_config in config[DOMAIN][platform.value]: for device_config in config[DOMAIN][platform.value]:
create_knx_device( create_knx_device(platform, hass.data[DATA_KNX].xknx, device_config)
hass, platform, hass.data[DATA_KNX].xknx, device_config
)
# We need to wait until all entities are loaded into the device list since they could also be created from other platforms # We need to wait until all entities are loaded into the device list since they could also be created from other platforms
for platform in SupportedPlatforms: for platform in SupportedPlatforms:
@ -181,7 +192,10 @@ class KNXModule:
self.xknx = XKNX( self.xknx = XKNX(
config=self.config_file(), config=self.config_file(),
loop=self.hass.loop, loop=self.hass.loop,
own_address=self.config[DOMAIN][CONF_KNX_INDIVIDUAL_ADDRESS],
rate_limit=self.config[DOMAIN][CONF_KNX_RATE_LIMIT], rate_limit=self.config[DOMAIN][CONF_KNX_RATE_LIMIT],
multicast_group=self.config[DOMAIN][CONF_KNX_MCAST_GRP],
multicast_port=self.config[DOMAIN][CONF_KNX_MCAST_PORT],
) )
async def start(self): async def start(self):
@ -229,12 +243,10 @@ class KNXModule:
def connection_config_tunneling(self): def connection_config_tunneling(self):
"""Return the connection_config if tunneling is configured.""" """Return the connection_config if tunneling is configured."""
gateway_ip = self.config[DOMAIN][CONF_KNX_TUNNELING][CONF_HOST] gateway_ip = self.config[DOMAIN][CONF_KNX_TUNNELING][CONF_HOST]
gateway_port = self.config[DOMAIN][CONF_KNX_TUNNELING].get(CONF_PORT) gateway_port = self.config[DOMAIN][CONF_KNX_TUNNELING][CONF_PORT]
local_ip = self.config[DOMAIN][CONF_KNX_TUNNELING].get( local_ip = self.config[DOMAIN][CONF_KNX_TUNNELING].get(
ConnectionSchema.CONF_KNX_LOCAL_IP ConnectionSchema.CONF_KNX_LOCAL_IP
) )
if gateway_port is None:
gateway_port = DEFAULT_MCAST_PORT
return ConnectionConfig( return ConnectionConfig(
connection_type=ConnectionType.TUNNELING, connection_type=ConnectionType.TUNNELING,
gateway_ip=gateway_ip, gateway_ip=gateway_ip,
@ -267,7 +279,7 @@ class KNXModule:
attribute = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_ATTRIBUTE) attribute = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_ATTRIBUTE)
default = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_DEFAULT) default = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_DEFAULT)
address = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_ADDRESS) address = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_ADDRESS)
if expose_type in ["time", "date", "datetime"]: if expose_type.lower() in ["time", "date", "datetime"]:
exposure = KNXExposeTime(self.xknx, expose_type, address) exposure = KNXExposeTime(self.xknx, expose_type, address)
exposure.async_register() exposure.async_register()
self.exposures.append(exposure) self.exposures.append(exposure)
@ -313,29 +325,29 @@ class KNXModule:
payload = calculate_payload(attr_payload) payload = calculate_payload(attr_payload)
address = GroupAddress(attr_address) address = GroupAddress(attr_address)
telegram = Telegram() telegram = Telegram(group_address=address, payload=payload)
telegram.payload = payload
telegram.group_address = address
await self.xknx.telegrams.put(telegram) await self.xknx.telegrams.put(telegram)
class KNXExposeTime: class KNXExposeTime:
"""Object to Expose Time/Date object to KNX bus.""" """Object to Expose Time/Date object to KNX bus."""
def __init__(self, xknx, expose_type, address): def __init__(self, xknx: XKNX, expose_type: str, address: str):
"""Initialize of Expose class.""" """Initialize of Expose class."""
self.xknx = xknx self.xknx = xknx
self.type = expose_type self.expose_type = expose_type
self.address = address self.address = address
self.device = None self.device = None
@callback @callback
def async_register(self): def async_register(self):
"""Register listener.""" """Register listener."""
broadcast_type_string = self.type.upper()
broadcast_type = broadcast_type_string
self.device = DateTime( self.device = DateTime(
self.xknx, "Time", broadcast_type=broadcast_type, group_address=self.address self.xknx,
name=self.expose_type.capitalize(),
broadcast_type=self.expose_type.upper(),
localtime=True,
group_address=self.address,
) )

View file

@ -1,10 +1,12 @@
"""Support for KNX/IP binary sensors.""" """Support for KNX/IP binary sensors."""
from typing import Any, Dict, Optional
from xknx.devices import BinarySensor as XknxBinarySensor from xknx.devices import BinarySensor as XknxBinarySensor
from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.core import callback from homeassistant.core import callback
from . import DATA_KNX from .const import ATTR_COUNTER, DATA_KNX
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
@ -27,7 +29,7 @@ class KNXBinarySensor(BinarySensorEntity):
def async_register_callbacks(self): def async_register_callbacks(self):
"""Register callbacks to update hass after device was changed.""" """Register callbacks to update hass after device was changed."""
async def after_update_callback(device): async def after_update_callback(device: XknxBinarySensor):
"""Call after device was updated.""" """Call after device was updated."""
self.async_write_ha_state() self.async_write_ha_state()
@ -65,3 +67,8 @@ class KNXBinarySensor(BinarySensorEntity):
def is_on(self): def is_on(self):
"""Return true if the binary sensor is on.""" """Return true if the binary sensor is on."""
return self.device.is_on() return self.device.is_on()
@property
def device_state_attributes(self) -> Optional[Dict[str, Any]]:
"""Return device specific state attributes."""
return {ATTR_COUNTER: self.device.counter}

View file

@ -60,3 +60,5 @@ PRESET_MODES = {
"Standby": PRESET_AWAY, "Standby": PRESET_AWAY,
"Comfort": PRESET_COMFORT, "Comfort": PRESET_COMFORT,
} }
ATTR_COUNTER = "counter"

View file

@ -1,7 +1,6 @@
"""Factory function to initialize KNX devices from config.""" """Factory function to initialize KNX devices from config."""
from xknx import XKNX from xknx import XKNX
from xknx.devices import ( from xknx.devices import (
ActionCallback as XknxActionCallback,
BinarySensor as XknxBinarySensor, BinarySensor as XknxBinarySensor,
Climate as XknxClimate, Climate as XknxClimate,
ClimateMode as XknxClimateMode, ClimateMode as XknxClimateMode,
@ -16,11 +15,9 @@ from xknx.devices import (
) )
from homeassistant.const import CONF_ADDRESS, CONF_DEVICE_CLASS, CONF_NAME, CONF_TYPE from homeassistant.const import CONF_ADDRESS, CONF_DEVICE_CLASS, CONF_NAME, CONF_TYPE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.script import Script
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN, ColorTempModes, SupportedPlatforms from .const import ColorTempModes, SupportedPlatforms
from .schema import ( from .schema import (
BinarySensorSchema, BinarySensorSchema,
ClimateSchema, ClimateSchema,
@ -34,7 +31,6 @@ from .schema import (
def create_knx_device( def create_knx_device(
hass: HomeAssistant,
platform: SupportedPlatforms, platform: SupportedPlatforms,
knx_module: XKNX, knx_module: XKNX,
config: ConfigType, config: ConfigType,
@ -62,7 +58,7 @@ def create_knx_device(
return _create_scene(knx_module, config) return _create_scene(knx_module, config)
if platform is SupportedPlatforms.binary_sensor: if platform is SupportedPlatforms.binary_sensor:
return _create_binary_sensor(hass, knx_module, config) return _create_binary_sensor(knx_module, config)
if platform is SupportedPlatforms.weather: if platform is SupportedPlatforms.weather:
return _create_weather(knx_module, config) return _create_weather(knx_module, config)
@ -239,24 +235,9 @@ def _create_scene(knx_module: XKNX, config: ConfigType) -> XknxScene:
) )
def _create_binary_sensor( def _create_binary_sensor(knx_module: XKNX, config: ConfigType) -> XknxBinarySensor:
hass: HomeAssistant, knx_module: XKNX, config: ConfigType
) -> XknxBinarySensor:
"""Return a KNX binary sensor to be used within XKNX.""" """Return a KNX binary sensor to be used within XKNX."""
device_name = config[CONF_NAME] device_name = config[CONF_NAME]
actions = []
automations = config.get(BinarySensorSchema.CONF_AUTOMATION)
if automations is not None:
for automation in automations:
counter = automation[BinarySensorSchema.CONF_COUNTER]
hook = automation[BinarySensorSchema.CONF_HOOK]
action = automation[BinarySensorSchema.CONF_ACTION]
script_name = f"{device_name} turn ON script"
script = Script(hass, action, script_name, DOMAIN)
action = XknxActionCallback(
knx_module, script.async_run, hook=hook, counter=counter
)
actions.append(action)
return XknxBinarySensor( return XknxBinarySensor(
knx_module, knx_module,
@ -265,8 +246,8 @@ def _create_binary_sensor(
sync_state=config[BinarySensorSchema.CONF_SYNC_STATE], sync_state=config[BinarySensorSchema.CONF_SYNC_STATE],
device_class=config.get(CONF_DEVICE_CLASS), device_class=config.get(CONF_DEVICE_CLASS),
ignore_internal_state=config[BinarySensorSchema.CONF_IGNORE_INTERNAL_STATE], ignore_internal_state=config[BinarySensorSchema.CONF_IGNORE_INTERNAL_STATE],
context_timeout=config[BinarySensorSchema.CONF_CONTEXT_TIMEOUT],
reset_after=config.get(BinarySensorSchema.CONF_RESET_AFTER), reset_after=config.get(BinarySensorSchema.CONF_RESET_AFTER),
actions=actions,
) )
@ -287,6 +268,9 @@ def _create_weather(knx_module: XKNX, config: ConfigType) -> XknxWeather:
group_address_brightness_west=config.get( group_address_brightness_west=config.get(
WeatherSchema.CONF_KNX_BRIGHTNESS_WEST_ADDRESS WeatherSchema.CONF_KNX_BRIGHTNESS_WEST_ADDRESS
), ),
group_address_brightness_north=config.get(
WeatherSchema.CONF_KNX_BRIGHTNESS_NORTH_ADDRESS
),
group_address_wind_speed=config.get(WeatherSchema.CONF_KNX_WIND_SPEED_ADDRESS), group_address_wind_speed=config.get(WeatherSchema.CONF_KNX_WIND_SPEED_ADDRESS),
group_address_rain_alarm=config.get(WeatherSchema.CONF_KNX_RAIN_ALARM_ADDRESS), group_address_rain_alarm=config.get(WeatherSchema.CONF_KNX_RAIN_ALARM_ADDRESS),
group_address_frost_alarm=config.get( group_address_frost_alarm=config.get(

View file

@ -2,6 +2,6 @@
"domain": "knx", "domain": "knx",
"name": "KNX", "name": "KNX",
"documentation": "https://www.home-assistant.io/integrations/knx", "documentation": "https://www.home-assistant.io/integrations/knx",
"requirements": ["xknx==0.13.0"], "requirements": ["xknx==0.14.2"],
"codeowners": ["@Julius2342", "@farmio", "@marvin-w"] "codeowners": ["@Julius2342", "@farmio", "@marvin-w"]
} }

View file

@ -1,6 +1,7 @@
"""Voluptuous schemas for the KNX integration.""" """Voluptuous schemas for the KNX integration."""
import voluptuous as vol import voluptuous as vol
from xknx.devices.climate import SetpointShiftMode from xknx.devices.climate import SetpointShiftMode
from xknx.io import DEFAULT_MCAST_PORT
from homeassistant.const import ( from homeassistant.const import (
CONF_ADDRESS, CONF_ADDRESS,
@ -29,9 +30,9 @@ class ConnectionSchema:
TUNNELING_SCHEMA = vol.Schema( TUNNELING_SCHEMA = vol.Schema(
{ {
vol.Optional(CONF_PORT, default=DEFAULT_MCAST_PORT): cv.port,
vol.Required(CONF_HOST): cv.string, vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_KNX_LOCAL_IP): cv.string, vol.Optional(CONF_KNX_LOCAL_IP): cv.string,
vol.Optional(CONF_PORT): cv.port,
} }
) )
@ -84,27 +85,14 @@ class BinarySensorSchema:
CONF_STATE_ADDRESS = CONF_STATE_ADDRESS CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
CONF_SYNC_STATE = CONF_SYNC_STATE CONF_SYNC_STATE = CONF_SYNC_STATE
CONF_IGNORE_INTERNAL_STATE = "ignore_internal_state" CONF_IGNORE_INTERNAL_STATE = "ignore_internal_state"
CONF_AUTOMATION = "automation" CONF_CONTEXT_TIMEOUT = "context_timeout"
CONF_HOOK = "hook"
CONF_DEFAULT_HOOK = "on"
CONF_COUNTER = "counter"
CONF_DEFAULT_COUNTER = 1
CONF_ACTION = "action"
CONF_RESET_AFTER = "reset_after" CONF_RESET_AFTER = "reset_after"
DEFAULT_NAME = "KNX Binary Sensor" DEFAULT_NAME = "KNX Binary Sensor"
AUTOMATION_SCHEMA = vol.Schema(
{
vol.Optional(CONF_HOOK, default=CONF_DEFAULT_HOOK): cv.string,
vol.Optional(CONF_COUNTER, default=CONF_DEFAULT_COUNTER): cv.port,
vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA,
}
)
AUTOMATIONS_SCHEMA = vol.All(cv.ensure_list, [AUTOMATION_SCHEMA])
SCHEMA = vol.All( SCHEMA = vol.All(
cv.deprecated("significant_bit"), cv.deprecated("significant_bit"),
cv.deprecated("automation"),
vol.Schema( vol.Schema(
{ {
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
@ -113,11 +101,13 @@ class BinarySensorSchema:
cv.boolean, cv.boolean,
cv.string, cv.string,
), ),
vol.Optional(CONF_IGNORE_INTERNAL_STATE, default=False): cv.boolean, vol.Optional(CONF_IGNORE_INTERNAL_STATE, default=True): cv.boolean,
vol.Optional(CONF_CONTEXT_TIMEOUT, default=1.0): vol.All(
vol.Coerce(float), vol.Range(min=0, max=10)
),
vol.Required(CONF_STATE_ADDRESS): cv.string, vol.Required(CONF_STATE_ADDRESS): cv.string,
vol.Optional(CONF_DEVICE_CLASS): cv.string, vol.Optional(CONF_DEVICE_CLASS): cv.string,
vol.Optional(CONF_RESET_AFTER): cv.positive_int, vol.Optional(CONF_RESET_AFTER): cv.positive_int,
vol.Optional(CONF_AUTOMATION): AUTOMATIONS_SCHEMA,
} }
), ),
) )
@ -350,6 +340,7 @@ class WeatherSchema:
CONF_KNX_BRIGHTNESS_SOUTH_ADDRESS = "address_brightness_south" CONF_KNX_BRIGHTNESS_SOUTH_ADDRESS = "address_brightness_south"
CONF_KNX_BRIGHTNESS_EAST_ADDRESS = "address_brightness_east" CONF_KNX_BRIGHTNESS_EAST_ADDRESS = "address_brightness_east"
CONF_KNX_BRIGHTNESS_WEST_ADDRESS = "address_brightness_west" CONF_KNX_BRIGHTNESS_WEST_ADDRESS = "address_brightness_west"
CONF_KNX_BRIGHTNESS_NORTH_ADDRESS = "address_brightness_north"
CONF_KNX_WIND_SPEED_ADDRESS = "address_wind_speed" CONF_KNX_WIND_SPEED_ADDRESS = "address_wind_speed"
CONF_KNX_RAIN_ALARM_ADDRESS = "address_rain_alarm" CONF_KNX_RAIN_ALARM_ADDRESS = "address_rain_alarm"
CONF_KNX_FROST_ALARM_ADDRESS = "address_frost_alarm" CONF_KNX_FROST_ALARM_ADDRESS = "address_frost_alarm"
@ -374,6 +365,7 @@ class WeatherSchema:
vol.Optional(CONF_KNX_BRIGHTNESS_SOUTH_ADDRESS): cv.string, vol.Optional(CONF_KNX_BRIGHTNESS_SOUTH_ADDRESS): cv.string,
vol.Optional(CONF_KNX_BRIGHTNESS_EAST_ADDRESS): cv.string, vol.Optional(CONF_KNX_BRIGHTNESS_EAST_ADDRESS): cv.string,
vol.Optional(CONF_KNX_BRIGHTNESS_WEST_ADDRESS): cv.string, vol.Optional(CONF_KNX_BRIGHTNESS_WEST_ADDRESS): cv.string,
vol.Optional(CONF_KNX_BRIGHTNESS_NORTH_ADDRESS): cv.string,
vol.Optional(CONF_KNX_WIND_SPEED_ADDRESS): cv.string, vol.Optional(CONF_KNX_WIND_SPEED_ADDRESS): cv.string,
vol.Optional(CONF_KNX_RAIN_ALARM_ADDRESS): cv.string, vol.Optional(CONF_KNX_RAIN_ALARM_ADDRESS): cv.string,
vol.Optional(CONF_KNX_FROST_ALARM_ADDRESS): cv.string, vol.Optional(CONF_KNX_FROST_ALARM_ADDRESS): cv.string,

View file

@ -1,4 +1,5 @@
"""Support for KNX/IP weather station.""" """Support for KNX/IP weather station."""
from xknx.devices import Weather as XknxWeather from xknx.devices import Weather as XknxWeather
from homeassistant.components.weather import WeatherEntity from homeassistant.components.weather import WeatherEntity

View file

@ -2265,7 +2265,7 @@ xboxapi==2.0.1
xfinity-gateway==0.0.4 xfinity-gateway==0.0.4
# homeassistant.components.knx # homeassistant.components.knx
xknx==0.13.0 xknx==0.14.2
# homeassistant.components.bluesound # homeassistant.components.bluesound
# homeassistant.components.rest # homeassistant.components.rest