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

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.
@@ -15,11 +15,14 @@
15
15
  inputLabels: ['Input'],
16
16
  button: {
17
17
  onclick: function() {
18
+ var self = this;
18
19
  $.ajax({
19
- url: 'ha-mqtt-button/' + this.id + '/add',
20
+ url: 'ha-mqtt-button/' + this.id + '/toggle',
20
21
  type: 'POST',
21
- success: function() { RED.notify('device:add sent','success'); },
22
- error: function() { RED.notify('Error check NR log','error'); }
22
+ success: function(resp) {
23
+ RED.notify('device:' + resp.action + ' sent', 'success');
24
+ },
25
+ error: function() { RED.notify('Error — check NR log', 'error'); }
23
26
  });
24
27
  }
25
28
  },
@@ -144,137 +147,88 @@
144
147
  <script type="text/html" data-template-name="ha-mqtt-button">
145
148
 
146
149
  <div class="form-row">
147
- <label for="node-input-name">
148
- <i class="fa fa-tag"></i> Name
149
- </label>
150
- <input type="text" id="node-input-name"
151
- placeholder="Optional — defaults to S-10-A on canvas" />
152
- </div>
153
-
154
- <!-- Discovery Mode -->
155
- <div class="form-row">
156
- <label for="node-input-discoveryMode">
157
- <i class="fa fa-toggle-on"></i> Discovery Mode
158
- </label>
159
- <select id="node-input-discoveryMode" style="width:55%">
160
- <option value="enabled">Enabled — discovered and visible in HA</option>
161
- <option value="hidden">Hidden — discovered but hidden in HA dashboard</option>
162
- <option value="disabled">Disabled — not discovered, not in HA</option>
163
- </select>
164
- <div style="margin-left:106px; margin-top:4px; color:#999; font-size:0.85em;">
165
- Use <strong>Disabled</strong> for future fixtures not yet wired.
166
- Use <strong>Hidden</strong> for fixtures installed but not ready for the homeowner.
167
- </div>
150
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
151
+ <input type="text" id="node-input-name" placeholder="Optional — defaults to S-10-A on canvas" />
168
152
  </div>
169
153
 
170
-
171
154
  <hr/>
172
155
 
173
- <!-- ── BUTTON (* Required) ───────────────────────────────── -->
174
156
  <div class="form-row">
175
157
  <label style="width:100%; font-weight:bold; color:#999; font-size:0.85em; text-transform:uppercase; letter-spacing:0.05em;">
176
158
  <i class="fa fa-hand-pointer-o"></i> Button &nbsp;<span style="color:#e74c3c">* Required</span>
177
159
  </label>
178
160
  </div>
179
161
 
162
+ <!-- 1. Cable ID -->
180
163
  <div class="form-row">
181
- <label for="node-input-uid">
182
- <i class="fa fa-tv"></i> Cable ID
183
- </label>
164
+ <label for="node-input-uid"><i class="fa fa-tv"></i> Cable ID</label>
184
165
  <span style="margin-right:4px; font-weight:bold;">S -</span>
185
- <input type="text" id="node-input-uid"
186
- placeholder="10" style="width:70px" />
166
+ <input type="text" id="node-input-uid" placeholder="10" style="width:70px" />
187
167
  &nbsp;
188
168
  <select id="node-input-uidPostfix" style="width:90px">
189
169
  <option value="">(none)</option>
190
- <option value="-A">-A</option>
191
- <option value="-B">-B</option>
192
- <option value="-C">-C</option>
193
- <option value="-D">-D</option>
194
- <option value="-E">-E</option>
195
- <option value="-F">-F</option>
196
- <option value="-G">-G</option>
197
- <option value="-H">-H</option>
198
- <option value="-J">-J</option>
199
- <option value="-K">-K</option>
200
- <option value="-L">-L</option>
201
- <option value="-M">-M</option>
202
- <option value="-N">-N</option>
203
- <option value="-P">-P</option>
204
- <option value="-Q">-Q</option>
205
- <option value="-R">-R</option>
206
- <option value="-S">-S</option>
207
- <option value="-T">-T</option>
208
- <option value="-U">-U</option>
209
- <option value="-V">-V</option>
210
- <option value="-W">-W</option>
211
- <option value="-X">-X</option>
212
- <option value="-Y">-Y</option>
213
- <option value="-Z">-Z</option>
170
+ <option value="-A">-A</option><option value="-B">-B</option><option value="-C">-C</option><option value="-D">-D</option>
171
+ <option value="-E">-E</option><option value="-F">-F</option><option value="-G">-G</option><option value="-H">-H</option>
172
+ <option value="-J">-J</option><option value="-K">-K</option><option value="-L">-L</option><option value="-M">-M</option>
173
+ <option value="-N">-N</option><option value="-P">-P</option><option value="-Q">-Q</option><option value="-R">-R</option>
174
+ <option value="-S">-S</option><option value="-T">-T</option><option value="-U">-U</option><option value="-V">-V</option>
175
+ <option value="-W">-W</option><option value="-X">-X</option><option value="-Y">-Y</option><option value="-Z">-Z</option>
214
176
  </select>
215
- <span style="margin-left:8px; color:#999; font-size:0.85em;">
216
- Plan ID — Button letter
217
- </span>
177
+ <span style="margin-left:8px; color:#999; font-size:0.85em;">Plan ID — Button letter</span>
218
178
  </div>
219
179
 
180
+ <!-- 2. Position -->
220
181
  <div class="form-row">
182
+ <label for="node-input-buttonPosition"><i class="fa fa-hand-pointer-o"></i> Position</label>
183
+ <select id="node-input-buttonPosition" style="width:55%">
184
+ <option value="Top Left">Top Left</option><option value="Top Right">Top Right</option>
185
+ <option value="Bottom Left">Bottom Left</option><option value="Bottom Right">Bottom Right</option>
186
+ <option value="Top">Top</option><option value="Bottom">Bottom</option>
187
+ <option value="Left">Left</option><option value="Right">Right</option>
188
+ <option value="Centre">Centre</option><option value="Single">Single</option>
189
+ </select>
190
+ </div>
191
+
192
+ <!-- 3. Situation -->
221
193
  <div class="form-row">
222
- <label for="node-input-config">
223
- <i class="fa fa-cog"></i> Config
224
- </label>
225
- <input type="text" id="node-input-config" />
194
+ <label for="node-input-situation"><i class="fa fa-compass"></i> Situation</label>
195
+ <select id="node-input-situation" style="width:55%">
196
+ <option value="in">in</option><option value="above">above</option><option value="below">below</option>
197
+ <option value="outside">outside</option><option value="throughout">throughout</option>
198
+ <option value="under">under</option><option value="over">over</option>
199
+ </select>
226
200
  </div>
227
201
 
202
+ <!-- 4. Config -->
228
203
  <div class="form-row">
229
- <label for="node-input-area">
230
- <i class="fa fa-map-marker"></i> Area
231
- </label>
232
- <select id="node-input-area" style="width:55%"></select>
204
+ <label for="node-input-config"><i class="fa fa-cog"></i> Config</label>
205
+ <input type="text" id="node-input-config" />
233
206
  </div>
234
207
 
208
+ <!-- 5. Area -->
235
209
  <div class="form-row">
236
- <label for="node-input-situation">
237
- <i class="fa fa-compass"></i> Situation
238
- </label>
239
- <select id="node-input-situation" style="width:55%">
240
- <option value="in">in</option>
241
- <option value="above">above</option>
242
- <option value="below">below</option>
243
- <option value="outside">outside</option>
244
- <option value="throughout">throughout</option>
245
- <option value="under">under</option>
246
- <option value="over">over</option>
247
- </select>
210
+ <label for="node-input-area"><i class="fa fa-map-marker"></i> Area</label>
211
+ <select id="node-input-area" style="width:55%"></select>
248
212
  </div>
249
213
 
214
+ <!-- 6. Sub-Area -->
250
215
  <div class="form-row">
251
- <label for="node-input-subLocation">
252
- <i class="fa fa-map-marker"></i> Sub-Area
253
- </label>
216
+ <label for="node-input-subLocation"><i class="fa fa-map-marker"></i> Sub-Area</label>
254
217
  <select id="node-input-subLocation" style="width:55%"></select>
255
218
  </div>
256
219
 
220
+ <!-- Discovery Mode -->
257
221
  <div class="form-row">
258
- <label for="node-input-buttonPosition">
259
- <i class="fa fa-hand-pointer-o"></i> Position
260
- </label>
261
- <select id="node-input-buttonPosition" style="width:55%">
262
- <option value="Top Left">Top Left</option>
263
- <option value="Top Right">Top Right</option>
264
- <option value="Bottom Left">Bottom Left</option>
265
- <option value="Bottom Right">Bottom Right</option>
266
- <option value="Top">Top</option>
267
- <option value="Bottom">Bottom</option>
268
- <option value="Left">Left</option>
269
- <option value="Right">Right</option>
270
- <option value="Centre">Centre</option>
271
- <option value="Single">Single</option>
222
+ <label for="node-input-discoveryMode"><i class="fa fa-toggle-on"></i> Discovery Mode</label>
223
+ <select id="node-input-discoveryMode" style="width:55%">
224
+ <option value="enabled">Enabled — discovered and visible in HA</option>
225
+ <option value="hidden">Hidden — discovered but hidden in HA dashboard</option>
226
+ <option value="disabled">Disabled — not discovered, not in HA</option>
272
227
  </select>
273
228
  </div>
274
229
 
275
230
  <hr/>
276
231
 
277
- <!-- ── CONTROLLER ────────────────────────────────────────── -->
278
232
  <div class="form-row">
279
233
  <label style="width:100%; font-weight:bold; color:#999; font-size:0.85em; text-transform:uppercase; letter-spacing:0.05em;">
280
234
  <i class="fa fa-exchange"></i> Controller
@@ -282,27 +236,18 @@
282
236
  </div>
283
237
 
284
238
  <div class="form-row">
285
- <label for="node-input-buttonPayload">
286
- <i class="fa fa-hand-pointer-o"></i> Payload
287
- </label>
288
- <input type="text" id="node-input-buttonPayload"
289
- placeholder="e.g. 10-54" style="width:120px" />
290
- <span style="margin-left:8px; color:#999; font-size:0.85em;">
291
- panelId-GPIOpin
292
- </span>
239
+ <label for="node-input-buttonPayload"><i class="fa fa-hand-pointer-o"></i> Payload</label>
240
+ <input type="text" id="node-input-buttonPayload" placeholder="Required — e.g. 10-54" style="width:120px" />
241
+ <span style="margin-left:8px; color:#999; font-size:0.85em;">panelId-GPIOpin</span>
293
242
  </div>
294
243
 
295
244
  <div class="form-row">
296
- <label for="node-input-subscribeTopic">
297
- <i class="fa fa-exchange"></i> Subscribe topic
298
- </label>
299
- <input type="text" id="node-input-subscribeTopic"
300
- placeholder="Required — e.g. buttons or home/zone/buttons" style="width:55%" />
245
+ <label for="node-input-subscribeTopic"><i class="fa fa-exchange"></i> Subscribe topic</label>
246
+ <input type="text" id="node-input-subscribeTopic" placeholder="Required — e.g. buttons or home/zone/buttons" style="width:55%" />
301
247
  </div>
302
248
 
303
249
  <hr/>
304
250
 
305
- <!-- ── OPTIONS ───────────────────────────────────────────── -->
306
251
  <div class="form-row">
307
252
  <label style="width:100%; font-weight:bold; color:#999; font-size:0.85em; text-transform:uppercase; letter-spacing:0.05em;">
308
253
  <i class="fa fa-sliders"></i> Options
@@ -310,40 +255,27 @@
310
255
  </div>
311
256
 
312
257
  <div class="form-row">
313
- <label for="node-input-haIcon">
314
- <i class="fa fa-image"></i> HA Icon
315
- </label>
316
- <input type="text" id="node-input-haIcon"
317
- placeholder="mdi:gesture-tap-button" style="width:55%" />
258
+ <label for="node-input-haIcon"><i class="fa fa-image"></i> HA Icon</label>
259
+ <input type="text" id="node-input-haIcon" placeholder="mdi:gesture-tap-button" style="width:55%" />
318
260
  </div>
319
261
 
320
262
  <div class="form-row">
321
- <label for="node-input-ledColor">
322
- <i class="fa fa-circle"></i> LED Colour
323
- </label>
263
+ <label for="node-input-ledColor"><i class="fa fa-circle"></i> LED Colour</label>
324
264
  <select id="node-input-ledColor" style="width:120px">
325
- <option value="Blue">Blue</option>
326
- <option value="Red">Red</option>
327
- <option value="Green">Green</option>
328
- <option value="White">White</option>
329
- <option value="Orange">Orange</option>
330
- <option value="None">None</option>
265
+ <option value="Blue">Blue</option><option value="Red">Red</option><option value="Green">Green</option>
266
+ <option value="White">White</option><option value="Orange">Orange</option><option value="None">None</option>
331
267
  </select>
332
268
  </div>
333
269
 
334
270
  <div class="form-row">
335
- <label for="node-input-holdTime">
336
- <i class="fa fa-clock-o"></i> Hold time (s)
337
- </label>
338
- <input type="number" id="node-input-holdTime"
339
- min="0.1" step="0.1" style="width:80px" />
340
- <span style="margin-left:8px; color:#999; font-size:0.85em;">
341
- HA auto-clears binary_sensor after this delay
342
- </span>
271
+ <label for="node-input-holdTime"><i class="fa fa-clock-o"></i> Hold time (s)</label>
272
+ <input type="number" id="node-input-holdTime" min="0.1" step="0.1" style="width:80px" />
273
+ <span style="margin-left:8px; color:#999; font-size:0.85em;">HA auto-clears binary_sensor after this delay</span>
343
274
  </div>
344
275
 
345
276
  </script>
346
277
 
278
+
347
279
  <script type="text/html" data-help-name="ha-mqtt-button">
348
280
  <p>
349
281
  Wall button receiver. Listens for hardware controller MQTT payloads
@@ -30,12 +30,29 @@ module.exports = function (RED) {
30
30
  // Small delay to allow broker connection to stabilise
31
31
  setTimeout(function () {
32
32
  handleDeviceAdd();
33
- }, 2000);
33
+ }, 1500);
34
34
  }
35
35
 
36
- broker.on('connect', function () {
36
+ // Track if already discovered to prevent double-fire
37
+ let _discovered = false;
38
+ function _tryDiscover() {
39
+ if (_discovered) return;
40
+ _discovered = true;
37
41
  autoDiscover();
42
+ }
43
+
44
+ // Auto-discover if broker already connected on deploy
45
+ if (broker.connected) {
46
+ _tryDiscover();
47
+ }
48
+ // Wait for broker connect event
49
+ broker.on('connect', function () {
50
+ _tryDiscover();
38
51
  });
52
+ // Fallback — if connect event already fired before listener registered
53
+ setTimeout(function () {
54
+ _tryDiscover();
55
+ }, 3000);
39
56
 
40
57
 
41
58
 
@@ -96,7 +113,7 @@ module.exports = function (RED) {
96
113
  name: `${S.buttonPosition} button ${S.situation} the ${cfg.zone} ${S.area} ${S.subLocation}`,
97
114
  stat_t: statTopic,
98
115
  off_delay: S.holdTime,
99
- enabled_by_default: cfg.enabledDefault,
116
+ enabled_by_default: (discoveryMode !== 'hidden'),
100
117
  icon: S.haIcon,
101
118
  device: {
102
119
  identifiers: `binary_sensor-${fixtureId}`,
@@ -118,7 +135,7 @@ module.exports = function (RED) {
118
135
  name: `${S.buttonPosition} (UI) ${S.situation} the ${cfg.zone} ${S.area} ${S.subLocation}`,
119
136
  cmd_t: uiBtnCmdTopic,
120
137
  payload_press: 'PRESS',
121
- enabled_by_default: cfg.enabledDefault,
138
+ enabled_by_default: (discoveryMode !== 'hidden'),
122
139
  icon: S.haIcon,
123
140
  device: { identifiers: `binary_sensor-${fixtureId}` },
124
141
  };
@@ -178,17 +195,18 @@ module.exports = function (RED) {
178
195
  }
179
196
 
180
197
 
181
- // ── HTTP endpoints for canvas buttons registered once at module level ──
182
- RED.httpAdmin.post('/ha-mqtt-button/:id/add', RED.auth.needsPermission('ha-mqtt-button.write'), function(req, res) {
198
+ // ── HTTP endpoint for canvas toggle button ───────────────────────────────
199
+ RED.httpAdmin.post('/ha-mqtt-button/:id/toggle', RED.auth.needsPermission('ha-mqtt-button.write'), function(req, res) {
183
200
  const n = RED.nodes.getNode(req.params.id);
184
- if (n) { n.receive({ device: 'add' }); res.sendStatus(200); }
185
- else res.sendStatus(404);
186
- });
187
-
188
- RED.httpAdmin.post('/ha-mqtt-button/:id/remove', RED.auth.needsPermission('ha-mqtt-button.write'), function(req, res) {
189
- const n = RED.nodes.getNode(req.params.id);
190
- if (n) { n.receive({ device: 'remove' }); res.sendStatus(200); }
191
- else res.sendStatus(404);
201
+ if (n) {
202
+ const last = n.context().get('_lastBtn') || 'add';
203
+ const next = last === 'add' ? 'remove' : 'add';
204
+ n.context().set('_lastBtn', next);
205
+ n.receive({ device: next });
206
+ res.json({ action: next });
207
+ } else {
208
+ res.sendStatus(404);
209
+ }
192
210
  });
193
211
 
194
212
  RED.nodes.registerType('ha-mqtt-button', HaMqttButtonNode);