Skip to content
Merged
15 changes: 15 additions & 0 deletions homeassistant/components/actron_air/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from homeassistant.const import CONF_API_TOKEN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr

from .const import DOMAIN, LOGGER
from .coordinator import (
Expand Down Expand Up @@ -37,13 +38,27 @@ async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) ->
translation_key="setup_connection_error",
) from err

device_registry = dr.async_get(hass)
system_coordinators: dict[str, ActronAirSystemCoordinator] = {}
for system in systems:
coordinator = ActronAirSystemCoordinator(hass, entry, api, system)
LOGGER.debug("Setting up coordinator for system: %s", system.serial)
await coordinator.async_config_entry_first_refresh()
system_coordinators[system.serial] = coordinator

# Register the AC system device so zone entities can link to it as their
# via device when they are set up.
ac_system = coordinator.data.ac_system
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, system.serial)},
name=ac_system.system_name,
manufacturer="Actron Air",
model_id=ac_system.master_wc_model,
sw_version=ac_system.master_wc_firmware_version,
serial_number=system.serial,
)

entry.runtime_data = ActronAirRuntimeData(
api=api,
system_coordinators=system_coordinators,
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/actron_air/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class ActronAirRuntimeData:
class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirStatus]):
"""System coordinator for Actron Air integration."""

config_entry: ActronAirConfigEntry

def __init__(
self,
hass: HomeAssistant,
Expand Down
7 changes: 6 additions & 1 deletion homeassistant/components/actron_air/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from actron_neo_api import ActronAirAPIError, ActronAirZone

from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

Expand Down Expand Up @@ -90,5 +91,9 @@ def __init__(
manufacturer="Actron Air",
model="Zone",
suggested_area=zone.title,
via_device=(DOMAIN, self._serial_number),
via_device_id=dr.async_get_device_id_by_identifier(
coordinator.hass,
(DOMAIN, self._serial_number),
config_entry_id=coordinator.config_entry.entry_id,
),
)
14 changes: 13 additions & 1 deletion homeassistant/components/advantage_air/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType

Expand Down Expand Up @@ -50,6 +50,18 @@ async def async_setup_entry(

entry.runtime_data = coordinator

# Register the system device so child devices can resolve it as their
# via_device parent regardless of platform setup order.
system = coordinator.data["system"]
dr.async_get(hass).async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, system["rid"])},
manufacturer="Advantage Air",
model=system["sysType"],
name=system["name"],
sw_version=system["myAppRev"],
)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True
Expand Down
13 changes: 11 additions & 2 deletions homeassistant/components/advantage_air/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from advantage_air import ApiError

from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

Expand Down Expand Up @@ -49,7 +50,11 @@ def __init__(self, coordinator: AdvantageAirCoordinator, ac_key: str) -> None:
self._attr_unique_id += f"-{ac_key}"

self._attr_device_info = DeviceInfo(
via_device=(DOMAIN, self.coordinator.data["system"]["rid"]),
via_device_id=dr.async_get_device_id_by_identifier(
self.coordinator.hass,
(DOMAIN, self.coordinator.data["system"]["rid"]),
config_entry_id=self.coordinator.config_entry.entry_id,
),
identifiers={(DOMAIN, self._attr_unique_id)},
manufacturer="Advantage Air",
model=self.coordinator.data["system"]["sysType"],
Expand Down Expand Up @@ -105,7 +110,11 @@ def __init__(
self._attr_unique_id += f"-{self._id}"

self._attr_device_info = DeviceInfo(
via_device=(DOMAIN, self.coordinator.data["system"]["rid"]),
via_device_id=dr.async_get_device_id_by_identifier(
self.coordinator.hass,
(DOMAIN, self.coordinator.data["system"]["rid"]),
config_entry_id=self.coordinator.config_entry.entry_id,
),
identifiers={(DOMAIN, self._attr_unique_id)},
manufacturer="Advantage Air",
model="MyPlace",
Expand Down
7 changes: 6 additions & 1 deletion homeassistant/components/advantage_air/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

Expand Down Expand Up @@ -55,7 +56,11 @@ def __init__(
self._attr_unique_id += f"-{self._id}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._attr_unique_id)},
via_device=(DOMAIN, self.coordinator.data["system"]["rid"]),
via_device_id=dr.async_get_device_id_by_identifier(
self.coordinator.hass,
(DOMAIN, self.coordinator.data["system"]["rid"]),
config_entry_id=self.coordinator.config_entry.entry_id,
),
manufacturer="Advantage Air",
model=light.get("moduleType"),
name=light["name"],
Expand Down
55 changes: 53 additions & 2 deletions homeassistant/components/airzone_cloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@

from aioairzone_cloud.cloudapi import AirzoneCloudApi
from aioairzone_cloud.common import ConnectionOptions
from aioairzone_cloud.const import (
AZD_FIRMWARE,
AZD_MODEL,
AZD_NAME,
AZD_SYSTEMS,
AZD_WEBSERVER,
AZD_WEBSERVERS,
)

from homeassistant.const import CONF_ID, CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import aiohttp_client, device_registry as dr

from .const import DOMAIN, MANUFACTURER
from .coordinator import AirzoneCloudConfigEntry, AirzoneUpdateCoordinator

PLATFORMS: list[Platform] = [
Expand Down Expand Up @@ -42,11 +51,53 @@ async def async_setup_entry(

entry.runtime_data = coordinator

_async_register_devices(hass, entry, coordinator)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True


@callback
def _async_register_devices(
hass: HomeAssistant,
entry: AirzoneCloudConfigEntry,
coordinator: AirzoneUpdateCoordinator,
) -> None:
"""Register WebServer and System devices referenced as via_device parents.

Child devices resolve their via_device_id at add time, so the parents must
already exist regardless of which platform creates their own entities.
"""
device_registry = dr.async_get(hass)

for ws_id, ws_data in coordinator.data.get(AZD_WEBSERVERS, {}).items():
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, ws_id)},
identifiers={(DOMAIN, ws_id)},
manufacturer=MANUFACTURER,
model="WebServer",
name=ws_data[AZD_NAME],
sw_version=ws_data[AZD_FIRMWARE],
)

for system_id, system_data in coordinator.data.get(AZD_SYSTEMS, {}).items():
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, system_id)},
manufacturer=MANUFACTURER,
model=system_data.get(AZD_MODEL),
name=system_data[AZD_NAME],
sw_version=system_data.get(AZD_FIRMWARE),
via_device_id=dr.async_get_device_id_by_identifier(
hass,
(DOMAIN, system_data[AZD_WEBSERVER]),
config_entry_id=entry.entry_id,
),
)


async def async_unload_entry(
hass: HomeAssistant, entry: AirzoneCloudConfigEntry
) -> bool:
Expand Down
24 changes: 20 additions & 4 deletions homeassistant/components/airzone_cloud/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ def __init__(
manufacturer=MANUFACTURER,
model=aidoo_data[AZD_MODEL],
name=aidoo_data[AZD_NAME],
via_device=(DOMAIN, aidoo_data[AZD_WEBSERVER]),
via_device_id=dr.async_get_device_id_by_identifier(
coordinator.hass,
(DOMAIN, aidoo_data[AZD_WEBSERVER]),
config_entry_id=coordinator.config_entry.entry_id,
),
)

@override
Expand Down Expand Up @@ -164,7 +168,11 @@ def __init__(
manufacturer=MANUFACTURER,
model="Hot Water",
name=dhw_data[AZD_NAME],
via_device=(DOMAIN, dhw_data[AZD_WEBSERVER]),
via_device_id=dr.async_get_device_id_by_identifier(
coordinator.hass,
(DOMAIN, dhw_data[AZD_WEBSERVER]),
config_entry_id=coordinator.config_entry.entry_id,
),
)

@override
Expand Down Expand Up @@ -257,7 +265,11 @@ def __init__(
model=system_data.get(AZD_MODEL),
manufacturer=MANUFACTURER,
name=system_data[AZD_NAME],
via_device=(DOMAIN, system_data[AZD_WEBSERVER]),
via_device_id=dr.async_get_device_id_by_identifier(
coordinator.hass,
(DOMAIN, system_data[AZD_WEBSERVER]),
config_entry_id=coordinator.config_entry.entry_id,
),
sw_version=system_data.get(AZD_FIRMWARE),
)

Expand Down Expand Up @@ -322,7 +334,11 @@ def __init__(
model=zone_data.get(AZD_THERMOSTAT_MODEL),
manufacturer=MANUFACTURER,
name=zone_data[AZD_NAME],
via_device=(DOMAIN, self.system_id),
via_device_id=dr.async_get_device_id_by_identifier(
coordinator.hass,
(DOMAIN, self.system_id),
config_entry_id=coordinator.config_entry.entry_id,
),
sw_version=zone_data.get(AZD_THERMOSTAT_FW),
)

Expand Down
20 changes: 20 additions & 0 deletions homeassistant/components/directv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
from homeassistant.const import CONF_HOST, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import DOMAIN

PLATFORMS = [Platform.MEDIA_PLAYER, Platform.REMOTE]
SCAN_INTERVAL = timedelta(seconds=30)

Expand All @@ -28,6 +31,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: DirecTVConfigEntry) -> b

entry.runtime_data = dtv

# Register the receiver device so client entities can link to it via_device_id.
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, dtv.device.info.receiver_id)},
manufacturer=dtv.device.info.brand,
name=next(
(
str.title(location.name)
for location in dtv.device.locations
if not location.client
),
None,
),
sw_version=dtv.device.info.version,
)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True
Expand Down
27 changes: 23 additions & 4 deletions homeassistant/components/directv/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

from directv import DIRECTV

from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import Entity

from . import DirecTVConfigEntry
from .const import DOMAIN


Expand All @@ -14,16 +17,32 @@ class DIRECTVEntity(Entity):
_attr_has_entity_name = True
_attr_name = None

def __init__(self, *, dtv: DIRECTV, name: str, address: str = "0") -> None:
def __init__(
self,
*,
hass: HomeAssistant,
dtv: DIRECTV,
entry: DirecTVConfigEntry,
name: str,
address: str = "0",
) -> None:
"""Initialize the DirecTV entity."""
self._address = address
self._device_id = address if address != "0" else dtv.device.info.receiver_id
self._is_client = address != "0"
self.dtv = dtv
via_device_id: str | None = None
if self._is_client:
via_device_id = dr.async_get_device_id_by_identifier(
hass,
(DOMAIN, dtv.device.info.receiver_id),
config_entry_id=entry.entry_id,
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._device_id)},
manufacturer=self.dtv.device.info.brand,
manufacturer=dtv.device.info.brand,
name=name,
sw_version=self.dtv.device.info.version,
via_device=(DOMAIN, self.dtv.device.info.receiver_id),
sw_version=dtv.device.info.version,
)
if via_device_id is not None:
self._attr_device_info["via_device_id"] = via_device_id
14 changes: 13 additions & 1 deletion homeassistant/components/directv/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ async def async_setup_entry(
async_add_entities(
(
DIRECTVMediaPlayer(
hass=hass,
dtv=dtv,
entry=entry,
name=str.title(location.name),
address=location.address,
)
Expand All @@ -74,10 +76,20 @@ async def async_setup_entry(
class DIRECTVMediaPlayer(DIRECTVEntity, MediaPlayerEntity):
"""Representation of a DirecTV receiver on the network."""

def __init__(self, *, dtv: DIRECTV, name: str, address: str = "0") -> None:
def __init__(
self,
*,
hass: HomeAssistant,
dtv: DIRECTV,
entry: DirecTVConfigEntry,
name: str,
address: str = "0",
) -> None:
"""Initialize DirecTV media player."""
super().__init__(
hass=hass,
dtv=dtv,
entry=entry,
name=name,
address=address,
)
Expand Down
Loading
Loading