Files
claude 027ff685b7 Multiple identities via config subentries + regenerate + editable name
Refactor from one-entry-one-identity to a hub config entry (shared RNS stack)
plus config subentries of type "identity", each an independent LXMF identity
with its own LXMRouter, address, device and entities. This is required because
LXMRouter allows only one delivery identity per instance.

- ReticulumStack: owns the single process-wide RNS instance, peer discovery and
  interface telemetry (hub device).
- IdentityManager: one per subentry, its own LXMRouter/identity/destination and
  delivery callback (identity device). Signal handlers suppressed and atexit
  handlers tamed for each router too.
- Config subentry flow (add / reconfigure identity); a first identity is seeded
  on stack creation. Editable announce display name = subentry title.
- Per-identity "Regenerate identity" button: new address + re-announce
  (clears the router's single delivery destination first).
- Services and events gain an "identity" field to target/distinguish identities.
- Per-identity assist bridge, greeting-when-assist-off, allow-list, notify,
  announce/sync buttons, message counters; hub-level connectivity/telemetry/peers.

Verified against current HA config_entries/entity_platform/selector and RNS/LXMF
sources. Docs and en/ru translations updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 20:29:05 +03:00

81 lines
2.5 KiB
Python

"""Base entities for the Reticulum integration."""
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.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from .const import DOMAIN, identity_signal, stack_signal
from .reticulum_client import IdentityManager, ReticulumStack
class ReticulumStackEntity(Entity):
"""Base for hub (stack-level) entities."""
_attr_should_poll = False
_attr_has_entity_name = True
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/",
)
async def async_added_to_hass(self) -> None:
self.async_on_remove(
async_dispatcher_connect(
self.hass, stack_signal(self._entry.entry_id), self._handle_update
)
)
@callback
def _handle_update(self) -> None:
self.async_write_ha_state()
class ReticulumIdentityEntity(Entity):
"""Base for per-identity (subentry) entities."""
_attr_should_poll = False
_attr_has_entity_name = True
def __init__(
self,
manager: IdentityManager,
entry: ConfigEntry,
subentry: ConfigSubentry,
) -> None:
self._manager = manager
self._entry = entry
self._subentry = subentry
self._attr_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),
)
async def async_added_to_hass(self) -> None:
self.async_on_remove(
async_dispatcher_connect(
self.hass,
identity_signal(self._subentry.subentry_id),
self._handle_update,
)
)
@callback
def _handle_update(self) -> None:
self.async_write_ha_state()