mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Migrate integrations to setting via_device_id in DeviceInfo (#177494)
This commit is contained in:
@@ -76,7 +76,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: BeoConfigEntry) -> bool:
|
||||
# Create device now as BeoWebsocket needs a device for
|
||||
# debug logging, firing events etc.
|
||||
device_registry = dr.async_get(hass)
|
||||
device_registry.async_get_or_create(
|
||||
device = device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, entry.unique_id)},
|
||||
model=entry.data[CONF_MODEL],
|
||||
@@ -92,7 +92,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: BeoConfigEntry) -> bool:
|
||||
serial_number=remote.serial_number,
|
||||
sw_version=remote.app_version,
|
||||
manufacturer=MANUFACTURER,
|
||||
via_device=(DOMAIN, entry.unique_id),
|
||||
via_device_id=device.id,
|
||||
)
|
||||
|
||||
websocket = BeoWebsocket(hass, entry, client)
|
||||
|
||||
@@ -35,7 +35,7 @@ async def async_setup_entry(
|
||||
# Add a device for the SAM itself.
|
||||
sam_uid = names.sam_device_uid(entry)
|
||||
device_registry = dr.async_get(hass)
|
||||
device_registry.async_get_or_create(
|
||||
sam_device = device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, sam_uid)},
|
||||
manufacturer="Bryant",
|
||||
@@ -55,7 +55,7 @@ async def async_setup_entry(
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, names.system_device_uid(sam_uid, sys_id))},
|
||||
via_device=(DOMAIN, names.sam_device_uid(entry)),
|
||||
via_device_id=sam_device.id,
|
||||
manufacturer="Bryant",
|
||||
name=f"System {sys_id}",
|
||||
)
|
||||
|
||||
@@ -17,6 +17,7 @@ from homeassistant.components.climate import (
|
||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
@@ -45,6 +46,7 @@ async def async_setup_entry(
|
||||
zone_id = sz[1]
|
||||
client = config_entry.runtime_data.get(tuple(sz))
|
||||
climate = BryantEvolutionClimate(
|
||||
hass,
|
||||
client,
|
||||
system_id,
|
||||
zone_id,
|
||||
@@ -82,6 +84,7 @@ class BryantEvolutionClimate(ClimateEntity):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
client: BryantEvolutionLocalClient,
|
||||
system_id: int,
|
||||
zone_id: int,
|
||||
@@ -94,7 +97,11 @@ class BryantEvolutionClimate(ClimateEntity):
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._attr_unique_id)},
|
||||
manufacturer="Bryant",
|
||||
via_device=(DOMAIN, names.system_device_uid(sam_uid, system_id)),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
hass,
|
||||
(DOMAIN, names.system_device_uid(sam_uid, system_id)),
|
||||
config_entry_id=sam_uid, # This is the config entry id
|
||||
),
|
||||
name=f"System {system_id} Zone {zone_id}",
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from typing import override
|
||||
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
@@ -53,7 +54,11 @@ class BSBLanCircuitEntity(BSBLanEntity):
|
||||
identifiers={(DOMAIN, f"{mac}-circuit-{circuit}")},
|
||||
translation_key="heating_circuit",
|
||||
translation_placeholders={"circuit": str(circuit)},
|
||||
via_device=(DOMAIN, mac),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
coordinator.hass,
|
||||
(DOMAIN, mac),
|
||||
config_entry_id=coordinator.config_entry.entry_id,
|
||||
),
|
||||
manufacturer=main_info["manufacturer"],
|
||||
model=main_info.get("model"),
|
||||
model_id=main_info.get("model_id"),
|
||||
@@ -101,7 +106,11 @@ class BSBLanWaterHeaterDeviceEntity(BSBLanDualCoordinatorEntity):
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, f"{mac}-water-heater")},
|
||||
translation_key="water_heater",
|
||||
via_device=(DOMAIN, mac),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
fast_coordinator.hass,
|
||||
(DOMAIN, mac),
|
||||
config_entry_id=fast_coordinator.config_entry.entry_id,
|
||||
),
|
||||
manufacturer=main_info["manufacturer"],
|
||||
model=main_info.get("model"),
|
||||
model_id=main_info.get("model_id"),
|
||||
|
||||
@@ -93,7 +93,11 @@ class ComelitBaseCoordinator(DataUpdateCoordinator[T]):
|
||||
f"{self.config_entry.entry_id}-{object_type}-{object_class.index}",
|
||||
)
|
||||
},
|
||||
via_device=(DOMAIN, self.config_entry.entry_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
self.hass,
|
||||
(DOMAIN, self.config_entry.entry_id),
|
||||
config_entry_id=self.config_entry.entry_id,
|
||||
),
|
||||
name=object_class.name,
|
||||
model=f"{self._device} {object_type}",
|
||||
manufacturer="Comelit",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from typing import Any, override
|
||||
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
@@ -47,5 +48,9 @@ class Control4Entity(CoordinatorEntity[Any]):
|
||||
manufacturer=self._device_manufacturer,
|
||||
model=self._device_model,
|
||||
name=self._device_name,
|
||||
via_device=(DOMAIN, self._controller_unique_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
self.hass,
|
||||
(DOMAIN, self._controller_unique_id),
|
||||
config_entry_id=self.coordinator.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ from pydeconz.models.scene import Scene as PydeconzScene
|
||||
from pydeconz.models.sensor import SensorBase as PydeconzSensorBase
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE, DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import Entity
|
||||
@@ -64,7 +65,11 @@ class DeconzBase[_DeviceT: _DeviceType]:
|
||||
model=self._device.model_id,
|
||||
name=self._device.name,
|
||||
sw_version=self._device.software_version,
|
||||
via_device=(DOMAIN, self.hub.api.config.bridge_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
self.hub.hass,
|
||||
(DOMAIN, self.hub.api.config.bridge_id),
|
||||
config_entry_id=self.hub.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -185,5 +190,9 @@ class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
|
||||
manufacturer="dresden elektronik",
|
||||
model="deCONZ group",
|
||||
name=self.deconz_group.name,
|
||||
via_device=(DOMAIN, self.hub.api.config.bridge_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
self.hub.hass,
|
||||
(DOMAIN, self.hub.api.config.bridge_id),
|
||||
config_entry_id=self.hub.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -27,6 +27,7 @@ from homeassistant.components.light import (
|
||||
LightEntityFeature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util.color import (
|
||||
@@ -411,7 +412,11 @@ class DeconzGroup(DeconzBaseLight[Group]):
|
||||
manufacturer="dresden elektronik",
|
||||
model="deCONZ group",
|
||||
name=self._device.name,
|
||||
via_device=(DOMAIN, self.hub.api.config.bridge_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
self.hub.hass,
|
||||
(DOMAIN, self.hub.api.config.bridge_id),
|
||||
config_entry_id=self.hub.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
|
||||
@@ -43,6 +43,7 @@ class EgaugeData:
|
||||
class EgaugeDataCoordinator(DataUpdateCoordinator[EgaugeData]):
|
||||
"""Class to manage fetching eGauge data."""
|
||||
|
||||
config_entry: EgaugeConfigEntry
|
||||
serial_number: str
|
||||
hostname: str
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Base entity for the eGauge integration."""
|
||||
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
@@ -29,5 +30,9 @@ class EgaugeEntity(CoordinatorEntity[EgaugeDataCoordinator]):
|
||||
name=register_name,
|
||||
manufacturer=MANUFACTURER,
|
||||
model=MODEL,
|
||||
via_device=(DOMAIN, coordinator.serial_number),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
coordinator.hass,
|
||||
(DOMAIN, coordinator.serial_number),
|
||||
config_entry_id=coordinator.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -542,13 +542,18 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
|
||||
self._devices[dev_mac] = device
|
||||
|
||||
# manually register device entry for new connected device
|
||||
dr.async_get(self.hass).async_get_or_create(
|
||||
device_registry = dr.async_get(self.hass)
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=self.config_entry.entry_id,
|
||||
connections={(CONNECTION_NETWORK_MAC, dev_mac)},
|
||||
default_manufacturer="FRITZ!",
|
||||
default_model="FRITZ!Box Tracked device",
|
||||
default_name=device.hostname,
|
||||
via_device=(DOMAIN, self.unique_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
self.hass,
|
||||
(DOMAIN, self.unique_id),
|
||||
config_entry_id=self.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
@@ -259,7 +259,11 @@ class FroniusSolarNet:
|
||||
"model", inverter["device_type"]["value"]
|
||||
),
|
||||
name=inverter.get("custom_name", {}).get("value"),
|
||||
via_device=(DOMAIN, self.solar_net_device_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
self.hass,
|
||||
(DOMAIN, self.solar_net_device_id),
|
||||
config_entry_id=self.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
inverter_infos.append(
|
||||
FroniusDeviceInfo(
|
||||
|
||||
@@ -31,7 +31,7 @@ from .sensor import (
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import FroniusSolarNet
|
||||
from . import FroniusConfigEntry, FroniusSolarNet
|
||||
|
||||
|
||||
class FroniusCoordinatorBase(
|
||||
@@ -39,6 +39,7 @@ class FroniusCoordinatorBase(
|
||||
):
|
||||
"""Query Fronius endpoint and keep track of seen conditions."""
|
||||
|
||||
config_entry: FroniusConfigEntry
|
||||
default_interval: timedelta
|
||||
error_interval: timedelta
|
||||
valid_descriptions: Mapping[Platform, Sequence[FroniusEntityDescription]]
|
||||
|
||||
@@ -24,6 +24,7 @@ from homeassistant.const import (
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
@@ -859,7 +860,11 @@ class MeterSensor(_FroniusSensorEntity):
|
||||
manufacturer=meter_data["manufacturer"]["value"],
|
||||
model=meter_data["model"]["value"],
|
||||
name=meter_data["model"]["value"],
|
||||
via_device=(DOMAIN, coordinator.solar_net.solar_net_device_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
coordinator.hass,
|
||||
(DOMAIN, coordinator.solar_net.solar_net_device_id),
|
||||
config_entry_id=coordinator.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
self._attr_unique_id = f"{meter_uid}-{description.key}"
|
||||
|
||||
@@ -883,7 +888,11 @@ class OhmpilotSensor(_FroniusSensorEntity):
|
||||
model=f"{device_data['model']['value']} {device_data['hardware']['value']}",
|
||||
name=device_data["model"]["value"],
|
||||
sw_version=device_data["software"]["value"],
|
||||
via_device=(DOMAIN, coordinator.solar_net.solar_net_device_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
coordinator.hass,
|
||||
(DOMAIN, coordinator.solar_net.solar_net_device_id),
|
||||
config_entry_id=coordinator.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
self._attr_unique_id = f"{device_data['serial']['value']}-{description.key}"
|
||||
|
||||
@@ -925,5 +934,9 @@ class StorageSensor(_FroniusSensorEntity):
|
||||
manufacturer=storage_data["manufacturer"]["value"],
|
||||
model=storage_data["model"]["value"],
|
||||
name=storage_data["model"]["value"],
|
||||
via_device=(DOMAIN, coordinator.solar_net.solar_net_device_id),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
coordinator.hass,
|
||||
(DOMAIN, coordinator.solar_net.solar_net_device_id),
|
||||
config_entry_id=coordinator.config_entry.entry_id,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -90,6 +90,10 @@ class ShadeEntity(HDEntity):
|
||||
manufacturer=MANUFACTURER,
|
||||
model=self._shade.type_name,
|
||||
sw_version=self._shade.firmware,
|
||||
via_device=(DOMAIN, self._device_info.serial_number),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
self.coordinator.hass,
|
||||
(DOMAIN, self._device_info.serial_number),
|
||||
config_entry_id=self.coordinator.config_entry.entry_id,
|
||||
),
|
||||
configuration_url=self._configuration_url,
|
||||
)
|
||||
|
||||
@@ -24,6 +24,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||
class AqualinkDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
"""Data coordinator for Aqualink systems."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, system: Any
|
||||
) -> None:
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import override
|
||||
|
||||
from iaqualink.device import AqualinkDevice
|
||||
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
@@ -34,7 +35,11 @@ class AqualinkEntity[AqualinkDeviceT: AqualinkDevice](
|
||||
self._attr_unique_id = f"{dev.system.serial}_{dev.name}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._attr_unique_id)},
|
||||
via_device=(DOMAIN, dev.system.serial),
|
||||
via_device_id=dr.async_get_device_id_by_identifier(
|
||||
coordinator.hass,
|
||||
(DOMAIN, dev.system.serial),
|
||||
config_entry_id=coordinator.config_entry.entry_id,
|
||||
),
|
||||
manufacturer=dev.manufacturer,
|
||||
model=dev.model,
|
||||
name=dev.label,
|
||||
|
||||
@@ -3149,6 +3149,27 @@ def async_get(hass: HomeAssistant) -> DeviceRegistry:
|
||||
raise RuntimeError("Device registry not set up") from ex
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_device_id_by_identifier(
|
||||
hass: HomeAssistant, identifier: tuple[str, str], *, config_entry_id: str
|
||||
) -> str:
|
||||
"""Get the id of the device with the identifier, owned by the config entry.
|
||||
|
||||
Convenience wrapper for linking a device to its via device through
|
||||
via_device_id. Identifiers are unique within a config entry, so the lookup
|
||||
cannot be ambiguous.
|
||||
|
||||
Raises ValueError if no such device exists.
|
||||
"""
|
||||
device = async_get(hass).async_get_device_by_identifier(identifier, config_entry_id)
|
||||
if device is None:
|
||||
raise ValueError(
|
||||
f"There is no device with identifier {identifier} in config entry "
|
||||
f"{config_entry_id}"
|
||||
)
|
||||
return device.id
|
||||
|
||||
|
||||
def async_setup(hass: HomeAssistant) -> None:
|
||||
"""Set up device registry."""
|
||||
if DATA_REGISTRY in hass.data:
|
||||
|
||||
@@ -2430,6 +2430,34 @@ async def test_async_get_device_by_connection_normalizes(
|
||||
)
|
||||
|
||||
|
||||
async def test_async_get_device_id_by_identifier(
|
||||
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
||||
) -> None:
|
||||
"""The id lookup returns the device id, and raises when there is no match."""
|
||||
entry = MockConfigEntry(domain="test")
|
||||
entry.add_to_hass(hass)
|
||||
device = device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id, identifiers={("test", "1")}
|
||||
)
|
||||
|
||||
assert (
|
||||
dr.async_get_device_id_by_identifier(
|
||||
hass, ("test", "1"), config_entry_id=entry.entry_id
|
||||
)
|
||||
== device.id
|
||||
)
|
||||
# A missing device is treated as an error: an unknown identifier or the
|
||||
# wrong config entry both raise rather than silently returning None.
|
||||
with pytest.raises(ValueError, match="no device with identifier"):
|
||||
dr.async_get_device_id_by_identifier(
|
||||
hass, ("test", "missing"), config_entry_id=entry.entry_id
|
||||
)
|
||||
with pytest.raises(ValueError, match="no device with identifier"):
|
||||
dr.async_get_device_id_by_identifier(
|
||||
hass, ("test", "1"), config_entry_id="unknown_entry_id"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("create_kwargs", "lookup_kwargs", "miss_kwargs"),
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user