Matter: Add option to synchronise HA device names to Matter NodeLabel#169844
Matter: Add option to synchronise HA device names to Matter NodeLabel#169844markvp wants to merge 3 commits intohome-assistant:devfrom
Conversation
|
Hey there @home-assistant/matter, mind taking a look at this pull request as it has been labeled with an integration ( Code owner commandsCode owners of
|
There was a problem hiding this comment.
Pull request overview
This PR adds an optional, per-config-entry feature to the Matter integration that synchronizes Home Assistant device renames to the corresponding Matter NodeLabel attribute (HA → Matter only).
Changes:
- Add a Matter options flow setting (
sync_names) to enable/disable one-way device name synchronization. - Listen for device registry
name_by_userupdates and write the appropriate Matter NodeLabel attribute (root vs bridged endpoints), including a one-shot backfill when enabled. - Add test coverage for rename behavior, bridged/root writes, truncation, backfill, and options flow persistence.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
homeassistant/components/matter/__init__.py |
Adds name-sync listener, NodeLabel writing logic, and backfill behavior. |
homeassistant/components/matter/config_flow.py |
Implements an options flow to toggle sync_names. |
homeassistant/components/matter/const.py |
Introduces CONF_SYNC_NAMES and NODE_LABEL_MAX_BYTES. |
homeassistant/components/matter/helpers.py |
Adds endpoint resolution helper used by name sync and backfill. |
homeassistant/components/matter/strings.json |
Adds user-facing options flow strings for the new setting. |
tests/components/matter/test_name_sync.py |
Adds tests for HA→Matter name synchronization behavior. |
tests/components/matter/test_config_flow.py |
Adds tests ensuring options flow persists the submitted value. |
| return next( | ||
| ( | ||
| endpoint | ||
| for node in matter_client.get_nodes() | ||
| for endpoint in node.endpoints.values() | ||
| if get_device_id(server_info, endpoint) == device_id | ||
| ), | ||
| None, | ||
| ) |
There was a problem hiding this comment.
Not changing for now. A typical Matter fabric is on the order of tens of nodes, the device-rename path is user-driven (one event per click), and the existing get_node_from_device_entry uses the same O(N) scan. Adding a separate cache would mean keeping it in sync with node add/remove events; happy to revisit if a real fabric demonstrates the cost.
| matter_client = entry.runtime_data.adapter.matter_client | ||
| devices = dr.async_entries_for_config_entry(dr.async_get(hass), entry.entry_id) | ||
| for device in devices: | ||
| endpoint = get_endpoint_from_device_entry(matter_client, device) | ||
| if endpoint is None: | ||
| continue | ||
| label = _resolve_label(device) | ||
| if label is None: | ||
| continue | ||
| await _async_write_node_label(matter_client, endpoint, label) | ||
|
|
There was a problem hiding this comment.
Sequential is intentional. Backfill is a one-shot event (toggle-on or setup-with-option-already-on), and Thread devices in particular don't react well to bursts of concurrent writes — sequential keeps it polite to the fabric. Cost is bounded by fabric size, which is small in practice.
Add a per-config-entry option that, when enabled, pushes Home Assistant device renames to the corresponding Matter device's NodeLabel attribute. Sync is one-way (HA → Matter): NodeLabel changes on the Matter side are not reflected back. Bridged endpoints write BridgedDeviceBasicInformation NodeLabel on the bridged endpoint; root nodes write BasicInformation NodeLabel on endpoint 0. Names are truncated to 32 UTF-8 bytes per spec. Toggling the option on (or having it on at setup) triggers a one-shot backfill of the current HA names to every Matter device for the entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lstrip() removes a character set, not a prefix; switch to removeprefix() in get_endpoint_from_device_entry. Safe by accident today (device IDs are hex+dash, no overlap with the "deviceid_" character set), but the right primitive for "strip this prefix once". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| except MatterError as err: | ||
| LOGGER.debug( | ||
| "Failed to sync NodeLabel for node %s endpoint %s: %s", | ||
| endpoint.node.node_id, | ||
| endpoint.endpoint_id, |
| endpoint | ||
| for node in matter_client.get_nodes() | ||
| for endpoint in node.endpoints.values() | ||
| if get_device_id(server_info, endpoint) == device_id |
| "step": { | ||
| "init": { | ||
| "data": { | ||
| "sync_names": "Synchronise device names to Matter (one-way)" |
| encoded = name.encode("utf-8") | ||
| if len(encoded) <= NODE_LABEL_MAX_BYTES: | ||
| return name | ||
| # Decode back, dropping a partial trailing multi-byte sequence if any. | ||
| return encoded[:NODE_LABEL_MAX_BYTES].decode("utf-8", errors="ignore") |
- Fix lstrip → removeprefix in get_node_from_device_entry - Return compose-parent endpoint from get_endpoint_from_device_entry - Catch NotConnected in _async_write_node_label - Fix spelling: Synchronise → Synchronize - Add multibyte UTF-8 truncation test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Proposed change
Adds a per-config-entry option (under Configure on the Matter integration card) that, when enabled, pushes Home Assistant device renames to the corresponding Matter device's NodeLabel attribute.
Behaviour:
BridgedDeviceBasicInformation.NodeLabelon the bridged endpoint.BasicInformation.NodeLabelon endpoint 0.name_by_userchanges; clearing an override falls back to the inferred device name so the Matter side is not left stale.The existing
get_endpoint_from_device_entryhelper now takes theMatterClientdirectly so resolution works during the entry's setup phase, before it transitions to LOADED.Type of change
Additional information
Checklist
If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
Updated and included derived files by running: `python3 -m script.hassfest`.
Updated by running `python3 -m script.gen_requirements_all`.
To help with the load of incoming pull requests: