node-red-contrib-nhc2-z2m-light
v0.5.6
Published
Node-RED node to convert NHC2 light payloads to Zigbee2MQTT set commands with shared rate-limit config.
Maintainers
Readme
node-red-contrib-nhc2-z2m-light
A small Node-RED node that converts NHC2 light-style payloads into Zigbee2MQTT /set commands and sends them at a rate-limited pace through an internal queue - paced per Zigbee command, on/off first, optionally with Zigbee scenes.
Dependencies
Both are dependencies and are installed automatically with this package:
node-red-contrib-nhc2>= 1.28.2 - providesnhc2-input, the NHC2 message sourcenode-red-contrib-zigbee2mqtt>= 2.7.5 (2.x or 3.x) - provideszigbee2mqtt-outand the Zigbee2MQTT server node (also used to confirm scenes and to seeBUSYreports)
Nodes
nhc2 - z2m (category: function)
Purpose
- Accepts partial NHC2 updates (only the changed fields)
- Remembers the last known values per device
- Builds a Zigbee2MQTT
/setcommand payload that is “complete enough” when needed (e.g. onOn, mode changes) - Queues commands and enforces a minimum interval (rate-limit) between sends, per message or per Zigbee command, plus an optional max commands per window budget
- Sends lamps switching on/off first, brightness/colour changes of lamps that stay on after that
- Merges updates for a lamp that is still waiting in the queue: only its newest state is sent, so the queue never holds more than one item per lamp
- Optionally stores white values NHC uses again as Zigbee scenes and recalls them with one command (only when Zigbee2MQTT confirms the scene)
- Sends a state again when Zigbee2MQTT reports the command as
BUSY
Input
msg.device(recommended): Zigbee2MQTT friendly name for the target device (or group)- Wired straight from an nhc2-input node,
msg.deviceis the NHC2 device object instead. The node then remembers the state per NHC2 device (by itsUuid) and usesmsg.topic(the NHC2 device name) as the name. Set the Zigbee2MQTT target in thezigbee2mqtt-outnode.
- Wired straight from an nhc2-input node,
msg.scenes = "refresh": re-add all scenes of this node (e.g. after adding or replacing a lamp in the group)msg.payload: object that can contain any subset of:
{
"Status": "On",
"Brightness": "100",
"ColorMode": "TunableWhite",
"TunableWhite": "cwww(4422,100)",
"Color": "hsv(12,100,100)"
}Notes:
TunableWhiteiscwww(<kelvin>,<pct>)— the<pct>part is ignored (brightness controls dimming).
Output (to zigbee2mqtt-out)
msg.topic= friendly namemsg.payload= Zigbee2MQTT/setcommand, e.g.
{
"transition": 1.0,
"state": "ON",
"brightness": 254,
"color_temp": 226
}Shared configuration: nhc2-z2m-config
Create one or more configs and select them from the node. This makes it easy to change the rate-limit for multiple nodes at once.
Config fields:
- Rate-limit (ms): minimum interval between sends (e.g. 1000ms = max 1 msg/sec)
- per Zigbee command (default for new configs): a message takes one slot per command Zigbee2MQTT sends for it. On/off, brightness or a scene recall is one; colour temperature and colour add one each, so
{"state":"ON","brightness":152,"color_temp":308}takes two slots. - per message: one slot per message, whatever it contains (configs from before 0.5.0 keep this).
- per Zigbee command (default for new configs): a message takes one slot per command Zigbee2MQTT sends for it. On/off, brightness or a scene recall is one; colour temperature and colour add one each, so
- Max commands per seconds (default 6 per 12 s for new configs, 0 = off): on top of the rate-limit, never more than this many Zigbee commands in any window. Scene creation only uses the budget when 2 commands stay free.
- Retry on BUSY (always on when wired to
zigbee2mqtt-out): when Zigbee2MQTT reportsPublish 'set' ... to '<group>' failed ... BUSYonbridge/logging, the latest state for that lamp is sent again after 6 s (at most twice). - On/off first (default on): lamps switching on or off are sent before brightness/colour changes of lamps that stay on.
- Auto scenes (default off): see below.
- Transition (seconds): always included in every outgoing command (decimal)
- Rate window (ms): window for msg/sec calculation shown in status (default 10 seconds)
Why per command
A command to a Zigbee group is a broadcast, and a coordinator only accepts a handful of broadcasts at a time.
Measured on a 73-lamp Hue network (Sonoff Dongle Plus V2, EmberZNet 7.4.5): about 9 group commands per 10 seconds,
after that every group command is refused with Failed to send with status=BUSY for 4-6 seconds and is lost
(Zigbee2MQTT does not retry). One command every 2 seconds never failed; one every 1.5 seconds sometimes did.
A mode change sends brightness + colour temperature = two commands per group, so with 2000 ms per message
a mode change in 5+ rooms overflowed, and so did 1000 ms per command (10 commands in 10 s: the 10th failed).
Use either 2000 ms per Zigbee command, or a shorter rate-limit together with max 6 commands per 12 s:
then a mode change goes at full speed for a few rooms and waits before it reaches the coordinator's limit.
Note that Zigbee2MQTT sends a scene_add to a group as two commands (remove + add).
Auto scenes
With Auto scenes ticked, a white light value NHC uses again is stored in the lamps as a Zigbee scene and
from then on recalled with one command ({"scene_recall": id}) instead of two. The lamps also never end up with
only the brightness or only the colour temperature changed.
- Only NHC's own values are used: a scene is created with
scene_addholding exactly the on + brightness + colour temperature + transition of the normal/setthe node would send.scene_adddoes not change the lamps (scene_store, which copies whatever the lamps show, is never used), and a scene is only recalled for exactly those values. - Off, brightness-only and colour (hs) changes are always sent as normal commands.
- A scene is created when the same value is used twice within 48 hours (settings "create after N uses within H hours"); the uses are kept in the scene file, so one before a restart counts. It is stored when nothing else is waiting.
- A scene is only recalled when Zigbee2MQTT confirms it was stored: Zigbee2MQTT lists a scene in
bridge/groups(bridge/devicesfor a device) only after a successfulscene_add, and the node reads that list from the zigbee2mqtt server node of thezigbee2mqtt-outnode(s) it is wired to. Not listed 8 s after an add: added again (up to 5 times, then again an hour later). So a lost broadcast never leads to a recall the lamps can't follow - a recall of a missing scene leaves the lamps unchanged, or off. The node's output must be wired tozigbee2mqtt-outnode(s) for this. - Ids come from a range (default 100-119, 20 per node). When all are used, new values are simply sent as normal commands. An id only gets other values after the clean-up removed its scene. Use one node per Zigbee group/device, or give each node its own range. (A Hue GU10 holds 50 scenes in total, shared by all groups it is in.)
- The ids are kept in
nhc2-z2m-scenes.jsonin the Node-RED user directory. After each Node-RED start the node re-adds the scenes Zigbee2MQTT does not list. Sendmsg.scenes = "refresh"to re-add all of them, e.g. after adding or replacing a lamp in the group. - A scene that is recalled more than 24 h after it was last added is added again (when idle), so a lost add repairs itself.
- Clean-up: once 80 % of the node's scene ids are in use, scenes not used for 14 days (not recalled and their value not sent) are removed from the lamps with
scene_remove(lowest priority, within the budget) and their ids become free. Both numbers are settings; 0 days = never.
Shared vs per-node rate-limit
Each nhc2-z2m-config has a checkbox:
- Shared rate-limit = enabled: one shared queue + rate-limit for all nodes using that config (good for groups).
- Shared group (optional): if set, any configs with the same group name share ONE FIFO queue + limiter, even if they are different config instances.
- Shared rate-limit = disabled: each node maintains its own queue + rate-limit even if they share the same config (good when devices should not block each other).
Status text
The node shows a status line like:
Q:0 0.3/s RL:1000ms/cmd W:0ms H:-/0ms ON B:60% CT:308m HS:? M:white Z:2/6 SC:3
Q: items of this node waiting in the queue (light changes and scene jobs)x/s: messages sent per second, averaged over the rate window (default 10 s) - for a shared queue, of all its nodesRL: rate-limit in ms;/cmdwhen it counts per Zigbee commandW: ms until the rate-limit allows the next sendH: first item in the queue - its owner (last 4 characters of the node id) and age in ms;-/0mswhen the queue is emptyON/OFF,Bbrightness %,CTcolour temperature (mired),HShue,saturation,Mwhite/color: the last state the node remembers for its lamp (?= not known yet)Z: Zigbee commands sent within the budget window / max (only when Max commands is set) - for a shared queue, of all its nodesSC: scenes this node has stored (only with Auto scenes)
Status icon: a ring while the queue holds something, a dot when it is empty. The colour reflects the configured rate-limit:
- Green: <= 1000 ms
- Yellow: 1001–2000 ms
- Red: > 2000 ms
Example flow
In Node-RED: Import → Examples → node-red-contrib-nhc2-z2m-light → nhc2-lights-to-zigbee2mqtt.
It mirrors three NHC lights to Zigbee2MQTT - two to a Zigbee group, one to a single Zigbee device - through one
shared config (1000 ms per Zigbee command, max 6 per 12 s, on/off first, auto scenes). After importing:
enter the NHC Hobby API password and the Zigbee2MQTT MQTT login in the config nodes, select the NHC light in each
nhc2-input and the Zigbee group/device in each zigbee2mqtt-out. The comment node in the flow lists the steps.
Install (local dev)
From your Node-RED user directory (typically ~/.node-red):
cd ~/.node-red
npm install /path/to/node-red-contrib-nhc2-z2m-lightThen restart Node-RED.
Changelog
0.5.6
node-red-contrib-zigbee2mqtt(>=2.7.5 <4, checked against 2.7.5 and 3.0.0) is now a dependency as well (was a peer dependency). An installed 2.7.5 satisfies it and is not upgraded.
0.5.5
node-red-contrib-nhc2(^1.28.2) is now a dependency, so it is installed with this package.
0.5.4
- Example flow (Import → Examples): NHC lights to a Zigbee group and to a single device, with the tested shared config.
- Docs: status text (
/cmd,W,H,Z,SC, ring/dot), node purpose and the edit-dialog help brought up to date.
0.5.3
- Default number of scene ids per node 20 (was 10); existing configs keep their saved number.
- New: scene clean-up - once 80 % of the scene ids are in use, scenes not used for 14 days are removed (
scene_remove) and their ids freed (both settings). Scenes from older versions count as used at the first check after the update. - New: a value becomes a scene only when it is used N times within H hours (default twice within 48 h); the uses are kept in the scene file, so a use before a Node-RED restart counts.
0.5.2
- Fix: just after a start, before the zigbee2mqtt server node has Zigbee2MQTT's group/device lists, a scene counts as "unknown" (not recalled, not re-added) instead of "missing" - 0.5.1 re-added scenes that were already stored.
- Scenes created by 0.5.0 (no add time stored) are no longer re-added on their first recall.
- Debug log: a wait for the command budget is logged once per item instead of every 250 ms.
- New: retry on BUSY - a command Zigbee2MQTT reports as failed with BUSY (
bridge/logging, read through the zigbee2mqtt server node) is sent again after 6 s, at most twice, unless a newer state for that lamp is already waiting. - Default budget window 12 s (was 10 s): in a live all-modes test, 6 per 10 s still got 2 BUSY in 12 mode changes - the coordinator remembers a broadcast ~10.5 s and Zigbee2MQTT sends the 2nd command of a pair ~0.6 s later.
- Verified live (7 groups, 73 Hue lamps, 1000 ms per command, 6 per 10 s): mode changes by scene recall reached all lit rooms in 2-9 s, no BUSY, every group read back equal to NHC.
0.5.1
- New: max N Zigbee commands per W seconds budget on top of the rate-limit (new configs: 6 per 10 s). Measured live: 1000 ms per command still overflowed the coordinator on a mode change (10 commands in 10 s).
- Fix:
scene_addcounts as two commands - Zigbee2MQTT sends remove + add to the group. With 1000 ms per command the scene adds went out at 2 commands/s and most failed with BUSY. - Fix: a scene is only recalled once Zigbee2MQTT lists it for the group (read from the zigbee2mqtt server node). 0.5.0 recalled a scene after two
scene_adds whether they had succeeded or not: in a live test Alrum's and Entré's adds were lost (BUSY), Zigbee2MQTT logged "Unknown scene was recalled" and the lamps kept the previous mode. Missing scenes are re-added (up to 5 times, then again an hour later); after a start only missing scenes are re-added. - Scene adds only use the budget when 2 commands stay free, and a scene not re-added for 24 h is added again when it is recalled (self-repair).
- Editor: configs from before 0.5.0 now open with "per Zigbee command" proposed and the empty scene/budget fields filled in (0.5.0 proposed "per message", which kept 2 commands per slot).
0.5.0
- New: rate-limit per Zigbee command (new configs default to it). A brightness + colour temperature change takes two slots, so a mode change in many rooms no longer overflows the coordinator (
status=BUSY, lost commands: lamps kept the old colour or brightness). - New: On/off first - lamps switching on/off are sent before brightness/colour changes of lamps that stay on.
- New: Auto scenes (option) - white values NHC uses again become Zigbee scenes and are recalled with one command.
- Status shows
/cmdwhen the rate-limit is per command andSC:n(scenes) when auto scenes are on.
0.4.1
- Fix: a colour <-> tunable white switch arriving while the lamp waits in the queue no longer merges into a /set with both
colorandcolor_temp(the lamp's final mode then depended on Zigbee2MQTT's order). Only the newest mode is kept. Lamps that are white-only were not affected.
0.4.0
- New: updates for a lamp that is still waiting in the queue are merged into its waiting item (keeping its place) instead of being queued again. Only the newest state is sent; an Off replaces the waiting item, an On adds to it. The queue holds at most one item per lamp, so a busy moment no longer sends outdated in-between states, the delay stays bounded (lamps × rate-limit) and the 60 s stale-item drop can't hit legitimate commands. Measured on a real busy minute (27 NHC events for 8 lamps): 21 instead of 27 Zigbee commands, longest delay 14 s instead of 25 s.
0.3.6
- Fix: fed straight from an nhc2-input node (
msg.device= NHC2 device object), every lamp was remembered under the same key"[object Object]". With a shared config all lamps shared one memory, so a lamp switched on with only{"Status":"On"}got the brightness and colour temperature of whichever device came through last. The memory is now kept per NHC2 device (Uuid). - Fix: the status line shows the last device of this node, not of any node sharing the config.
License
MIT
