Add subdevices support to ESPHome

This commit is contained in:
J. Nick Koston
2025-06-23 13:13:58 +02:00
parent 82c1751f85
commit 22492628d3
2 changed files with 70 additions and 5 deletions

View File

@@ -244,9 +244,21 @@ class EsphomeEntity(EsphomeBaseEntity, Generic[_InfoT, _StateT]):
self._key = entity_info.key self._key = entity_info.key
self._state_type = state_type self._state_type = state_type
self._on_static_info_update(entity_info) self._on_static_info_update(entity_info)
# Determine the device connection based on whether this entity belongs to a sub device
if entity_info.device_id:
# Entity belongs to a sub device
self._attr_device_info = DeviceInfo(
identifiers={
(DOMAIN, f"{device_info.mac_address}_{entity_info.device_id}")
}
)
else:
# Entity belongs to the main device
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
connections={(dr.CONNECTION_NETWORK_MAC, device_info.mac_address)} connections={(dr.CONNECTION_NETWORK_MAC, device_info.mac_address)}
) )
if entity_info.name: if entity_info.name:
self.entity_id = f"{domain}.{device_info.name}_{entity_info.object_id}" self.entity_id = f"{domain}.{device_info.name}_{entity_info.object_id}"
else: else:

View File

@@ -751,6 +751,26 @@ def _async_setup_device_registry(
device_info = entry_data.device_info device_info = entry_data.device_info
if TYPE_CHECKING: if TYPE_CHECKING:
assert device_info is not None assert device_info is not None
device_registry = dr.async_get(hass)
# Build sets of valid device identifiers and connections
valid_connections = {(dr.CONNECTION_NETWORK_MAC, device_info.mac_address)}
valid_identifiers = {
(DOMAIN, f"{device_info.mac_address}_{sub_device.device_id}")
for sub_device in device_info.devices
}
# Remove devices that no longer exist
for device in dr.async_entries_for_config_entry(device_registry, entry.entry_id):
# Skip devices we want to keep
if (
device.connections & valid_connections
or device.identifiers & valid_identifiers
):
continue
# Remove everything else
device_registry.async_remove_device(device.id)
sw_version = device_info.esphome_version sw_version = device_info.esphome_version
if device_info.compilation_time: if device_info.compilation_time:
sw_version += f" ({device_info.compilation_time})" sw_version += f" ({device_info.compilation_time})"
@@ -779,11 +799,14 @@ def _async_setup_device_registry(
f"{device_info.project_version} (ESPHome {device_info.esphome_version})" f"{device_info.project_version} (ESPHome {device_info.esphome_version})"
) )
suggested_area = None suggested_area: str | None = None
if device_info.suggested_area: if device_info.suggested_area:
suggested_area = device_info.suggested_area suggested_area = device_info.suggested_area
elif device_info.area:
# Use main device area if suggested_area is not set
suggested_area = device_info.area.name
device_registry = dr.async_get(hass) # Create/update main device
device_entry = device_registry.async_get_or_create( device_entry = device_registry.async_get_or_create(
config_entry_id=entry.entry_id, config_entry_id=entry.entry_id,
configuration_url=configuration_url, configuration_url=configuration_url,
@@ -794,6 +817,36 @@ def _async_setup_device_registry(
sw_version=sw_version, sw_version=sw_version,
suggested_area=suggested_area, suggested_area=suggested_area,
) )
# Handle sub devices
# Find available areas from device_info
areas_by_id = {area.area_id: area for area in device_info.areas}
# Create/update sub devices that should exist
for sub_device in device_info.devices:
# Create a unique identifier for this sub device
sub_device_identifier = f"{device_info.mac_address}_{sub_device.device_id}"
# Determine the area for this sub device
sub_device_suggested_area: str | None = None
if sub_device.area_id and sub_device.area_id in areas_by_id:
sub_device_suggested_area = areas_by_id[sub_device.area_id].name
sub_device_entry = device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, sub_device_identifier)},
name=sub_device.name,
manufacturer=manufacturer,
model=model,
sw_version=sw_version,
suggested_area=sub_device_suggested_area,
)
# Update the sub device to set via_device_id
device_registry.async_update_device(
sub_device_entry.id,
via_device_id=device_entry.id,
)
return device_entry.id return device_entry.id