From b583ded8b5c33aabc596bc5b7bfd8d1e25a512d6 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Tue, 23 Feb 2021 22:59:16 +0100 Subject: [PATCH] Fix KNX services.yaml (#46897) --- homeassistant/components/knx/__init__.py | 43 +++++++++++----------- homeassistant/components/knx/expose.py | 8 ++-- homeassistant/components/knx/schema.py | 3 +- homeassistant/components/knx/services.yaml | 19 +++++++--- 4 files changed, 42 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index 8716f03838c..f092bafe404 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -17,6 +17,7 @@ from xknx.telegram import AddressFilter, GroupAddress, Telegram from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite from homeassistant.const import ( + CONF_ADDRESS, CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP, @@ -64,7 +65,6 @@ CONF_KNX_RATE_LIMIT = "rate_limit" CONF_KNX_EXPOSE = "expose" SERVICE_KNX_SEND = "send" -SERVICE_KNX_ATTR_ADDRESS = "address" SERVICE_KNX_ATTR_PAYLOAD = "payload" SERVICE_KNX_ATTR_TYPE = "type" SERVICE_KNX_ATTR_REMOVE = "remove" @@ -146,7 +146,7 @@ CONFIG_SCHEMA = vol.Schema( SERVICE_KNX_SEND_SCHEMA = vol.Any( vol.Schema( { - vol.Required(SERVICE_KNX_ATTR_ADDRESS): cv.string, + vol.Required(CONF_ADDRESS): cv.string, vol.Required(SERVICE_KNX_ATTR_PAYLOAD): cv.match_all, vol.Required(SERVICE_KNX_ATTR_TYPE): vol.Any(int, float, str), } @@ -154,7 +154,7 @@ SERVICE_KNX_SEND_SCHEMA = vol.Any( vol.Schema( # without type given payload is treated as raw bytes { - vol.Required(SERVICE_KNX_ATTR_ADDRESS): cv.string, + vol.Required(CONF_ADDRESS): cv.string, vol.Required(SERVICE_KNX_ATTR_PAYLOAD): vol.Any( cv.positive_int, [cv.positive_int] ), @@ -164,7 +164,7 @@ SERVICE_KNX_SEND_SCHEMA = vol.Any( SERVICE_KNX_READ_SCHEMA = vol.Schema( { - vol.Required(SERVICE_KNX_ATTR_ADDRESS): vol.All( + vol.Required(CONF_ADDRESS): vol.All( cv.ensure_list, [cv.string], ) @@ -173,7 +173,7 @@ SERVICE_KNX_READ_SCHEMA = vol.Schema( SERVICE_KNX_EVENT_REGISTER_SCHEMA = vol.Schema( { - vol.Required(SERVICE_KNX_ATTR_ADDRESS): cv.string, + vol.Required(CONF_ADDRESS): cv.string, vol.Optional(SERVICE_KNX_ATTR_REMOVE, default=False): cv.boolean, } ) @@ -187,7 +187,7 @@ SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA = vol.Any( vol.Schema( # for removing only `address` is required { - vol.Required(SERVICE_KNX_ATTR_ADDRESS): cv.string, + vol.Required(CONF_ADDRESS): cv.string, vol.Required(SERVICE_KNX_ATTR_REMOVE): vol.All(cv.boolean, True), }, extra=vol.ALLOW_EXTRA, @@ -198,8 +198,9 @@ SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA = vol.Any( async def async_setup(hass, config): """Set up the KNX component.""" try: - hass.data[DOMAIN] = KNXModule(hass, config) - await hass.data[DOMAIN].start() + knx_module = KNXModule(hass, config) + hass.data[DOMAIN] = knx_module + await knx_module.start() except XKNXException as ex: _LOGGER.warning("Could not connect to KNX interface: %s", ex) hass.components.persistent_notification.async_create( @@ -208,14 +209,14 @@ async def async_setup(hass, config): if CONF_KNX_EXPOSE in config[DOMAIN]: for expose_config in config[DOMAIN][CONF_KNX_EXPOSE]: - hass.data[DOMAIN].exposures.append( - create_knx_exposure(hass, hass.data[DOMAIN].xknx, expose_config) + knx_module.exposures.append( + create_knx_exposure(hass, knx_module.xknx, expose_config) ) for platform in SupportedPlatforms: if platform.value in config[DOMAIN]: for device_config in config[DOMAIN][platform.value]: - create_knx_device(platform, hass.data[DOMAIN].xknx, device_config) + create_knx_device(platform, knx_module.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 for platform in SupportedPlatforms: @@ -223,7 +224,7 @@ async def async_setup(hass, config): discovery.async_load_platform(hass, platform.value, DOMAIN, {}, config) ) - if not hass.data[DOMAIN].xknx.devices: + if not knx_module.xknx.devices: _LOGGER.warning( "No KNX devices are configured. Please read " "https://www.home-assistant.io/blog/2020/09/17/release-115/#breaking-changes" @@ -232,14 +233,14 @@ async def async_setup(hass, config): hass.services.async_register( DOMAIN, SERVICE_KNX_SEND, - hass.data[DOMAIN].service_send_to_knx_bus, + knx_module.service_send_to_knx_bus, schema=SERVICE_KNX_SEND_SCHEMA, ) hass.services.async_register( DOMAIN, SERVICE_KNX_READ, - hass.data[DOMAIN].service_read_to_knx_bus, + knx_module.service_read_to_knx_bus, schema=SERVICE_KNX_READ_SCHEMA, ) @@ -247,7 +248,7 @@ async def async_setup(hass, config): hass, DOMAIN, SERVICE_KNX_EVENT_REGISTER, - hass.data[DOMAIN].service_event_register_modify, + knx_module.service_event_register_modify, schema=SERVICE_KNX_EVENT_REGISTER_SCHEMA, ) @@ -255,7 +256,7 @@ async def async_setup(hass, config): hass, DOMAIN, SERVICE_KNX_EXPOSURE_REGISTER, - hass.data[DOMAIN].service_exposure_register_modify, + knx_module.service_exposure_register_modify, schema=SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA, ) @@ -269,7 +270,7 @@ async def async_setup(hass, config): if not config or DOMAIN not in config: return - await hass.data[DOMAIN].xknx.stop() + await knx_module.xknx.stop() await asyncio.gather( *[platform.async_reset() for platform in async_get_platforms(hass, DOMAIN)] @@ -398,7 +399,7 @@ class KNXModule: 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)) + group_address = GroupAddress(call.data[CONF_ADDRESS]) if call.data.get(SERVICE_KNX_ATTR_REMOVE): try: self._knx_event_callback.group_addresses.remove(group_address) @@ -416,7 +417,7 @@ class KNXModule: async def service_exposure_register_modify(self, call): """Service for adding or removing an exposure to KNX bus.""" - group_address = call.data.get(SERVICE_KNX_ATTR_ADDRESS) + group_address = call.data.get(CONF_ADDRESS) if call.data.get(SERVICE_KNX_ATTR_REMOVE): try: @@ -448,7 +449,7 @@ class KNXModule: 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_KNX_ATTR_PAYLOAD) - attr_address = call.data.get(SERVICE_KNX_ATTR_ADDRESS) + attr_address = call.data.get(CONF_ADDRESS) attr_type = call.data.get(SERVICE_KNX_ATTR_TYPE) def calculate_payload(attr_payload): @@ -470,7 +471,7 @@ class KNXModule: async def service_read_to_knx_bus(self, call): """Service for sending a GroupValueRead telegram to the KNX bus.""" - for address in call.data.get(SERVICE_KNX_ATTR_ADDRESS): + for address in call.data.get(CONF_ADDRESS): telegram = Telegram( destination_address=GroupAddress(address), payload=GroupValueRead(), diff --git a/homeassistant/components/knx/expose.py b/homeassistant/components/knx/expose.py index 93abd7d7b43..5abc58f82cc 100644 --- a/homeassistant/components/knx/expose.py +++ b/homeassistant/components/knx/expose.py @@ -5,6 +5,7 @@ from xknx import XKNX from xknx.devices import DateTime, ExposeSensor from homeassistant.const import ( + CONF_ADDRESS, CONF_ENTITY_ID, STATE_OFF, STATE_ON, @@ -23,11 +24,11 @@ def create_knx_exposure( hass: HomeAssistant, xknx: XKNX, config: ConfigType ) -> Union["KNXExposeSensor", "KNXExposeTime"]: """Create exposures from config.""" - expose_type = config.get(ExposeSchema.CONF_KNX_EXPOSE_TYPE) - entity_id = config.get(CONF_ENTITY_ID) + address = config[CONF_ADDRESS] attribute = config.get(ExposeSchema.CONF_KNX_EXPOSE_ATTRIBUTE) + entity_id = config.get(CONF_ENTITY_ID) + expose_type = config.get(ExposeSchema.CONF_KNX_EXPOSE_TYPE) default = config.get(ExposeSchema.CONF_KNX_EXPOSE_DEFAULT) - address = config.get(ExposeSchema.CONF_KNX_EXPOSE_ADDRESS) exposure: Union["KNXExposeSensor", "KNXExposeTime"] if expose_type.lower() in ["time", "date", "datetime"]: @@ -83,6 +84,7 @@ class KNXExposeSensor: """Prepare for deletion.""" if self._remove_listener is not None: self._remove_listener() + self._remove_listener = None if self.device is not None: self.device.shutdown() diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index 61909013739..125115f28b2 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -327,7 +327,6 @@ class ExposeSchema: CONF_KNX_EXPOSE_TYPE = CONF_TYPE CONF_KNX_EXPOSE_ATTRIBUTE = "attribute" CONF_KNX_EXPOSE_DEFAULT = "default" - CONF_KNX_EXPOSE_ADDRESS = CONF_ADDRESS SCHEMA = vol.Schema( { @@ -335,7 +334,7 @@ class ExposeSchema: vol.Optional(CONF_ENTITY_ID): cv.entity_id, vol.Optional(CONF_KNX_EXPOSE_ATTRIBUTE): cv.string, vol.Optional(CONF_KNX_EXPOSE_DEFAULT): cv.match_all, - vol.Required(CONF_KNX_EXPOSE_ADDRESS): cv.string, + vol.Required(CONF_ADDRESS): cv.string, } ) diff --git a/homeassistant/components/knx/services.yaml b/homeassistant/components/knx/services.yaml index ef74acc49b1..3fae7dfce0e 100644 --- a/homeassistant/components/knx/services.yaml +++ b/homeassistant/components/knx/services.yaml @@ -1,4 +1,5 @@ send: + name: "Send to KNX bus" description: "Send arbitrary data directly to the KNX bus." fields: address: @@ -13,6 +14,8 @@ send: description: "Payload to send to the bus. Integers are treated as DPT 1/2/3 payloads. For DPTs > 6 bits send a list. Each value represents 1 octet (0-255). Pad with 0 to DPT byte length." required: true example: "[0, 4]" + selector: + object: type: name: "Value 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)." @@ -21,6 +24,7 @@ send: selector: text: read: + name: "Read from KNX bus" description: "Send GroupValueRead requests to the KNX bus. Response can be used from `knx_event` and will be processed in KNX entities." fields: address: @@ -31,6 +35,7 @@ read: selector: text: event_register: + name: "Register knx_event" 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: @@ -38,14 +43,16 @@ event_register: description: "Group address that shall be added or removed." required: true example: "1/1/0" + selector: + text: remove: name: "Remove event registration" description: "Optional. If `True` the group address will be removed." - required: false default: false selector: boolean: exposure_register: + name: "Expose to KNX bus" description: "Add or remove exposures to KNX bus. Only exposures added with this service can be removed." fields: address: @@ -72,19 +79,21 @@ exposure_register: attribute: name: "Entity attribute" description: "Optional. Attribute of the entity that shall be sent to the KNX bus. If not set the state will be sent. Eg. for a light the state is eigther “on” or “off” - with attribute you can expose its “brightness”." - required: false example: "brightness" + selector: + text: default: name: "Default value" description: "Optional. Default value to send to the bus if the state or attribute value is None. Eg. a light with state “off” has no brightness attribute so a default value of 0 could be used. If not set (or None) no value would be sent to the bus and a GroupReadRequest to the address would return the last known value." - required: false example: "0" + selector: + object: remove: name: "Remove exposure" description: "Optional. If `True` the exposure will be removed. Only `address` is required for removal." - required: false default: false selector: boolean: reload: - description: "Reload KNX configuration." + name: "Reload KNX configuration" + description: "Reload the KNX configuration from YAML."