Use KNX UI entity platform controller class (#123128)

This commit is contained in:
Matthias Alphart 2024-08-05 12:34:48 +02:00 committed by GitHub
parent 5b7fd29797
commit f2d99cb059
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 165 additions and 123 deletions

View File

@ -24,7 +24,7 @@ from homeassistant.helpers.typing import ConfigType
from . import KNXModule from . import KNXModule
from .const import ATTR_COUNTER, ATTR_SOURCE, DATA_KNX_CONFIG, DOMAIN 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 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.""" """Representation of a KNX binary sensor."""
_device: XknxBinarySensor _device: XknxBinarySensor

View File

@ -13,7 +13,7 @@ from homeassistant.helpers.typing import ConfigType
from . import KNXModule from . import KNXModule
from .const import CONF_PAYLOAD_LENGTH, DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS 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( 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.""" """Representation of a KNX button."""
_device: XknxRawValue _device: XknxRawValue

View File

@ -35,7 +35,7 @@ from .const import (
DOMAIN, DOMAIN,
PRESET_MODES, PRESET_MODES,
) )
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
from .schema import ClimateSchema from .schema import ClimateSchema
ATTR_COMMAND_VALUE = "command_value" 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.""" """Representation of a KNX climate device."""
_device: XknxClimate _device: XknxClimate

View File

@ -27,7 +27,7 @@ from homeassistant.helpers.typing import ConfigType
from . import KNXModule from . import KNXModule
from .const import DATA_KNX_CONFIG, DOMAIN from .const import DATA_KNX_CONFIG, DOMAIN
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
from .schema import CoverSchema 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) 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.""" """Representation of a KNX cover."""
_device: XknxCover _device: XknxCover

View File

@ -31,7 +31,7 @@ from .const import (
DOMAIN, DOMAIN,
KNX_ADDRESS, KNX_ADDRESS,
) )
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
async def async_setup_entry( 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.""" """Representation of a KNX date."""
_device: XknxDateDevice _device: XknxDateDevice

View File

@ -32,7 +32,7 @@ from .const import (
DOMAIN, DOMAIN,
KNX_ADDRESS, KNX_ADDRESS,
) )
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
async def async_setup_entry( 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.""" """Representation of a KNX datetime."""
_device: XknxDateTimeDevice _device: XknxDateTimeDevice

View File

@ -21,7 +21,7 @@ from homeassistant.util.scaling import int_states_in_range
from . import KNXModule from . import KNXModule
from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
from .schema import FanSchema from .schema import FanSchema
DEFAULT_PERCENTAGE: Final = 50 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) 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.""" """Representation of a KNX fan."""
_device: XknxFan _device: XknxFan

View File

@ -2,30 +2,55 @@
from __future__ import annotations 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 xknx.devices import Device as XknxDevice
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import EntityPlatform
from .const import DOMAIN from homeassistant.helpers.entity_registry import RegistryEntry
if TYPE_CHECKING: if TYPE_CHECKING:
from . import KNXModule 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.""" """Representation of a KNX entity."""
_attr_should_poll = False _attr_should_poll = False
_knx_module: KNXModule
def __init__(self, knx_module: KNXModule, device: XknxDevice) -> None: _device: XknxDevice
"""Set up device."""
self._knx_module = knx_module
self._device = device
@property @property
def name(self) -> str: def name(self) -> str:
@ -49,7 +74,7 @@ class KnxEntity(Entity):
"""Store register state change callback and start device object.""" """Store register state change callback and start device object."""
self._device.register_device_updated_cb(self.after_update_callback) self._device.register_device_updated_cb(self.after_update_callback)
self._device.xknx.devices.async_add(self._device) 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) # eg. for restoring state (like _KNXSwitch)
await super().async_added_to_hass() await super().async_added_to_hass()
@ -59,19 +84,22 @@ class KnxEntity(Entity):
self._device.xknx.devices.async_remove(self._device) 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.""" """Representation of a KNX UI entity."""
_attr_unique_id: str _attr_unique_id: str
async def async_added_to_hass(self) -> None: @abstractmethod
"""Register callbacks when entity added to hass.""" def __init__(
await super().async_added_to_hass() self, knx_module: KNXModule, unique_id: str, config: dict[str, Any]
self._knx_module.config_store.entities.add(self._attr_unique_id) ) -> None:
self.async_on_remove( """Initialize the UI entity."""
async_dispatcher_connect(
self.hass,
SIGNAL_ENTITY_REMOVE.format(self._attr_unique_id),
self.async_remove,
)
)

View File

@ -19,15 +19,18 @@ from homeassistant.components.light import (
LightEntity, LightEntity,
) )
from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, Platform 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.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 from homeassistant.helpers.typing import ConfigType
import homeassistant.util.color as color_util import homeassistant.util.color as color_util
from . import KNXModule from . import KNXModule
from .const import CONF_SYNC_STATE, DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS, ColorTempModes 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 .schema import LightSchema
from .storage.const import ( from .storage.const import (
CONF_COLOR_TEMP_MAX, CONF_COLOR_TEMP_MAX,
@ -63,8 +66,17 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up light(s) for KNX platform.""" """Set up light(s) for KNX platform."""
knx_module: KNXModule = hass.data[DOMAIN] 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): if yaml_platform_config := hass.data[DATA_KNX_CONFIG].get(Platform.LIGHT):
entities.extend( entities.extend(
KnxYamlLight(knx_module, entity_config) KnxYamlLight(knx_module, entity_config)
@ -78,13 +90,6 @@ async def async_setup_entry(
if entities: if entities:
async_add_entities(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: def _create_yaml_light(xknx: XKNX, config: ConfigType) -> XknxLight:
"""Return a KNX Light device to be used within XKNX.""" """Return a KNX Light device to be used within XKNX."""
@ -519,7 +524,7 @@ class _KnxLight(LightEntity):
await self._device.set_off() await self._device.set_off()
class KnxYamlLight(_KnxLight, KnxEntity): class KnxYamlLight(_KnxLight, KnxYamlEntity):
"""Representation of a KNX light.""" """Representation of a KNX light."""
_device: XknxLight _device: XknxLight
@ -546,7 +551,7 @@ class KnxYamlLight(_KnxLight, KnxEntity):
) )
class KnxUiLight(_KnxLight, KnxUIEntity): class KnxUiLight(_KnxLight, KnxUiEntity):
"""Representation of a KNX light.""" """Representation of a KNX light."""
_attr_has_entity_name = True _attr_has_entity_name = True
@ -556,11 +561,9 @@ class KnxUiLight(_KnxLight, KnxUIEntity):
self, knx_module: KNXModule, unique_id: str, config: ConfigType self, knx_module: KNXModule, unique_id: str, config: ConfigType
) -> None: ) -> None:
"""Initialize of KNX light.""" """Initialize of KNX light."""
super().__init__( self._knx_module = knx_module
knx_module=knx_module, self._device = _create_ui_light(
device=_create_ui_light( knx_module.xknx, config[DOMAIN], config[CONF_ENTITY][CONF_NAME]
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_max_color_temp_kelvin: int = config[DOMAIN][CONF_COLOR_TEMP_MAX]
self._attr_min_color_temp_kelvin: int = config[DOMAIN][CONF_COLOR_TEMP_MIN] self._attr_min_color_temp_kelvin: int = config[DOMAIN][CONF_COLOR_TEMP_MIN]

View File

@ -20,7 +20,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import KNXModule from . import KNXModule
from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
async def async_get_service( 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.""" """Representation of a KNX notification entity."""
_device: XknxNotification _device: XknxNotification

View File

@ -30,7 +30,7 @@ from .const import (
DOMAIN, DOMAIN,
KNX_ADDRESS, KNX_ADDRESS,
) )
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
from .schema import NumberSchema 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.""" """Representation of a KNX number."""
_device: NumericValue _device: NumericValue

View File

@ -15,7 +15,7 @@ from homeassistant.helpers.typing import ConfigType
from . import KNXModule from . import KNXModule
from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
from .schema import SceneSchema 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) 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.""" """Representation of a KNX scene."""
_device: XknxScene _device: XknxScene

View File

@ -30,7 +30,7 @@ from .const import (
DOMAIN, DOMAIN,
KNX_ADDRESS, KNX_ADDRESS,
) )
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
from .schema import SelectSchema 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.""" """Representation of a KNX select."""
_device: RawValue _device: RawValue

View File

@ -35,7 +35,7 @@ from homeassistant.util.enum import try_parse_enum
from . import KNXModule from . import KNXModule
from .const import ATTR_SOURCE, DATA_KNX_CONFIG, DOMAIN from .const import ATTR_SOURCE, DATA_KNX_CONFIG, DOMAIN
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
from .schema import SensorSchema from .schema import SensorSchema
SCAN_INTERVAL = timedelta(seconds=10) 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.""" """Representation of a KNX sensor."""
_device: XknxSensor _device: XknxSensor

View File

@ -1,6 +1,6 @@
"""KNX entity configuration store.""" """KNX entity configuration store."""
from collections.abc import Callable from abc import ABC, abstractmethod
import logging import logging
from typing import Any, Final, TypedDict 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.const import CONF_PLATFORM, Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.storage import Store from homeassistant.helpers.storage import Store
from homeassistant.util.ulid import ulid_now from homeassistant.util.ulid import ulid_now
from ..const import DOMAIN from ..const import DOMAIN
from ..knx_entity import SIGNAL_ENTITY_REMOVE
from .const import CONF_DATA from .const import CONF_DATA
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -33,6 +31,20 @@ class KNXConfigStoreModel(TypedDict):
entities: KNXEntityStoreModel 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: class KNXConfigStore:
"""Manage KNX config store data.""" """Manage KNX config store data."""
@ -46,12 +58,7 @@ class KNXConfigStore:
self.config_entry = config_entry self.config_entry = config_entry
self._store = Store[KNXConfigStoreModel](hass, STORAGE_VERSION, STORAGE_KEY) self._store = Store[KNXConfigStoreModel](hass, STORAGE_VERSION, STORAGE_KEY)
self.data = KNXConfigStoreModel(entities={}) self.data = KNXConfigStoreModel(entities={})
self._platform_controllers: dict[Platform, PlatformControllerBase] = {}
# 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]
] = {}
async def load_data(self) -> None: async def load_data(self) -> None:
"""Load config store data from storage.""" """Load config store data from storage."""
@ -62,14 +69,19 @@ class KNXConfigStore:
len(self.data["entities"]), 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( async def create_entity(
self, platform: Platform, data: dict[str, Any] self, platform: Platform, data: dict[str, Any]
) -> str | None: ) -> str | None:
"""Create a new entity.""" """Create a new entity."""
if platform not in self.async_add_entity: platform_controller = self._platform_controllers[platform]
raise ConfigStoreException(f"Entity platform not ready: {platform}")
unique_id = f"knx_es_{ulid_now()}" 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 # store data after entity was added to be sure config didn't raise exceptions
self.data["entities"].setdefault(platform, {})[unique_id] = data self.data["entities"].setdefault(platform, {})[unique_id] = data
await self._store.async_save(self.data) await self._store.async_save(self.data)
@ -95,8 +107,7 @@ class KNXConfigStore:
self, platform: Platform, entity_id: str, data: dict[str, Any] self, platform: Platform, entity_id: str, data: dict[str, Any]
) -> None: ) -> None:
"""Update an existing entity.""" """Update an existing entity."""
if platform not in self.async_add_entity: platform_controller = self._platform_controllers[platform]
raise ConfigStoreException(f"Entity platform not ready: {platform}")
entity_registry = er.async_get(self.hass) entity_registry = er.async_get(self.hass)
if (entry := entity_registry.async_get(entity_id)) is None: if (entry := entity_registry.async_get(entity_id)) is None:
raise ConfigStoreException(f"Entity not found: {entity_id}") raise ConfigStoreException(f"Entity not found: {entity_id}")
@ -108,8 +119,7 @@ class KNXConfigStore:
raise ConfigStoreException( raise ConfigStoreException(
f"Entity not found in storage: {entity_id} - {unique_id}" f"Entity not found in storage: {entity_id} - {unique_id}"
) )
async_dispatcher_send(self.hass, SIGNAL_ENTITY_REMOVE.format(unique_id)) await platform_controller.update_entity(entry, data)
self.async_add_entity[platform](unique_id, data)
# store data after entity is added to make sure config doesn't raise exceptions # store data after entity is added to make sure config doesn't raise exceptions
self.data["entities"][platform][unique_id] = data self.data["entities"][platform][unique_id] = data
await self._store.async_save(self.data) await self._store.async_save(self.data)
@ -125,23 +135,21 @@ class KNXConfigStore:
raise ConfigStoreException( raise ConfigStoreException(
f"Entity not found in {entry.domain}: {entry.unique_id}" f"Entity not found in {entry.domain}: {entry.unique_id}"
) from err ) 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) entity_registry.async_remove(entity_id)
await self._store.async_save(self.data) await self._store.async_save(self.data)
def get_entity_entries(self) -> list[er.RegistryEntry]: 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) entity_registry = er.async_get(self.hass)
unique_ids = {
uid for platform in self.data["entities"].values() for uid in platform
}
return [ return [
registry_entry registry_entry
for registry_entry in er.async_entries_for_config_entry( for registry_entry in er.async_entries_for_config_entry(
entity_registry, self.config_entry.entry_id entity_registry, self.config_entry.entry_id
) )
if registry_entry.unique_id in self.entities if registry_entry.unique_id in unique_ids
] ]

View File

@ -17,9 +17,12 @@ from homeassistant.const import (
STATE_UNKNOWN, STATE_UNKNOWN,
Platform, Platform,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo 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.restore_state import RestoreEntity
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
@ -32,7 +35,7 @@ from .const import (
DOMAIN, DOMAIN,
KNX_ADDRESS, KNX_ADDRESS,
) )
from .knx_entity import KnxEntity, KnxUIEntity from .knx_entity import KnxUiEntity, KnxUiEntityPlatformController, KnxYamlEntity
from .schema import SwitchSchema from .schema import SwitchSchema
from .storage.const import ( from .storage.const import (
CONF_DEVICE_INFO, CONF_DEVICE_INFO,
@ -51,8 +54,17 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up switch(es) for KNX platform.""" """Set up switch(es) for KNX platform."""
knx_module: KNXModule = hass.data[DOMAIN] 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): if yaml_platform_config := hass.data[DATA_KNX_CONFIG].get(Platform.SWITCH):
entities.extend( entities.extend(
KnxYamlSwitch(knx_module, entity_config) KnxYamlSwitch(knx_module, entity_config)
@ -66,13 +78,6 @@ async def async_setup_entry(
if entities: if entities:
async_add_entities(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): class _KnxSwitch(SwitchEntity, RestoreEntity):
"""Base class for a KNX switch.""" """Base class for a KNX switch."""
@ -102,7 +107,7 @@ class _KnxSwitch(SwitchEntity, RestoreEntity):
await self._device.set_off() await self._device.set_off()
class KnxYamlSwitch(_KnxSwitch, KnxEntity): class KnxYamlSwitch(_KnxSwitch, KnxYamlEntity):
"""Representation of a KNX switch configured from YAML.""" """Representation of a KNX switch configured from YAML."""
_device: XknxSwitch _device: XknxSwitch
@ -125,7 +130,7 @@ class KnxYamlSwitch(_KnxSwitch, KnxEntity):
self._attr_unique_id = str(self._device.switch.group_address) 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.""" """Representation of a KNX switch configured from UI."""
_attr_has_entity_name = True _attr_has_entity_name = True
@ -134,21 +139,19 @@ class KnxUiSwitch(_KnxSwitch, KnxUIEntity):
def __init__( def __init__(
self, knx_module: KNXModule, unique_id: str, config: dict[str, Any] self, knx_module: KNXModule, unique_id: str, config: dict[str, Any]
) -> None: ) -> None:
"""Initialize of KNX switch.""" """Initialize KNX switch."""
super().__init__( self._knx_module = knx_module
knx_module=knx_module, self._device = XknxSwitch(
device=XknxSwitch( knx_module.xknx,
knx_module.xknx, name=config[CONF_ENTITY][CONF_NAME],
name=config[CONF_ENTITY][CONF_NAME], group_address=config[DOMAIN][CONF_GA_SWITCH][CONF_GA_WRITE],
group_address=config[DOMAIN][CONF_GA_SWITCH][CONF_GA_WRITE], group_address_state=[
group_address_state=[ config[DOMAIN][CONF_GA_SWITCH][CONF_GA_STATE],
config[DOMAIN][CONF_GA_SWITCH][CONF_GA_STATE], *config[DOMAIN][CONF_GA_SWITCH][CONF_GA_PASSIVE],
*config[DOMAIN][CONF_GA_SWITCH][CONF_GA_PASSIVE], ],
], respond_to_read=config[DOMAIN][CONF_RESPOND_TO_READ],
respond_to_read=config[DOMAIN][CONF_RESPOND_TO_READ], sync_state=config[DOMAIN][CONF_SYNC_STATE],
sync_state=config[DOMAIN][CONF_SYNC_STATE], invert=config[DOMAIN][CONF_INVERT],
invert=config[DOMAIN][CONF_INVERT],
),
) )
self._attr_entity_category = config[CONF_ENTITY][CONF_ENTITY_CATEGORY] self._attr_entity_category = config[CONF_ENTITY][CONF_ENTITY_CATEGORY]
self._attr_unique_id = unique_id self._attr_unique_id = unique_id

View File

@ -30,7 +30,7 @@ from .const import (
DOMAIN, DOMAIN,
KNX_ADDRESS, KNX_ADDRESS,
) )
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
async def async_setup_entry( 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.""" """Representation of a KNX text."""
_device: XknxNotification _device: XknxNotification

View File

@ -31,7 +31,7 @@ from .const import (
DOMAIN, DOMAIN,
KNX_ADDRESS, KNX_ADDRESS,
) )
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
async def async_setup_entry( 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.""" """Representation of a KNX time."""
_device: XknxTimeDevice _device: XknxTimeDevice

View File

@ -21,7 +21,7 @@ from homeassistant.helpers.typing import ConfigType
from . import KNXModule from . import KNXModule
from .const import DATA_KNX_CONFIG, DOMAIN from .const import DATA_KNX_CONFIG, DOMAIN
from .knx_entity import KnxEntity from .knx_entity import KnxYamlEntity
from .schema import WeatherSchema 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.""" """Representation of a KNX weather device."""
_device: XknxWeather _device: XknxWeather