mirror of
https://github.com/home-assistant/core.git
synced 2025-05-01 04:37:52 +00:00
Use KNX UI entity platform controller class (#123128)
This commit is contained in:
parent
5b7fd29797
commit
f2d99cb059
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
@ -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]
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user