Fix KNX unique_id (#49677)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Matthias Alphart 2021-04-28 15:50:01 +02:00 committed by GitHub
parent e96cbccc92
commit 78befcd3fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 96 additions and 6 deletions

View File

@ -37,6 +37,7 @@ class KNXBinarySensor(KnxEntity, BinarySensorEntity):
"""Initialize of KNX binary sensor.""" """Initialize of KNX binary sensor."""
self._device: XknxBinarySensor self._device: XknxBinarySensor
super().__init__(device) super().__init__(device)
self._unique_id = f"{self._device.remote_value.group_address_state}"
@property @property
def device_class(self) -> str | None: def device_class(self) -> str | None:

View File

@ -6,6 +6,7 @@ from typing import Any, Callable
from xknx.devices import Climate as XknxClimate from xknx.devices import Climate as XknxClimate
from xknx.dpt.dpt_hvac_mode import HVACControllerMode, HVACOperationMode from xknx.dpt.dpt_hvac_mode import HVACControllerMode, HVACOperationMode
from xknx.telegram.address import parse_device_group_address
from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
@ -16,12 +17,14 @@ from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
) )
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONTROLLER_MODES, DOMAIN, PRESET_MODES from .const import CONTROLLER_MODES, DOMAIN, PRESET_MODES
from .knx_entity import KnxEntity from .knx_entity import KnxEntity
from .schema import ClimateSchema
CONTROLLER_MODES_INV = {value: key for key, value in CONTROLLER_MODES.items()} CONTROLLER_MODES_INV = {value: key for key, value in CONTROLLER_MODES.items()}
PRESET_MODES_INV = {value: key for key, value in PRESET_MODES.items()} PRESET_MODES_INV = {value: key for key, value in PRESET_MODES.items()}
@ -34,6 +37,7 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up climate(s) for KNX platform.""" """Set up climate(s) for KNX platform."""
_async_migrate_unique_id(hass, discovery_info)
entities = [] entities = []
for device in hass.data[DOMAIN].xknx.devices: for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxClimate): if isinstance(device, XknxClimate):
@ -41,6 +45,33 @@ async def async_setup_platform(
async_add_entities(entities) async_add_entities(entities)
@callback
def _async_migrate_unique_id(
hass: HomeAssistant, discovery_info: DiscoveryInfoType | None
) -> None:
"""Change unique_ids used in 2021.4 to include target_temperature GA."""
entity_registry = er.async_get(hass)
if not discovery_info or not discovery_info["platform_config"]:
return
platform_config = discovery_info["platform_config"]
for entity_config in platform_config:
# normalize group address strings - ga_temperature_state was the old uid
ga_temperature_state = parse_device_group_address(
entity_config[ClimateSchema.CONF_TEMPERATURE_ADDRESS][0]
)
old_uid = str(ga_temperature_state)
entity_id = entity_registry.async_get_entity_id("climate", DOMAIN, old_uid)
if entity_id is None:
continue
ga_target_temperature_state = parse_device_group_address(
entity_config[ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS][0]
)
new_uid = f"{ga_temperature_state}_{ga_target_temperature_state}"
entity_registry.async_update_entity(entity_id, new_unique_id=new_uid)
class KNXClimate(KnxEntity, ClimateEntity): class KNXClimate(KnxEntity, ClimateEntity):
"""Representation of a KNX climate device.""" """Representation of a KNX climate device."""
@ -48,7 +79,10 @@ class KNXClimate(KnxEntity, ClimateEntity):
"""Initialize of a KNX climate device.""" """Initialize of a KNX climate device."""
self._device: XknxClimate self._device: XknxClimate
super().__init__(device) super().__init__(device)
self._unique_id = (
f"{device.temperature.group_address_state}_"
f"{device.target_temperature.group_address_state}"
)
self._unit_of_measurement = TEMP_CELSIUS self._unit_of_measurement = TEMP_CELSIUS
@property @property

View File

@ -6,6 +6,7 @@ from datetime import datetime
from typing import Any, Callable from typing import Any, Callable
from xknx.devices import Cover as XknxCover, Device as XknxDevice from xknx.devices import Cover as XknxCover, Device as XknxDevice
from xknx.telegram.address import parse_device_group_address
from homeassistant.components.cover import ( from homeassistant.components.cover import (
ATTR_POSITION, ATTR_POSITION,
@ -23,12 +24,14 @@ from homeassistant.components.cover import (
CoverEntity, CoverEntity,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_utc_time_change from homeassistant.helpers.event import async_track_utc_time_change
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import DOMAIN from .const import DOMAIN
from .knx_entity import KnxEntity from .knx_entity import KnxEntity
from .schema import CoverSchema
async def async_setup_platform( async def async_setup_platform(
@ -38,6 +41,7 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up cover(s) for KNX platform.""" """Set up cover(s) for KNX platform."""
_async_migrate_unique_id(hass, discovery_info)
entities = [] entities = []
for device in hass.data[DOMAIN].xknx.devices: for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxCover): if isinstance(device, XknxCover):
@ -45,6 +49,37 @@ async def async_setup_platform(
async_add_entities(entities) async_add_entities(entities)
@callback
def _async_migrate_unique_id(
hass: HomeAssistant, discovery_info: DiscoveryInfoType | None
) -> None:
"""Change unique_ids used in 2021.4 to include position_target GA."""
entity_registry = er.async_get(hass)
if not discovery_info or not discovery_info["platform_config"]:
return
platform_config = discovery_info["platform_config"]
for entity_config in platform_config:
# normalize group address strings - ga_updown was the old uid but is optional
updown_addresses = entity_config.get(CoverSchema.CONF_MOVE_LONG_ADDRESS)
if updown_addresses is None:
continue
ga_updown = parse_device_group_address(updown_addresses[0])
old_uid = str(ga_updown)
entity_id = entity_registry.async_get_entity_id("cover", DOMAIN, old_uid)
if entity_id is None:
continue
position_target_addresses = entity_config.get(CoverSchema.CONF_POSITION_ADDRESS)
ga_position_target = (
parse_device_group_address(position_target_addresses[0])
if position_target_addresses is not None
else None
)
new_uid = f"{ga_updown}_{ga_position_target}"
entity_registry.async_update_entity(entity_id, new_unique_id=new_uid)
class KNXCover(KnxEntity, CoverEntity): class KNXCover(KnxEntity, CoverEntity):
"""Representation of a KNX cover.""" """Representation of a KNX cover."""
@ -52,7 +87,9 @@ class KNXCover(KnxEntity, CoverEntity):
"""Initialize the cover.""" """Initialize the cover."""
self._device: XknxCover self._device: XknxCover
super().__init__(device) super().__init__(device)
self._unique_id = (
f"{device.updown.group_address}_{device.position_target.group_address}"
)
self._unsubscribe_auto_updater: Callable[[], None] | None = None self._unsubscribe_auto_updater: Callable[[], None] | None = None
@callback @callback

View File

@ -44,7 +44,7 @@ class KNXFan(KnxEntity, FanEntity):
"""Initialize of KNX fan.""" """Initialize of KNX fan."""
self._device: XknxFan self._device: XknxFan
super().__init__(device) super().__init__(device)
self._unique_id = f"{self._device.speed.group_address}"
self._step_range: tuple[int, int] | None = None self._step_range: tuple[int, int] | None = None
if device.max_step: if device.max_step:
# FanSpeedMode.STEP: # FanSpeedMode.STEP:

View File

@ -17,6 +17,7 @@ class KnxEntity(Entity):
def __init__(self, device: XknxDevice) -> None: def __init__(self, device: XknxDevice) -> None:
"""Set up device.""" """Set up device."""
self._device = device self._device = device
self._unique_id: str | None = None
@property @property
def name(self) -> str: def name(self) -> str:
@ -37,7 +38,7 @@ class KnxEntity(Entity):
@property @property
def unique_id(self) -> str | None: def unique_id(self) -> str | None:
"""Return the unique id of the device.""" """Return the unique id of the device."""
return self._device.unique_id return self._unique_id
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."""

View File

@ -52,7 +52,7 @@ class KNXLight(KnxEntity, LightEntity):
"""Initialize of KNX light.""" """Initialize of KNX light."""
self._device: XknxLight self._device: XknxLight
super().__init__(device) super().__init__(device)
self._unique_id = self._device_unique_id()
self._min_kelvin = device.min_kelvin or LightSchema.DEFAULT_MIN_KELVIN self._min_kelvin = device.min_kelvin or LightSchema.DEFAULT_MIN_KELVIN
self._max_kelvin = device.max_kelvin or LightSchema.DEFAULT_MAX_KELVIN self._max_kelvin = device.max_kelvin or LightSchema.DEFAULT_MAX_KELVIN
self._min_mireds = color_util.color_temperature_kelvin_to_mired( self._min_mireds = color_util.color_temperature_kelvin_to_mired(
@ -62,6 +62,17 @@ class KNXLight(KnxEntity, LightEntity):
self._min_kelvin self._min_kelvin
) )
def _device_unique_id(self) -> str:
"""Return unique id for this device."""
if self._device.switch.group_address is not None:
return f"{self._device.switch.group_address}"
return (
f"{self._device.red.switch.group_address}_"
f"{self._device.green.switch.group_address}_"
f"{self._device.blue.switch.group_address}_"
f"{self._device.white.switch.group_address}"
)
@property @property
def brightness(self) -> int | None: def brightness(self) -> int | None:
"""Return the brightness of this light between 0..255.""" """Return the brightness of this light between 0..255."""

View File

@ -36,6 +36,9 @@ class KNXScene(KnxEntity, Scene):
"""Init KNX scene.""" """Init KNX scene."""
self._device: XknxScene self._device: XknxScene
super().__init__(device) super().__init__(device)
self._unique_id = (
f"{self._device.scene_value.group_address}_{self._device.scene_number}"
)
async def async_activate(self, **kwargs: Any) -> None: async def async_activate(self, **kwargs: Any) -> None:
"""Activate the scene.""" """Activate the scene."""

View File

@ -37,6 +37,7 @@ class KNXSensor(KnxEntity, SensorEntity):
"""Initialize of a KNX sensor.""" """Initialize of a KNX sensor."""
self._device: XknxSensor self._device: XknxSensor
super().__init__(device) super().__init__(device)
self._unique_id = f"{self._device.sensor_value.group_address_state}"
@property @property
def state(self) -> StateType: def state(self) -> StateType:

View File

@ -53,6 +53,7 @@ class KNXSwitch(KnxEntity, SwitchEntity):
invert=config[SwitchSchema.CONF_INVERT], invert=config[SwitchSchema.CONF_INVERT],
) )
) )
self._unique_id = f"{self._device.switch.group_address}"
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:

View File

@ -37,6 +37,7 @@ class KNXWeather(KnxEntity, WeatherEntity):
"""Initialize of a KNX sensor.""" """Initialize of a KNX sensor."""
self._device: XknxWeather self._device: XknxWeather
super().__init__(device) super().__init__(device)
self._unique_id = f"{self._device._temperature.group_address_state}"
@property @property
def temperature(self) -> float | None: def temperature(self) -> float | None: