Skip to content

Commit

Permalink
change TelegramQueue.Callback filter from HA service
Browse files Browse the repository at this point in the history
  • Loading branch information
farmio committed Dec 16, 2020
1 parent 339f526 commit 92d70f5
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 126 deletions.
3 changes: 3 additions & 0 deletions changelog.md
Expand Up @@ -8,7 +8,9 @@

### HA integration

- knx_event: renamed `fire_event_filter` to `event_filter` and deprecated `fire_event` config option. A callback is now always registered for HA to be able to modify its `group_addresses` filter from a service.
- knx_event: renamed `address` to `destination` and added `source`, `telegramtype`, `direction` attributes.
- added `knx.event_register` service allowing to add and remove group addresses to trigger knx_event without having to change configuration.

### Internals

Expand All @@ -19,6 +21,7 @@
- CEMIFrame: remove `CEMIFrame.cmd`, which can be derived from `CEMIFrame.payload`.
- Farewell Travis CI; Welcome Github Actions!
- StateUpdater allow float values for `register_remote_value(tracker_options)` attribute.
- TelegramQueue.Callback: add `group_addresses` attribute to store a list of GroupAddress triggering the callback (additionally to `address_filters`).

## 0.15.6 Bugfix for StateUpater 2020-11-26

Expand Down
171 changes: 102 additions & 69 deletions home-assistant-plugin/custom_components/xknx/__init__.py
Expand Up @@ -4,6 +4,7 @@

import voluptuous as vol
from xknx import XKNX
from xknx.core.telegram_queue import TelegramQueue
from xknx.devices import DateTime, ExposeSensor
from xknx.dpt import DPTArray, DPTBase, DPTBinary
from xknx.exceptions import XKNXException
Expand Down Expand Up @@ -59,7 +60,7 @@
CONF_XKNX_ROUTING = "routing"
CONF_XKNX_TUNNELING = "tunneling"
CONF_XKNX_FIRE_EVENT = "fire_event"
CONF_XKNX_FIRE_EVENT_FILTER = "fire_event_filter"
CONF_XKNX_EVENT_FILTER = "event_filter"
CONF_XKNX_INDIVIDUAL_ADDRESS = "individual_address"
CONF_XKNX_MCAST_GRP = "multicast_group"
CONF_XKNX_MCAST_PORT = "multicast_port"
Expand All @@ -71,62 +72,72 @@
SERVICE_XKNX_ATTR_ADDRESS = "address"
SERVICE_XKNX_ATTR_PAYLOAD = "payload"
SERVICE_XKNX_ATTR_TYPE = "type"
SERVICE_XKNX_ATTR_REMOVE = "remove"
SERVICE_XKNX_EVENT_REGISTER = "event_register"

CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(CONF_XKNX_CONFIG): cv.string,
vol.Exclusive(
CONF_XKNX_ROUTING, "connection_type"
): ConnectionSchema.ROUTING_SCHEMA,
vol.Exclusive(
CONF_XKNX_TUNNELING, "connection_type"
): ConnectionSchema.TUNNELING_SCHEMA,
vol.Inclusive(CONF_XKNX_FIRE_EVENT, "fire_ev"): cv.boolean,
vol.Inclusive(CONF_XKNX_FIRE_EVENT_FILTER, "fire_ev"): vol.All(
cv.ensure_list, [cv.string]
),
vol.Optional(
CONF_XKNX_INDIVIDUAL_ADDRESS, default=XKNX.DEFAULT_ADDRESS
): cv.string,
vol.Optional(CONF_XKNX_MCAST_GRP, default=DEFAULT_MCAST_GRP): cv.string,
vol.Optional(CONF_XKNX_MCAST_PORT, default=DEFAULT_MCAST_PORT): cv.port,
vol.Optional(CONF_XKNX_STATE_UPDATER, default=True): cv.boolean,
vol.Optional(CONF_XKNX_RATE_LIMIT, default=20): vol.All(
vol.Coerce(int), vol.Range(min=1, max=100)
),
vol.Optional(CONF_XKNX_EXPOSE): vol.All(
cv.ensure_list, [ExposeSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.cover.value): vol.All(
cv.ensure_list, [CoverSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.binary_sensor.value): vol.All(
cv.ensure_list, [BinarySensorSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.light.value): vol.All(
cv.ensure_list, [LightSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.climate.value): vol.All(
cv.ensure_list, [ClimateSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.notify.value): vol.All(
cv.ensure_list, [NotifySchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.switch.value): vol.All(
cv.ensure_list, [SwitchSchema.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]
),
}
DOMAIN: vol.All(
cv.deprecated(CONF_XKNX_FIRE_EVENT, invalidation_version="2021.12"),
cv.deprecated("fire_event_filter", replacement_key=CONF_XKNX_EVENT_FILTER),
vol.Schema(
{
vol.Optional(CONF_XKNX_CONFIG): cv.string,
vol.Exclusive(
CONF_XKNX_ROUTING, "connection_type"
): ConnectionSchema.ROUTING_SCHEMA,
vol.Exclusive(
CONF_XKNX_TUNNELING, "connection_type"
): ConnectionSchema.TUNNELING_SCHEMA,
vol.Optional(CONF_XKNX_FIRE_EVENT): cv.boolean,
vol.Optional(CONF_XKNX_EVENT_FILTER, default=[]): vol.All(
cv.ensure_list, [cv.string]
),
vol.Optional(
CONF_XKNX_INDIVIDUAL_ADDRESS, default=XKNX.DEFAULT_ADDRESS
): cv.string,
vol.Optional(
CONF_XKNX_MCAST_GRP, default=DEFAULT_MCAST_GRP
): cv.string,
vol.Optional(
CONF_XKNX_MCAST_PORT, default=DEFAULT_MCAST_PORT
): cv.port,
vol.Optional(CONF_XKNX_STATE_UPDATER, default=True): cv.boolean,
vol.Optional(CONF_XKNX_RATE_LIMIT, default=20): vol.All(
vol.Coerce(int), vol.Range(min=1, max=100)
),
vol.Optional(CONF_XKNX_EXPOSE): vol.All(
cv.ensure_list, [ExposeSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.cover.value): vol.All(
cv.ensure_list, [CoverSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.binary_sensor.value): vol.All(
cv.ensure_list, [BinarySensorSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.light.value): vol.All(
cv.ensure_list, [LightSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.climate.value): vol.All(
cv.ensure_list, [ClimateSchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.notify.value): vol.All(
cv.ensure_list, [NotifySchema.SCHEMA]
),
vol.Optional(SupportedPlatforms.switch.value): vol.All(
cv.ensure_list, [SwitchSchema.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,
Expand All @@ -142,6 +153,13 @@
}
)

SERVICE_XKNX_EVENT_REGISTER_SCHEMA = vol.Schema(
{
vol.Required(SERVICE_XKNX_ATTR_ADDRESS): cv.string,
vol.Optional(SERVICE_XKNX_ATTR_REMOVE, default=False): cv.boolean,
}
)


async def async_setup(hass, config):
"""Set up the KNX component."""
Expand Down Expand Up @@ -179,6 +197,14 @@ async def async_setup(hass, config):
schema=SERVICE_XKNX_SEND_SCHEMA,
)

async_register_admin_service(
hass,
DOMAIN,
SERVICE_XKNX_EVENT_REGISTER,
hass.data[DOMAIN].service_event_register_modify,
schema=SERVICE_XKNX_EVENT_REGISTER_SCHEMA,
)

async def reload_service_handler(service_call: ServiceCallType) -> None:
"""Remove all KNX components and load new ones from config."""

Expand Down Expand Up @@ -212,10 +238,11 @@ def __init__(self, hass, config):
self.hass = hass
self.config = config
self.connected = False
self.init_xknx()
self.register_callbacks()
self.exposures = []

self.init_xknx()
self._knx_event_callback: TelegramQueue.Callback = self.register_callback()

def init_xknx(self):
"""Initialize of KNX object."""
self.xknx = XKNX(
Expand Down Expand Up @@ -280,19 +307,6 @@ def connection_config_tunneling(self):
auto_reconnect=True,
)

def register_callbacks(self):
"""Register callbacks within XKNX object."""
if (
CONF_XKNX_FIRE_EVENT in self.config[DOMAIN]
and self.config[DOMAIN][CONF_XKNX_FIRE_EVENT]
):
address_filters = list(
map(AddressFilter, self.config[DOMAIN][CONF_XKNX_FIRE_EVENT_FILTER])
)
self.xknx.telegram_queue.register_telegram_received_cb(
self.telegram_received_cb, address_filters
)

@callback
def async_create_exposures(self):
"""Create exposures."""
Expand Down Expand Up @@ -340,6 +354,25 @@ async def telegram_received_cb(self, telegram):
},
)

def register_callback(self) -> TelegramQueue.Callback:
"""Register callback within XKNX TelegramQueue."""
address_filters = list(
map(AddressFilter, self.config[DOMAIN][CONF_XKNX_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_XKNX_ATTR_ADDRESS))
if call.data.get(SERVICE_XKNX_ATTR_REMOVE):
self._knx_event_callback.group_addresses.remove(group_address)
else:
self._knx_event_callback.group_addresses.append(group_address)

async def service_send_to_knx_bus(self, call):
"""Service for sending an arbitrary KNX message to the KNX bus."""
attr_payload = call.data.get(SERVICE_XKNX_ATTR_PAYLOAD)
Expand Down
8 changes: 8 additions & 0 deletions home-assistant-plugin/custom_components/xknx/services.yaml
Expand Up @@ -10,3 +10,11 @@ send:
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)."
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`."

0 comments on commit 92d70f5

Please sign in to comment.