diff --git a/README.md b/README.md
index 069ca18..401938a 100644
--- a/README.md
+++ b/README.md
@@ -126,8 +126,9 @@ node --version # confirm v22.x
Leave **Node bin directory** blank in the plugin config — auto-detect finds Homebrew
first. (nvm works too; pin `nodeBinDir` explicitly if you use it.)
-**2. Install matter-server:** **Plugins ▸ Matter ▸ Install/update matter-server**. This
-installs the pinned version with the *same* Node it will run the server with, and pins
+**2. Install the Matter controller:** **Plugins ▸ Matter ▸ Install/update the Matter
+controller (matter-server)**. This installs the pinned version with the *same* Node it
+will run the server with, and pins
that Node — which avoids the most common failure, installing with one Node and running
with another whose native modules won't load.
@@ -144,7 +145,7 @@ manual mode, fabric backups, upgrading and uninstalling, is covered in the
[full install guide](https://simons-plugins.github.io/indigo-matter/INSTALL.html).
To **export** Indigo devices as well, add a fourth step — **Plugins ▸ Matter ▸
-Install/update the Matter export bridge** — and then pick your devices in *Manage Matter
+Install/update the Matter bridge** — and then pick your devices in *Manage Matter
Exports…*. The bridge starts itself once something is exported. Full walkthrough in the
install guide.
@@ -188,7 +189,7 @@ Developer reference, in the repo — not intended as user documentation:
- [`docs/API.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/API.md) — the Domio ↔ plugin HTTP contract (v1.3).
- [`docs/BRIDGE_PROTOCOL.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/BRIDGE_PROTOCOL.md) — the plugin ⇄ bridge-node local protocol (export side).
- [`docs/PRD-indigo-matter-plugin.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/PRD-indigo-matter-plugin.md) — product requirements and milestones (inbound).
-- [`docs/PRD-indigo-matter-export.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/PRD-indigo-matter-export.md) — the same for the export bridge.
+- [`docs/PRD-indigo-matter-export.md`](https://github.com/simons-plugins/indigo-matter/blob/main/docs/PRD-indigo-matter-export.md) — the same for the Matter bridge.
- [`bridge-node/`](https://github.com/simons-plugins/indigo-matter/tree/main/bridge-node) — the bridge node itself (TypeScript; the only place matter.js is imported). `npm run build && npm test`.
- [`CLAUDE.md`](https://github.com/simons-plugins/indigo-matter/blob/main/CLAUDE.md) — architecture and workspace conventions.
@@ -217,7 +218,7 @@ What that means in practice:
attestation so uncertified devices (development boards, Homebridge/Matterbridge
bridges) can be commissioned. It is off by default, warns on every start, and should
be turned off once the device is paired.
-- **The export bridge is uncertified in the other direction, and visibly so.** It
+- **The Matter bridge is uncertified in the other direction, and visibly so.** It
advertises with the specification's test vendor ID, so every ecosystem shows an
"uncertified accessory" warning when you add it and you choose *Add Anyway*. That is the
normal state for this class of software — Homebridge, matterbridge and Home Assistant's
diff --git a/bridge-node/README.md b/bridge-node/README.md
index 489027c..9299176 100644
--- a/bridge-node/README.md
+++ b/bridge-node/README.md
@@ -12,7 +12,7 @@ Home and SmartThings are untested and unclaimed.
> no discovery, and no idea what an Indigo device is. It is one half of a pair:
> the plugin decides *what* to export and the node makes it a Matter accessory.
> Install the Indigo plugin; it installs and manages this package for you, via
-> its own **Plugins ▸ Matter ▸ Install/update the Matter export bridge** menu
+> its own **Plugins ▸ Matter ▸ Install/update the Matter bridge** menu
> item and a launchd LaunchAgent it writes.
## Why it is a separate process
diff --git a/docs/HANDOVER.md b/docs/HANDOVER.md
index 4ff103c..f5ab1a3 100644
--- a/docs/HANDOVER.md
+++ b/docs/HANDOVER.md
@@ -83,7 +83,7 @@ bump both to `0.8.0` together, `npm publish` (Simon's npm login),
**jarvis needs a one-time hand edit** — its stale entry predates the marker,
so the fix cannot clear it retroactively. With the bridge node stopped (*Stop
-the Matter export bridge…*), edit
+the Matter bridge…*), edit
`~/Library/Application Support/com.simons-plugins.indigo-matter/bridge-node/endpoint-map.json`:
set `indigo-459564566`'s number to the live `2`, and resolve the duplicate
number 5 (two entries record it — the reset's fingerprint; the non-live one
@@ -572,9 +572,9 @@ batch does instead is say so, prominently, on the page itself and in the menu's
log line. See `docs/INSTALL.md` → "Before you pair the export bridge".
**Two new bridge menu items** close the gap where the controller had recovery
-exits and the bridge had none: "Reinstall the Matter export bridge (clean)…"
+exits and the bridge had none: "Reinstall the Matter bridge (clean)…"
(safe only because `remove_package` became per-package in E7) and "Stop the
-Matter export bridge…" — for the user who disables the plugin and is otherwise
+Matter bridge…" — for the user who disables the plugin and is otherwise
left with a running node and no UI at all, since the allow-list lever needs the
plugin to be running.
@@ -831,7 +831,7 @@ bundle ships **no JavaScript**; `bridge-node/` stays a top-level source
directory in the repo and is published from there.
**`indigo-matter-bridge` is not on the registry yet.** Until Simon publishes it,
-"Install/update the Matter export bridge" cannot resolve the pin and E7 cannot
+"Install/update the Matter bridge" cannot resolve the pin and E7 cannot
be exercised end to end on jarvis.
#### Publishing the bridge node
@@ -872,7 +872,7 @@ menu will still try the registry spec.
**So the managed LaunchAgent is not, strictly, blocked on `npm publish`.** It is
blocked on the package being *installed*, and the local-install recipe above
installs it. What the publish unblocks is the shipped route — the
-"Install/update the Matter export bridge" menu item — which is what a user would
+"Install/update the Matter bridge" menu item — which is what a user would
use and is therefore what "end to end" means. Do not restate the blocker as
"npm" when the distinction matters.
@@ -980,7 +980,7 @@ gates the next:
"Enable Matter export" is ticked, and that the readout line appears. If the
readout is blank, Indigo dispatched neither callback spelling — say so, do
not guess.
-4. **Plugins ▸ Matter ▸ Install/update the Matter export bridge.** Watch for
+4. **Plugins ▸ Matter ▸ Install/update the Matter bridge.** Watch for
"Matter export bridge installed"; it will refuse to start anything while
nothing is exported, which is correct (XG5).
5. **Export one relay** in "Manage Matter Exports…". This is XAC2: the agent
@@ -1167,7 +1167,7 @@ Highs, 5 Mediums and 9 coverage gaps. The five that would have hurt a real user:
caller anywhere in the plugin, while three user-facing strings told the user
to confirm the rebuild "in the plugin". Two menu items now exist:
**"Rebuild Matter Endpoint Map…"** (§3.11, one confirm + the duplication
- warning) and **"Reset Matter Export Pairings…"** (§3.10 `preserve=true`, two
+ warning) and **"Reset Matter Bridge Pairings…"** (§3.10 `preserve=true`, two
confirms). Both gate on `client.connected`, not `attached` — §1.1 holds the
socket open un-attached and that is the only state a rebuild is needed in.
`remove_fabric` still has no UI; it needs E6's fabric readout to pick an
@@ -1838,7 +1838,7 @@ All merged to `main`, all deployed to jarvis. Plugin **2026.0.1 → 2026.1.1** o
- Two `/review-pr` passes found + fixed real safety holes (ignored stop/start bools → success-over-dead-server; swallowed rollback; unvalidated backup output; empty-archive wipe). **#31** fixed a live-caught menu bug: Indigo passes ConfigUI menu callbacks `(self, valuesDict, menuId)` — handlers were 2-arg.
- **Live on jarvis:** Export → valid 915-member zip; Restore → server stop → swap (original → `matter-server.pre-restore-…`) → start → reconnect → **5 nodes reconciled** → device toggles. The two `Matter Warning: connection lost` lines during restore are the **expected ~6s reconnect window**, not errors.
-**Known polish (not filed):** during an *intentional* matter-server restart (restore / "Restart matter-server"), the reconnect loop logs `connection lost` WARNINGs for ~6s. Consider debouncing — log first attempts at debug, escalate to WARNING only past the normal restart window (`matter_client.py`). Small change + test.
+**Known polish (not filed):** during an *intentional* matter-server restart (restore / "Restart the Matter controller"), the reconnect loop logs `connection lost` WARNINGs for ~6s. Consider debouncing — log first attempts at debug, escalate to WARNING only past the normal restart window (`matter_client.py`). Small change + test.
**Next milestone — device-class gaps:** umbrella **#15**; priority order #3 Power Source/battery (promised in PRD, never wired), #4 Generic Switch (buttons — needs `node_event` plumbing through protocol.py + device_sync), #5 Window Covering, #6 Door Lock, #7 bridges-done-properly, #8 standalone fans (a fan-only endpoint currently creates NO device), #9 energy, #10 smoke/CO, #11 air quality, #12 valve, #13 pressure/flow, #14 RVC (icebox). All follow the handler-registry pattern (one `matter_handlers/*.py` + `Devices.xml` type + one line in `registry.default_handlers()`; `thermostat.py` is the precedent for attribute writes).
diff --git a/docs/INSTALL.md b/docs/INSTALL.md
index e1c5aab..a02fc8d 100644
--- a/docs/INSTALL.md
+++ b/docs/INSTALL.md
@@ -3,14 +3,13 @@
The plugin has two halves, and each one is a **separate Node process with its own
npm package**. This guide covers both:
-- **matter-server** — the inbound controller. The plugin drives it so that Matter
- devices in your house become Indigo devices. **Steps 1–4**, and this is what a
- normal install needs. These steps are derived from the live
+- **The Matter controller** (`matter-server`) — the inbound half. The plugin drives it so
+ that Matter devices in your house become Indigo devices. **Steps 1–4**, and this is what
+ a normal install needs. These steps are derived from the live
managed-LaunchAgent bring-up on the reference Indigo server.
-- **the Matter export bridge** — the optional outbound half, which publishes
- selected *Indigo* devices to Apple Home as Matter accessories. **Steps E1–E4**.
- Its npm package (`indigo-matter-bridge`) is on the registry and installs from
- its own menu item — see
+- **The Matter bridge** (`indigo-matter-bridge`) — the optional outbound half, which
+ publishes selected *Indigo* devices to Apple Home as Matter accessories. **Steps E1–E4**.
+ Its npm package is on the registry and installs from its own menu item — see
[Exporting Indigo devices](#exporting-indigo-devices-indigo-as-a-matter-bridge).
If you just installed the plugin and Indigo is logging
@@ -92,7 +91,7 @@ A few things to know before you start:
- **There is a second, optional half.** The plugin can also work the other way round and
publish selected *Indigo* devices to Apple Home as Matter accessories, from a separate
bridge process with its own npm package (`indigo-matter-bridge`), installed from
- **Plugins ▸ Matter ▸ Install/update the Matter export bridge**. Nothing is exported
+ **Plugins ▸ Matter ▸ Install/update the Matter bridge**. Nothing is exported
and no bridge process runs until you add a device to the export list, and it needs
Node (Step 1) and a storage path (Step 3) — *not* a working matter-server. See
[Exporting Indigo devices](#exporting-indigo-devices-indigo-as-a-matter-bridge).
@@ -108,9 +107,9 @@ did not mean to touch.
|---|---|---|
| 1 | *Commission device by setup code (advanced)…*, *Decommission Matter device…* | Matter devices Indigo **controls** — the everyday actions |
| 2 | *Manage Matter Exports…*, *Pair Matter Bridge…*, *Unpair an Ecosystem…* | Indigo devices Indigo **publishes** — the everyday actions |
-| 3 | *Install/update matter-server*, *Restart…*, *Reinstall (clean)…*, *Open matter-server log…* | The inbound controller's own plumbing |
-| 4 | *Install/update the Matter export bridge*, *Reinstall… (clean)…*, *Stop…* | The outbound bridge node's own plumbing |
-| 5 | *Back up the Matter fabric…*, *Restore a fabric backup…*, *Rebuild Matter Endpoint Map…*, *Reset Matter Export Pairings…* | Backup and recovery. The last two are destructive — see [the two destructive recovery actions](#the-two-destructive-recovery-actions) |
+| 3 | *Install/update the Matter controller (matter-server)*, *Restart…*, *Reinstall (clean)…*, *Open the Matter controller log…* | The inbound controller's own plumbing |
+| 4 | *Install/update the Matter bridge*, *Reinstall… (clean)…*, *Stop…* | The outbound bridge node's own plumbing |
+| 5 | *Back up the Matter fabric…*, *Restore a fabric backup…*, *Rebuild Matter Endpoint Map…*, *Reset Matter Bridge Pairings…* | Backup and recovery. The last two are destructive — see [the two destructive recovery actions](#the-two-destructive-recovery-actions) |
---
@@ -172,7 +171,7 @@ The package lives at `~/indigo-matter/node_modules/matter-server`, where the plu
managed LaunchAgent expects it.
**Easiest (recommended): let the plugin do it.** In local/managed mode, use
-**Plugins ▸ Matter ▸ Install/update matter-server**. The plugin installs the package
+**Plugins ▸ Matter ▸ Install/update the Matter controller (matter-server)**. The plugin installs the package
with the *same* node it will run the server with, and pins that node in `nodeBinDir` —
which is the thing that prevents the most common failure (installing with one node and
running with another, whose native modules won't load). No Terminal needed. Skip to
@@ -262,7 +261,7 @@ node ~/indigo-matter/node_modules/matter-server/dist/esm/MatterServer.js \
| **Manage matter-server LaunchAgent automatically** | off | On = plugin supervises matter-server (Mode A); off = you run it (Mode B). |
| **Primary network interface** | `en0` | macOS interface matter-server binds to. Managed mode only. |
| **matter-server listen address** | `127.0.0.1` | IP the managed server binds its (unauthenticated) control API to. Keep loopback. Managed mode only. See Security. |
-| **Allow test/development device certificates** | off | Passes `--enable-test-net-dcl` to the managed matter-server, trusting the Matter test-net DCL alongside the production one (production verification is unchanged). It **also** allows test-net OTA firmware to be offered to commissioned devices. Turn on only if commissioning fails on device attestation because the device uses a test certificate (Homebridge and other dev bridges do). This is a global setting that stays on until you turn it off — untick it once the device is paired. Managed mode only; takes effect on plugin reload or via Plugins ▸ Matter ▸ Restart matter-server. While it is on, the plugin logs a warning on every startup. |
+| **Allow test/development device certificates** | off | Passes `--enable-test-net-dcl` to the managed matter-server, trusting the Matter test-net DCL alongside the production one (production verification is unchanged). It **also** allows test-net OTA firmware to be offered to commissioned devices. Turn on only if commissioning fails on device attestation because the device uses a test certificate (Homebridge and other dev bridges do). This is a global setting that stays on until you turn it off — untick it once the device is paired. Managed mode only; takes effect on plugin reload or via Plugins ▸ Matter ▸ Restart the Matter controller. While it is on, the plugin logs a warning on every startup. |
| **Verbose Matter logging** | off | Extra plugin logging. |
| **Trace matter-server WebSocket messages** | off | Logs raw WS frames (debug). |
@@ -300,7 +299,7 @@ Commission device by setup code…** — and it will appear as a native Indigo d
> **You cannot install this yet.** The export bridge needs a second npm package,
> `indigo-matter-bridge`, and it **has not been published to the npm registry**.
-> Until it is, **Install/update the Matter export bridge** cannot resolve the
+> Until it is, **Install/update the Matter bridge** cannot resolve the
> version the plugin pins, and the install fails. Nothing else in the plugin is
> affected — the inbound controller, your commissioned devices and every Indigo
> device carry on as normal — but export cannot be brought up. Everything below
@@ -371,13 +370,13 @@ noise in that configuration, not an export fault.
### Step E1 — Install the export bridge
-**Plugins ▸ Matter ▸ Install/update the Matter export bridge.**
+**Plugins ▸ Matter ▸ Install/update the Matter bridge.**
It installs into the same `~/indigo-matter` npm root as matter-server, with the
same Node, and takes a minute; watch the Event Log. On success you will see:
```
-Matter export bridge installed. It is NOT being started: nothing is exported yet,
+Matter bridge installed. It is NOT being started: nothing is exported yet,
and the bridge only runs while the export list is non-empty.
```
@@ -416,8 +415,8 @@ plugin writes the bridge node's LaunchAgent, starts it, and connects. The log
should show:
```
-Matter export: bridge node LaunchAgent is running (protocol port 5581, Matter port 5540)
-Matter export: bridge node attached — 1 endpoint(s) live, not yet paired
+Matter bridge: bridge node LaunchAgent is running (protocol port 5581, Matter port 5540)
+Matter bridge: bridge node attached — 1 endpoint(s) live, not yet paired
```
+
diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/PluginConfig.xml b/indigo-matter.indigoPlugin/Contents/Server Plugin/PluginConfig.xml
index e2d0fe1..6e65648 100644
--- a/indigo-matter.indigoPlugin/Contents/Server Plugin/PluginConfig.xml
+++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/PluginConfig.xml
@@ -4,11 +4,11 @@
(local) view and the taller "On another computer" / advanced sections overflow. -->
- Where is matter-server running? On this Mac, the plugin starts and manages it for you — there is nothing else to set up.
+ Where is the Matter controller (matter-server) running? On this Mac, the plugin starts and manages it for you — there is nothing else to set up.
- matter-server location:
+ Matter controller location:
@@ -45,7 +45,7 @@
defaultValue="~/Library/Application Support/com.simons-plugins.indigo-matter/matter-server"
visibleBindingId="showAdvanced" visibleBindingValue="true"
alwaysUseInDialogHeightCalc="true">
- matter-server storage path:
+ Matter controller storage path:
+ the Matter controller.
@@ -73,7 +73,7 @@
- matter-server host:
+ Matter controller host:
- matter-server port:
+ Matter controller port:
- matter-server's control API is UNAUTHENTICATED. Only connect over a network you trust.
+ The Matter controller (matter-server) exposes an UNAUTHENTICATED control API. Only connect over a network you trust.
- Matter export (Indigo as a bridge)
+ Matter bridge (export)Enable Matter export:
diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/bridge_agent.py b/indigo-matter.indigoPlugin/Contents/Server Plugin/bridge_agent.py
index 5a9f0ec..046788f 100644
--- a/indigo-matter.indigoPlugin/Contents/Server Plugin/bridge_agent.py
+++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/bridge_agent.py
@@ -1,4 +1,4 @@
-"""The Matter **export bridge node** LaunchAgent (PRD-indigo-matter-export §4.2).
+"""The Matter **bridge node** LaunchAgent (PRD-indigo-matter-export §4.2).
The second of the plugin's two launchd-managed node processes, and the reason
:mod:`launch_agent` exists at all: the controller's hard-won recovery machinery
@@ -79,7 +79,7 @@
#: that does not exist. It fires on the first-run path (no package, so no plist,
#: so the preflight error), where a name the user cannot find in the menu is the
#: difference between a fixable state and giving up.
-BRIDGE_INSTALL_MENU = "Install/update the Matter export bridge"
+BRIDGE_INSTALL_MENU = "Install/update the Matter bridge"
#: Matter UDP port the node binds (PRD §4.4). 5540 is Matter's default and the
#: one matter.js's ECOSYSTEMS.md records as Alexa's hard requirement; the pref is
@@ -159,7 +159,7 @@ def bridge_arguments(agent: "BridgeProcess") -> list[str]:
class BridgeProcess(LaunchAgent):
- """Install / control the Matter export bridge node's LaunchAgent.
+ """Install / control the Matter bridge node's LaunchAgent.
The bridge specialisation of :class:`~launch_agent.LaunchAgent`: it reads the
export prefs, builds its :class:`~launch_agent.AgentSpec`, and adds nothing
diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/export_bridge.py b/indigo-matter.indigoPlugin/Contents/Server Plugin/export_bridge.py
index 3b10d40..55e81f5 100644
--- a/indigo-matter.indigoPlugin/Contents/Server Plugin/export_bridge.py
+++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/export_bridge.py
@@ -267,7 +267,7 @@ def start(self) -> None:
if self._un_exporting:
self._start_after_un_export = True
self._logger.debug(
- "Matter export: start deferred until the in-flight un-export finishes")
+ "Matter bridge: start deferred until the in-flight un-export finishes")
return
if self.client is not None:
return
@@ -276,7 +276,7 @@ def start(self) -> None:
# Configure…, and every caller of start() is a path that would
# ordinarily bring it up. Saying so once per attempt at debug is
# enough — the config dialog's own readout is where this belongs.
- self._logger.debug("Matter export: export is switched off in Configure…; "
+ self._logger.debug("Matter bridge: export is switched off in Configure…; "
"not connecting to the bridge node")
return
# The agent BEFORE the client, and deliberately not conditional on it
@@ -307,7 +307,7 @@ def start(self) -> None:
self._fire(self.client.run(), "bridge client run loop",
lost="nothing will be exported until the plugin is reloaded")
self._logger.info(
- "Matter export: connecting to the bridge node (%d device(s) exported)",
+ "Matter bridge: connecting to the bridge node (%d device(s) exported)",
len(self._store))
def stop(self, timeout: float = 4.0) -> None:
@@ -323,7 +323,7 @@ def stop(self, timeout: float = 4.0) -> None:
# is the count of tiles now showing something the house is not
# doing — and nothing retries a §5 command.
self._logger.warning(
- "Matter export: shutting down with %d ecosystem command(s) still queued or "
+ "Matter bridge: shutting down with %d ecosystem command(s) still queued or "
"in flight — they will NOT be applied, and paired ecosystems already show "
"them as done.", dropped)
# Not `wait=True`: a dispatch blocked on a wedged IndigoServer would
@@ -397,7 +397,7 @@ def exports_changed(self) -> None:
# "delete them from every ecosystem". Drop the socket and stop the
# agent; the endpoints, the pairings and the allow-list all stand.
if self.client is not None:
- self._logger.info("Matter export: export is switched off — disconnecting from the "
+ self._logger.info("Matter bridge: export is switched off — disconnecting from the "
"bridge node. Exported accessories are LEFT paired and will show "
"as unavailable until export is switched back on.")
self.stop()
@@ -417,7 +417,7 @@ def exports_changed(self) -> None:
# landed. XG5 says no client while nothing is exported; this is the
# one exception, and it lasts exactly one successful attach.
self._logger.info(
- "Matter export: reconnecting to finish an un-export that did not complete "
+ "Matter bridge: reconnecting to finish an un-export that did not complete "
"earlier (%d accessory record(s) still owed removal)",
self._pending_replace_all())
self.start()
@@ -468,7 +468,7 @@ def _ensure_agent(self) -> None:
self._agent_start()
except Exception as exc: # pylint: disable=broad-except
self._logger.error(
- "Matter export: could not start the bridge node's LaunchAgent (%s). Indigo "
+ "Matter bridge: could not start the bridge node's LaunchAgent (%s). Indigo "
"devices and inbound Matter control are unaffected; exported accessories will "
"not be reachable until this is fixed.", exc)
self._logger.exception(exc)
@@ -492,7 +492,7 @@ def _stop_agent(self) -> None:
self._agent_stop()
except Exception as exc: # pylint: disable=broad-except
self._logger.warning(
- "Matter export: could not stop the bridge node's LaunchAgent (%s). It will keep "
+ "Matter bridge: could not stop the bridge node's LaunchAgent (%s). It will keep "
"running with nothing to export, which is harmless — pairings are untouched.", exc)
async def _stop_agent_off_loop(self) -> None:
@@ -556,7 +556,7 @@ def _pending_replace_all(self) -> int:
return int(raw or 0)
except (TypeError, ValueError) as exc:
self._logger.warning(
- "Matter export: the outstanding un-export count in prefs is unreadable (%r: %s) — "
+ "Matter bridge: the outstanding un-export count in prefs is unreadable (%r: %s) — "
"treating it as nothing owed. If accessories from a previous session are still "
"showing in a paired ecosystem, re-export one device and remove it again to "
"re-record the removal.", raw, exc)
@@ -602,7 +602,7 @@ def _record_pending_replace_all(self, removing: int) -> None:
self._save_prefs()
except Exception as exc: # pylint: disable=broad-except
self._logger.warning(
- "Matter export: could not record that the un-export is outstanding (%s). "
+ "Matter bridge: could not record that the un-export is outstanding (%s). "
"If it does not complete now, exported accessories may linger.", exc)
def _clear_pending_replace_all(self) -> None:
@@ -623,7 +623,7 @@ def _clear_pending_replace_all(self) -> None:
# it has been paid, so the next attach will carry `replace_all`
# again for an un-export that already happened.
self._logger.warning(
- "Matter export: the un-export completed but the outstanding-work flag could "
+ "Matter bridge: the un-export completed but the outstanding-work flag could "
"not be cleared (%s). A later reconnect may repeat the removal request; "
"nothing extra is removed by it.", exc)
@@ -658,7 +658,7 @@ def _replace_all_then_stop(self, removing: int) -> None:
# not land include the plugin being reloaded and the Mac losing power
# mid-attach, and neither of those reaches an `except`.
self._record_pending_replace_all(removing)
- self._logger.info("Matter export: allow-list is now empty — removing every "
+ self._logger.info("Matter bridge: allow-list is now empty — removing every "
"exported accessory (pairings are kept)")
async def _un_export() -> None:
@@ -670,7 +670,7 @@ async def _un_export() -> None:
self._clear_pending_replace_all()
except Exception as exc: # pylint: disable=broad-except
self._logger.warning(
- "Matter export: could not tell the bridge node the export list is empty "
+ "Matter bridge: could not tell the bridge node the export list is empty "
"(%s). Accessories will LINGER in paired ecosystems until this is finished. "
"It is recorded and will be finished automatically the next time the plugin "
"connects to the node — which is when you next export a device, or when the "
@@ -765,7 +765,7 @@ def _warn_if_wholly_unbridgeable(self, declared: int, specs: list) -> None:
return
self._wholly_unbridgeable = reasons
self._logger.warning(
- "Matter export: NONE of the %d device(s) in the export list can be bridged right "
+ "Matter bridge: NONE of the %d device(s) in the export list can be bridged right "
"now — %s. Every exported accessory is being removed from paired ecosystems until "
"this is fixed; their endpoint numbers are kept, so putting the devices right "
"brings the same accessories back rather than new ones.",
@@ -841,7 +841,7 @@ def _skip(self, device_id: int, why: str, detail: str = "") -> None:
if self._skipped.get(device_id) != why:
self._skipped[device_id] = why
self._logger.warning(
- "Matter export: device %s is in the export list but will NOT be bridged — %s%s.",
+ "Matter bridge: device %s is in the export list but will NOT be bridged — %s%s.",
device_id, why, f" ({detail})" if detail else "")
# ------------------------------------------------------------------
@@ -895,7 +895,7 @@ def device_updated(self, orig_dev: Any, new_dev: Any) -> None:
if new_dev.id not in self._update_failed:
self._update_failed.add(new_dev.id)
self._logger.error(
- "Matter export: could not work out what changed about %s (id %s, exported "
+ "Matter bridge: could not work out what changed about %s (id %s, exported "
"as %s) — %s. Its accessory will show stale state until this clears.",
getattr(new_dev, "name", ""), new_dev.id, entry.role, exc)
self._logger.exception(exc)
@@ -949,7 +949,7 @@ def _report_stopped_keys(self, dev: Any, role: str, stopped: frozenset) -> None:
return
self._stopped_keys[dev.id] = stopped
self._logger.warning(
- "Matter export: device %s (id %s, exported as %s) stopped reporting %s — paired "
+ "Matter bridge: device %s (id %s, exported as %s) stopped reporting %s — paired "
"ecosystems will keep showing the last known value for it until it reports again.",
getattr(dev, "name", ""), dev.id, role, ", ".join(sorted(stopped)))
@@ -977,30 +977,30 @@ def _live_client(self, what: str, device_id: int) -> Optional[BridgeClient]:
client = self.client
if client is None:
self._logger.debug(
- "Matter export: no bridge client; dropping %s for device %s", what, device_id)
+ "Matter bridge: no bridge client; dropping %s for device %s", what, device_id)
return None
if client.attached:
return client
if client.halted:
- self._logger.debug("Matter export: bridge client halted; dropping %s for device %s",
+ self._logger.debug("Matter bridge: bridge client halted; dropping %s for device %s",
what, device_id)
if not self._halted_reported:
self._halted_reported = True
self._logger.warning(
- "Matter export: the bridge client is HALTED (%s) — device %s and everything "
+ "Matter bridge: the bridge client is HALTED (%s) — device %s and everything "
"after it is NOT reaching any ecosystem, and nothing will retry on its own.",
client.halted_reason or "no reason recorded", device_id)
elif client.recovery:
- self._logger.debug("Matter export: bridge in recovery; dropping %s for device %s",
+ self._logger.debug("Matter bridge: bridge in recovery; dropping %s for device %s",
what, device_id)
if not self._recovery_reported:
self._recovery_reported = True
self._logger.warning(
- "Matter export: the bridge node is awaiting an endpoint-map rebuild — "
+ "Matter bridge: the bridge node is awaiting an endpoint-map rebuild — "
"device %s and everything after it is NOT reaching any ecosystem.", device_id)
else:
self._logger.debug(
- "Matter export: bridge node not attached; dropping %s for device %s "
+ "Matter bridge: bridge node not attached; dropping %s for device %s "
"(the next attach reconciles it)", what, device_id)
return None
@@ -1083,14 +1083,14 @@ def on_command(self, command: bridge_protocol.BridgeCommand) -> None:
if entry is None:
# PRD §7 race row: the endpoint outlived the allow-list entry.
self._logger.warning(
- "Matter export: the bridge node sent %r for Indigo device %s, which is not "
+ "Matter bridge: the bridge node sent %r for Indigo device %s, which is not "
"exported — ignoring. The accessory should disappear at the next reconnect.",
command.command, device_id)
return
handler = export_handlers.handler_for(entry.role)
if handler is None:
self._logger.warning(
- "Matter export: %r arrived for device %s exported as %s, a role this version "
+ "Matter bridge: %r arrived for device %s exported as %s, a role this version "
"cannot bridge — ignoring.", command.command, device_id, entry.role)
return
self._fire(self._dispatch_off_loop(command, entry, handler),
@@ -1139,7 +1139,7 @@ async def _dispatch_off_loop(self, command, entry, handler) -> None:
await asyncio.wait_for(asyncio.shield(future), timeout=COMMAND_TIMEOUT)
except asyncio.TimeoutError:
self._logger.error(
- "Matter export: %r for device %s (%s) has not returned after %.0fs. The command "
+ "Matter bridge: %r for device %s (%s) has not returned after %.0fs. The command "
"worker is single-threaded, so every §5 command after it is queued behind this "
"one — check whether that Indigo device or its plugin is responding. The "
"ecosystem that sent it already shows it as done and NOTHING will correct that "
@@ -1153,7 +1153,7 @@ def _note_command_done(self, future) -> None:
return
exc = future.exception()
if exc is not None:
- self._logger.warning("Matter export: a §5 command dispatch failed — %s", exc)
+ self._logger.warning("Matter bridge: a §5 command dispatch failed — %s", exc)
def _command_worker(self):
"""The single thread §5 commands are applied on. Built on first use.
@@ -1171,7 +1171,7 @@ def _command_worker(self):
# A coroutine that was already queued on the loop when `stop()` ran
# would otherwise build a brand-new worker thread underneath a
# bridge that has shut down, and nothing would ever join it.
- self._logger.debug("Matter export: command worker requested after stop; ignoring")
+ self._logger.debug("Matter bridge: command worker requested after stop; ignoring")
return None
if self._executor is None:
self._executor = self._executor_factory()
@@ -1183,14 +1183,14 @@ def _apply_command(self, command: bridge_protocol.BridgeCommand, entry, handler)
dev = self._device_getter(device_id)
if dev is None:
self._logger.warning(
- "Matter export: %r arrived for device %s, which no longer exists in Indigo — "
+ "Matter bridge: %r arrived for device %s, which no longer exists in Indigo — "
"ignoring.", command.command, device_id)
return
try:
outcome = handler.dispatch(command.command, command.args, dev, entry.options)
except Exception as exc: # pylint: disable=broad-except
self._logger.error(
- "Matter export: %r failed for device %s (%s) with args %r — %s. The ecosystem "
+ "Matter bridge: %r failed for device %s (%s) with args %r — %s. The ecosystem "
"still shows the state it asked for; pushing the real one back.",
command.command, device_id, entry.role, command.args, exc)
self._logger.exception(exc)
@@ -1198,7 +1198,7 @@ def _apply_command(self, command: bridge_protocol.BridgeCommand, entry, handler)
return
if outcome is False:
self._logger.warning(
- "Matter export: the bridge node sent %r for device %s (%s), which that role "
+ "Matter bridge: the bridge node sent %r for device %s (%s), which that role "
"does not define — ignoring.", command.command, device_id, entry.role)
elif isinstance(outcome, str):
self._report_no_op(command.command, device_id, entry.role, outcome)
@@ -1223,7 +1223,7 @@ def _report_no_op(self, command: str, device_id: int, role: str, reason: str) ->
return
self._no_op_reported[device_id] = reason
self._logger.warning(
- "Matter export: %r reached device %s (exported as %s) but changed nothing — %s.",
+ "Matter bridge: %r reached device %s (exported as %s) but changed nothing — %s.",
command, device_id, role, reason)
def _correct(self, handler, dev: Any, device_id: int,
@@ -1254,7 +1254,7 @@ def _correct(self, handler, dev: Any, device_id: int,
states = handler.states_for(dev, options)
except Exception as exc: # pylint: disable=broad-except
self._logger.warning(
- "Matter export: could not read device %s back to correct the ecosystem (%s) — "
+ "Matter bridge: could not read device %s back to correct the ecosystem (%s) — "
"it will show the failed command's state until the next attach.", device_id, exc)
return
if not states:
@@ -1263,7 +1263,7 @@ def _correct(self, handler, dev: Any, device_id: int,
# return. It is the same absence `diff_with_gaps` reports: a lock
# whose `onState` is None has no truth to tell.
self._logger.warning(
- "Matter export: cannot push truth for device %s — it reports no readable state "
+ "Matter bridge: cannot push truth for device %s — it reports no readable state "
"at all, so the ecosystem keeps showing the command that failed.", device_id)
return
self._note_pushed(device_id, states)
@@ -1303,7 +1303,7 @@ def _on_attached(self, status, carried_replace_all: bool = False) -> None:
# both sides are present and the node can simply be asked. Fire-and-
# forget: a readout is not worth blocking a handshake for.
self._fire(self._refresh_pairing_window(), "reading the bridge node's pairing window")
- self._logger.info("Matter export: bridge node attached — %d endpoint(s) live, %s",
+ self._logger.info("Matter bridge: bridge node attached — %d endpoint(s) live, %s",
status.endpoint_count,
"commissioned" if status.commissioned else "not yet paired")
self._report_node_warnings(status)
@@ -1311,7 +1311,7 @@ def _on_attached(self, status, carried_replace_all: bool = False) -> None:
owed = self._pending_replace_all()
self._clear_pending_replace_all()
self._logger.info(
- "Matter export: the outstanding un-export completed — %d accessory record(s) "
+ "Matter bridge: the outstanding un-export completed — %d accessory record(s) "
"removed from the bridge node; paired ecosystems will drop them.", owed)
if len(self._store) == 0:
# XG5 again: nothing is exported, so nothing needs a socket —
@@ -1344,7 +1344,7 @@ def _report_node_warnings(self, status) -> None:
return
self._node_warnings = warnings
for warning in sorted(warnings):
- self._logger.warning("Matter export: the bridge node reports — %s", warning)
+ self._logger.warning("Matter bridge: the bridge node reports — %s", warning)
def _on_attach_refused(self, code: str, details: str) -> None:
"""Surface a refusal with its remedy. The client has already triaged it.
@@ -1365,7 +1365,7 @@ def _on_attach_refused(self, code: str, details: str) -> None:
# on the way.
if bridge_protocol.REFUSE_IDENTITY_UNREADABLE in details:
self._logger.error(
- "Matter export: the bridge node is serving NOTHING because its identity file "
+ "Matter bridge: the bridge node is serving NOTHING because its identity file "
"is unreadable (%s). Rebuilding the endpoint map will NOT fix this and the "
"node refuses to try — the unusable file was moved aside as "
"identity.json.unreadable- in the bridge storage folder. Restore "
@@ -1374,7 +1374,7 @@ def _on_attach_refused(self, code: str, details: str) -> None:
details)
return
self._logger.error(
- "Matter export: the bridge node is serving NOTHING because its endpoint-number "
+ "Matter bridge: the bridge node is serving NOTHING because its endpoint-number "
"map is unreadable (%s). Nothing will be exported until it is rebuilt (Plugins "
"▸ Matter ▸ Rebuild Matter Endpoint Map…). The rebuild renumbers nothing: if "
"only the map file was damaged no paired ecosystem will see any change, and if "
@@ -1385,12 +1385,12 @@ def _on_attach_refused(self, code: str, details: str) -> None:
if self._refusal_reported == code:
return
self._refusal_reported = code
- self._logger.error("Matter export: the bridge node refused the connection (%s: %s). "
+ self._logger.error("Matter bridge: the bridge node refused the connection (%s: %s). "
"Nothing is being exported.", code, details)
def _on_version_skew(self, hello) -> None:
self._logger.error(
- "Matter export: the bridge node speaks protocol version %s, this plugin speaks %s "
+ "Matter bridge: the bridge node speaks protocol version %s, this plugin speaks %s "
"(node %s). Export is STOPPED and pairings are untouched — restart the bridge agent "
"so it picks up the node that ships with this plugin.",
hello.protocol_version, bridge_protocol.PROTOCOL_VERSION, hello.bridge_version)
@@ -1421,7 +1421,7 @@ def _on_drift_detected(self, drift: list) -> None:
return
self._drift_reported = seen
self._logger.error(
- "Matter export: endpoint-number DRIFT detected — %s. Exported accessories may have "
+ "Matter bridge: endpoint-number DRIFT detected — %s. Exported accessories may have "
"swapped identities in paired ecosystems. Bridge nodes 0.8.0 and newer adopt a "
"factory reset's own renumbering automatically, so on a current node persistent "
"drift means the bridge's storage changed OUTSIDE any reset — treat it as a real "
@@ -1444,13 +1444,13 @@ def _on_fabrics_changed(self, fabrics: list, change: str) -> None:
self.fabrics = list(fabrics)
described = ", ".join(_describe_fabric(fabric) for fabric in fabrics) or "none"
self._logger.info(
- "Matter export: the bridge node's paired ecosystems changed (%s) — now paired with: %s",
+ "Matter bridge: the bridge node's paired ecosystems changed (%s) — now paired with: %s",
change or "changed", described)
def _on_commissioned(self) -> None:
"""First fabric (§5 ``commissioned``) — a transition, not a repeat."""
self._logger.info(
- "Matter export: the Matter bridge has been PAIRED for the first time. Exported "
+ "Matter bridge: the Matter bridge has been PAIRED for the first time. Exported "
"accessories should now appear in that ecosystem. To add a second ecosystem, use "
"Plugins ▸ Matter ▸ Pair Matter Bridge… — the original pairing code no longer works.")
@@ -1465,7 +1465,7 @@ def _on_decommissioned(self) -> None:
self.fabrics = []
self.window_expires_at = None
self._logger.warning(
- "Matter export: the Matter bridge is no longer paired with ANY ecosystem. Every "
+ "Matter bridge: the Matter bridge is no longer paired with ANY ecosystem. Every "
"exported accessory has gone with the last fabric. Indigo devices are unaffected; "
"use Plugins ▸ Matter ▸ Pair Matter Bridge… to pair it again.")
@@ -1473,11 +1473,11 @@ def _on_window_closed(self, reason: str) -> None:
"""The commissioning window ended (§5 ``window_closed``)."""
self.window_expires_at = None
if reason == "commissioned":
- self._logger.info("Matter export: the pairing window closed — an ecosystem completed "
+ self._logger.info("Matter bridge: the pairing window closed — an ecosystem completed "
"commissioning.")
return
self._logger.info(
- "Matter export: the pairing window has expired without an ecosystem completing "
+ "Matter bridge: the pairing window has expired without an ecosystem completing "
"commissioning. Open a new one with Plugins ▸ Matter ▸ Pair Matter Bridge… — the "
"code it showed is now dead.")
@@ -1499,7 +1499,7 @@ async def _refresh_pairing_window(self) -> None:
try:
pairing = await client.get_pairing()
except Exception as exc: # pylint: disable=broad-except
- self._logger.debug("Matter export: could not read the pairing window (%s)", exc)
+ self._logger.debug("Matter bridge: could not read the pairing window (%s)", exc)
return
self.window_expires_at = pairing.window_expires_at if pairing.window_open else None
@@ -1527,7 +1527,7 @@ def _on_unreachable(self, attempts: int) -> None:
return
self._unreachable_reported = True
self._logger.warning(
- "Matter export: the bridge node is not responding after %d attempts on port %s. "
+ "Matter bridge: the bridge node is not responding after %d attempts on port %s. "
"Indigo devices and inbound Matter control are unaffected; exported accessories "
"will show as unavailable.%s",
attempts,
@@ -1542,7 +1542,7 @@ def _agent_diagnosis(self) -> str:
try:
detail = self._agent_diagnose()
except Exception as exc: # pylint: disable=broad-except
- self._logger.debug("Matter export: the bridge agent diagnostic failed (%s)", exc)
+ self._logger.debug("Matter bridge: the bridge agent diagnostic failed (%s)", exc)
return ""
return f" {detail}" if detail else ""
@@ -1589,14 +1589,14 @@ def health_tick(self) -> None:
if not self._halted_reported:
self._halted_reported = True
self._logger.warning(
- "Matter export: the bridge client is HALTED (%s) — nothing is being exported "
+ "Matter bridge: the bridge client is HALTED (%s) — nothing is being exported "
"and it will not retry on its own.",
client.halted_reason or "no reason recorded")
return
if client.recovery:
if not self._recovery_reported:
self._recovery_reported = True
- self._logger.warning("Matter export: the bridge node is awaiting an endpoint-map "
+ self._logger.warning("Matter bridge: the bridge node is awaiting an endpoint-map "
"rebuild; nothing is being exported.")
return
if client.attached:
@@ -1605,10 +1605,10 @@ def health_tick(self) -> None:
return
self._disconnect_ticks += 1
if self._disconnect_ticks == DISCONNECT_WARN_TICKS:
- self._logger.warning("Matter export: still not attached to the bridge node after "
+ self._logger.warning("Matter bridge: still not attached to the bridge node after "
"~1 min")
else:
- self._logger.debug("Matter export: bridge node not currently attached")
+ self._logger.debug("Matter bridge: bridge node not currently attached")
def _poll_node_status(self, client) -> None:
"""Ask the node how it is, and say what it answers (§3.6 → §4.3).
@@ -1628,7 +1628,7 @@ async def _poll() -> None:
try:
status = await client.get_status()
except Exception as exc: # pylint: disable=broad-except
- self._logger.debug("Matter export: status poll failed (%s)", exc)
+ self._logger.debug("Matter bridge: status poll failed (%s)", exc)
return
self._report_node_warnings(status)
@@ -1650,7 +1650,7 @@ def _check_command_queue(self) -> None:
return
self._queue_warned = True
self._logger.warning(
- "Matter export: %d ecosystem command(s) are queued on the command worker and not "
+ "Matter bridge: %d ecosystem command(s) are queued on the command worker and not "
"completing. They run one at a time, so something at the front is not returning — "
"the timeout line above names it.", outstanding)
@@ -1680,10 +1680,10 @@ def _fire(self, coro, what: str, lost: str = "") -> bool:
except Exception as exc: # pylint: disable=broad-except
coro.close()
if lost:
- self._logger.warning("Matter export: could not schedule %s (%s) — %s.",
+ self._logger.warning("Matter bridge: could not schedule %s (%s) — %s.",
what, exc, lost)
else:
- self._logger.debug("Matter export: could not schedule %s (%s)", what, exc)
+ self._logger.debug("Matter bridge: could not schedule %s (%s)", what, exc)
return False
future.add_done_callback(lambda fut: self._log_future(fut, what))
return True
@@ -1693,7 +1693,7 @@ def _log_future(self, future, what: str) -> None:
return
exc = future.exception()
if exc is not None:
- self._logger.warning("Matter export: %s failed — %s", what, exc)
+ self._logger.warning("Matter bridge: %s failed — %s", what, exc)
#: Matter vendor IDs whose ecosystems a user is likely to recognise.
@@ -1798,7 +1798,7 @@ def _indigo_device(device_id: int, logger: Any = None) -> Any:
except Exception as exc: # pylint: disable=broad-except
if logger is not None:
logger.warning(
- "Matter export: could not read Indigo device %s (%s: %s). This is NOT the device "
+ "Matter bridge: could not read Indigo device %s (%s: %s). This is NOT the device "
"having been deleted — it is Indigo failing to answer.",
device_id, type(exc).__name__, exc)
return None
diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/export_catalog.py b/indigo-matter.indigoPlugin/Contents/Server Plugin/export_catalog.py
index f8fdfb5..f5a485e 100644
--- a/indigo-matter.indigoPlugin/Contents/Server Plugin/export_catalog.py
+++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/export_catalog.py
@@ -236,7 +236,7 @@ def classify(dev, plugin_id: str = DEFAULT_PLUGIN_ID) -> Verdict:
return Excluded(REASON_NO_ROLE)
return handler(dev)
except Exception as exc: # pylint: disable=broad-except
- _LOG.error("Matter export: could not classify a device for export — %s", exc,
+ _LOG.error("Matter bridge: could not classify a device for export — %s", exc,
exc_info=True)
return Excluded(REASON_DEVICE_ERROR)
diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/fabric_backup.py b/indigo-matter.indigoPlugin/Contents/Server Plugin/fabric_backup.py
index 9b87d00..61ac9ca 100644
--- a/indigo-matter.indigoPlugin/Contents/Server Plugin/fabric_backup.py
+++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/fabric_backup.py
@@ -147,7 +147,7 @@ def create_backup(storage_path: str, *, now: datetime, logger: Optional[Any] = N
# which is exactly the archive they will reach for after
# losing those two files.
log.warning(
- "Fabric backup: no Matter export bridge storage at %s, so this backup "
+ "Fabric backup: no Matter bridge storage at %s, so this backup "
"does NOT contain identity.json or endpoint-map.json. That is expected "
"if you export nothing; if you do export devices, check where the "
"bridge node's --storage-path actually points.", bridge_storage_path)
@@ -178,7 +178,7 @@ def create_backup(storage_path: str, *, now: datetime, logger: Optional[Any] = N
# rather than configured, so "which directory did this actually cover?"
# is a question the log should answer without anybody having to guess.
log.info("Fabric backup written: %s (%d member(s), including %d from the Matter "
- "export bridge node at %s)", archive_path, members_written, bridge_members,
+ "bridge node at %s)", archive_path, members_written, bridge_members,
bridge_storage_path)
else:
log.info("Fabric backup written: %s (%d member(s))", archive_path, members_written)
@@ -328,7 +328,7 @@ def restore_backup(
skipped = bridge_members_in(archive_path)
if skipped:
log.warning(
- "This backup also contains %d file(s) from the Matter export bridge node. They are "
+ "This backup also contains %d file(s) from the Matter bridge node. They are "
"NOT being restored: the bridge node has to be stopped first and this plugin cannot "
"stop it yet. If you need them, stop the bridge node and extract the '%s' entries of "
"%s over %s by hand. Restoring the controller fabric only.",
diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/launch_agent.py b/indigo-matter.indigoPlugin/Contents/Server Plugin/launch_agent.py
index 163dd11..5106e35 100644
--- a/indigo-matter.indigoPlugin/Contents/Server Plugin/launch_agent.py
+++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/launch_agent.py
@@ -122,7 +122,7 @@ class AgentSpec:
item. Every message that tells a user to run it interpolated
:attr:`package` instead, producing "Plugins ▸ Matter ▸ Install/update
indigo-matter-bridge" — a menu that does not exist (the real one is
- "Install/update the Matter export bridge"). It fires on the first-run
+ "Install/update the Matter bridge"). It fires on the first-run
path, where the user is already stuck, so a menu name they cannot find
is the difference between a fixable state and a support thread. Blank
falls back to the old wording via :attr:`install_menu_name`, which is
@@ -1187,7 +1187,7 @@ def _warn_on_argument_drift(self, running_args: list[str]) -> None:
"the running %s was started with different arguments than the "
"current settings would use — it is serving STALE configuration. Running: "
"%s. Expected: %s. Reload the plugin (or Plugins ▸ Matter ▸ Restart "
- "matter-server) to apply the current settings.",
+ "the Matter controller) to apply the current settings.",
self.spec.package, " ".join(running_args), " ".join(desired),
)
diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/plugin.py b/indigo-matter.indigoPlugin/Contents/Server Plugin/plugin.py
index f5a3178..3555366 100644
--- a/indigo-matter.indigoPlugin/Contents/Server Plugin/plugin.py
+++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/plugin.py
@@ -480,7 +480,7 @@ def _issue_device_subscription(self) -> bool:
try:
indigo.devices.subscribeToChanges()
except Exception as exc: # noqa: BLE001
- self.logger.error("Matter export: could not subscribe to Indigo device changes — "
+ self.logger.error("Matter bridge: could not subscribe to Indigo device changes — "
"exported accessories will not follow Indigo state. %s", exc)
self.logger.exception(exc)
return False
@@ -525,7 +525,7 @@ def _resubscribe_tick(self) -> None:
if not self._resubscribe_gave_up:
self._resubscribe_gave_up = True
self.logger.warning(
- "Matter export: no Indigo device update has arrived since exporting, after "
+ "Matter bridge: no Indigo device update has arrived since exporting, after "
"re-issuing subscribeToChanges %d times. Unless the house is simply idle, "
"exported accessories are NOT following Indigo state and nothing further will "
"retry on its own — reload the plugin.", MAX_RESUBSCRIBE_ATTEMPTS)
@@ -542,7 +542,7 @@ def _resubscribe_tick(self) -> None:
# subscription that never registered — and after that the evidence is
# indistinguishable from nothing having happened.
self.logger.debug(
- "Matter export: no device updates since subscribing; re-issuing "
+ "Matter bridge: no device updates since subscribing; re-issuing "
"subscribeToChanges (attempt %d of %d)",
self._resubscribe_attempts, MAX_RESUBSCRIBE_ATTEMPTS)
self._issue_device_subscription()
@@ -576,7 +576,7 @@ def deviceUpdated(self, origDev, newDev): # noqa: N802
if newDev.id not in self._export_callback_failed:
self._export_callback_failed.add(newDev.id)
self.logger.error(
- "Matter export: the update of %s (id %s) could not be handed to the bridge "
+ "Matter bridge: the update of %s (id %s) could not be handed to the bridge "
"— %s. Its accessory will show stale state until this clears.",
getattr(newDev, "name", ""), newDev.id, exc)
self.logger.exception(exc)
@@ -590,13 +590,13 @@ def deviceDeleted(self, dev): # noqa: N802
return
try:
self.exports.remove(dev.id)
- self.logger.info("Removed Matter export: %s (id %s) — the Indigo device was deleted",
+ self.logger.info("Removed Matter export for %s (id %s) — the Indigo device was deleted",
getattr(dev, "name", ""), dev.id)
except Exception as exc: # noqa: BLE001
# The store rolled back, so the entry survives; the endpoint removal
# below is still right (the device is gone either way) and the
# startup sweep will report the orphan.
- self.logger.error("Matter export: removing the deleted device %s from the export "
+ self.logger.error("Matter bridge: removing the deleted device %s from the export "
"list FAILED — %s", dev.id, exc)
self.logger.exception(exc)
self._export_callback_failed.discard(dev.id)
@@ -720,8 +720,8 @@ def _on_server_unreachable(self, attempts: int) -> None:
# storage lock. The plugin now reaps such strays on start/restart; point
# the user at that in case a reap couldn't run (e.g. ps unavailable).
hint = ("\nAnother matter-server appears to be holding the storage lock. "
- "Use Plugins ▸ Matter ▸ Restart matter-server (it stops stray "
- "servers), or reboot the Mac if it persists.")
+ "Use Plugins ▸ Matter ▸ Restart the Matter controller (it stops "
+ "stray servers), or reboot the Mac if it persists.")
self.logger.error(
"matter-server is not responding after %d attempts and appears to be "
"crashing. Recent matter-server errors:\n%s%s", attempts, tail, hint,
@@ -730,7 +730,8 @@ def _on_server_unreachable(self, attempts: int) -> None:
self.logger.error(
"matter-server is not responding after %d attempts and its error log is "
"empty — it may not be installed (checked %s). Use Plugins ▸ Matter ▸ "
- "Install/update matter-server, then restart the plugin.",
+ "Install/update the Matter controller (matter-server), then restart the "
+ "plugin.",
attempts, sp.project_dir,
)
@@ -811,7 +812,7 @@ def get_prefs_config_ui_values(self):
return self.getPrefsConfigUiValues()
def _export_readout(self) -> str:
- """One line describing the export bridge for the config dialog (PRD §5.5).
+ """One line describing the Matter bridge for the config dialog (PRD §5.5).
What the PRD asks for is *which* ecosystems hold a fabric and whether a
window is open — not slot arithmetic. matter.js allows 254 fabrics, so
@@ -890,7 +891,7 @@ def closedPrefsConfigUi(self, valuesDict, userCancelled): # noqa: N802
# snapshot, so a changed location/host only takes effect on reload.
self.logger.info(
"matter-server settings saved — reload the plugin (or Plugins ▸ Matter ▸ "
- "Restart matter-server) to apply them"
+ "Restart the Matter controller) to apply them"
)
# Export is the exception: its switch and its ports are read on every
# connect, and the ONE change a user expects to act immediately is
@@ -904,7 +905,7 @@ def closedPrefsConfigUi(self, valuesDict, userCancelled): # noqa: N802
# (⊗ `test_saving_config_applies_the_export_switch_immediately`).
if self.export_bridge is None:
self.logger.debug(
- "Matter export: the export engine is not running, so the export switch will take "
+ "Matter bridge: the export engine is not running, so the export switch will take "
"effect when the plugin next starts.")
return
try:
@@ -913,7 +914,7 @@ def closedPrefsConfigUi(self, valuesDict, userCancelled): # noqa: N802
# A bare traceback here reads as a crash in "save settings". Say what
# did not happen and what to do instead — the prefs ARE saved.
self.logger.error(
- "Matter export: your settings were saved, but applying the export switch "
+ "Matter bridge: your settings were saved, but applying the export switch "
"immediately FAILED (%s). Reload the plugin to apply it.", exc)
self.logger.exception(exc)
@@ -1351,8 +1352,9 @@ def _install_matter_server(self, clean: bool = False) -> None:
return
if not sp.install():
self.logger.error(
- "Install/update matter-server did not complete — see the error "
- "above. The server was not (re)installed; retry when resolved."
+ "Install/update of the Matter controller (matter-server) did not "
+ "complete — see the error above. The server was not (re)installed; "
+ "retry when resolved."
)
return
if self._stopping: # plugin is tearing down — don't mutate its state
@@ -1374,8 +1376,8 @@ def _install_matter_server(self, clean: bool = False) -> None:
self.logger.error(
"matter-server was installed and pinned to node at %s, but the "
"restart onto the new version FAILED — the old version may still be "
- "running. Use Plugins ▸ Matter ▸ Restart matter-server, or reload "
- "the plugin.", sp.resolved_bin_dir,
+ "running. Use Plugins ▸ Matter ▸ Restart the Matter controller, or "
+ "reload the plugin.", sp.resolved_bin_dir,
)
return
self.logger.info(
@@ -1390,7 +1392,7 @@ def _install_matter_server(self, clean: bool = False) -> None:
"matter-server install did not complete after the npm step — the "
"package may be installed but the node was not pinned and the server "
"was not (re)started. See the trace above, then retry Plugins ▸ Matter "
- "▸ Install/update matter-server."
+ "▸ Install/update the Matter controller (matter-server)."
)
def menuReinstallMatterServerClean(self, valuesDict, menuId=""): # noqa: N802, ARG002
@@ -1819,7 +1821,7 @@ def _log_row_failure(self, exc, first: bool) -> None:
if first:
self.logger.exception(exc)
else:
- self.logger.error("Matter export: another device could not be read — %s", exc)
+ self.logger.error("Matter bridge: another device could not be read — %s", exc)
@staticmethod
def _candidate_row(dev, name: str, plugin_id: str, exported) -> Optional[tuple[str, str]]:
@@ -2110,12 +2112,12 @@ def exportAddOrUpdate(self, valuesDict, typeId="", devId=0):
except Exception as exc: # pylint: disable=broad-except
# The store rolled back, so nothing was saved — say so rather than
# reporting the success the old code reported unconditionally.
- self.logger.error("Matter export: saving the export list FAILED — %s", exc)
+ self.logger.error("Matter bridge: saving the export list FAILED — %s", exc)
self.logger.exception(exc)
values["exportStatus"] = "FAILED to save the export list — see Event Log"
return values
verb = "Updated" if existed else "Added"
- self.logger.info("%s Matter export: %s (id %s) as %s%s",
+ self.logger.info("%s Matter export for %s (id %s) as %s%s",
verb, dev.name, device_id, role,
f' named "{name_override}"' if name_override else "")
self._nudge_export(device_id, role_changed=role_changed)
@@ -2168,7 +2170,7 @@ def exportRemove(self, valuesDict, typeId="", devId=0):
try:
removed = self.exports.remove(device_id)
except Exception as exc: # pylint: disable=broad-except
- self.logger.error("Matter export: saving the export list FAILED — %s", exc)
+ self.logger.error("Matter bridge: saving the export list FAILED — %s", exc)
self.logger.exception(exc)
values["exportStatus"] = "FAILED to save the export list — see Event Log"
return values
@@ -2177,7 +2179,7 @@ def exportRemove(self, valuesDict, typeId="", devId=0):
return values
dev = self._indigo_device(device_id)
name = str(getattr(dev, "name", "") or "") if dev is not None else f"device {device_id}"
- self.logger.info("Removed Matter export: %s (id %s)", name, device_id)
+ self.logger.info("Removed Matter export for %s (id %s)", name, device_id)
# XAC7: the accessory has to leave every paired ecosystem, not just the
# allow-list. Order matters — remove the endpoint BEFORE the empty
# allow-list stops the client out from under it.
@@ -2208,7 +2210,7 @@ def _recovery_client(self, errors, field: str):
bridge = self.export_bridge
client = bridge.client if bridge is not None else None
if client is None or not client.connected:
- msg = ("Not connected to the Matter export bridge node. Start it (it is launched by "
+ msg = ("Not connected to the Matter bridge node. Start it (it is launched by "
"hand in this build), export at least one device so the plugin connects, then "
"try again.")
self.logger.warning(msg)
@@ -2244,7 +2246,7 @@ def menuRebuildEndpointMap(self, valuesDict, menuId=""): # noqa: N802, ARG002
# every export that is not currently live — §3.3 keeps them exactly
# so that re-adding a device restores its accessory — and there is
# nothing to recover from in the first place.
- msg = ("Matter export: the bridge node is NOT refusing to serve endpoints, so there "
+ msg = ("Matter bridge: the bridge node is NOT refusing to serve endpoints, so there "
"is nothing to rebuild. Rebuilding anyway would discard the retained endpoint "
"numbers of every device that is not currently exported, and those are what "
"make re-adding one restore the same accessory.")
@@ -2263,31 +2265,31 @@ def menuRebuildEndpointMap(self, valuesDict, menuId=""): # noqa: N802, ARG002
# Never report success over a rebuild that did not persist: the node
# answers with an error rather than a StatusReport when the new map
# could not be written, and the refusal is still in force.
- self.logger.error("Matter export: rebuilding the endpoint map FAILED — %s. The bridge "
+ self.logger.error("Matter bridge: rebuilding the endpoint map FAILED — %s. The bridge "
"node is unchanged and still refusing to export.", exc)
self.logger.exception(exc)
errors["confirm"] = "Rebuild failed — see the log. Nothing was changed."
return (False, valuesDict, errors)
self.logger.warning(
- "Matter export: endpoint map REBUILT — the bridge node has stopped refusing and is "
+ "Matter bridge: endpoint map REBUILT — the bridge node has stopped refusing and is "
"serving %d endpoint(s). Nothing was renumbered. If only the map file was damaged, "
"no paired ecosystem will see any change; if the bridge's Matter storage was lost, "
"your ecosystems already hold dead accessories under the old numbers — delete those "
"by hand. Do NOT run this again for the same fault.",
status.endpoint_count)
for warning in status.warnings:
- self.logger.warning("Matter export: the bridge node reports — %s", warning)
+ self.logger.warning("Matter bridge: the bridge node reports — %s", warning)
if not client.attached:
# The rebuild stands; only the connection step after it did not. The
# client's own triage has already named the reason at error level.
self.logger.warning(
- "Matter export: the rebuild succeeded but re-attaching to the bridge node did "
+ "Matter bridge: the rebuild succeeded but re-attaching to the bridge node did "
"not. The rebuild does NOT need repeating — exports resume when the connection "
"does, and the reason is logged above.")
return (True, valuesDict)
def menuResetBridgePairings(self, valuesDict, menuId=""): # noqa: N802, ARG002
- """§3.10 — wipe the export bridge's commissioning and re-advertise."""
+ """§3.10 — wipe the Matter bridge's commissioning and re-advertise."""
errors = indigo.Dict()
# Two boxes, deliberately. This is the only plugin action that destroys
# every ecosystem pairing at once, and it is irreversible without
@@ -2306,19 +2308,19 @@ def menuResetBridgePairings(self, valuesDict, menuId=""): # noqa: N802, ARG002
# "the map itself is corrupt" path is the rebuild above.
self.runtime.submit(client.factory_reset(True)).result(timeout=FACTORY_RESET_TIMEOUT)
except Exception as exc: # noqa: BLE001
- self.logger.error("Matter export: resetting the bridge pairings FAILED — %s. "
+ self.logger.error("Matter bridge: resetting the bridge pairings FAILED — %s. "
"Pairings are unchanged.", exc)
self.logger.exception(exc)
errors["confirmAgain"] = "Reset failed — see the log. Pairings were not changed."
return (False, valuesDict, errors)
self.logger.warning(
- "Matter export: the bridge node's pairings have been RESET. It is advertising for "
+ "Matter bridge: the bridge node's pairings have been RESET. It is advertising for "
"commissioning again — pair it from each ecosystem, and remove the now-dead Indigo "
"bridge from any ecosystem that still lists it.")
return (True, valuesDict)
# ------------------------------------------------------------------
- # The export bridge node's LaunchAgent (E7 — PRD §4.2, XG5, XAC1)
+ # The Matter bridge node's LaunchAgent (E7 — PRD §4.2, XG5, XAC1)
# ------------------------------------------------------------------
def _start_bridge_agent(self) -> None:
"""Install (if needed) and start the bridge node's LaunchAgent.
@@ -2349,18 +2351,18 @@ def _start_bridge_agent(self) -> None:
# was reported here as "bridge node LaunchAgent is running". Ask launchd.
state = agent.run_state()
if state == agent.RUNNING:
- self.logger.info("Matter export: bridge node LaunchAgent is running (protocol port %s, "
+ self.logger.info("Matter bridge: bridge node LaunchAgent is running (protocol port %s, "
"Matter port %s)", agent.ws_port, agent.matter_port)
elif state == agent.UNKNOWN:
# A pid line we could not parse. It may well be serving; what we must
# not do is assert either way.
self.logger.info(
- "Matter export: the bridge node's LaunchAgent is loaded (protocol port %s, Matter "
+ "Matter bridge: the bridge node's LaunchAgent is loaded (protocol port %s, Matter "
"port %s); launchd did not report a readable pid, so whether the process is up "
"will show as the plugin connects — or fails to.", agent.ws_port, agent.matter_port)
else:
self.logger.error(
- "Matter export: the bridge node's LaunchAgent %s. Exported accessories will be "
+ "Matter bridge: the bridge node's LaunchAgent %s. Exported accessories will be "
"unavailable until it does. %s",
"did not start" if state == agent.LOADED_NOT_RUNNING
else "could not be loaded by launchd",
@@ -2397,7 +2399,7 @@ def _stop_bridge_agent(self) -> None:
# retried: the node kept serving every paired ecosystem with the log
# asserting the opposite by omission.
self.logger.warning(
- "Matter export: nothing is exported, but the bridge node's LaunchAgent could not "
+ "Matter bridge: nothing is exported, but the bridge node's LaunchAgent could not "
"be %s (%s). It keeps running and serving every paired ecosystem, and NOTHING "
"retries this on its own — reload the plugin, or run 'launchctl bootout "
"gui/$(id -u)/%s' and delete that file by hand.",
@@ -2406,14 +2408,14 @@ def _stop_bridge_agent(self) -> None:
return
if was_loaded:
self.logger.info(
- "Matter export: nothing is exported — the bridge node has been stopped and its "
+ "Matter bridge: nothing is exported — the bridge node has been stopped and its "
"LaunchAgent removed, so a restart of this Mac cannot bring it back. Its pairings "
"are kept.")
else:
# The two Falses `stop()` conflated: this one is "there was no job",
# which is not a failure and must not be reported as one.
self.logger.debug(
- "Matter export: nothing is exported and no bridge node LaunchAgent was loaded; "
+ "Matter bridge: nothing is exported and no bridge node LaunchAgent was loaded; "
"any plist has been removed. Pairings are kept.")
def _bridge_agent_diagnosis(self) -> Optional[str]:
@@ -2495,7 +2497,7 @@ def _run_bridge_install(self, *, clean: bool) -> bool:
self.logger.warning("An npm install is already in progress — wait for it to finish.")
return False
self.logger.info(
- "%s the Matter export bridge node in the background — watch the log for progress; "
+ "%s the Matter bridge node in the background — watch the log for progress; "
"this can take a minute.",
"Removing and reinstalling" if clean else "Installing")
self._install_thread = threading.Thread(
@@ -2507,7 +2509,7 @@ def _run_bridge_install(self, *, clean: bool) -> bool:
def menuStopBridgeNode(self, valuesDict, menuId=""): # noqa: N802, ARG002
"""Stop the bridge node and remove its LaunchAgent, by hand.
- The controller has "Restart matter-server"; the bridge had nothing at
+ The controller has "Restart the Matter controller"; the bridge had nothing at
all, because it is started and stopped by the allow-list. That leaves one
state with no UI: a user who disables the plugin (or whose plugin dies
mid-session) has a node still running and still serving every paired
@@ -2558,12 +2560,12 @@ def _install_bridge_node(self, clean: bool = False) -> None:
# remove_package has said what is still there. Installing over a
# wedged install is what the clean variant exists to avoid.
self.logger.error(
- "Clean reinstall of the Matter export bridge ABANDONED — the old package "
+ "Clean reinstall of the Matter bridge ABANDONED — the old package "
"could not be removed, so nothing was reinstalled over it.")
return
if not agent.install():
self.logger.error(
- "Install/update of the Matter export bridge did not complete — see the error "
+ "Install/update of the Matter bridge did not complete — see the error "
"above. Nothing was changed; retry when resolved.")
return
if self._stopping: # plugin is tearing down — don't mutate its state
@@ -2571,7 +2573,7 @@ def _install_bridge_node(self, clean: bool = False) -> None:
self.bridge_process = bridge_agent.BridgeProcess(dict(self.pluginPrefs), self.logger)
if self.exports is None or not len(self.exports):
self.logger.info(
- "Matter export bridge installed. It is NOT being started: nothing is exported "
+ "Matter bridge installed. It is NOT being started: nothing is exported "
"yet, and the bridge only runs while the export list is non-empty. Add a "
"device in 'Manage Matter Exports…' and it will start itself.")
return
@@ -2583,7 +2585,7 @@ def _install_bridge_node(self, clean: bool = False) -> None:
applied = self.bridge_process.ensure_installed()
if applied is None:
self.logger.error(
- "The Matter export bridge was installed, but its LaunchAgent could not be "
+ "The Matter bridge was installed, but its LaunchAgent could not be "
"written — see the reason above. The package is on disk; fix that and reload "
"the plugin.")
return
@@ -2591,7 +2593,7 @@ def _install_bridge_node(self, clean: bool = False) -> None:
# left alone by ensure_installed is still executing the OLD version.
if applied is False and not self.bridge_process.restart():
self.logger.error(
- "The Matter export bridge was installed but the restart onto the new version "
+ "The Matter bridge was installed but the restart onto the new version "
"FAILED — the old version may still be running. Check %s.",
os.path.join(self.bridge_process.log_dir, bridge_agent.BRIDGE_ERR_LOG))
return
@@ -2603,17 +2605,17 @@ def _install_bridge_node(self, clean: bool = False) -> None:
# reached a run loop, so the log must not claim it is reconnecting.
poked = self.export_bridge is not None and self.export_bridge.retry_now()
if poked:
- self.logger.info("Matter export bridge installed and restarted onto the new "
+ self.logger.info("Matter bridge installed and restarted onto the new "
"version — reconnecting now.")
else:
- self.logger.info("Matter export bridge installed and restarted onto the new "
+ self.logger.info("Matter bridge installed and restarted onto the new "
"version — reload the plugin to reconnect.")
except Exception as exc: # noqa: BLE001
self.logger.exception(exc)
self.logger.error(
- "Install of the Matter export bridge did not complete after the npm step — the "
+ "Install of the Matter bridge did not complete after the npm step — the "
"package may be installed but the agent was not restarted. See the trace above, "
- "then retry Plugins ▸ Matter ▸ Install/update the Matter export bridge.")
+ "then retry Plugins ▸ Matter ▸ Install/update the Matter bridge.")
# ------------------------------------------------------------------
# Pairing and fabric management (PRD §6, BRIDGE_PROTOCOL §3.7-§3.9)
@@ -2639,7 +2641,7 @@ def _pairing_client(self, errors, field: str):
why = ("Export at least one device in 'Manage Matter Exports…' first — the bridge only "
"runs while something is exported." if not exported else
"The bridge node is not answering; see the log for what its own error log says.")
- self.logger.warning("Matter export: cannot reach the bridge node for pairing. %s", why)
+ self.logger.warning("Matter bridge: cannot reach the bridge node for pairing. %s", why)
errors[field] = "Not connected to the bridge node — see the log."
return None
return client
@@ -2670,7 +2672,7 @@ def menuPairMatterBridge(self, valuesDict, menuId=""): # noqa: N802, ARG002
try:
pairing = self.runtime.submit(client.get_pairing()).result(timeout=PAIRING_READ_TIMEOUT)
except Exception as exc: # noqa: BLE001
- self.logger.error("Matter export: could not read the bridge node's pairing state — %s. "
+ self.logger.error("Matter bridge: could not read the bridge node's pairing state — %s. "
"No pairing window was opened.", exc)
self.logger.exception(exc)
errors["duration"] = "Could not reach the bridge node — see the log."
@@ -2690,7 +2692,7 @@ def menuPairMatterBridge(self, valuesDict, menuId=""): # noqa: N802, ARG002
client.open_commissioning_window(duration)).result(timeout=WINDOW_OPEN_TIMEOUT)
except Exception as exc: # noqa: BLE001
self.logger.error(
- "Matter export: opening a pairing window FAILED — %s. Nothing was changed and "
+ "Matter bridge: opening a pairing window FAILED — %s. Nothing was changed and "
"existing pairings are untouched. If the bridge says a window is already open, "
"wait for it to expire (up to 15 minutes) and try again.", exc)
self.logger.exception(exc)
@@ -2828,7 +2830,7 @@ def menuUnpairEcosystem(self, valuesDict, menuId=""): # noqa: N802, ARG002
removal = self.runtime.submit(
client.remove_fabric(fabric_index)).result(timeout=UNPAIR_TIMEOUT)
except Exception as exc: # noqa: BLE001
- self.logger.error("Matter export: unpairing ecosystem %s FAILED — %s. Pairings are "
+ self.logger.error("Matter bridge: unpairing ecosystem %s FAILED — %s. Pairings are "
"unchanged.", fabric_index, exc)
self.logger.exception(exc)
errors["confirmAgain"] = "Unpair failed — see the log. Nothing was changed."
@@ -2843,7 +2845,7 @@ def menuUnpairEcosystem(self, valuesDict, menuId=""): # noqa: N802, ARG002
# answered `{}` either way and the menu logged "has been unpaired.
# Every accessory has been removed" over a node-side no-op.
self.logger.warning(
- "Matter export: ecosystem %s was ALREADY gone from the bridge node — nothing was "
+ "Matter bridge: ecosystem %s was ALREADY gone from the bridge node — nothing was "
"removed by this action, because there was nothing there to remove. It had most "
"likely unpaired itself since this dialog was opened. The ecosystem list has been "
"refreshed%s.", fabric_index,
@@ -2858,13 +2860,13 @@ def menuUnpairEcosystem(self, valuesDict, menuId=""): # noqa: N802, ARG002
# that actually means rather than reporting a routine removal, because
# the user has just reset the whole bridge without using the reset menu.
self.logger.warning(
- "Matter export: ecosystem %s was the LAST one paired, so the bridge node has "
+ "Matter bridge: ecosystem %s was the LAST one paired, so the bridge node has "
"reset itself and is advertising for commissioning again — exactly as 'Reset "
- "Matter Export Pairings…' would have done. Nothing in Indigo changed. Use "
+ "Matter Bridge Pairings…' would have done. Nothing in Indigo changed. Use "
"'Pair Matter Bridge…' to pair it again.", fabric_index)
else:
self.logger.warning(
- "Matter export: ecosystem %s has been unpaired. Every accessory Indigo exports "
+ "Matter bridge: ecosystem %s has been unpaired. Every accessory Indigo exports "
"has been removed from it; remove any leftover 'Indigo' bridge entry in that "
"ecosystem's app by hand.", fabric_index)
return (True, valuesDict)
@@ -2888,7 +2890,7 @@ def _refresh_fabric_cache(self, client) -> None:
except Exception as exc: # noqa: BLE001
# The removal itself already succeeded or was already true; failing
# to re-read the list afterwards is not worth reporting as a failure.
- self.logger.debug("Matter export: could not refresh the ecosystem list (%s)", exc)
+ self.logger.debug("Matter bridge: could not refresh the ecosystem list (%s)", exc)
def _is_last_fabric(self, fabric_index: int) -> bool:
"""Whether removing ``fabric_index`` empties the fabric set.
diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/server_process.py b/indigo-matter.indigoPlugin/Contents/Server Plugin/server_process.py
index 2519357..0eb0062 100644
--- a/indigo-matter.indigoPlugin/Contents/Server Plugin/server_process.py
+++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/server_process.py
@@ -53,7 +53,7 @@
#: The EXACT wording of ``MenuItems.xml``'s ``installMatterServer`` item, for the
#: messages that tell a user to run it. See ``AgentSpec.install_menu``.
-INSTALL_MENU = "Install/update matter-server"
+INSTALL_MENU = "Install/update the Matter controller (matter-server)"
# Records the sha256 of the plist launchd was last told to load (bootstrap). launchd
# caches a job's ProgramArguments at bootstrap time — rewriting the plist FILE does not
# touch an already-loaded job — so we compare against this to tell "current definition
diff --git a/tests/test_plugin_module.py b/tests/test_plugin_module.py
index 4f4fb30..0d683f6 100644
--- a/tests/test_plugin_module.py
+++ b/tests/test_plugin_module.py
@@ -118,20 +118,20 @@ def test_dynamic_list_methods_exist_on_plugin(plugin_cls):
["Manage Matter Exports…",
"Pair Matter Bridge…",
"Unpair an Ecosystem…"],
- # 3 · matter-server
- ["Install/update matter-server",
- "Restart matter-server",
- "Reinstall matter-server (clean)…",
- "Open matter-server log…"],
- # 4 · the export bridge node
- ["Install/update the Matter export bridge",
- "Reinstall the Matter export bridge (clean)…",
- "Stop the Matter export bridge…"],
+ # 3 · the Matter controller (matter-server)
+ ["Install/update the Matter controller (matter-server)",
+ "Restart the Matter controller",
+ "Reinstall the Matter controller (clean)…",
+ "Open the Matter controller log…"],
+ # 4 · the Matter bridge node
+ ["Install/update the Matter bridge",
+ "Reinstall the Matter bridge (clean)…",
+ "Stop the Matter bridge…"],
# 5 · backup and recovery
["Back up the Matter fabric…",
"Restore a fabric backup…",
"Rebuild Matter Endpoint Map…",
- "Reset Matter Export Pairings…"],
+ "Reset Matter Bridge Pairings…"],
]
@@ -166,15 +166,16 @@ def test_menu_is_grouped_into_the_documented_sections():
def test_the_two_install_items_are_separated():
- """#134: 'Install/update matter-server' and 'Install/update the Matter
- export bridge' sat eleven apart in one flat list and were clicked for one
- another live — reinstalling the inbound controller when the outbound
- bridge was meant. They are separately versioned, separately installed npm
- packages, so they must not share a section."""
+ """#134: 'Install/update the Matter controller (matter-server)' and
+ 'Install/update the Matter bridge' sat eleven apart in one flat list and
+ were clicked for one another live — reinstalling the inbound controller
+ when the outbound bridge was meant. They are separately versioned,
+ separately installed npm packages, so they must not share a section."""
sections = _menu_sections()
- controller = next(i for i, s in enumerate(sections) if "Install/update matter-server" in s)
+ controller = next(i for i, s in enumerate(sections)
+ if "Install/update the Matter controller (matter-server)" in s)
bridge = next(i for i, s in enumerate(sections)
- if "Install/update the Matter export bridge" in s)
+ if "Install/update the Matter bridge" in s)
assert controller != bridge
@@ -514,3 +515,65 @@ def test_ipv6_literal_preserved(self, plugin_module):
def test_blank_stays_blank(self, plugin_module):
assert plugin_module.sanitize_host("") == ""
+
+
+# ---------------------------------------------------------------------------
+# Issue #147 — retired menu names must not survive anywhere a user reads
+# ---------------------------------------------------------------------------
+
+#: Menu names retired by the #147 terminology sweep. A message or doc that
+#: still uses one sends the user to a menu item that no longer exists — and
+#: the #147 review found exactly that, twice, in strings WRAPPED across
+#: source lines, which no grep of the source text can see. Python strings are
+#: therefore collected from the AST (adjacent literals concatenate) and docs
+#: are scanned with newlines collapsed.
+RETIRED_MENU_NAMES = [
+ "Install/update matter-server",
+ "Restart matter-server",
+ "Reinstall matter-server (clean)…",
+ "Open matter-server log…",
+ "Install/update the Matter export bridge",
+ "Reinstall the Matter export bridge (clean)…",
+ "Stop the Matter export bridge…",
+ "Reset Matter Export Pairings…",
+]
+
+#: Docs a user (or the next session) follows today. The PRDs are deliberately
+#: absent: they are frozen planning records whose milestone tables name the
+#: menus as they were when each milestone landed.
+SCANNED_DOCS = [
+ Path(__file__).parent.parent / "README.md",
+ Path(__file__).parent.parent / "bridge-node" / "README.md",
+ Path(__file__).parent.parent / "docs" / "INSTALL.md",
+ Path(__file__).parent.parent / "docs" / "MATTER.md",
+ Path(__file__).parent.parent / "docs" / "HANDOVER.md",
+]
+
+
+def _py_string_constants(path):
+ import ast
+ tree = ast.parse(path.read_text(encoding="utf-8"))
+ return [n.value for n in ast.walk(tree)
+ if isinstance(n, ast.Constant) and isinstance(n.value, str)]
+
+
+@pytest.mark.parametrize("retired", RETIRED_MENU_NAMES)
+def test_no_python_string_names_a_retired_menu_item(retired):
+ offenders = []
+ for py in sorted(SERVER_PLUGIN.glob("*.py")):
+ for value in _py_string_constants(py):
+ if retired in value:
+ offenders.append(py.name)
+ break
+ assert not offenders, (
+ f"{offenders} still reference the retired menu name {retired!r} — "
+ "the menu was renamed in #147; users cannot find the old name")
+
+
+@pytest.mark.parametrize("doc", SCANNED_DOCS, ids=lambda p: p.name)
+def test_no_current_doc_names_a_retired_menu_item(doc):
+ # Newlines collapsed so a name wrapped across lines cannot hide — the
+ # exact way both #147 review criticals escaped the original sweep.
+ text = " ".join(doc.read_text(encoding="utf-8").split())
+ hits = [name for name in RETIRED_MENU_NAMES if name in text]
+ assert not hits, f"{doc.name} still references retired menu name(s): {hits}"