From 6316af863b0b62c1e24dabedb579f232757aef53 Mon Sep 17 00:00:00 2001 From: Artem Eliseev Date: Mon, 7 Sep 2026 21:54:51 +0300 Subject: [PATCH] Replace the deprecated via_device with via_device_id Home Assistant 2026.9 deprecates the `via_device` (domain, identifier) tuple in favour of `via_device_id`, which takes the parent device's registry id; `via_device` stops working in 2027.8.0. Register the hub device explicitly in async_setup_entry before the platforms are forwarded, so the per-identity entities can look it up and pass its `.id`. The hub device info moves into a shared helper so both call sites describe the same device. --- custom_components/reticulum/__init__.py | 8 +++++ custom_components/reticulum/entity.py | 42 ++++++++++++++++++------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/custom_components/reticulum/__init__.py b/custom_components/reticulum/__init__.py index 17297b0..b144053 100644 --- a/custom_components/reticulum/__init__.py +++ b/custom_components/reticulum/__init__.py @@ -22,6 +22,7 @@ from homeassistant.core import ( ) from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.event import async_track_time_interval from .const import ( @@ -62,6 +63,7 @@ from .const import ( STORAGE_SUBDIR, SUBENTRY_TYPE_IDENTITY, ) +from .entity import hub_device_info from .reticulum_client import ( IdentityManager, ReticulumError, @@ -130,6 +132,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) runtime.identities[subentry.subentry_id] = manager + # Register the hub device before the platforms, so the per-identity devices can + # link to it with `via_device_id`. + dr.async_get(hass).async_get_or_create( + config_entry_id=entry.entry_id, **hub_device_info(entry) + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) # Per-identity announce + periodic schedules. diff --git a/custom_components/reticulum/entity.py b/custom_components/reticulum/entity.py index e02208b..9f5f15e 100644 --- a/custom_components/reticulum/entity.py +++ b/custom_components/reticulum/entity.py @@ -4,7 +4,11 @@ from __future__ import annotations from homeassistant.config_entries import ConfigEntry, ConfigSubentry from homeassistant.core import callback -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo +from homeassistant.helpers.device_registry import ( + DeviceEntryType, + DeviceInfo, + async_get as async_get_device_registry, +) from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity @@ -12,6 +16,22 @@ from .const import DOMAIN, identity_signal, stack_signal from .reticulum_client import IdentityManager, ReticulumStack +def hub_device_info(entry: ConfigEntry) -> DeviceInfo: + """Device info for the stack (hub) device. + + Shared with ``async_setup_entry``, which registers this device up front so the + per-identity devices can reference it by registry id. + """ + return DeviceInfo( + identifiers={(DOMAIN, entry.entry_id)}, + name="Reticulum", + manufacturer="Reticulum Network Stack", + model="RNS stack", + entry_type=DeviceEntryType.SERVICE, + configuration_url="https://reticulum.network/", + ) + + class ReticulumStackEntity(Entity): """Base for hub (stack-level) entities.""" @@ -21,14 +41,7 @@ class ReticulumStackEntity(Entity): def __init__(self, stack: ReticulumStack, entry: ConfigEntry) -> None: self._stack = stack self._entry = entry - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, entry.entry_id)}, - name="Reticulum", - manufacturer="Reticulum Network Stack", - model="RNS stack", - entry_type=DeviceEntryType.SERVICE, - configuration_url="https://reticulum.network/", - ) + self._attr_device_info = hub_device_info(entry) async def async_added_to_hass(self) -> None: self.async_on_remove( @@ -57,14 +70,21 @@ class ReticulumIdentityEntity(Entity): self._manager = manager self._entry = entry self._subentry = subentry - self._attr_device_info = DeviceInfo( + device_info = DeviceInfo( identifiers={(DOMAIN, subentry.subentry_id)}, name=subentry.title, manufacturer="Reticulum Network Stack", model="LXMF identity", entry_type=DeviceEntryType.SERVICE, - via_device=(DOMAIN, entry.entry_id), ) + # `via_device_id` takes the parent's registry id, not its identifiers, so + # look up the hub device registered by async_setup_entry. + hub = async_get_device_registry(manager.hass).async_get_device_by_identifier( + (DOMAIN, entry.entry_id), entry.entry_id + ) + if hub is not None: + device_info["via_device_id"] = hub.id + self._attr_device_info = device_info async def async_added_to_hass(self) -> None: self.async_on_remove(