node-red-contrib-dmx-for-ha 0.2.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@ No YAML. No custom MQTT discovery config. No external wiring inside Node-RED.
12
12
 
13
13
  Bridges the gap between DMX lighting hardware and Home Assistant.
14
14
 
15
- Most DMX implementations require either expensive proprietary hardware or complex custom integrations. This package gives you a drop-in Node-RED node that handles everything:
15
+ Most DMX implementations require either expensive proprietary hardware or complex custom integrations. This package gives you drop-in Node-RED nodes that handle everything:
16
16
 
17
17
  - Full Home Assistant MQTT discovery — entities appear automatically in HA
18
18
  - RGBW, RGBWW, RGB, Colour Temperature, Brightness, and On/Off colour modes
@@ -20,6 +20,7 @@ Most DMX implementations require either expensive proprietary hardware or comple
20
20
  - Transitions, effects, and group control
21
21
  - State persistence across reboots
22
22
  - Wall button and PIR motion sensor integration
23
+ - 230V relay switching
23
24
 
24
25
  > **Note:** This is building automation DMX — fixtures, decoders, dimmers, relay switching. Not entertainment industry DMX (no movers, gobos, or fixture profiles). DMX is an open standard and this package works with any DMX controller that accepts MQTT payloads.
25
26
 
@@ -27,14 +28,16 @@ Most DMX implementations require either expensive proprietary hardware or comple
27
28
 
28
29
  ## Nodes included
29
30
 
30
- | Node | Palette label | Purpose |
31
- |---|---|---|
32
- | `ha-mqtt-config` | *(config)* | Shared site config — broker, zone, HA settings |
33
- | `ha-mqtt-dmx` | DMX | Single DMX fixture |
34
- | `ha-mqtt-dmx-group` | DMX Group | Virtual group — fans commands to child nodes |
35
- | `ha-mqtt-relay` | Relay | 230V relay switching (not DMX) |
36
- | `ha-mqtt-button` | Button | Wall button receiver |
37
- | `ha-mqtt-pir` | PIR | Motion sensor receiver |
31
+ | Node | Palette label | Colour | Purpose |
32
+ |---|---|---|---|
33
+ | `ha-mqtt-config` | *(config)* | — | Shared site config — broker, zone, HA settings |
34
+ | `ha-mqtt-dmx` | DMX | Yellow | Single DMX fixture |
35
+ | `ha-mqtt-dmx-group` | DMX Group | Gold | Virtual group — fans commands to child nodes |
36
+ | `ha-mqtt-relay` | Relay | Red | 230V relay switching |
37
+ | `ha-mqtt-button` | Button | Green | Wall button receiver |
38
+ | `ha-mqtt-pir` | PIR | Purple | Motion sensor receiver |
39
+
40
+ Node colours match the cable colour convention used on electrical plans.
38
41
 
39
42
  ---
40
43
 
@@ -49,60 +52,52 @@ Most DMX implementations require either expensive proprietary hardware or comple
49
52
 
50
53
  ## Installation
51
54
 
55
+ ### Via HA Node-RED Add-on config
56
+
57
+ In HA → Settings → Add-ons → Node-RED → Configuration, add to the packages list:
58
+ ```
59
+ node-red-contrib-dmx-for-ha
60
+ ```
61
+ Restart the add-on. Nodes appear in the palette under **DMX for HA**.
62
+
63
+ ### Via Node-RED Palette Manager
64
+
65
+ Node-RED → Menu → Manage Palette → Install → search `node-red-contrib-dmx-for-ha`
66
+
67
+ ### Via npm
68
+
52
69
  ```bash
53
70
  cd ~/.node-red
54
71
  npm install node-red-contrib-dmx-for-ha
55
72
  ```
56
73
 
57
- Restart Node-RED. The nodes appear in the palette under **DMX for HA**.
58
-
59
74
  ---
60
75
 
61
76
  ## ⚠️ Required: Configure Node-RED context storage
62
77
 
63
- This package stores fixture state (brightness, colour, on/off) so it survives Node-RED restarts. This requires a **disk context store** to be configured in Node-RED.
78
+ This package stores fixture state so it survives Node-RED restarts. A **disk context store** must be configured in Node-RED.
64
79
 
65
80
  **Without this, fixture state resets to default every time Node-RED restarts.**
66
81
 
67
- ### What this means
68
-
69
- Node-RED has a context store — a key/value store for saving data. By default it only stores in memory (lost on restart). To persist across restarts you need to add a disk-backed store.
70
-
71
82
  ### How to configure it
72
83
 
73
- 1. Find your Node-RED `settings.js` file. Location depends on your setup:
74
- - Home Assistant add-on: `/config/node-red/settings.js`
75
- - Standard install: `~/.node-red/settings.js`
84
+ Find your Node-RED settings file:
85
+ - HA add-on: `/etc/node-red/config.js`
86
+ - Standard install: `~/.node-red/settings.js`
76
87
 
77
- 2. Find the `contextStorage` section (it may be commented out) and update it:
88
+ Add or update the `contextStorage` section:
78
89
 
79
90
  ```javascript
80
91
  contextStorage: {
81
- default: {
82
- module: "memory"
83
- },
84
- disk: {
85
- module: "localfilesystem"
86
- }
92
+ default: { module: 'memory' },
93
+ disk_values: { module: 'localfilesystem' },
87
94
  },
88
95
  ```
89
96
 
90
- 3. Restart Node-RED.
91
-
92
- ### How to check it is working
93
-
94
- After restarting, open Node-RED → Menu → Context Data. You should see both `memory` and `disk` listed as available stores.
95
-
96
- If you only see `default` or `memory`, the disk store is not configured correctly.
97
-
98
- ### What happens without it
99
-
100
- The nodes will still work — they fall back to memory storage gracefully. But:
101
- - All fixture states reset to their default (usually OFF) every time Node-RED restarts
102
- - Physical relays may not be re-asserted correctly after a reboot
103
- - DMX channels may not restore to their previous values
104
-
105
- For a production deployment, disk context storage is essential.
97
+ Restart Node-RED. Verify in the startup log:
98
+ ```
99
+ Context store : 'disk_values' [module=localfilesystem]
100
+ ```
106
101
 
107
102
  ---
108
103
 
@@ -110,58 +105,65 @@ For a production deployment, disk context storage is essential.
110
105
 
111
106
  ### 1. Create a config node
112
107
 
113
- Every node needs a config node. You create it once per zone.
114
-
115
- 1. Drag a **DMX** node onto the canvas
116
- 2. Open its editor and click the pencil icon next to **Config**
108
+ 1. Drag a **DMX** node onto the canvas and open its editor
109
+ 2. Click **Add new ha-mqtt-config...** next to the Config field
117
110
  3. Fill in:
118
- - **Name** — e.g. `Master Zone`
119
- - **Site ID** — a short slug for your MQTT topics, e.g. `home` (no spaces)
120
- - **Zone** — e.g. `Master` (no spaces or special characters)
111
+ - **Config Label** — a friendly name e.g. "My House". Don't stress — this can be changed at any time.
112
+ - **Site ID** — short slug for MQTT topics e.g. `home` (no spaces)
113
+ - **Zone** — physical zone e.g. `Master`, `BnB`. Used in MQTT topics. This can be updated later if needed.
121
114
  - **Broker** — select your existing MQTT broker config
122
- 4. Click **Add** — the config is saved and shared across all nodes
115
+ 4. Click **Add**
123
116
 
124
117
  ### 2. Add a DMX fixture
125
118
 
126
- 1. Drag a **DMX** node onto the canvas
127
- 2. Select your **Config** node
128
- 3. Choose **Colour Mode** — RGBW, RGB, CCT etc.
129
- 4. Set **Fixture ID** — Prefix (`L`), Plan ID number, optional channel letter
130
- 5. Enter the **DMX channels** for this fixture
131
- 6. Set the **Controller** and **Universe** numbers
132
- 7. Deploy
119
+ Fill in the node editor:
120
+ 1. **Fixture ID** — Prefix (`L`), Plan ID, optional channel letter e.g. `L` `992` `-A`
121
+ 2. **Colour Mode** — RGBW, RGB, CCT etc.
122
+ 3. **Device Type** — e.g. Downlight, Strip light
123
+ 4. **Situation** in, above, throughout etc.
124
+ 5. **Config** select your config node
125
+ 6. **Area** and **Sub-Area** — location on the property
126
+ 7. **DMX Channels** — channel numbers matching your fixture's DMX start address
127
+ 8. **Controller** and **Universe** numbers
128
+ 9. **Discovery Mode** — Enabled, Hidden, or Disabled
133
129
 
134
- Your fixture appears in Home Assistant automatically under **Settings → Devices**.
130
+ Deploy — the fixture appears in HA automatically.
135
131
 
136
- ### 3. Wire the SYSTEM node
132
+ ### 3. Auto-discovery
137
133
 
138
- The SYSTEM node sends `device:add` on startup so nodes register with HA.
139
- Wire an inject node (set to fire on deploy + every X seconds) with:
134
+ Nodes auto-discover when deployed if the MQTT broker is connected. No external inject nodes or SYSTEM node needed.
140
135
 
141
- ```
142
- msg.device = "add"
143
- ```
136
+ ---
137
+
138
+ ## Discovery Modes
139
+
140
+ | Mode | Behaviour |
141
+ |---|---|
142
+ | **Enabled** | Discovered and visible in HA dashboard |
143
+ | **Hidden** | Discovered and usable in automations, not auto-placed in dashboard |
144
+ | **Disabled** | Not discovered — entity does not exist in HA |
144
145
 
145
- Wire it to the input of every node on your canvas.
146
+ Use **Disabled** for fixtures not yet installed or wired.
147
+ Use **Hidden** for fixtures installed but not ready for the homeowner.
146
148
 
147
149
  ---
148
150
 
149
- ## Multi-zone deployments
151
+ ## Canvas button
152
+
153
+ Each node has a clickable button on the right side of the canvas node. It toggles between `device:remove` and `device:add`:
150
154
 
151
- Create one `ha-mqtt-config` node per zone:
155
+ - First press → `device:remove` cleanly removes entity from HA
156
+ - Second press → `device:add` — re-announces to HA
152
157
 
153
- - `Master Zone` Site ID: `home`, Zone: `Master`
154
- - `Guest Wing` → Site ID: `home`, Zone: `GuestWing`
158
+ A toast notification confirms which action fired.
155
159
 
156
- Each DMX, Relay, Button and PIR node picks its zone via the Config dropdown.
157
- MQTT topics are built automatically: `{siteId}/{zone}/dmx/{universe}`.
160
+ Typical workflow: Remove update node settings Deploy node auto-discovers with new settings.
158
161
 
159
162
  ---
160
163
 
161
164
  ## DMX Group Node
162
165
 
163
- The DMX Group node appears as a single light entity in HA. Commands sent to it
164
- are forwarded to all nodes connected to its **Link** output.
166
+ Appears as a single light entity in HA. Commands fan out to all nodes on its **Link** output.
165
167
 
166
168
  ```
167
169
  [DMX Group LG-992]
@@ -171,32 +173,38 @@ are forwarded to all nodes connected to its **Link** output.
171
173
  └──→ [DMX L-992-C]
172
174
  ```
173
175
 
174
- Wire the **Link** output to the input of each child DMX or Relay node.
175
- You can also wire it to another DMX Group node for nested hierarchies.
176
+ Wire **Link** to child DMX or Relay node inputs. Supports nested groups.
177
+
178
+ Naming: `L-992-A`, `L-992-B`, `L-992-C` group naturally under `LG-992`.
176
179
 
177
180
  ---
178
181
 
179
182
  ## Wall buttons
180
183
 
181
- The Button node creates two HA entities per button:
182
-
184
+ Creates two HA entities per button:
183
185
  - `binary_sensor.s_10_a` — automation trigger, auto-clears after hold time
184
- - `button.s_10_a_btn` — dashboard UI mirror (press from HA frontend)
186
+ - `button.s_10_a_btn` — dashboard UI mirror
185
187
 
186
- Build your automations using the `binary_sensor` entity. The `button` entity
187
- lets you test automations from the HA dashboard without physically pressing the wall button.
188
+ Letters `I` and `O` are never used as suffixes — they look too similar to `1` and `0`.
188
189
 
189
190
  ---
190
191
 
191
192
  ## PIR sensors
192
193
 
193
- The PIR node goes **offline** in HA for a configurable warm-up period after
194
- Node-RED starts. HA ignores all motion triggers during this time.
194
+ Goes **offline** in HA during a configurable warm-up period after NR starts — prevents false triggers during reboots. Default warm-up: 120s.
195
+
196
+ ---
197
+
198
+ ## Fixture ID conventions
195
199
 
196
- This prevents spurious motion events during controller reboot from triggering
197
- lights in the middle of the night.
200
+ | Prefix | Meaning | Example |
201
+ |---|---|---|
202
+ | `L` | Light (DMX) | `L-992-A` |
203
+ | `P` | Power (Relay) | `P-51` |
204
+ | `LG` | Light Group | `LG-992` |
205
+ | `S` | Sensor (Button/PIR) | `S-10-A` |
198
206
 
199
- Set the warm-up time to match your PIR hardware's stabilisation period (default 120s).
207
+ Entity IDs in HA are locked to the fixture ID and survive friendly name changes.
200
208
 
201
209
  ---
202
210
 
@@ -205,71 +213,55 @@ Set the warm-up time to match your PIR hardware's stabilisation period (default
205
213
  | Node | Topic |
206
214
  |---|---|
207
215
  | DMX | `{siteId}/{zone}/dmx/{universe}` |
208
- | Relay controller | `{siteId}/{zone}/{controller}/relay/{relayNum}` |
216
+ | Relay | `{siteId}/{zone}/{controller}/relay/{relayNum}` |
209
217
  | HA discovery | `homeassistant/light/{fixtureId}/config` |
210
- | HA state | `homeassistant/light/{fixtureId}/state` |
211
- | HA command | `homeassistant/light/{fixtureId}/cmd` |
218
+
219
+ DMX payload format: `"212255"` = channel 212, value 255 (3 digits each).
212
220
 
213
221
  ---
214
222
 
215
- ## DMX output format
223
+ ## Multi-zone deployments
216
224
 
217
- DMX values are published as a 6-character string:
225
+ Create one `ha-mqtt-config` per zone. Each node selects its zone via the Config dropdown.
218
226
 
219
227
  ```
220
- "212255" channel 212, value 255
221
- "201000" channel 201, value 0
228
+ Master Zone config Zone: Master → topics: home/Master/dmx/1
229
+ Guest Wing configZone: GuestWing → topics: home/GuestWing/dmx/1
222
230
  ```
223
231
 
224
- Three digits for channel, three digits for value. Your DMX controller firmware
225
- must expect this format.
226
-
227
232
  ---
228
233
 
229
- ## Fixture ID conventions
230
-
231
- Fixture IDs match your electrical plan cable IDs:
232
-
233
- | Prefix | Meaning | Example |
234
- |---|---|---|
235
- | `L` | Light (DMX) | `L-992-A` |
236
- | `P` | Power (Relay) | `P-51` |
237
- | `LG` | Light Group (DMX Group) | `LG-992` |
238
- | `S` | Sensor (Button / PIR) | `S-10-A` |
234
+ ## Troubleshooting
239
235
 
240
- The channel letter (`-A`, `-B`, `-C`, `-D`) identifies individual channels
241
- within a multi-channel cable. `L-992-A`, `L-992-B`, `L-992-C` are three
242
- separate fixtures on cable 992, grouped under `LG-992`.
236
+ **Nodes don't appear in palette** Restart Node-RED. Check log for errors.
243
237
 
244
- ---
238
+ **Fixture doesn't appear in HA** — Check broker connection. Check canvas status. Use canvas button to manually trigger `device:add`.
245
239
 
246
- ## Troubleshooting
240
+ **State resets on restart** — Configure disk context storage. Check log for `disk_values` store confirmation.
247
241
 
248
- **Nodes don't appear in palette**
249
- Restart Node-RED. Check the NR log for install errors.
242
+ **Node shows "Broker disconnected"** — Check config node broker settings.
250
243
 
251
- **Fixture doesn't appear in HA**
252
- - Check the MQTT broker connection in the config node
253
- - Confirm HA has the MQTT integration installed and configured
254
- - Send a `device:add` message manually via an inject node
255
- - Check Node-RED logs for error messages
244
+ **Node shows "Disabled not discovered"** — Change Discovery Mode to Enabled or Hidden and deploy.
256
245
 
257
- **State resets on restart**
258
- Configure disk context storage — see the [Required setup](#️-required-configure-node-red-context-storage) section above.
246
+ **PIR stuck offline** — Warm-up timer running. Wait for configured warm-up period.
259
247
 
260
- **PIR stuck offline**
261
- The warm-up timer is running. Wait for the configured warm-up period.
262
- Or send `msg.device = "avty"` with `msg.payload = "online"` to bring it online manually.
248
+ ---
263
249
 
264
- **Relay not switching**
265
- - Check the relay MQTT topic in the NR debug panel
266
- - Confirm your relay controller subscribes to `{siteId}/{zone}/{controller}/relay/{relayNum}`
267
- - Relay payload is `"1"` (ON) or `"0"` (OFF) as a string
250
+ ## Version history
268
251
 
269
- **DMX channels not responding**
270
- - Verify DMX channel numbers match your fixture's start address
271
- - Check the DMX MQTT topic in the NR debug panel
272
- - Confirm your DMX controller subscribes to `{siteId}/{zone}/dmx/{universe}`
252
+ | Version | Changes |
253
+ |---|---|
254
+ | 0.2.2 | Group Node status updates, broker feedback, version in editor panel |
255
+ | 0.2.1 | Hidden mode entity not auto-placed in dashboard |
256
+ | 0.2.0 | Canvas button toggles remove/add, auto-discovery on deploy |
257
+ | 0.1.9 | Correct disk store name, auto-discovery fallback, node ordering |
258
+ | 0.1.8 | Section reordering all nodes, Config picker position |
259
+ | 0.1.7 | Discovery Mode, broker already-connected fix |
260
+ | 0.1.6 | Canvas button fix |
261
+ | 0.1.5 | Context store error suppressed |
262
+ | 0.1.2 | Original subflow colours restored |
263
+ | 0.1.1 | UI fixes, postfix alphabet, Config Label, Discovery Mode |
264
+ | 0.1.0 | Initial release |
273
265
 
274
266
  ---
275
267
 
@@ -51,9 +51,11 @@
51
51
 
52
52
  label: function () {
53
53
  if (this.name) return this.name;
54
- const id = this.uid || '?';
55
- const postfix = this.uidPostfix || '';
56
- return 'S-' + id + postfix;
54
+ const id = `S-${this.uid || '?'}${this.uidPostfix || ''}`;
55
+ const pos = this.buttonPosition || '';
56
+ const sit = this.situation || 'in';
57
+ const area = this.area || '';
58
+ return `${id} · ${pos} button ${sit} ${area}`.trim();
57
59
  },
58
60
 
59
61
  labelStyle: function () {
@@ -273,6 +275,10 @@
273
275
  <span style="margin-left:8px; color:#999; font-size:0.85em;">HA auto-clears binary_sensor after this delay</span>
274
276
  </div>
275
277
 
278
+ <div style="margin-top:16px; padding-top:8px; border-top:1px solid #444; color:#666; font-size:0.8em; text-align:right;">
279
+ node-red-contrib-dmx-for-ha &nbsp;v0.2.2
280
+ </div>
281
+
276
282
  </script>
277
283
 
278
284
 
@@ -49,6 +49,13 @@ module.exports = function (RED) {
49
49
  broker.on('connect', function () {
50
50
  _tryDiscover();
51
51
  });
52
+ broker.on('close', function () {
53
+ setStatus('red', 'ring', 'Broker disconnected');
54
+ });
55
+ broker.on('error', function (err) {
56
+ node.warn('MQTT broker error: ' + (err.message || err));
57
+ setStatus('red', 'dot', 'Broker error — check config');
58
+ });
52
59
  // Fallback — if connect event already fired before listener registered
53
60
  setTimeout(function () {
54
61
  _tryDiscover();
@@ -113,14 +120,14 @@ module.exports = function (RED) {
113
120
  name: `${S.buttonPosition} button ${S.situation} the ${cfg.zone} ${S.area} ${S.subLocation}`,
114
121
  stat_t: statTopic,
115
122
  off_delay: S.holdTime,
116
- enabled_by_default: (discoveryMode !== 'hidden'),
123
+ enabled_by_default: true,
117
124
  icon: S.haIcon,
118
125
  device: {
119
126
  identifiers: `binary_sensor-${fixtureId}`,
120
127
  name: `(${fixtureId}) - Wall Button ${S.situation} the ${cfg.zone} - ${S.area} - ${S.subLocation}`,
121
128
  model: `Wall button located ${S.situation} the ${cfg.zone} - ${S.area}`,
122
129
  model_id: `referenced on plan as: (${fixtureId}`,
123
- suggested_area: `${cfg.zone} ${S.area} ${S.subLocation}`,
130
+ suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
124
131
  hw_version: `Wall button — ${S.ledColor} LED. Publishes "${S.buttonPayload}" on topic: ${S.subscribeTopic}`,
125
132
  serial_number: `(${fixtureId}) Payload: ${S.buttonPayload}`,
126
133
  sw_version: 'ha-mqtt-button: 0.1.0',
@@ -135,7 +142,7 @@ module.exports = function (RED) {
135
142
  name: `${S.buttonPosition} (UI) ${S.situation} the ${cfg.zone} ${S.area} ${S.subLocation}`,
136
143
  cmd_t: uiBtnCmdTopic,
137
144
  payload_press: 'PRESS',
138
- enabled_by_default: (discoveryMode !== 'hidden'),
145
+ enabled_by_default: true,
139
146
  icon: S.haIcon,
140
147
  device: { identifiers: `binary_sensor-${fixtureId}` },
141
148
  };
@@ -53,9 +53,11 @@
53
53
 
54
54
  label: function () {
55
55
  if (this.name) return this.name;
56
- const id = this.uid || '?';
57
- const postfix = this.uidPostfix || '';
58
- return 'LG-' + id + postfix;
56
+ const id = `LG-${this.uid || '?'}${this.uidPostfix || ''}`;
57
+ const type = this.deviceType || '';
58
+ const sit = this.situation || 'in';
59
+ const area = this.area || '';
60
+ return `${id} · ${type} Group ${sit} ${area}`.trim();
59
61
  },
60
62
 
61
63
  labelStyle: function () {
@@ -301,6 +303,10 @@
301
303
  <span style="margin-left:8px; color:#999; font-size:0.85em;">0 = disable loop detection</span>
302
304
  </div>
303
305
 
306
+ <div style="margin-top:16px; padding-top:8px; border-top:1px solid #444; color:#666; font-size:0.8em; text-align:right;">
307
+ node-red-contrib-dmx-for-ha &nbsp;v0.2.2
308
+ </div>
309
+
304
310
  </script>
305
311
 
306
312
 
@@ -52,6 +52,13 @@ module.exports = function (RED) {
52
52
  broker.on('connect', function () {
53
53
  _tryDiscover();
54
54
  });
55
+ broker.on('close', function () {
56
+ setStatus('red', 'ring', 'Broker disconnected');
57
+ });
58
+ broker.on('error', function (err) {
59
+ node.warn('MQTT broker error: ' + (err.message || err));
60
+ setStatus('red', 'dot', 'Broker error — check config');
61
+ });
55
62
  // Fallback — if connect event already fired before listener registered
56
63
  setTimeout(function () {
57
64
  _tryDiscover();
@@ -197,7 +204,7 @@ module.exports = function (RED) {
197
204
  schema: 'json',
198
205
  object_id: objectId,
199
206
  optimistic: false,
200
- enabled_by_default: (discoveryMode !== 'hidden'),
207
+ enabled_by_default: true,
201
208
  icon: S.haIcon,
202
209
  supported_color_modes: [S.colorMode],
203
210
  brightness: true,
@@ -216,7 +223,7 @@ module.exports = function (RED) {
216
223
  name: `(${groupId}) - ${S.deviceType} Group ${S.situation} the ${cfg.zone} - ${S.area} - ${S.subLocation}`,
217
224
  model: `${S.colorMode} ${S.deviceType} Group located ${S.situation} the ${cfg.zone} - ${S.area}`,
218
225
  model_id: `referenced on plan as: (${groupId}`,
219
- suggested_area: `${cfg.zone} ${S.area} ${S.subLocation}`,
226
+ suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
220
227
  hw_version: 'Virtual fixture — exists as a DMX Group Node in Node-RED',
221
228
  sw_version: 'ha-mqtt-dmx-group: 0.1.0',
222
229
  manufacturer: 'DeSwaggy — Discord: @deswaggy',
@@ -233,6 +240,9 @@ module.exports = function (RED) {
233
240
  saveGroupState(payload);
234
241
  pubState({ state: payload.state, color_mode: S.colorMode, brightness: payload.brightness });
235
242
  forwardToChildren(payload, null);
243
+ const _lbl = payload.effect ? `effect:${payload.effect}` : `${payload.state}`;
244
+ setStatus('green', 'dot', `${groupId} → ${_lbl}`);
245
+ setTimeout(() => setStatus('yellow', 'ring', `${groupId} ready`), 3000);
236
246
  }, node.id);
237
247
 
238
248
  setStatus('green', 'ring', `${groupId} discovery sent`);
@@ -277,6 +287,8 @@ module.exports = function (RED) {
277
287
  saveGroupState(msg.payload);
278
288
  pubState({ state: msg.payload.state, color_mode: S.colorMode, brightness: msg.payload.brightness });
279
289
  forwardToChildren(msg.payload, msg.dmx_trace);
290
+ setStatus('green', 'dot', `${groupId} → cascade:${msg.payload.state}`);
291
+ setTimeout(() => setStatus('yellow', 'ring', `${groupId} ready`), 3000);
280
292
  }
281
293
  } else {
282
294
  const devReq = typeof msg.device === 'string'
@@ -293,6 +305,8 @@ module.exports = function (RED) {
293
305
  saveGroupState(msg.payload);
294
306
  pubState({ state: msg.payload.state, color_mode: S.colorMode, brightness: msg.payload.brightness });
295
307
  forwardToChildren(msg.payload, null);
308
+ setStatus('green', 'dot', `${groupId} → ${msg.payload.state}`);
309
+ setTimeout(() => setStatus('yellow', 'ring', `${groupId} ready`), 3000);
296
310
  } else {
297
311
  node.warn(`${groupId} — unrecognised message received and dropped. See node documentation.`);
298
312
  }
@@ -175,10 +175,11 @@
175
175
 
176
176
  label: function () {
177
177
  if (this.name) return this.name;
178
- const prefix = this.uidPrefix || 'L';
179
- const id = this.uid || '?';
180
- const postfix = this.uidPostfix || '';
181
- return prefix + '-' + id + postfix;
178
+ const id = `${this.uidPrefix || 'L'}-${this.uid || '?'}${this.uidPostfix || ''}`;
179
+ const type = this.deviceType || '';
180
+ const sit = this.situation || 'in';
181
+ const area = this.area || '';
182
+ return `${id} · ${type} ${sit} ${area}`.trim();
182
183
  },
183
184
 
184
185
  labelStyle: function () {
@@ -538,6 +539,10 @@
538
539
  <span style="margin-left:8px; color:#999; font-size:0.85em;">Default 31</span>
539
540
  </div>
540
541
 
542
+ <div style="margin-top:16px; padding-top:8px; border-top:1px solid #444; color:#666; font-size:0.8em; text-align:right;">
543
+ node-red-contrib-dmx-for-ha &nbsp;v0.2.2
544
+ </div>
545
+
541
546
  </script>
542
547
 
543
548
  </script>
@@ -59,6 +59,13 @@ module.exports = function (RED) {
59
59
  broker.on('connect', function () {
60
60
  _tryDiscover();
61
61
  });
62
+ broker.on('close', function () {
63
+ setStatus('red', 'ring', 'Broker disconnected');
64
+ });
65
+ broker.on('error', function (err) {
66
+ node.warn('MQTT broker error: ' + (err.message || err));
67
+ setStatus('red', 'dot', 'Broker error — check config');
68
+ });
62
69
  // Fallback — if connect event already fired before listener registered
63
70
  setTimeout(function () {
64
71
  _tryDiscover();
@@ -455,7 +462,7 @@ module.exports = function (RED) {
455
462
  schema: 'json',
456
463
  object_id: objectId,
457
464
  optimistic: false,
458
- enabled_by_default: (discoveryMode !== 'hidden'),
465
+ enabled_by_default: true,
459
466
  icon: S.haIcon,
460
467
  supported_color_modes: [S.colorMode],
461
468
  brightness: true,
@@ -474,7 +481,7 @@ module.exports = function (RED) {
474
481
  name: `(${fixtureId}) - ${S.deviceType} ${S.situation} the ${cfg.zone} - ${S.area} - ${S.subLocation}`,
475
482
  model: `${S.colorMode} ${S.deviceType} located ${S.situation} the ${cfg.zone} - ${S.area}`,
476
483
  model_id: `referenced on plan as: (${fixtureId}`,
477
- suggested_area: `${cfg.zone} ${S.area} ${S.subLocation}`,
484
+ suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
478
485
  hw_version: `DMX Controller in ${cfg.zone}. MQTT: ${dmxTopic}`,
479
486
  serial_number: fixtureId,
480
487
  sw_version: 'ha-mqtt-dmx: 0.1.0',
@@ -52,9 +52,11 @@
52
52
 
53
53
  label: function () {
54
54
  if (this.name) return this.name;
55
- const id = this.uid || '?';
56
- const postfix = this.uidPostfix || '';
57
- return 'S-' + id + postfix;
55
+ const id = `S-${this.uid || '?'}${this.uidPostfix || ''}`;
56
+ const type = this.pirType || 'PIR';
57
+ const sit = this.situation || 'in';
58
+ const area = this.area || '';
59
+ return `${id} · ${type} PIR ${sit} ${area}`.trim();
58
60
  },
59
61
 
60
62
  labelStyle: function () {
@@ -278,6 +280,10 @@
278
280
  <span style="margin-left:8px; color:#999; font-size:0.85em;">Offline delay after boot before PIR activates</span>
279
281
  </div>
280
282
 
283
+ <div style="margin-top:16px; padding-top:8px; border-top:1px solid #444; color:#666; font-size:0.8em; text-align:right;">
284
+ node-red-contrib-dmx-for-ha &nbsp;v0.2.2
285
+ </div>
286
+
281
287
  </script>
282
288
 
283
289
 
@@ -49,6 +49,13 @@ module.exports = function (RED) {
49
49
  broker.on('connect', function () {
50
50
  _tryDiscover();
51
51
  });
52
+ broker.on('close', function () {
53
+ setStatus('red', 'ring', 'Broker disconnected');
54
+ });
55
+ broker.on('error', function (err) {
56
+ node.warn('MQTT broker error: ' + (err.message || err));
57
+ setStatus('red', 'dot', 'Broker error — check config');
58
+ });
52
59
  // Fallback — if connect event already fired before listener registered
53
60
  setTimeout(function () {
54
61
  _tryDiscover();
@@ -154,14 +161,14 @@ module.exports = function (RED) {
154
161
  payload_not_available: 'offline',
155
162
  off_delay: S.holdTime,
156
163
  device_class: 'motion',
157
- enabled_by_default: (discoveryMode !== 'hidden'),
164
+ enabled_by_default: true,
158
165
  icon: S.haIcon,
159
166
  device: {
160
167
  identifiers: `binary_sensor-${fixtureId}`,
161
168
  name: `(${fixtureId}) - ${S.pirType} PIR ${S.situation} the ${cfg.zone} - ${S.area} - ${S.subLocation}`,
162
169
  model: `${S.pirType} PIR located ${S.situation} the ${cfg.zone} - ${S.area}`,
163
170
  model_id: `referenced on plan as: (${fixtureId}`,
164
- suggested_area: `${cfg.zone} ${S.area} ${S.subLocation}`,
171
+ suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
165
172
  hw_version: `${S.pirType} PIR — ${S.cableColor} Cat5 cable. Publishes "${S.pirPayload}" on topic: ${S.subscribeTopic}`,
166
173
  serial_number: `(${fixtureId}) Payload: ${S.pirPayload}`,
167
174
  sw_version: `ha-mqtt-pir: ${RED.version ? RED.version() : '0.1.0'}`,
@@ -53,10 +53,11 @@
53
53
 
54
54
  label: function () {
55
55
  if (this.name) return this.name;
56
- const prefix = this.uidPrefix || 'P';
57
- const id = this.uid || '?';
58
- const postfix = this.uidPostfix || '';
59
- return prefix + '-' + id + postfix;
56
+ const id = `${this.uidPrefix || 'P'}-${this.uid || '?'}${this.uidPostfix || ''}`;
57
+ const type = this.deviceType || '';
58
+ const sit = this.situation || 'in';
59
+ const area = this.area || '';
60
+ return `${id} · ${type} ${sit} ${area}`.trim();
60
61
  },
61
62
 
62
63
  labelStyle: function () {
@@ -275,6 +276,10 @@
275
276
  <label for="node-input-showEffects" style="width:auto">Show effects in HA</label>
276
277
  </div>
277
278
 
279
+ <div style="margin-top:16px; padding-top:8px; border-top:1px solid #444; color:#666; font-size:0.8em; text-align:right;">
280
+ node-red-contrib-dmx-for-ha &nbsp;v0.2.2
281
+ </div>
282
+
278
283
  </script>
279
284
 
280
285
 
@@ -52,6 +52,13 @@ module.exports = function (RED) {
52
52
  broker.on('connect', function () {
53
53
  _tryDiscover();
54
54
  });
55
+ broker.on('close', function () {
56
+ setStatus('red', 'ring', 'Broker disconnected');
57
+ });
58
+ broker.on('error', function (err) {
59
+ node.warn('MQTT broker error: ' + (err.message || err));
60
+ setStatus('red', 'dot', 'Broker error — check config');
61
+ });
55
62
  // Fallback — if connect event already fired before listener registered
56
63
  setTimeout(function () {
57
64
  _tryDiscover();
@@ -228,7 +235,7 @@ module.exports = function (RED) {
228
235
  cmd_t: cmdTopic,
229
236
  stat_t: statTopic,
230
237
  optimistic: false,
231
- enabled_by_default: (discoveryMode !== 'hidden'),
238
+ enabled_by_default: true,
232
239
  icon: S.haIcon,
233
240
  supported_color_modes: ['onoff'],
234
241
  brightness: false,
@@ -241,7 +248,7 @@ module.exports = function (RED) {
241
248
  name: `(${fixtureId}) - ${S.deviceType} ${S.situation} the ${cfg.zone} - ${S.area} - ${S.subLocation}`,
242
249
  model: `${S.deviceType} located ${S.situation} the ${cfg.zone} - ${S.area}`,
243
250
  model_id: `referenced on plan as: (${fixtureId}`,
244
- suggested_area: `${cfg.zone} ${S.area} ${S.subLocation}`,
251
+ suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
245
252
  hw_version: `Relay Controller in ${cfg.zone}. Topic: ${relayTopic}`,
246
253
  serial_number: fixtureId,
247
254
  sw_version: 'ha-mqtt-relay: 0.1.0',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-dmx-for-ha",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "DMX lighting control for Home Assistant via Node-RED and MQTT. Place a node, fill in the settings, deploy. Full HA device registry integration with RGBW/RGBWW/CCT/brightness colour modes, transitions, effects, and group control.",
5
5
  "keywords": [
6
6
  "node-red",