{"id":49595076,"url":"https://github.com/minhtuannguyen/node-red-contrib-matter","last_synced_at":"2026-05-04T03:38:59.125Z","repository":{"id":355063845,"uuid":"1226497472","full_name":"minhtuannguyen/node-red-contrib-matter","owner":"minhtuannguyen","description":"A generic Matter protocol controller for Node-RED.","archived":false,"fork":false,"pushed_at":"2026-05-01T18:07:37.000Z","size":62,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-01T18:25:48.986Z","etag":null,"topics":["home-automation","matter","nodered","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/minhtuannguyen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-01T13:29:28.000Z","updated_at":"2026-05-01T18:07:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/minhtuannguyen/node-red-contrib-matter","commit_stats":null,"previous_names":["minhtuannguyen/node-red-contrib-matter"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/minhtuannguyen/node-red-contrib-matter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minhtuannguyen%2Fnode-red-contrib-matter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minhtuannguyen%2Fnode-red-contrib-matter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minhtuannguyen%2Fnode-red-contrib-matter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minhtuannguyen%2Fnode-red-contrib-matter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/minhtuannguyen","download_url":"https://codeload.github.com/minhtuannguyen/node-red-contrib-matter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/minhtuannguyen%2Fnode-red-contrib-matter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32593948,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T22:12:39.696Z","status":"online","status_checked_at":"2026-05-04T02:00:06.625Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["home-automation","matter","nodered","typescript"],"created_at":"2026-05-04T03:38:58.470Z","updated_at":"2026-05-04T03:38:59.115Z","avatar_url":"https://github.com/minhtuannguyen.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# node-red-contrib-matter\n\n![Flow Example](pic/screenshot.png)\n\nA generic [Matter](https://csa-iot.org/all-solutions/matter/) protocol controller for [Node-RED](https://nodered.org/).  \nCommission and control **any Matter device** — smart locks, lights, sensors — directly from Node-RED flows over IP or Thread.\n\nBuilt on [matter.js](https://github.com/project-chip/matter.js) (`@project-chip/matter.js`).\n\n---\n\n## Features\n\n- **Commission** any Matter device into your own fabric (multi-admin alongside Apple Home, Google Home, etc.)\n- **Device registry** — after commissioning, devices are auto-discovered and stored; all other nodes show a **device dropdown** instead of requiring manual ID entry\n- **Send commands** to any cluster and endpoint (lock/unlock, on/off, scenes, …)\n- **Read attributes** on demand (lock state, brightness, temperature, …)\n- **Subscribe** to real-time attribute changes and events\n- **Decommission** devices cleanly from the fabric, or force-remove when offline\n- Works on **Raspberry Pi** (Ethernet or WiFi) and any Node.js-capable Linux/macOS host\n- Thread devices reachable via a Thread Border Router (HomePod, Apple TV, OTBR)\n\n---\n\n## Prerequisites\n\n| Requirement | Version |\n|---|---|\n| Node.js | ≥ 20.0.0 |\n| Node-RED | ≥ 3.0.0 |\n\n---\n\n## Installation\n\n### From GitHub\n\n```bash\ncd ~/.node-red\nnpm install github:minhtuannguyen/node-red-contrib-matter\n```\n\n### From a local clone\n\n```bash\ngit clone https://github.com/minhtuannguyen/node-red-contrib-matter.git\ncd ~/.node-red\nnpm install ../node-red-contrib-matter\n```\n\n\u003e **Note:** The compiled `dist/` folder is included in the repository, so no TypeScript compiler is needed on the target machine.\n\nThen restart Node-RED:\n\n```bash\nnode-red-restart\n# or\nsudo systemctl restart nodered\n```\n\n---\n\n## Nodes\n\n### `matter-controller` (config node)\n\nShared configuration node. Manages the Matter controller lifecycle — one per Node-RED instance.\n\n| Property | Description | Default |\n|---|---|---|\n| Storage Path | Directory where fabric credentials and device registry are persisted | `~/.node-red-matter` |\n| UDP Port | Matter controller port | `5540` |\n| Log Level | matter.js log verbosity | `Info` |\n\nThe controller also serves an internal HTTP endpoint (`GET /matter-nodes/:id/registry`) used by the edit dialogs of all other nodes to populate their device dropdowns. This requires the controller node to be **deployed** before opening any other node's edit dialog.\n\n---\n\n### `matter-commission`\n\nCommission a Matter device into the Node-RED controller fabric.\n\nAfter successful commissioning the device is **automatically registered** in the device registry so it immediately appears in the dropdowns of the command, read, subscribe, and decommission nodes.\n\n**Input `msg.payload`:**\n\n| Field | Type | Description |\n|---|---|---|\n| `pairingCode` | string | 11-digit manual pairing code (hyphens optional). Overrides node config. |\n| `knownAddress` | string | *(optional)* IPv6/IPv4 address to skip mDNS discovery. Useful for Thread devices. |\n\n**Output `msg.payload`:**\n\n```json\n{ \"nodeId\": \"\u003ccommissioned-node-id\u003e\", \"endpoints\": [...] }\n```\n\n\u003e Before commissioning, open a commissioning window from your primary controller:  \n\u003e **Apple Home** → device → ⚙️ → *Turn on Pairing Mode*\n\n---\n\n### `matter-discover`\n\nDiscovers all endpoints, clusters, attributes, commands, and events of a commissioned device and **saves the result to the device registry**. Run this node once after commissioning (or any time you want to refresh the registry) so the dropdowns in the other nodes reflect the device's current structure.\n\n**Input:** any message (triggers the discovery).  \nOverride `nodeId` at runtime via `msg.nodeId`.\n\n**Output `msg.payload`:**\n\n```json\n{\n  \"nodeId\": \"\u003ccommissioned-node-id\u003e\",\n  \"endpoints\": [\n    {\n      \"endpointId\": 0,\n      \"clusters\": [\n        {\n          \"clusterId\": 40,\n          \"clusterIdHex\": \"0028\",\n          \"clusterName\": \"BasicInformation\",\n          \"attributes\": [\"vendorName\", \"productName\", \"serialNumber\", ...],\n          \"commands\": [],\n          \"events\": []\n        }\n      ]\n    },\n    {\n      \"endpointId\": 1,\n      \"clusters\": [\n        {\n          \"clusterId\": 257,\n          \"clusterIdHex\": \"0101\",\n          \"clusterName\": \"DoorLock\",\n          \"attributes\": [\"lockState\", \"lockType\", \"doorState\", ...],\n          \"commands\": [\"lockDoor\", \"unlockDoor\", \"unlockWithTimeout\", ...],\n          \"events\": [\"doorLockAlarm\", \"lockOperation\", ...]\n        }\n      ]\n    }\n  ]\n}\n```\n\nConnect the output to a Debug node set to **\"complete msg object\"** to inspect the full structure.\n\n\u003e **Tip:** If the device dropdown in another node shows *\"select a controller first\"* or is empty after commissioning, run this node with the device's `nodeId`, then re-open the edit dialog.\n\n---\n\n### `matter-command`\n\nSend a command to a cluster on a commissioned device.\n\nThe edit dialog shows **cascading dropdowns** (Device → Endpoint → Cluster → Command) populated from the device registry. The selected values fill the underlying text fields; all fields can also be overridden at runtime via `msg`:\n\n| Field | Description | Example |\n|---|---|---|\n| `nodeId` | Commissioned node ID | `7825526669137635300` |\n| `endpointId` | Endpoint number | `1` |\n| `clusterId` | Cluster ID (hex) | `0101` (DoorLock) |\n| `commandName` | Command name (camelCase) | `lockDoor`, `unlockDoor` |\n| `payload` | Command arguments object | `{ \"timeout\": 30 }` |\n\n---\n\n### `matter-read`\n\nRead an attribute value from a commissioned device on any input message.\n\nThe edit dialog shows **cascading dropdowns** (Device → Endpoint → Cluster → Attribute).\n\n| Field | Description | Example |\n|---|---|---|\n| `nodeId` | Commissioned node ID | `7825526669137635300` |\n| `endpointId` | Endpoint number | `1` |\n| `clusterId` | Cluster ID (hex) | `0101` |\n| `attributeName` | Attribute name (camelCase) | `lockState` |\n\n**Output `msg.payload`:** the raw attribute value.\n\n---\n\n### `matter-subscribe`\n\nSubscribes to real-time attribute changes and/or events from a device. Starts automatically on deploy, no input needed.\n\nThe edit dialog shows **cascading dropdowns** (Device → Endpoint → Cluster → Attribute / Event). All filter fields are optional — leave blank to receive everything from the device.\n\n| Field | Description | Example |\n|---|---|---|\n| `nodeId` | Commissioned node ID (required) | `7825526669137635300` |\n| `endpointId` | *(optional)* Filter by endpoint | `1` |\n| `clusterId` | *(optional)* Filter by cluster (hex) | `0101` |\n| `attributeName` | *(optional)* Filter by attribute | `lockState` |\n| `eventName` | *(optional)* Filter by event | `doorLockAlarm` |\n\n**Output `msg.payload` — attribute change:**\n```json\n{\n  \"type\": \"attribute\",\n  \"nodeId\": \"7825526669137635300\",\n  \"endpointId\": 1,\n  \"clusterId\": 257,\n  \"attributeName\": \"lockState\",\n  \"value\": 1,\n  \"timestamp\": \"2026-05-01T17:00:00.000Z\"\n}\n```\n\n**Output `msg.payload` — event:**\n```json\n{\n  \"type\": \"event\",\n  \"nodeId\": \"7825526669137635300\",\n  \"endpointId\": 1,\n  \"clusterId\": 257,\n  \"eventName\": \"doorLockAlarm\",\n  \"events\": [...],\n  \"timestamp\": \"2026-05-01T17:00:00.000Z\"\n}\n```\n\n---\n\n### `matter-decommission`\n\nDecommission (unpair) a Matter device from the controller fabric and remove it from the device registry.\n\nThe edit dialog shows a **Device dropdown** populated from the registry and a **Force** checkbox.\n\n**Input `msg.payload`:**\n\n| Field | Type | Description |\n|---|---|---|\n| `nodeId` | string | Decimal node ID of the device to remove. Overrides node config. |\n| `force` | boolean | When `true`, skips the fabric-level RemoveFabric command and only erases local storage. Use when the device is offline or already factory-reset. Default: `false`. |\n\n**Output `msg.payload`:**\n```json\n{ \"ok\": true, \"nodeId\": \"7825526669137635300\", \"force\": false }\n```\n\n| Mode | Behaviour |\n|---|---|\n| Normal (force = false) | Sends *RemoveFabric* to the device, then erases local storage and registry entry |\n| Force (force = true) | Erases local storage and registry entry only — device must be manually factory-reset |\n\n---\n\n## Device Registry\n\nThe device registry is the central store that connects the `matter-commission` node to all other nodes. Instead of manually typing node IDs, cluster IDs, and attribute names, every node's edit dialog loads the registry and shows human-readable **cascading dropdowns**.\n\n### How it works\n\n```\ncommission → [auto-discover in background] → registry.json\n                                                  ↓\n                    command / read / subscribe / decommission / discover\n                    edit dialogs load registry → device dropdown\n```\n\n1. **Commission** a device with `matter-commission`. Immediately after the pairing handshake completes, the node triggers a background discovery that writes the device's label, node ID, and full cluster/attribute/command structure to the registry.\n2. **Open any other node's edit dialog**. The dialog fetches the registry from the controller's HTTP endpoint and populates a **Device** dropdown. Selecting a device pre-fills the node ID. Nodes that need more detail (command, read, subscribe) show further cascading dropdowns: Endpoint → Cluster → Attribute / Command / Event.\n3. **Refresh the registry** at any time by triggering a `matter-discover` node. This is useful if the background discovery after commissioning was slow (e.g. the device rebooted after joining the fabric) or if the device firmware updated and exposed new clusters.\n4. **Decommission** with `matter-decommission` — this removes the device from both the Matter fabric and the registry.\n\n### Registry file\n\nPersisted at `\u003cstoragePath\u003e/node-red-matter/registry.json`  \nDefault: `~/.node-red-matter/node-red-matter/registry.json`\n\n```json\n{\n  \"7825526669137635300\": {\n    \"label\": \"Nuki Smart Lock\",\n    \"nodeId\": \"7825526669137635300\",\n    \"discoveredAt\": \"2026-05-01T12:00:00.000Z\",\n    \"discovery\": {\n      \"nodeId\": \"7825526669137635300\",\n      \"endpoints\": [\n        {\n          \"endpointId\": 1,\n          \"clusters\": [\n            {\n              \"clusterId\": 257,\n              \"clusterIdHex\": \"0101\",\n              \"clusterName\": \"DoorLock\",\n              \"attributes\": [\"lockState\", \"lockType\", ...],\n              \"commands\": [\"lockDoor\", \"unlockDoor\", ...],\n              \"events\": [\"doorLockAlarm\", \"lockOperation\", ...]\n            }\n          ]\n        }\n      ]\n    }\n  }\n}\n```\n\n### Registry lifecycle\n\n| Event | Registry change |\n|---|---|\n| `matter-commission` completes | Entry added (background discovery, may take a few seconds) |\n| `matter-discover` is triggered | Entry updated with latest cluster structure |\n| `matter-decommission` runs | Entry removed |\n| Node-RED restart | Registry loaded from disk — no re-discovery needed |\n\n### Troubleshooting the dropdowns\n\n- **\"select a controller first\"** — no controller is selected or the controller config node is not deployed yet.\n- **\"deploy the flow first, then re-open this dialog\"** — the controller node has not been deployed. Click **Deploy** then re-open the edit dialog.\n- **Device not in dropdown after commissioning** — the background discovery may still be in progress (the device reboots after joining the fabric). Wait 10–15 seconds, then trigger `matter-discover` with `msg.nodeId` set to the commissioned node ID, and re-open the dialog.\n- **Dropdown shows a device but no endpoints** — the registry entry has no `discovery` data yet. Run `matter-discover` to populate it.\n\n---\n\n## Example: Nuki Smart Lock\n\nImport `examples/nuki-lock.json` into Node-RED for a ready-made flow covering:\n\n1. Commission the lock (one-time)\n2. Discover and register in the device registry\n3. Lock / Unlock / Unlock with timeout\n4. Read `lockState` on demand\n5. Subscribe to `lockState` changes and `doorLockAlarm` events\n\n**DoorLock cluster reference:**\n\n| Property | Value |\n|---|---|\n| Cluster ID | `0101` |\n| Endpoint | `1` |\n| Commands | `lockDoor`, `unlockDoor`, `unlockWithTimeout` |\n| `lockState` values | `0` = NotFullyLocked, `1` = Locked, `2` = Unlocked, `3` = Unlatched |\n\n**Battery level (PowerSource cluster):**\n\n| Property | Value |\n|---|---|\n| Cluster ID | `002F` |\n| Endpoint | `1` |\n| Attribute | `batPercentRemaining` |\n| Raw value | 0–200 (divide by 2 for %) — e.g. `170` → 85% |\n| `batChargeLevel` | `0` = OK, `1` = Warning, `2` = Critical |\n\nAdd a Function node after `matter-read` to convert the raw value:\n```javascript\nmsg.payload = msg.payload / 2;  // e.g. 170 → 85\nreturn msg;\n```\n\n---\n\n## Thread devices (HomePod, Apple TV as Border Router)\n\nIf your device is Thread-only (e.g. Nuki Smart Lock connected via HomePod), standard mDNS commissioning discovery won't bridge from Thread to your WiFi/Ethernet network. Pass the device's IPv6 address directly:\n\n```json\n{\n  \"pairingCode\": \"1234-567-8901\",\n  \"knownAddress\": \"fd00::1234\"\n}\n```\n\nFind the IPv6 address using `avahi-browse -rt _matterc._udp` (Linux) or `dns-sd -B _matterc._udp local` (macOS) while the commissioning window is open.\n\n---\n\n## Development\n\n```bash\ngit clone https://github.com/minhtuannguyen/node-red-contrib-matter.git\ncd node-red-contrib-matter\nnpm install\nnpm run build       # compile TypeScript → dist/\nnpm run dev         # watch mode\n```\n\nInstall as a live symlink into Node-RED for development:\n\n```bash\ncd ~/.node-red/node_modules\nln -s /path/to/node-red-contrib-matter node-red-contrib-matter\n```\n\n---\n\n## License\n\nApache 2.0\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fminhtuannguyen%2Fnode-red-contrib-matter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fminhtuannguyen%2Fnode-red-contrib-matter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fminhtuannguyen%2Fnode-red-contrib-matter/lists"}