node-red-contrib-dmx-for-ha 0.2.2 → 0.2.4

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 () {
@@ -5,6 +5,10 @@
5
5
  // ============================================================
6
6
 
7
7
  module.exports = function (RED) {
8
+ // Package version — read once at module load
9
+ const PKG_VERSION = require('path').join(__dirname, '../package.json');
10
+ const _pkgVer = (() => { try { return require(PKG_VERSION).version; } catch(e) { return '?'; } })();
11
+
8
12
 
9
13
  function HaMqttButtonNode(config) {
10
14
  RED.nodes.createNode(this, config);
@@ -130,7 +134,7 @@ module.exports = function (RED) {
130
134
  suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
131
135
  hw_version: `Wall button — ${S.ledColor} LED. Publishes "${S.buttonPayload}" on topic: ${S.subscribeTopic}`,
132
136
  serial_number: `(${fixtureId}) Payload: ${S.buttonPayload}`,
133
- sw_version: 'ha-mqtt-button: 0.1.0',
137
+ sw_version: `ha-mqtt-button: ${_pkgVer}`,
134
138
  manufacturer: 'DeSwaggy — Discord: @deswaggy',
135
139
  },
136
140
  };
@@ -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 () {
@@ -8,6 +8,10 @@
8
8
  // ============================================================
9
9
 
10
10
  module.exports = function (RED) {
11
+ // Package version — read once at module load
12
+ const PKG_VERSION = require('path').join(__dirname, '../package.json');
13
+ const _pkgVer = (() => { try { return require(PKG_VERSION).version; } catch(e) { return '?'; } })();
14
+
11
15
 
12
16
  function HaMqttDmxGroupNode(config) {
13
17
  RED.nodes.createNode(this, config);
@@ -200,7 +204,7 @@ module.exports = function (RED) {
200
204
  }
201
205
 
202
206
  const discovery = {
203
- unique_id: `group(${groupId})`,
207
+ unique_id: groupId,
204
208
  schema: 'json',
205
209
  object_id: objectId,
206
210
  optimistic: false,
@@ -225,7 +229,7 @@ module.exports = function (RED) {
225
229
  model_id: `referenced on plan as: (${groupId}`,
226
230
  suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
227
231
  hw_version: 'Virtual fixture — exists as a DMX Group Node in Node-RED',
228
- sw_version: 'ha-mqtt-dmx-group: 0.1.0',
232
+ sw_version: 'ha-mqtt-dmx-group: ' + _pkgVer,
229
233
  manufacturer: 'DeSwaggy — Discord: @deswaggy',
230
234
  },
231
235
  };
@@ -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 () {
@@ -5,6 +5,10 @@
5
5
  // ============================================================
6
6
 
7
7
  module.exports = function (RED) {
8
+ // Package version — read once at module load
9
+ const PKG_VERSION = require('path').join(__dirname, '../package.json');
10
+ const _pkgVer = (() => { try { return require(PKG_VERSION).version; } catch(e) { return '?'; } })();
11
+
8
12
 
9
13
  // ── Gamma correction table ────────────────────────────────────
10
14
  // Pre-computed CIE 1931 gamma 2.2 lookup — 0..255 → 0..255
@@ -458,7 +462,7 @@ module.exports = function (RED) {
458
462
  ] : [];
459
463
 
460
464
  const discovery = {
461
- unique_id: `${S.deviceType}(${fixtureId})`,
465
+ unique_id: fixtureId,
462
466
  schema: 'json',
463
467
  object_id: objectId,
464
468
  optimistic: false,
@@ -484,7 +488,7 @@ module.exports = function (RED) {
484
488
  suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
485
489
  hw_version: `DMX Controller in ${cfg.zone}. MQTT: ${dmxTopic}`,
486
490
  serial_number: fixtureId,
487
- sw_version: 'ha-mqtt-dmx: 0.1.0',
491
+ sw_version: 'ha-mqtt-dmx: ' + _pkgVer,
488
492
  manufacturer: 'DeSwaggy — Discord: @deswaggy',
489
493
  },
490
494
  };
@@ -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 () {
@@ -5,6 +5,10 @@
5
5
  // ============================================================
6
6
 
7
7
  module.exports = function (RED) {
8
+ // Package version — read once at module load
9
+ const PKG_VERSION = require('path').join(__dirname, '../package.json');
10
+ const _pkgVer = (() => { try { return require(PKG_VERSION).version; } catch(e) { return '?'; } })();
11
+
8
12
 
9
13
  function HaMqttPirNode(config) {
10
14
  RED.nodes.createNode(this, config);
@@ -171,7 +175,7 @@ module.exports = function (RED) {
171
175
  suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
172
176
  hw_version: `${S.pirType} PIR — ${S.cableColor} Cat5 cable. Publishes "${S.pirPayload}" on topic: ${S.subscribeTopic}`,
173
177
  serial_number: `(${fixtureId}) Payload: ${S.pirPayload}`,
174
- sw_version: `ha-mqtt-pir: ${RED.version ? RED.version() : '0.1.0'}`,
178
+ sw_version: `ha-mqtt-pir: ${_pkgVer}`,
175
179
  manufacturer: 'DeSwaggy — Discord: @deswaggy',
176
180
  },
177
181
  };
@@ -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 () {
@@ -8,6 +8,10 @@
8
8
  // ============================================================
9
9
 
10
10
  module.exports = function (RED) {
11
+ // Package version — read once at module load
12
+ const PKG_VERSION = require('path').join(__dirname, '../package.json');
13
+ const _pkgVer = (() => { try { return require(PKG_VERSION).version; } catch(e) { return '?'; } })();
14
+
11
15
 
12
16
  function HaMqttRelayNode(config) {
13
17
  RED.nodes.createNode(this, config);
@@ -228,7 +232,7 @@ module.exports = function (RED) {
228
232
  }
229
233
 
230
234
  const discovery = {
231
- unique_id: `${S.deviceType}(${fixtureId})`,
235
+ unique_id: fixtureId,
232
236
  schema: 'json',
233
237
  object_id: objectId,
234
238
  name: `${S.deviceType} ${S.situation} the ${cfg.zone} ${S.area} ${S.subLocation}`,
@@ -251,7 +255,7 @@ module.exports = function (RED) {
251
255
  suggested_area: discoveryMode !== 'hidden' ? `${cfg.zone} ${S.area} ${S.subLocation}` : undefined,
252
256
  hw_version: `Relay Controller in ${cfg.zone}. Topic: ${relayTopic}`,
253
257
  serial_number: fixtureId,
254
- sw_version: 'ha-mqtt-relay: 0.1.0',
258
+ sw_version: 'ha-mqtt-relay: ' + _pkgVer,
255
259
  manufacturer: 'DeSwaggy — Discord: @deswaggy',
256
260
  },
257
261
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-dmx-for-ha",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
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",