Add KNX service event_register (#45248)

This commit is contained in:
Matthias Alphart 2021-01-22 15:27:51 +01:00 committed by GitHub
parent 89fc92f68a
commit 4aceb0dd27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 120 additions and 69 deletions

View File

@ -4,6 +4,7 @@ import logging
import voluptuous as vol import voluptuous as vol
from xknx import XKNX from xknx import XKNX
from xknx.core.telegram_queue import TelegramQueue
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
@ -59,7 +60,7 @@ CONF_KNX_CONFIG = "config_file"
CONF_KNX_ROUTING = "routing" 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_EVENT_FILTER = "event_filter"
CONF_KNX_INDIVIDUAL_ADDRESS = "individual_address" CONF_KNX_INDIVIDUAL_ADDRESS = "individual_address"
CONF_KNX_MCAST_GRP = "multicast_group" CONF_KNX_MCAST_GRP = "multicast_group"
CONF_KNX_MCAST_PORT = "multicast_port" CONF_KNX_MCAST_PORT = "multicast_port"
@ -71,62 +72,72 @@ SERVICE_KNX_SEND = "send"
SERVICE_KNX_ATTR_ADDRESS = "address" SERVICE_KNX_ATTR_ADDRESS = "address"
SERVICE_KNX_ATTR_PAYLOAD = "payload" SERVICE_KNX_ATTR_PAYLOAD = "payload"
SERVICE_KNX_ATTR_TYPE = "type" SERVICE_KNX_ATTR_TYPE = "type"
SERVICE_KNX_ATTR_REMOVE = "remove"
SERVICE_KNX_EVENT_REGISTER = "event_register"
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{ {
DOMAIN: vol.Schema( DOMAIN: vol.All(
{ cv.deprecated(CONF_KNX_FIRE_EVENT),
vol.Optional(CONF_KNX_CONFIG): cv.string, cv.deprecated("fire_event_filter", replacement_key=CONF_KNX_EVENT_FILTER),
vol.Exclusive( vol.Schema(
CONF_KNX_ROUTING, "connection_type" {
): ConnectionSchema.ROUTING_SCHEMA, vol.Optional(CONF_KNX_CONFIG): cv.string,
vol.Exclusive( vol.Exclusive(
CONF_KNX_TUNNELING, "connection_type" CONF_KNX_ROUTING, "connection_type"
): ConnectionSchema.TUNNELING_SCHEMA, ): ConnectionSchema.ROUTING_SCHEMA,
vol.Inclusive(CONF_KNX_FIRE_EVENT, "fire_ev"): cv.boolean, vol.Exclusive(
vol.Inclusive(CONF_KNX_FIRE_EVENT_FILTER, "fire_ev"): vol.All( CONF_KNX_TUNNELING, "connection_type"
cv.ensure_list, [cv.string] ): ConnectionSchema.TUNNELING_SCHEMA,
), vol.Optional(CONF_KNX_FIRE_EVENT): cv.boolean,
vol.Optional( vol.Optional(CONF_KNX_EVENT_FILTER, default=[]): vol.All(
CONF_KNX_INDIVIDUAL_ADDRESS, default=XKNX.DEFAULT_ADDRESS cv.ensure_list, [cv.string]
): cv.string, ),
vol.Optional(CONF_KNX_MCAST_GRP, default=DEFAULT_MCAST_GRP): cv.string, vol.Optional(
vol.Optional(CONF_KNX_MCAST_PORT, default=DEFAULT_MCAST_PORT): cv.port, CONF_KNX_INDIVIDUAL_ADDRESS, default=XKNX.DEFAULT_ADDRESS
vol.Optional(CONF_KNX_STATE_UPDATER, default=True): cv.boolean, ): cv.string,
vol.Optional(CONF_KNX_RATE_LIMIT, default=20): vol.All( vol.Optional(
vol.Coerce(int), vol.Range(min=1, max=100) CONF_KNX_MCAST_GRP, default=DEFAULT_MCAST_GRP
), ): cv.string,
vol.Optional(CONF_KNX_EXPOSE): vol.All( vol.Optional(
cv.ensure_list, [ExposeSchema.SCHEMA] CONF_KNX_MCAST_PORT, default=DEFAULT_MCAST_PORT
), ): cv.port,
vol.Optional(SupportedPlatforms.cover.value): vol.All( vol.Optional(CONF_KNX_STATE_UPDATER, default=True): cv.boolean,
cv.ensure_list, [CoverSchema.SCHEMA] vol.Optional(CONF_KNX_RATE_LIMIT, default=20): vol.All(
), vol.Coerce(int), vol.Range(min=1, max=100)
vol.Optional(SupportedPlatforms.binary_sensor.value): vol.All( ),
cv.ensure_list, [BinarySensorSchema.SCHEMA] vol.Optional(CONF_KNX_EXPOSE): vol.All(
), cv.ensure_list, [ExposeSchema.SCHEMA]
vol.Optional(SupportedPlatforms.light.value): vol.All( ),
cv.ensure_list, [LightSchema.SCHEMA] vol.Optional(SupportedPlatforms.cover.value): vol.All(
), cv.ensure_list, [CoverSchema.SCHEMA]
vol.Optional(SupportedPlatforms.climate.value): vol.All( ),
cv.ensure_list, [ClimateSchema.SCHEMA] vol.Optional(SupportedPlatforms.binary_sensor.value): vol.All(
), cv.ensure_list, [BinarySensorSchema.SCHEMA]
vol.Optional(SupportedPlatforms.notify.value): vol.All( ),
cv.ensure_list, [NotifySchema.SCHEMA] vol.Optional(SupportedPlatforms.light.value): vol.All(
), cv.ensure_list, [LightSchema.SCHEMA]
vol.Optional(SupportedPlatforms.switch.value): vol.All( ),
cv.ensure_list, [SwitchSchema.SCHEMA] vol.Optional(SupportedPlatforms.climate.value): vol.All(
), cv.ensure_list, [ClimateSchema.SCHEMA]
vol.Optional(SupportedPlatforms.sensor.value): vol.All( ),
cv.ensure_list, [SensorSchema.SCHEMA] vol.Optional(SupportedPlatforms.notify.value): vol.All(
), cv.ensure_list, [NotifySchema.SCHEMA]
vol.Optional(SupportedPlatforms.scene.value): vol.All( ),
cv.ensure_list, [SceneSchema.SCHEMA] vol.Optional(SupportedPlatforms.switch.value): vol.All(
), cv.ensure_list, [SwitchSchema.SCHEMA]
vol.Optional(SupportedPlatforms.weather.value): vol.All( ),
cv.ensure_list, [WeatherSchema.SCHEMA] vol.Optional(SupportedPlatforms.sensor.value): vol.All(
), cv.ensure_list, [SensorSchema.SCHEMA]
} ),
vol.Optional(SupportedPlatforms.scene.value): vol.All(
cv.ensure_list, [SceneSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.weather.value): vol.All(
cv.ensure_list, [WeatherSchema.SCHEMA]
),
}
),
) )
}, },
extra=vol.ALLOW_EXTRA, extra=vol.ALLOW_EXTRA,
@ -151,6 +162,13 @@ SERVICE_KNX_SEND_SCHEMA = vol.Any(
), ),
) )
SERVICE_KNX_EVENT_REGISTER_SCHEMA = vol.Schema(
{
vol.Required(SERVICE_KNX_ATTR_ADDRESS): cv.string,
vol.Optional(SERVICE_KNX_ATTR_REMOVE, default=False): cv.boolean,
}
)
async def async_setup(hass, config): async def async_setup(hass, config):
"""Set up the KNX component.""" """Set up the KNX component."""
@ -188,6 +206,14 @@ async def async_setup(hass, config):
schema=SERVICE_KNX_SEND_SCHEMA, schema=SERVICE_KNX_SEND_SCHEMA,
) )
async_register_admin_service(
hass,
DOMAIN,
SERVICE_KNX_EVENT_REGISTER,
hass.data[DOMAIN].service_event_register_modify,
schema=SERVICE_KNX_EVENT_REGISTER_SCHEMA,
)
async def reload_service_handler(service_call: ServiceCallType) -> None: async def reload_service_handler(service_call: ServiceCallType) -> None:
"""Remove all KNX components and load new ones from config.""" """Remove all KNX components and load new ones from config."""
@ -221,10 +247,11 @@ class KNXModule:
self.hass = hass self.hass = hass
self.config = config self.config = config
self.connected = False self.connected = False
self.init_xknx()
self.register_callbacks()
self.exposures = [] self.exposures = []
self.init_xknx()
self._knx_event_callback: TelegramQueue.Callback = self.register_callback()
def init_xknx(self): def init_xknx(self):
"""Initialize of KNX object.""" """Initialize of KNX object."""
self.xknx = XKNX( self.xknx = XKNX(
@ -289,19 +316,6 @@ class KNXModule:
auto_reconnect=True, auto_reconnect=True,
) )
def register_callbacks(self):
"""Register callbacks within XKNX object."""
if (
CONF_KNX_FIRE_EVENT in self.config[DOMAIN]
and self.config[DOMAIN][CONF_KNX_FIRE_EVENT]
):
address_filters = list(
map(AddressFilter, self.config[DOMAIN][CONF_KNX_FIRE_EVENT_FILTER])
)
self.xknx.telegram_queue.register_telegram_received_cb(
self.telegram_received_cb, address_filters
)
@callback @callback
def async_create_exposures(self): def async_create_exposures(self):
"""Create exposures.""" """Create exposures."""
@ -349,6 +363,35 @@ class KNXModule:
}, },
) )
def register_callback(self) -> TelegramQueue.Callback:
"""Register callback within XKNX TelegramQueue."""
address_filters = list(
map(AddressFilter, self.config[DOMAIN][CONF_KNX_EVENT_FILTER])
)
return self.xknx.telegram_queue.register_telegram_received_cb(
self.telegram_received_cb,
address_filters=address_filters,
group_addresses=[],
)
async def service_event_register_modify(self, call):
"""Service for adding or removing a GroupAddress to the knx_event filter."""
group_address = GroupAddress(call.data.get(SERVICE_KNX_ATTR_ADDRESS))
if call.data.get(SERVICE_KNX_ATTR_REMOVE):
try:
self._knx_event_callback.group_addresses.remove(group_address)
except ValueError:
_LOGGER.warning(
"Service event_register could not remove event for '%s'",
group_address,
)
elif group_address not in self._knx_event_callback.group_addresses:
self._knx_event_callback.group_addresses.append(group_address)
_LOGGER.debug(
"Service event_register registered event for '%s'",
group_address,
)
async def service_send_to_knx_bus(self, call): async def service_send_to_knx_bus(self, call):
"""Service for sending an arbitrary KNX message to the KNX bus.""" """Service for sending an arbitrary KNX message to the KNX bus."""
attr_payload = call.data.get(SERVICE_KNX_ATTR_PAYLOAD) attr_payload = call.data.get(SERVICE_KNX_ATTR_PAYLOAD)

View File

@ -10,3 +10,11 @@ send:
type: type:
description: "Optional. If set, the payload will not be sent as raw bytes, but encoded as given DPT. Knx sensor types are valid values (see https://www.home-assistant.io/integrations/sensor.knx)." description: "Optional. If set, the payload will not be sent as raw bytes, but encoded as given DPT. Knx sensor types are valid values (see https://www.home-assistant.io/integrations/sensor.knx)."
example: "temperature" example: "temperature"
event_register:
description: "Add or remove single group address to knx_event filter for triggering `knx_event`s. Only addresses added with this service can be removed."
fields:
address:
description: "Group address that shall be added or removed."
example: "1/1/0"
remove:
description: "Optional. If `True` the group address will be removed. Defaults to `False`."