AirNexus Matter bridge + dynamic devices + pairing management for Node-RED
npm install @airnexus/node-red-contrib-matter-airnexusAirNexus Node-RED Matter Dynamic
Dynamic Matter bridge + dynamic devices for Node-RED. Create any Matter device by specifying the device type in JSON configuration — no need for device-specific nodes.
This AirNexus fork adds:
Matter Pairing node (get QR/manual codes, reopen pairing, factory reset commissioning)
Matter Device “hidden until enabled” mode (devices don’t appear in Alexa/HomeKit until you enable them by payload)
Dynamic rename by payload
Thermostat-friendly event handling (captures attribute writes even when controllers don’t send explicit commands)
Installation
npm install @airnexus/node-red-contrib-matter-airnexus
Requirements
Node.js >= 18.0.0
Node-RED >= 3.0.0
Nodes Included
1) Matter Dynamic Bridge (matter-dynamic-bridge)
Creates a Matter bridge and hosts all devices.
2) Matter Device (matter-device)
Creates a dynamic Matter endpoint from JSON config.
AirNexus behavior (Option 2 – hidden until enabled):
Device is created locally, but NOT registered into the bridge/aggregator until enabled
Once enabled, the device becomes visible in Alexa/HomeKit/Google
Disable sets reachable=false and suppresses outputs (device may remain listed in the controller until you remove it there)
3) Matter Pairing (matter-pairing)
Provides commissioning info (QR/manual) and supports “reset pairing” flows.
Quick Start
1) Create a Bridge
Add Matter Dynamic Bridge
Configure name/port/interface (default port 5540)
Deploy
2) Get Pairing Codes (recommended: use Matter Pairing node)
Add Matter Pairing
Select your bridge
Deploy
Inject get to output pairing info (QR code string + manual code)
3) Add Devices
Add Matter Device
Select the same bridge
Set your JSON device config (examples below)
Deploy
4) Enable a Device (AirNexus Option 2)
Devices will not show up in Alexa until you enable them:
Inject to the device:
topic: enable
payload can include a name
Example:
{
"topic": "enable",
"payload": { "name": "Lounge Thermostat" }
}
Matter Pairing Node
Inputs (commands)
Send an Inject into the Matter Pairing node:
Command How to send What it does
get msg.topic="get" Outputs current pairing info (if commissioned → state=commissioned)
pair msg.topic="pair" Ensures bridge is online and ready to commission (best effort)
reset msg.topic="reset" Factory reset commissioning (wipes bridge commissioning storage, regenerates codes)
unpair / delete msg.topic="unpair" Same as reset
disable msg.topic="disable" Best-effort stop advertising (takes server offline)
Optional payload:
{ "timeoutMins": 15, "includeSvg": true, "forceReset": true }
Output payload (example)
{
"bridgeId": "5318704ab571aeeb",
"pairingEnabled": true,
"pairingUntil": "2026-01-25T02:30:00.000Z",
"state": "ready",
"commissioned": false,
"qrPairingCode": "MT:....",
"manualPairingCode": "123-45-678",
"qrSvg": ""
}
Matter Device Node (AirNexus Option 2: hidden until enabled)
Enable / Disable / Rename (by payload)
Enable
msg.topic = "enable";
msg.payload = { name: "Zone 1 Thermostat" }; // optional
return msg;
Disable
msg.topic = "disable";
return msg;
Rename
msg.topic = "name";
msg.payload = "New Name Here";
return msg;
Config combined
msg.topic = "config";
msg.payload = { enabled: true, name: "Zone 2 Thermostat" };
return msg;
State query
msg.topic = "state";
return msg;
Inputs / Outputs (Matter Device)
The Matter Device node has 3 outputs:
Output 1 – Events / State
$Changed attribute events (when Matter.js emits them)
Thermostat writes often appear as interactionEnd snapshot diffs (even when no explicit commands are used)
Output 2 – Commands
Real Matter cluster commands (e.g. OnOff.on, LevelControl.moveToLevel)
Some controllers do attribute writes instead of commands (especially thermostats)
Output 3 – Debug / Diagnostics
subscription logs, init status, bridgeReset, write retries, sanitization info, etc.
Example Output 2 (command)
{
"command": "on",
"cluster": "OnOff",
"data": {}
}
Example Output 1 (thermostat diff from Alexa)
{
"thermostat": {
"systemMode": 3,
"occupiedCoolingSetpoint": 2000,
"occupiedHeatingSetpoint": 2000
}
}
Command vs Attribute Writes (Thermostats)
Many Matter thermostats are controlled via attribute writes rather than explicit commands.
Typical behavior:
Changing setpoint in Alexa/HomeKit → Output 1 (state diff)
Using something like setpointRaiseLower (if controller uses it) → Output 2 (command)
Always monitor Output 1 for thermostat changes.
Configuration Examples
Simple On/Off Light
{
"deviceType": "OnOffLightDevice"
}
Thermostat (Heating + Cooling + Auto)
{
"deviceType": "ThermostatDevice",
"behaviorFeatures": {
"Thermostat": ["Heating", "Cooling", "AutoMode"]
},
"initialState": {
"thermostat": {
"controlSequenceOfOperation": 4,
"systemMode": 1,
"localTemperature": 2500,
"minSetpointDeadBand": 100,
"occupiedHeatingSetpoint": 2000,
"occupiedCoolingSetpoint": 2600,
"minHeatSetpointLimit": 500,
"maxHeatSetpointLimit": 3500,
"minCoolSetpointLimit": 500,
"maxCoolSetpointLimit": 3500
}
}
}
Troubleshooting
Device doesn’t appear in Alexa/HomeKit
If you’re using AirNexus Option 2:
You must enable the Matter Device node:
msg.topic="enable"
optionally set name
Pairing issues / want to re-pair
Use Matter Pairing node:
reset to wipe commissioning + generate new QR/manual
Commands not appearing in Output 2
Expected for devices controlled by attribute writes (thermostats). Use Output 1 diffs.
License
MIT
Acknowledgments
Inspired by and based on patterns from node-red-matter-bridge and Matter.js.
- Node-RED Forum: Get help from the community
Contributions are welcome! Please feel free to submit a Pull Request.
MIT
This project was heavily inspired by and based on the excellent work done in node-red-matter-bridge by Sam Machin. The architecture and implementation patterns from that project served as a fundamental guide for developing this dynamic Matter bridge implementation.
Built on top of the excellent Matter.js library.