1 """KNX integration services."""
3 from __future__
import annotations
5 from functools
import partial
7 from typing
import TYPE_CHECKING
9 import voluptuous
as vol
10 from xknx.dpt
import DPTArray, DPTBase, DPTBinary
11 from xknx.exceptions
import ConversionError
12 from xknx.telegram
import Telegram
13 from xknx.telegram.address
import parse_device_group_address
14 from xknx.telegram.apci
import GroupValueRead, GroupValueResponse, GroupValueWrite
26 SERVICE_KNX_ATTR_PAYLOAD,
27 SERVICE_KNX_ATTR_REMOVE,
28 SERVICE_KNX_ATTR_RESPONSE,
29 SERVICE_KNX_ATTR_TYPE,
30 SERVICE_KNX_EVENT_REGISTER,
31 SERVICE_KNX_EXPOSURE_REGISTER,
35 from .expose
import create_knx_exposure
36 from .schema
import ExposeSchema, dpt_base_type_validator, ga_validator
39 from .
import KNXModule
41 _LOGGER = logging.getLogger(__name__)
46 """Register KNX integration services."""
47 hass.services.async_register(
50 partial(service_send_to_knx_bus, hass),
51 schema=SERVICE_KNX_SEND_SCHEMA,
54 hass.services.async_register(
57 partial(service_read_to_knx_bus, hass),
58 schema=SERVICE_KNX_READ_SCHEMA,
64 SERVICE_KNX_EVENT_REGISTER,
65 partial(service_event_register_modify, hass),
66 schema=SERVICE_KNX_EVENT_REGISTER_SCHEMA,
72 SERVICE_KNX_EXPOSURE_REGISTER,
73 partial(service_exposure_register_modify, hass),
74 schema=SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA,
81 partial(service_reload_integration, hass),
87 """Return KNXModule instance."""
89 return hass.data[KNX_MODULE_KEY]
90 except KeyError
as err:
94 SERVICE_KNX_EVENT_REGISTER_SCHEMA = vol.Schema(
96 vol.Required(KNX_ADDRESS): vol.All(
100 vol.Optional(CONF_TYPE): dpt_base_type_validator,
101 vol.Optional(SERVICE_KNX_ATTR_REMOVE, default=
False): cv.boolean,
107 """Service for adding or removing a GroupAddress to the knx_event filter."""
110 attr_address = call.data[KNX_ADDRESS]
111 group_addresses =
list(map(parse_device_group_address, attr_address))
113 if call.data.get(SERVICE_KNX_ATTR_REMOVE):
114 for group_address
in group_addresses:
116 knx_module.knx_event_callback.group_addresses.remove(group_address)
119 "Service event_register could not remove event for '%s'",
122 if group_address
in knx_module.group_address_transcoder:
123 del knx_module.group_address_transcoder[group_address]
126 if (dpt := call.data.get(CONF_TYPE))
and (
127 transcoder := DPTBase.parse_transcoder(dpt)
129 knx_module.group_address_transcoder.update(
130 {_address: transcoder
for _address
in group_addresses}
132 for group_address
in group_addresses:
133 if group_address
in knx_module.knx_event_callback.group_addresses:
135 knx_module.knx_event_callback.group_addresses.append(group_address)
137 "Service event_register registered event for '%s'",
142 SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA = vol.Any(
143 ExposeSchema.EXPOSE_SENSOR_SCHEMA.extend(
145 vol.Optional(SERVICE_KNX_ATTR_REMOVE, default=
False): cv.boolean,
151 vol.Required(KNX_ADDRESS): ga_validator,
152 vol.Required(SERVICE_KNX_ATTR_REMOVE): vol.All(cv.boolean,
True),
154 extra=vol.ALLOW_EXTRA,
160 hass: HomeAssistant, call: ServiceCall
162 """Service for adding or removing an exposure to KNX bus."""
165 group_address = call.data[KNX_ADDRESS]
167 if call.data.get(SERVICE_KNX_ATTR_REMOVE):
169 removed_exposure = knx_module.service_exposures.pop(group_address)
170 except KeyError
as err:
172 f
"Could not find exposure for '{group_address}' to remove."
175 removed_exposure.async_remove()
178 if group_address
in knx_module.service_exposures:
179 replaced_exposure = knx_module.service_exposures.pop(group_address)
182 "Service exposure_register replacing already registered exposure"
186 replaced_exposure.device.name,
188 replaced_exposure.async_remove()
190 knx_module.service_exposures[group_address] = exposure
192 "Service exposure_register registered exposure for '%s' - %s",
194 exposure.device.name,
198 SERVICE_KNX_SEND_SCHEMA = vol.Any(
201 vol.Required(KNX_ADDRESS): vol.All(
205 vol.Required(SERVICE_KNX_ATTR_PAYLOAD): cv.match_all,
206 vol.Required(SERVICE_KNX_ATTR_TYPE): dpt_base_type_validator,
207 vol.Optional(SERVICE_KNX_ATTR_RESPONSE, default=
False): cv.boolean,
213 vol.Required(KNX_ADDRESS): vol.All(
217 vol.Required(SERVICE_KNX_ATTR_PAYLOAD): vol.Any(
218 cv.positive_int, [cv.positive_int]
220 vol.Optional(SERVICE_KNX_ATTR_RESPONSE, default=
False): cv.boolean,
227 """Service for sending an arbitrary KNX message to the KNX bus."""
230 attr_address = call.data[KNX_ADDRESS]
231 attr_payload = call.data[SERVICE_KNX_ATTR_PAYLOAD]
232 attr_type = call.data.get(SERVICE_KNX_ATTR_TYPE)
233 attr_response = call.data[SERVICE_KNX_ATTR_RESPONSE]
235 payload: DPTBinary | DPTArray
236 if attr_type
is not None:
237 transcoder = DPTBase.parse_transcoder(attr_type)
238 if transcoder
is None:
240 f
"Invalid type for knx.send service: {attr_type}"
243 payload = transcoder.to_knx(attr_payload)
244 except ConversionError
as err:
246 f
"Invalid payload for knx.send service: {err}"
248 elif isinstance(attr_payload, int):
249 payload = DPTBinary(attr_payload)
251 payload = DPTArray(attr_payload)
253 for address
in attr_address:
255 destination_address=parse_device_group_address(address),
256 payload=GroupValueResponse(payload)
258 else GroupValueWrite(payload),
259 source_address=knx_module.xknx.current_address,
261 await knx_module.xknx.telegrams.put(telegram)
264 SERVICE_KNX_READ_SCHEMA = vol.Schema(
266 vol.Required(KNX_ADDRESS): vol.All(
275 """Service for sending a GroupValueRead telegram to the KNX bus."""
278 for address
in call.data[KNX_ADDRESS]:
280 destination_address=parse_device_group_address(address),
281 payload=GroupValueRead(),
282 source_address=knx_module.xknx.current_address,
284 await knx_module.xknx.telegrams.put(telegram)
288 """Reload the integration."""
290 await hass.config_entries.async_reload(knx_module.entry.entry_id)
291 hass.bus.async_fire(f
"event_{DOMAIN}_reloaded", context=call.context)
KNXExposeSensor|KNXExposeTime create_knx_exposure(HomeAssistant hass, XKNX xknx, ConfigType config)
None service_exposure_register_modify(HomeAssistant hass, ServiceCall call)
None service_reload_integration(HomeAssistant hass, ServiceCall call)
None service_event_register_modify(HomeAssistant hass, ServiceCall call)
KNXModule get_knx_module(HomeAssistant hass)
None service_send_to_knx_bus(HomeAssistant hass, ServiceCall call)
None register_knx_services(HomeAssistant hass)
None service_read_to_knx_bus(HomeAssistant hass, ServiceCall call)
None async_register_admin_service(HomeAssistant hass, str domain, str service, Callable[[ServiceCall], Awaitable[None]|None] service_func, VolSchemaType schema=vol.Schema({}, extra=vol.PREVENT_EXTRA))