Address post-merge reviews for KNX integration (#123038)

This commit is contained in:
Matthias Alphart 2024-08-02 12:53:39 +02:00 committed by GitHub
parent 4a06e20318
commit 42234e6a09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 211 additions and 125 deletions

View File

@ -302,7 +302,7 @@ class KNXModule:
self.entry = entry self.entry = entry
self.project = KNXProject(hass=hass, entry=entry) self.project = KNXProject(hass=hass, entry=entry)
self.config_store = KNXConfigStore(hass=hass, entry=entry) self.config_store = KNXConfigStore(hass=hass, config_entry=entry)
self.xknx = XKNX( self.xknx = XKNX(
connection_config=self.connection_config(), connection_config=self.connection_config(),

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from typing import Any from typing import Any
from xknx import XKNX
from xknx.devices import BinarySensor as XknxBinarySensor from xknx.devices import BinarySensor as XknxBinarySensor
from homeassistant import config_entries from homeassistant import config_entries
@ -23,6 +22,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
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
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 KnxEntity
from .schema import BinarySensorSchema from .schema import BinarySensorSchema
@ -34,11 +34,11 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the KNX binary sensor platform.""" """Set up the KNX binary sensor platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: ConfigType = hass.data[DATA_KNX_CONFIG] config: ConfigType = hass.data[DATA_KNX_CONFIG]
async_add_entities( async_add_entities(
KNXBinarySensor(xknx, entity_config) KNXBinarySensor(knx_module, entity_config)
for entity_config in config[Platform.BINARY_SENSOR] for entity_config in config[Platform.BINARY_SENSOR]
) )
@ -48,11 +48,12 @@ class KNXBinarySensor(KnxEntity, BinarySensorEntity, RestoreEntity):
_device: XknxBinarySensor _device: XknxBinarySensor
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize of KNX binary sensor.""" """Initialize of KNX binary sensor."""
super().__init__( super().__init__(
knx_module=knx_module,
device=XknxBinarySensor( device=XknxBinarySensor(
xknx, xknx=knx_module.xknx,
name=config[CONF_NAME], name=config[CONF_NAME],
group_address_state=config[BinarySensorSchema.CONF_STATE_ADDRESS], group_address_state=config[BinarySensorSchema.CONF_STATE_ADDRESS],
invert=config[BinarySensorSchema.CONF_INVERT], invert=config[BinarySensorSchema.CONF_INVERT],
@ -62,7 +63,7 @@ class KNXBinarySensor(KnxEntity, BinarySensorEntity, RestoreEntity):
], ],
context_timeout=config.get(BinarySensorSchema.CONF_CONTEXT_TIMEOUT), context_timeout=config.get(BinarySensorSchema.CONF_CONTEXT_TIMEOUT),
reset_after=config.get(BinarySensorSchema.CONF_RESET_AFTER), reset_after=config.get(BinarySensorSchema.CONF_RESET_AFTER),
) ),
) )
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_device_class = config.get(CONF_DEVICE_CLASS) self._attr_device_class = config.get(CONF_DEVICE_CLASS)

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
from xknx import XKNX
from xknx.devices import RawValue as XknxRawValue from xknx.devices import RawValue as XknxRawValue
from homeassistant import config_entries from homeassistant import config_entries
@ -12,6 +11,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
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 KnxEntity
@ -22,11 +22,12 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the KNX binary sensor platform.""" """Set up the KNX binary sensor platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: ConfigType = hass.data[DATA_KNX_CONFIG] config: ConfigType = hass.data[DATA_KNX_CONFIG]
async_add_entities( async_add_entities(
KNXButton(xknx, entity_config) for entity_config in config[Platform.BUTTON] KNXButton(knx_module, entity_config)
for entity_config in config[Platform.BUTTON]
) )
@ -35,15 +36,16 @@ class KNXButton(KnxEntity, ButtonEntity):
_device: XknxRawValue _device: XknxRawValue
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize a KNX button.""" """Initialize a KNX button."""
super().__init__( super().__init__(
knx_module=knx_module,
device=XknxRawValue( device=XknxRawValue(
xknx, xknx=knx_module.xknx,
name=config[CONF_NAME], name=config[CONF_NAME],
payload_length=config[CONF_PAYLOAD_LENGTH], payload_length=config[CONF_PAYLOAD_LENGTH],
group_address=config[KNX_ADDRESS], group_address=config[KNX_ADDRESS],
) ),
) )
self._payload = config[CONF_PAYLOAD] self._payload = config[CONF_PAYLOAD]
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)

View File

@ -27,6 +27,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from . import KNXModule
from .const import ( from .const import (
CONTROLLER_MODES, CONTROLLER_MODES,
CURRENT_HVAC_ACTIONS, CURRENT_HVAC_ACTIONS,
@ -48,10 +49,12 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up climate(s) for KNX platform.""" """Set up climate(s) for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.CLIMATE] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.CLIMATE]
async_add_entities(KNXClimate(xknx, entity_config) for entity_config in config) async_add_entities(
KNXClimate(knx_module, entity_config) for entity_config in config
)
def _create_climate(xknx: XKNX, config: ConfigType) -> XknxClimate: def _create_climate(xknx: XKNX, config: ConfigType) -> XknxClimate:
@ -137,9 +140,12 @@ class KNXClimate(KnxEntity, ClimateEntity):
_attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_temperature_unit = UnitOfTemperature.CELSIUS
_enable_turn_on_off_backwards_compatibility = False _enable_turn_on_off_backwards_compatibility = False
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize of a KNX climate device.""" """Initialize of a KNX climate device."""
super().__init__(_create_climate(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_climate(knx_module.xknx, config),
)
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
if self._device.supports_on_off: if self._device.supports_on_off:

View File

@ -5,7 +5,6 @@ from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from typing import Any from typing import Any
from xknx import XKNX
from xknx.devices import Cover as XknxCover from xknx.devices import Cover as XknxCover
from homeassistant import config_entries from homeassistant import config_entries
@ -26,6 +25,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
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 KnxEntity
from .schema import CoverSchema from .schema import CoverSchema
@ -37,10 +37,10 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up cover(s) for KNX platform.""" """Set up cover(s) for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.COVER] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.COVER]
async_add_entities(KNXCover(xknx, 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(KnxEntity, CoverEntity):
@ -48,11 +48,12 @@ class KNXCover(KnxEntity, CoverEntity):
_device: XknxCover _device: XknxCover
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize the cover.""" """Initialize the cover."""
super().__init__( super().__init__(
knx_module=knx_module,
device=XknxCover( device=XknxCover(
xknx, xknx=knx_module.xknx,
name=config[CONF_NAME], name=config[CONF_NAME],
group_address_long=config.get(CoverSchema.CONF_MOVE_LONG_ADDRESS), group_address_long=config.get(CoverSchema.CONF_MOVE_LONG_ADDRESS),
group_address_short=config.get(CoverSchema.CONF_MOVE_SHORT_ADDRESS), group_address_short=config.get(CoverSchema.CONF_MOVE_SHORT_ADDRESS),
@ -70,7 +71,7 @@ class KNXCover(KnxEntity, CoverEntity):
invert_updown=config[CoverSchema.CONF_INVERT_UPDOWN], invert_updown=config[CoverSchema.CONF_INVERT_UPDOWN],
invert_position=config[CoverSchema.CONF_INVERT_POSITION], invert_position=config[CoverSchema.CONF_INVERT_POSITION],
invert_angle=config[CoverSchema.CONF_INVERT_ANGLE], invert_angle=config[CoverSchema.CONF_INVERT_ANGLE],
) ),
) )
self._unsubscribe_auto_updater: Callable[[], None] | None = None self._unsubscribe_auto_updater: Callable[[], None] | None = None

View File

@ -22,6 +22,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
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
from . import KNXModule
from .const import ( from .const import (
CONF_RESPOND_TO_READ, CONF_RESPOND_TO_READ,
CONF_STATE_ADDRESS, CONF_STATE_ADDRESS,
@ -39,10 +40,12 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up entities for KNX platform.""" """Set up entities for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.DATE] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.DATE]
async_add_entities(KNXDateEntity(xknx, entity_config) for entity_config in config) async_add_entities(
KNXDateEntity(knx_module, entity_config) for entity_config in config
)
def _create_xknx_device(xknx: XKNX, config: ConfigType) -> XknxDateDevice: def _create_xknx_device(xknx: XKNX, config: ConfigType) -> XknxDateDevice:
@ -63,9 +66,12 @@ class KNXDateEntity(KnxEntity, DateEntity, RestoreEntity):
_device: XknxDateDevice _device: XknxDateDevice
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize a KNX time.""" """Initialize a KNX time."""
super().__init__(_create_xknx_device(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_xknx_device(knx_module.xknx, config),
)
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_unique_id = str(self._device.remote_value.group_address) self._attr_unique_id = str(self._device.remote_value.group_address)

View File

@ -23,6 +23,7 @@ from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from . import KNXModule
from .const import ( from .const import (
CONF_RESPOND_TO_READ, CONF_RESPOND_TO_READ,
CONF_STATE_ADDRESS, CONF_STATE_ADDRESS,
@ -40,11 +41,11 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up entities for KNX platform.""" """Set up entities for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.DATETIME] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.DATETIME]
async_add_entities( async_add_entities(
KNXDateTimeEntity(xknx, entity_config) for entity_config in config KNXDateTimeEntity(knx_module, entity_config) for entity_config in config
) )
@ -66,9 +67,12 @@ class KNXDateTimeEntity(KnxEntity, DateTimeEntity, RestoreEntity):
_device: XknxDateTimeDevice _device: XknxDateTimeDevice
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize a KNX time.""" """Initialize a KNX time."""
super().__init__(_create_xknx_device(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_xknx_device(knx_module.xknx, config),
)
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_unique_id = str(self._device.remote_value.group_address) self._attr_unique_id = str(self._device.remote_value.group_address)

View File

@ -5,7 +5,6 @@ from __future__ import annotations
import math import math
from typing import Any, Final from typing import Any, Final
from xknx import XKNX
from xknx.devices import Fan as XknxFan from xknx.devices import Fan as XknxFan
from homeassistant import config_entries from homeassistant import config_entries
@ -20,6 +19,7 @@ from homeassistant.util.percentage import (
) )
from homeassistant.util.scaling import int_states_in_range from homeassistant.util.scaling import int_states_in_range
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 KnxEntity
from .schema import FanSchema from .schema import FanSchema
@ -33,10 +33,10 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up fan(s) for KNX platform.""" """Set up fan(s) for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.FAN] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.FAN]
async_add_entities(KNXFan(xknx, 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(KnxEntity, FanEntity):
@ -45,12 +45,13 @@ class KNXFan(KnxEntity, FanEntity):
_device: XknxFan _device: XknxFan
_enable_turn_on_off_backwards_compatibility = False _enable_turn_on_off_backwards_compatibility = False
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize of KNX fan.""" """Initialize of KNX fan."""
max_step = config.get(FanSchema.CONF_MAX_STEP) max_step = config.get(FanSchema.CONF_MAX_STEP)
super().__init__( super().__init__(
knx_module=knx_module,
device=XknxFan( device=XknxFan(
xknx, xknx=knx_module.xknx,
name=config[CONF_NAME], name=config[CONF_NAME],
group_address_speed=config.get(KNX_ADDRESS), group_address_speed=config.get(KNX_ADDRESS),
group_address_speed_state=config.get(FanSchema.CONF_STATE_ADDRESS), group_address_speed_state=config.get(FanSchema.CONF_STATE_ADDRESS),
@ -61,7 +62,7 @@ class KNXFan(KnxEntity, FanEntity):
FanSchema.CONF_OSCILLATION_STATE_ADDRESS FanSchema.CONF_OSCILLATION_STATE_ADDRESS
), ),
max_step=max_step, max_step=max_step,
) ),
) )
# FanSpeedMode.STEP if max_step is set # FanSpeedMode.STEP if max_step is set
self._step_range: tuple[int, int] | None = (1, max_step) if max_step else None self._step_range: tuple[int, int] | None = (1, max_step) if max_step else None

View File

@ -2,23 +2,29 @@
from __future__ import annotations from __future__ import annotations
from typing import cast from typing import TYPE_CHECKING
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 . import KNXModule
from .const import DOMAIN from .const import DOMAIN
if TYPE_CHECKING:
from . import KNXModule
SIGNAL_ENTITY_REMOVE = f"{DOMAIN}_entity_remove_signal.{{}}"
class KnxEntity(Entity): class KnxEntity(Entity):
"""Representation of a KNX entity.""" """Representation of a KNX entity."""
_attr_should_poll = False _attr_should_poll = False
def __init__(self, device: XknxDevice) -> None: def __init__(self, knx_module: KNXModule, device: XknxDevice) -> None:
"""Set up device.""" """Set up device."""
self._knx_module = knx_module
self._device = device self._device = device
@property @property
@ -29,8 +35,7 @@ class KnxEntity(Entity):
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Return True if entity is available.""" """Return True if entity is available."""
knx_module = cast(KNXModule, self.hass.data[DOMAIN]) return self._knx_module.connected
return knx_module.connected
async def async_update(self) -> None: async def async_update(self) -> None:
"""Request a state update from KNX bus.""" """Request a state update from KNX bus."""
@ -44,8 +49,29 @@ 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
# eg. for restoring state (like _KNXSwitch)
await super().async_added_to_hass()
async def async_will_remove_from_hass(self) -> None: async def async_will_remove_from_hass(self) -> None:
"""Disconnect device object when removed.""" """Disconnect device object when removed."""
self._device.unregister_device_updated_cb(self.after_update_callback) self._device.unregister_device_updated_cb(self.after_update_callback)
self._device.xknx.devices.async_remove(self._device) self._device.xknx.devices.async_remove(self._device)
class KnxUIEntity(KnxEntity):
"""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,
)
)

View File

@ -27,7 +27,7 @@ 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 from .knx_entity import KnxEntity, KnxUIEntity
from .schema import LightSchema from .schema import LightSchema
from .storage.const import ( from .storage.const import (
CONF_COLOR_TEMP_MAX, CONF_COLOR_TEMP_MAX,
@ -65,10 +65,10 @@ async def async_setup_entry(
knx_module: KNXModule = hass.data[DOMAIN] knx_module: KNXModule = hass.data[DOMAIN]
entities: list[KnxEntity] = [] entities: list[KnxEntity] = []
if yaml_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.xknx, entity_config) KnxYamlLight(knx_module, entity_config)
for entity_config in yaml_config for entity_config in yaml_platform_config
) )
if ui_config := knx_module.config_store.data["entities"].get(Platform.LIGHT): if ui_config := knx_module.config_store.data["entities"].get(Platform.LIGHT):
entities.extend( entities.extend(
@ -294,7 +294,7 @@ def _create_ui_light(xknx: XKNX, knx_config: ConfigType, name: str) -> XknxLight
) )
class _KnxLight(KnxEntity, LightEntity): class _KnxLight(LightEntity):
"""Representation of a KNX light.""" """Representation of a KNX light."""
_attr_max_color_temp_kelvin: int _attr_max_color_temp_kelvin: int
@ -519,14 +519,17 @@ class _KnxLight(KnxEntity, LightEntity):
await self._device.set_off() await self._device.set_off()
class KnxYamlLight(_KnxLight): class KnxYamlLight(_KnxLight, KnxEntity):
"""Representation of a KNX light.""" """Representation of a KNX light."""
_device: XknxLight _device: XknxLight
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize of KNX light.""" """Initialize of KNX light."""
super().__init__(_create_yaml_light(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_yaml_light(knx_module.xknx, config),
)
self._attr_max_color_temp_kelvin: int = config[LightSchema.CONF_MAX_KELVIN] self._attr_max_color_temp_kelvin: int = config[LightSchema.CONF_MAX_KELVIN]
self._attr_min_color_temp_kelvin: int = config[LightSchema.CONF_MIN_KELVIN] self._attr_min_color_temp_kelvin: int = config[LightSchema.CONF_MIN_KELVIN]
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
@ -543,20 +546,21 @@ class KnxYamlLight(_KnxLight):
) )
class KnxUiLight(_KnxLight): class KnxUiLight(_KnxLight, KnxUIEntity):
"""Representation of a KNX light.""" """Representation of a KNX light."""
_device: XknxLight
_attr_has_entity_name = True _attr_has_entity_name = True
_device: XknxLight
def __init__( def __init__(
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__( super().__init__(
_create_ui_light( knx_module=knx_module,
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]
@ -565,5 +569,3 @@ class KnxUiLight(_KnxLight):
self._attr_unique_id = unique_id self._attr_unique_id = unique_id
if device_info := config[CONF_ENTITY].get(CONF_DEVICE_INFO): if device_info := config[CONF_ENTITY].get(CONF_DEVICE_INFO):
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, device_info)}) self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, device_info)})
knx_module.config_store.entities[unique_id] = self

View File

@ -18,6 +18,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
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 KnxEntity
@ -44,7 +45,7 @@ async def async_get_service(
class KNXNotificationService(BaseNotificationService): class KNXNotificationService(BaseNotificationService):
"""Implement demo notification service.""" """Implement notification service."""
def __init__(self, devices: list[XknxNotification]) -> None: def __init__(self, devices: list[XknxNotification]) -> None:
"""Initialize the service.""" """Initialize the service."""
@ -86,10 +87,10 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up notify(s) for KNX platform.""" """Set up notify(s) for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.NOTIFY] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.NOTIFY]
async_add_entities(KNXNotify(xknx, entity_config) for entity_config in config) async_add_entities(KNXNotify(knx_module, entity_config) for entity_config in config)
def _create_notification_instance(xknx: XKNX, config: ConfigType) -> XknxNotification: def _create_notification_instance(xknx: XKNX, config: ConfigType) -> XknxNotification:
@ -107,9 +108,12 @@ class KNXNotify(KnxEntity, NotifyEntity):
_device: XknxNotification _device: XknxNotification
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize a KNX notification.""" """Initialize a KNX notification."""
super().__init__(_create_notification_instance(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_notification_instance(knx_module.xknx, config),
)
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_unique_id = str(self._device.remote_value.group_address) self._attr_unique_id = str(self._device.remote_value.group_address)

View File

@ -22,6 +22,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from . import KNXModule
from .const import ( from .const import (
CONF_RESPOND_TO_READ, CONF_RESPOND_TO_READ,
CONF_STATE_ADDRESS, CONF_STATE_ADDRESS,
@ -39,10 +40,10 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up number(s) for KNX platform.""" """Set up number(s) for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.NUMBER] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.NUMBER]
async_add_entities(KNXNumber(xknx, entity_config) for entity_config in config) async_add_entities(KNXNumber(knx_module, entity_config) for entity_config in config)
def _create_numeric_value(xknx: XKNX, config: ConfigType) -> NumericValue: def _create_numeric_value(xknx: XKNX, config: ConfigType) -> NumericValue:
@ -62,9 +63,12 @@ class KNXNumber(KnxEntity, RestoreNumber):
_device: NumericValue _device: NumericValue
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize a KNX number.""" """Initialize a KNX number."""
super().__init__(_create_numeric_value(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_numeric_value(knx_module.xknx, config),
)
self._attr_native_max_value = config.get( self._attr_native_max_value = config.get(
NumberSchema.CONF_MAX, NumberSchema.CONF_MAX,
self._device.sensor_value.dpt_class.value_max, self._device.sensor_value.dpt_class.value_max,

View File

@ -8,9 +8,11 @@ from typing import Final
from xknx import XKNX from xknx import XKNX
from xknx.dpt import DPTBase from xknx.dpt import DPTBase
from xknx.telegram.address import DeviceAddressableType
from xknxproject import XKNXProj from xknxproject import XKNXProj
from xknxproject.models import ( from xknxproject.models import (
Device, Device,
DPTType,
GroupAddress as GroupAddressModel, GroupAddress as GroupAddressModel,
KNXProject as KNXProjectModel, KNXProject as KNXProjectModel,
ProjectInfo, ProjectInfo,
@ -89,7 +91,7 @@ class KNXProject:
self.devices = project["devices"] self.devices = project["devices"]
self.info = project["info"] self.info = project["info"]
xknx.group_address_dpt.clear() xknx.group_address_dpt.clear()
xknx_ga_dict = {} xknx_ga_dict: dict[DeviceAddressableType, DPTType] = {}
for ga_model in project["group_addresses"].values(): for ga_model in project["group_addresses"].values():
ga_info = _create_group_address_info(ga_model) ga_info = _create_group_address_info(ga_model)
@ -97,7 +99,7 @@ class KNXProject:
if (dpt_model := ga_model.get("dpt")) is not None: if (dpt_model := ga_model.get("dpt")) is not None:
xknx_ga_dict[ga_model["address"]] = dpt_model xknx_ga_dict[ga_model["address"]] = dpt_model
xknx.group_address_dpt.set(xknx_ga_dict) # type: ignore[arg-type] xknx.group_address_dpt.set(xknx_ga_dict)
_LOGGER.debug( _LOGGER.debug(
"Loaded KNX project data with %s group addresses from storage", "Loaded KNX project data with %s group addresses from storage",

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from typing import Any from typing import Any
from xknx import XKNX
from xknx.devices import Scene as XknxScene from xknx.devices import Scene as XknxScene
from homeassistant import config_entries from homeassistant import config_entries
@ -14,6 +13,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
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 KnxEntity
from .schema import SceneSchema from .schema import SceneSchema
@ -25,10 +25,10 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up scene(s) for KNX platform.""" """Set up scene(s) for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.SCENE] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.SCENE]
async_add_entities(KNXScene(xknx, 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(KnxEntity, Scene):
@ -36,15 +36,16 @@ class KNXScene(KnxEntity, Scene):
_device: XknxScene _device: XknxScene
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Init KNX scene.""" """Init KNX scene."""
super().__init__( super().__init__(
knx_module=knx_module,
device=XknxScene( device=XknxScene(
xknx, xknx=knx_module.xknx,
name=config[CONF_NAME], name=config[CONF_NAME],
group_address=config[KNX_ADDRESS], group_address=config[KNX_ADDRESS],
scene_number=config[SceneSchema.CONF_SCENE_NUMBER], scene_number=config[SceneSchema.CONF_SCENE_NUMBER],
) ),
) )
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_unique_id = ( self._attr_unique_id = (

View File

@ -20,6 +20,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
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
from . import KNXModule
from .const import ( from .const import (
CONF_PAYLOAD_LENGTH, CONF_PAYLOAD_LENGTH,
CONF_RESPOND_TO_READ, CONF_RESPOND_TO_READ,
@ -39,10 +40,10 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up select(s) for KNX platform.""" """Set up select(s) for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.SELECT] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.SELECT]
async_add_entities(KNXSelect(xknx, entity_config) for entity_config in config) async_add_entities(KNXSelect(knx_module, entity_config) for entity_config in config)
def _create_raw_value(xknx: XKNX, config: ConfigType) -> RawValue: def _create_raw_value(xknx: XKNX, config: ConfigType) -> RawValue:
@ -63,9 +64,12 @@ class KNXSelect(KnxEntity, SelectEntity, RestoreEntity):
_device: RawValue _device: RawValue
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize a KNX select.""" """Initialize a KNX select."""
super().__init__(_create_raw_value(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_raw_value(knx_module.xknx, config),
)
self._option_payloads: dict[str, int] = { self._option_payloads: dict[str, int] = {
option[SelectSchema.CONF_OPTION]: option[CONF_PAYLOAD] option[SelectSchema.CONF_OPTION]: option[CONF_PAYLOAD]
for option in config[SelectSchema.CONF_OPTIONS] for option in config[SelectSchema.CONF_OPTIONS]

View File

@ -116,17 +116,17 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up sensor(s) for KNX platform.""" """Set up sensor(s) for KNX platform."""
knx_module: KNXModule = hass.data[DOMAIN] knx_module: KNXModule = hass.data[DOMAIN]
entities: list[SensorEntity] = []
async_add_entities( entities.extend(
KNXSystemSensor(knx_module, description) KNXSystemSensor(knx_module, description)
for description in SYSTEM_ENTITY_DESCRIPTIONS for description in SYSTEM_ENTITY_DESCRIPTIONS
) )
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG].get(Platform.SENSOR) config: list[ConfigType] = hass.data[DATA_KNX_CONFIG].get(Platform.SENSOR)
if config: if config:
async_add_entities( entities.extend(
KNXSensor(knx_module.xknx, entity_config) for entity_config in config KNXSensor(knx_module, entity_config) for entity_config in config
) )
async_add_entities(entities)
def _create_sensor(xknx: XKNX, config: ConfigType) -> XknxSensor: def _create_sensor(xknx: XKNX, config: ConfigType) -> XknxSensor:
@ -146,9 +146,12 @@ class KNXSensor(KnxEntity, SensorEntity):
_device: XknxSensor _device: XknxSensor
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize of a KNX sensor.""" """Initialize of a KNX sensor."""
super().__init__(_create_sensor(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_sensor(knx_module.xknx, config),
)
if device_class := config.get(CONF_DEVICE_CLASS): if device_class := config.get(CONF_DEVICE_CLASS):
self._attr_device_class = device_class self._attr_device_class = device_class
else: else:

View File

@ -2,21 +2,20 @@
from collections.abc import Callable from collections.abc import Callable
import logging import logging
from typing import TYPE_CHECKING, Any, Final, TypedDict from typing import Any, Final, TypedDict
from homeassistant.config_entries import ConfigEntry 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
if TYPE_CHECKING:
from ..knx_entity import KnxEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
STORAGE_VERSION: Final = 1 STORAGE_VERSION: Final = 1
@ -40,15 +39,16 @@ class KNXConfigStore:
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, config_entry: ConfigEntry,
) -> None: ) -> None:
"""Initialize config store.""" """Initialize config store."""
self.hass = hass self.hass = hass
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={})
# entities and async_add_entity are filled by platform setups # entities and async_add_entity are filled by platform / entity setups
self.entities: dict[str, KnxEntity] = {} # unique_id as key self.entities: set[str] = set() # unique_id as values
self.async_add_entity: dict[ self.async_add_entity: dict[
Platform, Callable[[str, dict[str, Any]], None] Platform, Callable[[str, dict[str, Any]], None]
] = {} ] = {}
@ -108,7 +108,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}"
) )
await self.entities.pop(unique_id).async_remove() async_dispatcher_send(self.hass, SIGNAL_ENTITY_REMOVE.format(unique_id))
self.async_add_entity[platform](unique_id, 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
@ -126,7 +126,7 @@ class KNXConfigStore:
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: try:
del self.entities[entry.unique_id] self.entities.remove(entry.unique_id)
except KeyError: except KeyError:
_LOGGER.warning("Entity not initialized when deleted: %s", entity_id) _LOGGER.warning("Entity not initialized when deleted: %s", entity_id)
entity_registry.async_remove(entity_id) entity_registry.async_remove(entity_id)
@ -134,10 +134,14 @@ class KNXConfigStore:
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 configured entities by platform."""
entity_registry = er.async_get(self.hass)
return [ return [
entity.registry_entry registry_entry
for entity in self.entities.values() for registry_entry in er.async_entries_for_config_entry(
if entity.registry_entry is not None entity_registry, self.config_entry.entry_id
)
if registry_entry.unique_id in self.entities
] ]

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from typing import Any from typing import Any
from xknx import XKNX
from xknx.devices import Switch as XknxSwitch from xknx.devices import Switch as XknxSwitch
from homeassistant import config_entries from homeassistant import config_entries
@ -33,7 +32,7 @@ from .const import (
DOMAIN, DOMAIN,
KNX_ADDRESS, KNX_ADDRESS,
) )
from .knx_entity import KnxEntity from .knx_entity import KnxEntity, KnxUIEntity
from .schema import SwitchSchema from .schema import SwitchSchema
from .storage.const import ( from .storage.const import (
CONF_DEVICE_INFO, CONF_DEVICE_INFO,
@ -54,10 +53,10 @@ async def async_setup_entry(
knx_module: KNXModule = hass.data[DOMAIN] knx_module: KNXModule = hass.data[DOMAIN]
entities: list[KnxEntity] = [] entities: list[KnxEntity] = []
if yaml_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.xknx, entity_config) KnxYamlSwitch(knx_module, entity_config)
for entity_config in yaml_config for entity_config in yaml_platform_config
) )
if ui_config := knx_module.config_store.data["entities"].get(Platform.SWITCH): if ui_config := knx_module.config_store.data["entities"].get(Platform.SWITCH):
entities.extend( entities.extend(
@ -75,7 +74,7 @@ async def async_setup_entry(
knx_module.config_store.async_add_entity[Platform.SWITCH] = add_new_ui_switch knx_module.config_store.async_add_entity[Platform.SWITCH] = add_new_ui_switch
class _KnxSwitch(KnxEntity, SwitchEntity, RestoreEntity): class _KnxSwitch(SwitchEntity, RestoreEntity):
"""Base class for a KNX switch.""" """Base class for a KNX switch."""
_device: XknxSwitch _device: XknxSwitch
@ -103,36 +102,41 @@ class _KnxSwitch(KnxEntity, SwitchEntity, RestoreEntity):
await self._device.set_off() await self._device.set_off()
class KnxYamlSwitch(_KnxSwitch): class KnxYamlSwitch(_KnxSwitch, KnxEntity):
"""Representation of a KNX switch configured from YAML.""" """Representation of a KNX switch configured from YAML."""
def __init__(self, xknx: XKNX, config: ConfigType) -> None: _device: XknxSwitch
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize of KNX switch.""" """Initialize of KNX switch."""
super().__init__( super().__init__(
knx_module=knx_module,
device=XknxSwitch( device=XknxSwitch(
xknx, xknx=knx_module.xknx,
name=config[CONF_NAME], name=config[CONF_NAME],
group_address=config[KNX_ADDRESS], group_address=config[KNX_ADDRESS],
group_address_state=config.get(SwitchSchema.CONF_STATE_ADDRESS), group_address_state=config.get(SwitchSchema.CONF_STATE_ADDRESS),
respond_to_read=config[CONF_RESPOND_TO_READ], respond_to_read=config[CONF_RESPOND_TO_READ],
invert=config[SwitchSchema.CONF_INVERT], invert=config[SwitchSchema.CONF_INVERT],
) ),
) )
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_device_class = config.get(CONF_DEVICE_CLASS) self._attr_device_class = config.get(CONF_DEVICE_CLASS)
self._attr_unique_id = str(self._device.switch.group_address) self._attr_unique_id = str(self._device.switch.group_address)
class KnxUiSwitch(_KnxSwitch): 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
_device: XknxSwitch
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 of KNX switch."""
super().__init__( super().__init__(
knx_module=knx_module,
device=XknxSwitch( device=XknxSwitch(
knx_module.xknx, knx_module.xknx,
name=config[CONF_ENTITY][CONF_NAME], name=config[CONF_ENTITY][CONF_NAME],
@ -144,11 +148,9 @@ class KnxUiSwitch(_KnxSwitch):
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
if device_info := config[CONF_ENTITY].get(CONF_DEVICE_INFO): if device_info := config[CONF_ENTITY].get(CONF_DEVICE_INFO):
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, device_info)}) self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, device_info)})
knx_module.config_store.entities[unique_id] = self

View File

@ -22,6 +22,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
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
from . import KNXModule
from .const import ( from .const import (
CONF_RESPOND_TO_READ, CONF_RESPOND_TO_READ,
CONF_STATE_ADDRESS, CONF_STATE_ADDRESS,
@ -38,10 +39,10 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up sensor(s) for KNX platform.""" """Set up sensor(s) for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.TEXT] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.TEXT]
async_add_entities(KNXText(xknx, entity_config) for entity_config in config) async_add_entities(KNXText(knx_module, entity_config) for entity_config in config)
def _create_notification(xknx: XKNX, config: ConfigType) -> XknxNotification: def _create_notification(xknx: XKNX, config: ConfigType) -> XknxNotification:
@ -62,9 +63,12 @@ class KNXText(KnxEntity, TextEntity, RestoreEntity):
_device: XknxNotification _device: XknxNotification
_attr_native_max = 14 _attr_native_max = 14
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize a KNX text.""" """Initialize a KNX text."""
super().__init__(_create_notification(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_notification(knx_module.xknx, config),
)
self._attr_mode = config[CONF_MODE] self._attr_mode = config[CONF_MODE]
self._attr_pattern = ( self._attr_pattern = (
r"[\u0000-\u00ff]*" # Latin-1 r"[\u0000-\u00ff]*" # Latin-1

View File

@ -3,7 +3,6 @@
from __future__ import annotations from __future__ import annotations
from datetime import time as dt_time from datetime import time as dt_time
from typing import Final
from xknx import XKNX from xknx import XKNX
from xknx.devices import TimeDevice as XknxTimeDevice from xknx.devices import TimeDevice as XknxTimeDevice
@ -23,6 +22,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
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
from . import KNXModule
from .const import ( from .const import (
CONF_RESPOND_TO_READ, CONF_RESPOND_TO_READ,
CONF_STATE_ADDRESS, CONF_STATE_ADDRESS,
@ -33,8 +33,6 @@ from .const import (
) )
from .knx_entity import KnxEntity from .knx_entity import KnxEntity
_TIME_TRANSLATION_FORMAT: Final = "%H:%M:%S"
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
@ -42,10 +40,12 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up entities for KNX platform.""" """Set up entities for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.TIME] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.TIME]
async_add_entities(KNXTimeEntity(xknx, entity_config) for entity_config in config) async_add_entities(
KNXTimeEntity(knx_module, entity_config) for entity_config in config
)
def _create_xknx_device(xknx: XKNX, config: ConfigType) -> XknxTimeDevice: def _create_xknx_device(xknx: XKNX, config: ConfigType) -> XknxTimeDevice:
@ -66,9 +66,12 @@ class KNXTimeEntity(KnxEntity, TimeEntity, RestoreEntity):
_device: XknxTimeDevice _device: XknxTimeDevice
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize a KNX time.""" """Initialize a KNX time."""
super().__init__(_create_xknx_device(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_xknx_device(knx_module.xknx, config),
)
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_unique_id = str(self._device.remote_value.group_address) self._attr_unique_id = str(self._device.remote_value.group_address)

View File

@ -19,6 +19,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
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 KnxEntity
from .schema import WeatherSchema from .schema import WeatherSchema
@ -30,10 +31,12 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up switch(es) for KNX platform.""" """Set up switch(es) for KNX platform."""
xknx: XKNX = hass.data[DOMAIN].xknx knx_module: KNXModule = hass.data[DOMAIN]
config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.WEATHER] config: list[ConfigType] = hass.data[DATA_KNX_CONFIG][Platform.WEATHER]
async_add_entities(KNXWeather(xknx, entity_config) for entity_config in config) async_add_entities(
KNXWeather(knx_module, entity_config) for entity_config in config
)
def _create_weather(xknx: XKNX, config: ConfigType) -> XknxWeather: def _create_weather(xknx: XKNX, config: ConfigType) -> XknxWeather:
@ -80,9 +83,12 @@ class KNXWeather(KnxEntity, WeatherEntity):
_attr_native_temperature_unit = UnitOfTemperature.CELSIUS _attr_native_temperature_unit = UnitOfTemperature.CELSIUS
_attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND
def __init__(self, xknx: XKNX, config: ConfigType) -> None: def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
"""Initialize of a KNX sensor.""" """Initialize of a KNX sensor."""
super().__init__(_create_weather(xknx, config)) super().__init__(
knx_module=knx_module,
device=_create_weather(knx_module.xknx, config),
)
self._attr_unique_id = str(self._device._temperature.group_address_state) # noqa: SLF001 self._attr_unique_id = str(self._device._temperature.group_address_state) # noqa: SLF001
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)