From f2d99cb059187f06dba3b26e04f1254fc42e23e0 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Mon, 5 Aug 2024 12:34:48 +0200 Subject: [PATCH] Use KNX UI entity platform controller class (#123128) --- homeassistant/components/knx/binary_sensor.py | 4 +- homeassistant/components/knx/button.py | 4 +- homeassistant/components/knx/climate.py | 4 +- homeassistant/components/knx/cover.py | 4 +- homeassistant/components/knx/date.py | 4 +- homeassistant/components/knx/datetime.py | 4 +- homeassistant/components/knx/fan.py | 4 +- homeassistant/components/knx/knx_entity.py | 76 +++++++++++++------ homeassistant/components/knx/light.py | 39 +++++----- homeassistant/components/knx/notify.py | 4 +- homeassistant/components/knx/number.py | 4 +- homeassistant/components/knx/scene.py | 4 +- homeassistant/components/knx/select.py | 4 +- homeassistant/components/knx/sensor.py | 4 +- .../components/knx/storage/config_store.py | 54 +++++++------ homeassistant/components/knx/switch.py | 59 +++++++------- homeassistant/components/knx/text.py | 4 +- homeassistant/components/knx/time.py | 4 +- homeassistant/components/knx/weather.py | 4 +- 19 files changed, 165 insertions(+), 123 deletions(-) diff --git a/homeassistant/components/knx/binary_sensor.py b/homeassistant/components/knx/binary_sensor.py index ff15f725fae..7d80ca55bf6 100644 --- a/homeassistant/components/knx/binary_sensor.py +++ b/homeassistant/components/knx/binary_sensor.py @@ -24,7 +24,7 @@ from homeassistant.helpers.typing import ConfigType from . import KNXModule from .const import ATTR_COUNTER, ATTR_SOURCE, DATA_KNX_CONFIG, DOMAIN -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity from .schema import BinarySensorSchema @@ -43,7 +43,7 @@ async def async_setup_entry( ) -class KNXBinarySensor(KnxEntity, BinarySensorEntity, RestoreEntity): +class KNXBinarySensor(KnxYamlEntity, BinarySensorEntity, RestoreEntity): """Representation of a KNX binary sensor.""" _device: XknxBinarySensor diff --git a/homeassistant/components/knx/button.py b/homeassistant/components/knx/button.py index 2eb68eebe43..f6627fc527b 100644 --- a/homeassistant/components/knx/button.py +++ b/homeassistant/components/knx/button.py @@ -13,7 +13,7 @@ from homeassistant.helpers.typing import ConfigType from . import KNXModule from .const import CONF_PAYLOAD_LENGTH, DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity async def async_setup_entry( @@ -31,7 +31,7 @@ async def async_setup_entry( ) -class KNXButton(KnxEntity, ButtonEntity): +class KNXButton(KnxYamlEntity, ButtonEntity): """Representation of a KNX button.""" _device: XknxRawValue diff --git a/homeassistant/components/knx/climate.py b/homeassistant/components/knx/climate.py index 7470d60ef4b..9abc9023617 100644 --- a/homeassistant/components/knx/climate.py +++ b/homeassistant/components/knx/climate.py @@ -35,7 +35,7 @@ from .const import ( DOMAIN, PRESET_MODES, ) -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity from .schema import ClimateSchema ATTR_COMMAND_VALUE = "command_value" @@ -133,7 +133,7 @@ def _create_climate(xknx: XKNX, config: ConfigType) -> XknxClimate: ) -class KNXClimate(KnxEntity, ClimateEntity): +class KNXClimate(KnxYamlEntity, ClimateEntity): """Representation of a KNX climate device.""" _device: XknxClimate diff --git a/homeassistant/components/knx/cover.py b/homeassistant/components/knx/cover.py index 1962db0ad3f..408f746e094 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -27,7 +27,7 @@ from homeassistant.helpers.typing import ConfigType from . import KNXModule from .const import DATA_KNX_CONFIG, DOMAIN -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity from .schema import CoverSchema @@ -43,7 +43,7 @@ async def async_setup_entry( async_add_entities(KNXCover(knx_module, entity_config) for entity_config in config) -class KNXCover(KnxEntity, CoverEntity): +class KNXCover(KnxYamlEntity, CoverEntity): """Representation of a KNX cover.""" _device: XknxCover diff --git a/homeassistant/components/knx/date.py b/homeassistant/components/knx/date.py index 80fea63d0a6..9f04a4acd7e 100644 --- a/homeassistant/components/knx/date.py +++ b/homeassistant/components/knx/date.py @@ -31,7 +31,7 @@ from .const import ( DOMAIN, KNX_ADDRESS, ) -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity async def async_setup_entry( @@ -61,7 +61,7 @@ def _create_xknx_device(xknx: XKNX, config: ConfigType) -> XknxDateDevice: ) -class KNXDateEntity(KnxEntity, DateEntity, RestoreEntity): +class KNXDateEntity(KnxYamlEntity, DateEntity, RestoreEntity): """Representation of a KNX date.""" _device: XknxDateDevice diff --git a/homeassistant/components/knx/datetime.py b/homeassistant/components/knx/datetime.py index 16ccb7474a7..8f1a25e6e3c 100644 --- a/homeassistant/components/knx/datetime.py +++ b/homeassistant/components/knx/datetime.py @@ -32,7 +32,7 @@ from .const import ( DOMAIN, KNX_ADDRESS, ) -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity async def async_setup_entry( @@ -62,7 +62,7 @@ def _create_xknx_device(xknx: XKNX, config: ConfigType) -> XknxDateTimeDevice: ) -class KNXDateTimeEntity(KnxEntity, DateTimeEntity, RestoreEntity): +class KNXDateTimeEntity(KnxYamlEntity, DateTimeEntity, RestoreEntity): """Representation of a KNX datetime.""" _device: XknxDateTimeDevice diff --git a/homeassistant/components/knx/fan.py b/homeassistant/components/knx/fan.py index 940e241ccda..6fd87be97d1 100644 --- a/homeassistant/components/knx/fan.py +++ b/homeassistant/components/knx/fan.py @@ -21,7 +21,7 @@ from homeassistant.util.scaling import int_states_in_range from . import KNXModule from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity from .schema import FanSchema DEFAULT_PERCENTAGE: Final = 50 @@ -39,7 +39,7 @@ async def async_setup_entry( async_add_entities(KNXFan(knx_module, entity_config) for entity_config in config) -class KNXFan(KnxEntity, FanEntity): +class KNXFan(KnxYamlEntity, FanEntity): """Representation of a KNX fan.""" _device: XknxFan diff --git a/homeassistant/components/knx/knx_entity.py b/homeassistant/components/knx/knx_entity.py index 2b8d2e71186..c81a6ee06db 100644 --- a/homeassistant/components/knx/knx_entity.py +++ b/homeassistant/components/knx/knx_entity.py @@ -2,30 +2,55 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any from xknx.devices import Device as XknxDevice -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity - -from .const import DOMAIN +from homeassistant.helpers.entity_platform import EntityPlatform +from homeassistant.helpers.entity_registry import RegistryEntry if TYPE_CHECKING: from . import KNXModule -SIGNAL_ENTITY_REMOVE = f"{DOMAIN}_entity_remove_signal.{{}}" +from .storage.config_store import PlatformControllerBase -class KnxEntity(Entity): +class KnxUiEntityPlatformController(PlatformControllerBase): + """Class to manage dynamic adding and reloading of UI entities.""" + + def __init__( + self, + knx_module: KNXModule, + entity_platform: EntityPlatform, + entity_class: type[KnxUiEntity], + ) -> None: + """Initialize the UI platform.""" + self._knx_module = knx_module + self._entity_platform = entity_platform + self._entity_class = entity_class + + async def create_entity(self, unique_id: str, config: dict[str, Any]) -> None: + """Add a new UI entity.""" + await self._entity_platform.async_add_entities( + [self._entity_class(self._knx_module, unique_id, config)] + ) + + async def update_entity( + self, entity_entry: RegistryEntry, config: dict[str, Any] + ) -> None: + """Update an existing UI entities configuration.""" + await self._entity_platform.async_remove_entity(entity_entry.entity_id) + await self.create_entity(unique_id=entity_entry.unique_id, config=config) + + +class _KnxEntityBase(Entity): """Representation of a KNX entity.""" _attr_should_poll = False - - def __init__(self, knx_module: KNXModule, device: XknxDevice) -> None: - """Set up device.""" - self._knx_module = knx_module - self._device = device + _knx_module: KNXModule + _device: XknxDevice @property def name(self) -> str: @@ -49,7 +74,7 @@ class KnxEntity(Entity): """Store register state change callback and start device object.""" self._device.register_device_updated_cb(self.after_update_callback) self._device.xknx.devices.async_add(self._device) - # super call needed to have methods of mulit-inherited classes called + # super call needed to have methods of multi-inherited classes called # eg. for restoring state (like _KNXSwitch) await super().async_added_to_hass() @@ -59,19 +84,22 @@ class KnxEntity(Entity): self._device.xknx.devices.async_remove(self._device) -class KnxUIEntity(KnxEntity): +class KnxYamlEntity(_KnxEntityBase): + """Representation of a KNX entity configured from YAML.""" + + def __init__(self, knx_module: KNXModule, device: XknxDevice) -> None: + """Initialize the YAML entity.""" + self._knx_module = knx_module + self._device = device + + +class KnxUiEntity(_KnxEntityBase, ABC): """Representation of a KNX UI entity.""" _attr_unique_id: str - async def async_added_to_hass(self) -> None: - """Register callbacks when entity added to hass.""" - await super().async_added_to_hass() - self._knx_module.config_store.entities.add(self._attr_unique_id) - self.async_on_remove( - async_dispatcher_connect( - self.hass, - SIGNAL_ENTITY_REMOVE.format(self._attr_unique_id), - self.async_remove, - ) - ) + @abstractmethod + def __init__( + self, knx_module: KNXModule, unique_id: str, config: dict[str, Any] + ) -> None: + """Initialize the UI entity.""" diff --git a/homeassistant/components/knx/light.py b/homeassistant/components/knx/light.py index 1197f09354b..a2ce8f8d2cb 100644 --- a/homeassistant/components/knx/light.py +++ b/homeassistant/components/knx/light.py @@ -19,15 +19,18 @@ from homeassistant.components.light import ( LightEntity, ) from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, Platform -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo -from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.entity_platform import ( + AddEntitiesCallback, + async_get_current_platform, +) from homeassistant.helpers.typing import ConfigType import homeassistant.util.color as color_util from . import KNXModule from .const import CONF_SYNC_STATE, DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS, ColorTempModes -from .knx_entity import KnxEntity, KnxUIEntity +from .knx_entity import KnxUiEntity, KnxUiEntityPlatformController, KnxYamlEntity from .schema import LightSchema from .storage.const import ( CONF_COLOR_TEMP_MAX, @@ -63,8 +66,17 @@ async def async_setup_entry( ) -> None: """Set up light(s) for KNX platform.""" knx_module: KNXModule = hass.data[DOMAIN] + platform = async_get_current_platform() + knx_module.config_store.add_platform( + platform=Platform.LIGHT, + controller=KnxUiEntityPlatformController( + knx_module=knx_module, + entity_platform=platform, + entity_class=KnxUiLight, + ), + ) - entities: list[KnxEntity] = [] + entities: list[KnxYamlEntity | KnxUiEntity] = [] if yaml_platform_config := hass.data[DATA_KNX_CONFIG].get(Platform.LIGHT): entities.extend( KnxYamlLight(knx_module, entity_config) @@ -78,13 +90,6 @@ async def async_setup_entry( if entities: async_add_entities(entities) - @callback - def add_new_ui_light(unique_id: str, config: dict[str, Any]) -> None: - """Add KNX entity at runtime.""" - async_add_entities([KnxUiLight(knx_module, unique_id, config)]) - - knx_module.config_store.async_add_entity[Platform.LIGHT] = add_new_ui_light - def _create_yaml_light(xknx: XKNX, config: ConfigType) -> XknxLight: """Return a KNX Light device to be used within XKNX.""" @@ -519,7 +524,7 @@ class _KnxLight(LightEntity): await self._device.set_off() -class KnxYamlLight(_KnxLight, KnxEntity): +class KnxYamlLight(_KnxLight, KnxYamlEntity): """Representation of a KNX light.""" _device: XknxLight @@ -546,7 +551,7 @@ class KnxYamlLight(_KnxLight, KnxEntity): ) -class KnxUiLight(_KnxLight, KnxUIEntity): +class KnxUiLight(_KnxLight, KnxUiEntity): """Representation of a KNX light.""" _attr_has_entity_name = True @@ -556,11 +561,9 @@ class KnxUiLight(_KnxLight, KnxUIEntity): self, knx_module: KNXModule, unique_id: str, config: ConfigType ) -> None: """Initialize of KNX light.""" - super().__init__( - knx_module=knx_module, - device=_create_ui_light( - knx_module.xknx, config[DOMAIN], config[CONF_ENTITY][CONF_NAME] - ), + self._knx_module = knx_module + self._device = _create_ui_light( + knx_module.xknx, config[DOMAIN], config[CONF_ENTITY][CONF_NAME] ) self._attr_max_color_temp_kelvin: int = config[DOMAIN][CONF_COLOR_TEMP_MAX] self._attr_min_color_temp_kelvin: int = config[DOMAIN][CONF_COLOR_TEMP_MIN] diff --git a/homeassistant/components/knx/notify.py b/homeassistant/components/knx/notify.py index b349681990c..173ab3119a0 100644 --- a/homeassistant/components/knx/notify.py +++ b/homeassistant/components/knx/notify.py @@ -20,7 +20,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import KNXModule from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity async def async_get_service( @@ -103,7 +103,7 @@ def _create_notification_instance(xknx: XKNX, config: ConfigType) -> XknxNotific ) -class KNXNotify(KnxEntity, NotifyEntity): +class KNXNotify(KnxYamlEntity, NotifyEntity): """Representation of a KNX notification entity.""" _device: XknxNotification diff --git a/homeassistant/components/knx/number.py b/homeassistant/components/knx/number.py index 3d4af503dff..cbbe91aba54 100644 --- a/homeassistant/components/knx/number.py +++ b/homeassistant/components/knx/number.py @@ -30,7 +30,7 @@ from .const import ( DOMAIN, KNX_ADDRESS, ) -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity from .schema import NumberSchema @@ -58,7 +58,7 @@ def _create_numeric_value(xknx: XKNX, config: ConfigType) -> NumericValue: ) -class KNXNumber(KnxEntity, RestoreNumber): +class KNXNumber(KnxYamlEntity, RestoreNumber): """Representation of a KNX number.""" _device: NumericValue diff --git a/homeassistant/components/knx/scene.py b/homeassistant/components/knx/scene.py index fc37f36dd01..2de832ae54a 100644 --- a/homeassistant/components/knx/scene.py +++ b/homeassistant/components/knx/scene.py @@ -15,7 +15,7 @@ from homeassistant.helpers.typing import ConfigType from . import KNXModule from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity from .schema import SceneSchema @@ -31,7 +31,7 @@ async def async_setup_entry( async_add_entities(KNXScene(knx_module, entity_config) for entity_config in config) -class KNXScene(KnxEntity, Scene): +class KNXScene(KnxYamlEntity, Scene): """Representation of a KNX scene.""" _device: XknxScene diff --git a/homeassistant/components/knx/select.py b/homeassistant/components/knx/select.py index 1b862010c2a..6c73bf8d573 100644 --- a/homeassistant/components/knx/select.py +++ b/homeassistant/components/knx/select.py @@ -30,7 +30,7 @@ from .const import ( DOMAIN, KNX_ADDRESS, ) -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity from .schema import SelectSchema @@ -59,7 +59,7 @@ def _create_raw_value(xknx: XKNX, config: ConfigType) -> RawValue: ) -class KNXSelect(KnxEntity, SelectEntity, RestoreEntity): +class KNXSelect(KnxYamlEntity, SelectEntity, RestoreEntity): """Representation of a KNX select.""" _device: RawValue diff --git a/homeassistant/components/knx/sensor.py b/homeassistant/components/knx/sensor.py index ab363e2a35f..a28c1a339e6 100644 --- a/homeassistant/components/knx/sensor.py +++ b/homeassistant/components/knx/sensor.py @@ -35,7 +35,7 @@ from homeassistant.util.enum import try_parse_enum from . import KNXModule from .const import ATTR_SOURCE, DATA_KNX_CONFIG, DOMAIN -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity from .schema import SensorSchema SCAN_INTERVAL = timedelta(seconds=10) @@ -141,7 +141,7 @@ def _create_sensor(xknx: XKNX, config: ConfigType) -> XknxSensor: ) -class KNXSensor(KnxEntity, SensorEntity): +class KNXSensor(KnxYamlEntity, SensorEntity): """Representation of a KNX sensor.""" _device: XknxSensor diff --git a/homeassistant/components/knx/storage/config_store.py b/homeassistant/components/knx/storage/config_store.py index 876fe19a4b9..ce7a705e629 100644 --- a/homeassistant/components/knx/storage/config_store.py +++ b/homeassistant/components/knx/storage/config_store.py @@ -1,6 +1,6 @@ """KNX entity configuration store.""" -from collections.abc import Callable +from abc import ABC, abstractmethod import logging from typing import Any, Final, TypedDict @@ -8,12 +8,10 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PLATFORM, Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.storage import Store from homeassistant.util.ulid import ulid_now from ..const import DOMAIN -from ..knx_entity import SIGNAL_ENTITY_REMOVE from .const import CONF_DATA _LOGGER = logging.getLogger(__name__) @@ -33,6 +31,20 @@ class KNXConfigStoreModel(TypedDict): entities: KNXEntityStoreModel +class PlatformControllerBase(ABC): + """Entity platform controller base class.""" + + @abstractmethod + async def create_entity(self, unique_id: str, config: dict[str, Any]) -> None: + """Create a new entity.""" + + @abstractmethod + async def update_entity( + self, entity_entry: er.RegistryEntry, config: dict[str, Any] + ) -> None: + """Update an existing entities configuration.""" + + class KNXConfigStore: """Manage KNX config store data.""" @@ -46,12 +58,7 @@ class KNXConfigStore: self.config_entry = config_entry self._store = Store[KNXConfigStoreModel](hass, STORAGE_VERSION, STORAGE_KEY) self.data = KNXConfigStoreModel(entities={}) - - # entities and async_add_entity are filled by platform / entity setups - self.entities: set[str] = set() # unique_id as values - self.async_add_entity: dict[ - Platform, Callable[[str, dict[str, Any]], None] - ] = {} + self._platform_controllers: dict[Platform, PlatformControllerBase] = {} async def load_data(self) -> None: """Load config store data from storage.""" @@ -62,14 +69,19 @@ class KNXConfigStore: len(self.data["entities"]), ) + def add_platform( + self, platform: Platform, controller: PlatformControllerBase + ) -> None: + """Add platform controller.""" + self._platform_controllers[platform] = controller + async def create_entity( self, platform: Platform, data: dict[str, Any] ) -> str | None: """Create a new entity.""" - if platform not in self.async_add_entity: - raise ConfigStoreException(f"Entity platform not ready: {platform}") + platform_controller = self._platform_controllers[platform] unique_id = f"knx_es_{ulid_now()}" - self.async_add_entity[platform](unique_id, data) + await platform_controller.create_entity(unique_id, data) # store data after entity was added to be sure config didn't raise exceptions self.data["entities"].setdefault(platform, {})[unique_id] = data await self._store.async_save(self.data) @@ -95,8 +107,7 @@ class KNXConfigStore: self, platform: Platform, entity_id: str, data: dict[str, Any] ) -> None: """Update an existing entity.""" - if platform not in self.async_add_entity: - raise ConfigStoreException(f"Entity platform not ready: {platform}") + platform_controller = self._platform_controllers[platform] entity_registry = er.async_get(self.hass) if (entry := entity_registry.async_get(entity_id)) is None: raise ConfigStoreException(f"Entity not found: {entity_id}") @@ -108,8 +119,7 @@ class KNXConfigStore: raise ConfigStoreException( f"Entity not found in storage: {entity_id} - {unique_id}" ) - async_dispatcher_send(self.hass, SIGNAL_ENTITY_REMOVE.format(unique_id)) - self.async_add_entity[platform](unique_id, data) + await platform_controller.update_entity(entry, data) # store data after entity is added to make sure config doesn't raise exceptions self.data["entities"][platform][unique_id] = data await self._store.async_save(self.data) @@ -125,23 +135,21 @@ class KNXConfigStore: raise ConfigStoreException( f"Entity not found in {entry.domain}: {entry.unique_id}" ) from err - try: - self.entities.remove(entry.unique_id) - except KeyError: - _LOGGER.warning("Entity not initialized when deleted: %s", entity_id) entity_registry.async_remove(entity_id) await self._store.async_save(self.data) def get_entity_entries(self) -> list[er.RegistryEntry]: - """Get entity_ids of all configured entities by platform.""" + """Get entity_ids of all UI configured entities.""" entity_registry = er.async_get(self.hass) - + unique_ids = { + uid for platform in self.data["entities"].values() for uid in platform + } return [ registry_entry for registry_entry in er.async_entries_for_config_entry( entity_registry, self.config_entry.entry_id ) - if registry_entry.unique_id in self.entities + if registry_entry.unique_id in unique_ids ] diff --git a/homeassistant/components/knx/switch.py b/homeassistant/components/knx/switch.py index a5f430e6157..ebe930957d6 100644 --- a/homeassistant/components/knx/switch.py +++ b/homeassistant/components/knx/switch.py @@ -17,9 +17,12 @@ from homeassistant.const import ( STATE_UNKNOWN, Platform, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo -from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.entity_platform import ( + AddEntitiesCallback, + async_get_current_platform, +) from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType @@ -32,7 +35,7 @@ from .const import ( DOMAIN, KNX_ADDRESS, ) -from .knx_entity import KnxEntity, KnxUIEntity +from .knx_entity import KnxUiEntity, KnxUiEntityPlatformController, KnxYamlEntity from .schema import SwitchSchema from .storage.const import ( CONF_DEVICE_INFO, @@ -51,8 +54,17 @@ async def async_setup_entry( ) -> None: """Set up switch(es) for KNX platform.""" knx_module: KNXModule = hass.data[DOMAIN] + platform = async_get_current_platform() + knx_module.config_store.add_platform( + platform=Platform.SWITCH, + controller=KnxUiEntityPlatformController( + knx_module=knx_module, + entity_platform=platform, + entity_class=KnxUiSwitch, + ), + ) - entities: list[KnxEntity] = [] + entities: list[KnxYamlEntity | KnxUiEntity] = [] if yaml_platform_config := hass.data[DATA_KNX_CONFIG].get(Platform.SWITCH): entities.extend( KnxYamlSwitch(knx_module, entity_config) @@ -66,13 +78,6 @@ async def async_setup_entry( if entities: async_add_entities(entities) - @callback - def add_new_ui_switch(unique_id: str, config: dict[str, Any]) -> None: - """Add KNX entity at runtime.""" - async_add_entities([KnxUiSwitch(knx_module, unique_id, config)]) - - knx_module.config_store.async_add_entity[Platform.SWITCH] = add_new_ui_switch - class _KnxSwitch(SwitchEntity, RestoreEntity): """Base class for a KNX switch.""" @@ -102,7 +107,7 @@ class _KnxSwitch(SwitchEntity, RestoreEntity): await self._device.set_off() -class KnxYamlSwitch(_KnxSwitch, KnxEntity): +class KnxYamlSwitch(_KnxSwitch, KnxYamlEntity): """Representation of a KNX switch configured from YAML.""" _device: XknxSwitch @@ -125,7 +130,7 @@ class KnxYamlSwitch(_KnxSwitch, KnxEntity): self._attr_unique_id = str(self._device.switch.group_address) -class KnxUiSwitch(_KnxSwitch, KnxUIEntity): +class KnxUiSwitch(_KnxSwitch, KnxUiEntity): """Representation of a KNX switch configured from UI.""" _attr_has_entity_name = True @@ -134,21 +139,19 @@ class KnxUiSwitch(_KnxSwitch, KnxUIEntity): def __init__( self, knx_module: KNXModule, unique_id: str, config: dict[str, Any] ) -> None: - """Initialize of KNX switch.""" - super().__init__( - knx_module=knx_module, - device=XknxSwitch( - knx_module.xknx, - name=config[CONF_ENTITY][CONF_NAME], - group_address=config[DOMAIN][CONF_GA_SWITCH][CONF_GA_WRITE], - group_address_state=[ - config[DOMAIN][CONF_GA_SWITCH][CONF_GA_STATE], - *config[DOMAIN][CONF_GA_SWITCH][CONF_GA_PASSIVE], - ], - respond_to_read=config[DOMAIN][CONF_RESPOND_TO_READ], - sync_state=config[DOMAIN][CONF_SYNC_STATE], - invert=config[DOMAIN][CONF_INVERT], - ), + """Initialize KNX switch.""" + self._knx_module = knx_module + self._device = XknxSwitch( + knx_module.xknx, + name=config[CONF_ENTITY][CONF_NAME], + group_address=config[DOMAIN][CONF_GA_SWITCH][CONF_GA_WRITE], + group_address_state=[ + config[DOMAIN][CONF_GA_SWITCH][CONF_GA_STATE], + *config[DOMAIN][CONF_GA_SWITCH][CONF_GA_PASSIVE], + ], + respond_to_read=config[DOMAIN][CONF_RESPOND_TO_READ], + sync_state=config[DOMAIN][CONF_SYNC_STATE], + invert=config[DOMAIN][CONF_INVERT], ) self._attr_entity_category = config[CONF_ENTITY][CONF_ENTITY_CATEGORY] self._attr_unique_id = unique_id diff --git a/homeassistant/components/knx/text.py b/homeassistant/components/knx/text.py index 9bca37434ac..381cb95ad32 100644 --- a/homeassistant/components/knx/text.py +++ b/homeassistant/components/knx/text.py @@ -30,7 +30,7 @@ from .const import ( DOMAIN, KNX_ADDRESS, ) -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity async def async_setup_entry( @@ -57,7 +57,7 @@ def _create_notification(xknx: XKNX, config: ConfigType) -> XknxNotification: ) -class KNXText(KnxEntity, TextEntity, RestoreEntity): +class KNXText(KnxYamlEntity, TextEntity, RestoreEntity): """Representation of a KNX text.""" _device: XknxNotification diff --git a/homeassistant/components/knx/time.py b/homeassistant/components/knx/time.py index 5d9225a1e41..b4e562a8869 100644 --- a/homeassistant/components/knx/time.py +++ b/homeassistant/components/knx/time.py @@ -31,7 +31,7 @@ from .const import ( DOMAIN, KNX_ADDRESS, ) -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity async def async_setup_entry( @@ -61,7 +61,7 @@ def _create_xknx_device(xknx: XKNX, config: ConfigType) -> XknxTimeDevice: ) -class KNXTimeEntity(KnxEntity, TimeEntity, RestoreEntity): +class KNXTimeEntity(KnxYamlEntity, TimeEntity, RestoreEntity): """Representation of a KNX time.""" _device: XknxTimeDevice diff --git a/homeassistant/components/knx/weather.py b/homeassistant/components/knx/weather.py index 11dae452e2f..99f4be962fe 100644 --- a/homeassistant/components/knx/weather.py +++ b/homeassistant/components/knx/weather.py @@ -21,7 +21,7 @@ from homeassistant.helpers.typing import ConfigType from . import KNXModule from .const import DATA_KNX_CONFIG, DOMAIN -from .knx_entity import KnxEntity +from .knx_entity import KnxYamlEntity from .schema import WeatherSchema @@ -75,7 +75,7 @@ def _create_weather(xknx: XKNX, config: ConfigType) -> XknxWeather: ) -class KNXWeather(KnxEntity, WeatherEntity): +class KNXWeather(KnxYamlEntity, WeatherEntity): """Representation of a KNX weather device.""" _device: XknxWeather