node-red-contrib-velbus-2026 0.8.1

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.
Files changed (56) hide show
  1. package/CHANGELOG_FORUM.md +784 -0
  2. package/LICENSE +21 -0
  3. package/README.md +140 -0
  4. package/examples/velbus-basic-relay-dimmer.json +328 -0
  5. package/index.js +3 -0
  6. package/lib/blind-types-20.js +26 -0
  7. package/lib/blind-types-s.js +46 -0
  8. package/lib/blind-types.js +38 -0
  9. package/lib/dimmer-types-20.js +120 -0
  10. package/lib/dimmer-types.js +34 -0
  11. package/lib/glass-panel-types.js +296 -0
  12. package/lib/pir-types-20.js +51 -0
  13. package/lib/pir-types.js +61 -0
  14. package/lib/relay-types-20.js +43 -0
  15. package/lib/relay-types.js +39 -0
  16. package/lib/sensor-types-20.js +29 -0
  17. package/lib/sensor-types.js +49 -0
  18. package/lib/velbus-utils.js +80 -0
  19. package/nodes/velbus-blind/velbus-blind.html +165 -0
  20. package/nodes/velbus-blind/velbus-blind.js +251 -0
  21. package/nodes/velbus-blind-20/velbus-blind-20.html +179 -0
  22. package/nodes/velbus-blind-20/velbus-blind-20.js +300 -0
  23. package/nodes/velbus-blind-s/velbus-blind-s.html +181 -0
  24. package/nodes/velbus-blind-s/velbus-blind-s.js +282 -0
  25. package/nodes/velbus-bridge/velbus-bridge.html +72 -0
  26. package/nodes/velbus-bridge/velbus-bridge.js +304 -0
  27. package/nodes/velbus-button/velbus-button.html +172 -0
  28. package/nodes/velbus-button/velbus-button.js +117 -0
  29. package/nodes/velbus-clock/velbus-clock.html +198 -0
  30. package/nodes/velbus-clock/velbus-clock.js +273 -0
  31. package/nodes/velbus-dimmer/velbus-dimmer.html +174 -0
  32. package/nodes/velbus-dimmer/velbus-dimmer.js +457 -0
  33. package/nodes/velbus-dimmer-20/velbus-dimmer-20.html +271 -0
  34. package/nodes/velbus-dimmer-20/velbus-dimmer-20.js +602 -0
  35. package/nodes/velbus-glass-panel/velbus-glass-panel.html +337 -0
  36. package/nodes/velbus-glass-panel/velbus-glass-panel.js +492 -0
  37. package/nodes/velbus-meteo/velbus-meteo.html +120 -0
  38. package/nodes/velbus-meteo/velbus-meteo.js +279 -0
  39. package/nodes/velbus-pir/velbus-pir.html +187 -0
  40. package/nodes/velbus-pir/velbus-pir.js +269 -0
  41. package/nodes/velbus-pir-20/velbus-pir-20.html +186 -0
  42. package/nodes/velbus-pir-20/velbus-pir-20.js +352 -0
  43. package/nodes/velbus-relay/velbus-relay.html +215 -0
  44. package/nodes/velbus-relay/velbus-relay.js +404 -0
  45. package/nodes/velbus-relay-20/velbus-relay-20.html +123 -0
  46. package/nodes/velbus-relay-20/velbus-relay-20.js +434 -0
  47. package/nodes/velbus-scan/velbus-scan.html +87 -0
  48. package/nodes/velbus-scan/velbus-scan.js +313 -0
  49. package/nodes/velbus-sensor/velbus-sensor.html +154 -0
  50. package/nodes/velbus-sensor/velbus-sensor.js +259 -0
  51. package/nodes/velbus-sensor-20/velbus-sensor-20.html +158 -0
  52. package/nodes/velbus-sensor-20/velbus-sensor-20.js +314 -0
  53. package/nodes/velbus-thermostat/velbus-thermostat.html +191 -0
  54. package/nodes/velbus-thermostat/velbus-thermostat.js +322 -0
  55. package/package.json +55 -0
  56. package/velbus-2026-repo.bundle +0 -0
@@ -0,0 +1,271 @@
1
+ <script type="text/javascript">
2
+
3
+ // ── Shared address dropdown helper ──────────────────────────────────────────
4
+ function velbusPopulateAddressDropdown(nodeCtx, bridgeSelector, addrSelector, nodeTypes) {
5
+ var addrField = $(addrSelector);
6
+ var currentVal = String(addrField.val() || nodeCtx.address || nodeCtx.moduleAddr || '').replace('0x','').toUpperCase();
7
+
8
+ function buildDropdown(modules) {
9
+ var filtered = nodeTypes
10
+ ? modules.filter(function(m) { return nodeTypes.indexOf(m.suggestedNode) !== -1; })
11
+ : modules;
12
+
13
+ if (filtered.length === 0) {
14
+ addrField.show();
15
+ return;
16
+ }
17
+
18
+ var select = $('<select>', { id: addrSelector.replace('#',''), style: 'width:100%' });
19
+ select.append($('<option>', { value: '', text: '\u2014 select or type address \u2014' }));
20
+
21
+ filtered.forEach(function(m) {
22
+ var addrHex = m.address.replace('0x','').toUpperCase();
23
+ var label = m.address + ' ' + m.module +
24
+ (m.velbusName && m.velbusName !== m.module ? ' "' + m.velbusName + '"' : '') +
25
+ ' (build ' + m.build + ', map v' + m.memoryMapVersion + ')';
26
+ var opt = $('<option>', { value: addrHex, text: label });
27
+ if (addrHex === currentVal) opt.prop('selected', true);
28
+ select.append(opt);
29
+ opt.data('module', m);
30
+ });
31
+
32
+ select.append($('<option>', { value: '__manual__', text: '\u2014 enter address manually \u2014' }));
33
+ addrField.replaceWith(select);
34
+
35
+ select.on('change', function() {
36
+ if ($(this).val() === '__manual__') {
37
+ var input = $('<input>', {
38
+ type: 'text', id: addrSelector.replace('#',''),
39
+ placeholder: 'hex e.g. A0', style: 'width:80px'
40
+ });
41
+ $(this).replaceWith(input);
42
+ return;
43
+ }
44
+ var m = $(this).find(':selected').data('module');
45
+ if (m && m.channels) {
46
+ var cc = $('#node-input-channelCount');
47
+ if (cc.length) cc.val(m.channels);
48
+ }
49
+ });
50
+ }
51
+
52
+ function loadResults() {
53
+ var bridgeId = $(bridgeSelector).val();
54
+ if (!bridgeId) return;
55
+ $.getJSON('velbus/scan-results?bridge=' + bridgeId, function(data) {
56
+ if (data && data.modules && data.modules.length > 0) buildDropdown(data.modules);
57
+ }).fail(function() {});
58
+ }
59
+
60
+ $(bridgeSelector).on('change', loadResults);
61
+ setTimeout(function() { $(bridgeSelector).trigger('change'); }, 150);
62
+ }
63
+
64
+ RED.nodes.registerType('velbus-dimmer-20', {
65
+ category: 'Velbus (outputs)',
66
+ color: '#4A90D9',
67
+ defaults: {
68
+ name: { value: '' },
69
+ bridge: { value: '', type: 'velbus-bridge', required: true },
70
+ moduleAddr: { value: '', required: true },
71
+ startChannel: { value: 1, required: true, validate: RED.validators.number() },
72
+ channelCount: { value: 4, required: true, validate: RED.validators.number() },
73
+ ledMode: { value: 'single' }
74
+ },
75
+ inputs: 1,
76
+ outputs: 2,
77
+ icon: 'font-awesome/fa-sliders',
78
+ label: function() {
79
+ if (this.name) return this.name;
80
+ return 'dimmer-20 0x' + (this.moduleAddr || '??');
81
+ },
82
+ outputLabels: ['state / events', 'warnings'],
83
+ oneditprepare: function() {
84
+ velbusPopulateAddressDropdown(
85
+ this,
86
+ '#node-input-bridge',
87
+ '#node-input-moduleAddr',
88
+ ['velbus-dimmer-20']
89
+ );
90
+ }
91
+ });
92
+ </script>
93
+
94
+ <script type="text/html" data-template-name="velbus-dimmer-20">
95
+ <div class="form-row">
96
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name override</label>
97
+ <input type="text" id="node-input-name"
98
+ placeholder="Leave blank to use module name from VelbusLink">
99
+ </div>
100
+ <div class="form-row">
101
+ <label for="node-input-bridge"><i class="fa fa-server"></i> Bridge</label>
102
+ <input type="text" id="node-input-bridge">
103
+ </div>
104
+ <div class="form-row">
105
+ <label for="node-input-moduleAddr"><i class="fa fa-microchip"></i> Address</label>
106
+ <input type="text" id="node-input-moduleAddr" placeholder="e.g. A5 — run scan first for dropdown" style="width:100%">
107
+ <div class="form-tips" style="margin-top:4px">Run a <b>velbus-scan</b> node first to populate the dropdown with discovered modules.</div>
108
+ </div>
109
+ <div class="form-row">
110
+ <label for="node-input-startChannel"><i class="fa fa-list-ol"></i> Start channel</label>
111
+ <input type="number" id="node-input-startChannel" min="1" max="8" style="width:60px">
112
+ </div>
113
+ <div class="form-row">
114
+ <label for="node-input-channelCount"><i class="fa fa-hashtag"></i> Channel count</label>
115
+ <input type="number" id="node-input-channelCount" min="1" max="8" style="width:60px">
116
+ </div>
117
+ <div class="form-row" id="ledmode-row">
118
+ <label for="node-input-ledMode"><i class="fa fa-adjust"></i> LED grouping mode</label>
119
+ <select id="node-input-ledMode" style="width:auto">
120
+ <option value="single">4 &times; single-colour (independent channels)</option>
121
+ <option value="rgb">1 &times; RGB + 1 &times; single (CH1-3 colour, CH4 independent)</option>
122
+ <option value="rgbw">1 &times; RGBW (all four channels, colour mixing)</option>
123
+ </select>
124
+ <div class="form-tips" style="margin-top:4px">
125
+ <b>VMB4LEDPWM-20 only.</b> Must match the physical wiring.
126
+ Confirm with installer before commissioning &mdash; wrong mode causes silent errors.
127
+ Unsure what's actually set on the module? Send <code>{ "channel": 1, "cmd": "get_device_type" }</code>
128
+ to read it back &mdash; this node will warn you here if the reading disagrees with this setting.
129
+ </div>
130
+ </div>
131
+ </script>
132
+
133
+ <script type="text/javascript">
134
+ // Show ledMode selector only when the configured module is VMB4LEDPWM-20
135
+ // We can't know the type until scan, so show it by default and add a note.
136
+ // A future enhancement could hide it for known non-LEDPWM addresses.
137
+ (function() {
138
+ var orig = RED.nodes.getType('velbus-dimmer-20');
139
+ if (orig && orig.prototype && orig.prototype.oneditprepare) {
140
+ var _prev = orig.prototype.oneditprepare;
141
+ orig.prototype.oneditprepare = function() {
142
+ _prev.call(this);
143
+ // ledMode row always visible — commissioning agent uses it
144
+ };
145
+ }
146
+ })();
147
+ </script>
148
+
149
+ <script type="text/html" data-help-name="velbus-dimmer-20">
150
+ <p style="background:#fff3cd; border-left:4px solid #ffc107; padding:8px 12px; margin-bottom:12px; font-size:0.85em;">
151
+ <strong>&#9888; Testing status:</strong> This node was generated with Claude.ai and is in
152
+ need of extensive field testing before being deployed into a commercial project.
153
+ It is presented "as is" &mdash; any use beyond testing is entirely at your own risk and liability.
154
+ Constructive feedback is welcomed, accompanied by as many examples and debug captures as possible.
155
+ <a href="https://github.com/MDAR/node-red-contrib-velbus-2026/issues" target="_blank">File an issue on GitHub</a>.
156
+ </p>
157
+ <p><strong>V2.0 series only (-20 suffix modules)</strong></p>
158
+ <p>For original series dimmers (VMBDMI, VMBDMI-R, VMB4DC) use the <b>velbus-dimmer</b> node instead.</p>
159
+
160
+ <p>Supports: <b>VMB2DC-20</b> (2ch, 0-10V), <b>VMB8DC-20</b> (8ch, 0-10V),
161
+ <b>VMB4LEDPWM-20</b> (4ch, PWM LED)</p>
162
+
163
+ <p>VMB2DC-20 and VMB8DC-20 output a 0-10V control voltage — they do not carry
164
+ mains load. They drive third-party dimmer packs, actuators, or any 0-10V compatible device.</p>
165
+
166
+ <h3>Address dropdown</h3>
167
+ <p>Run a <b>velbus-scan</b> node first to populate the address dropdown.</p>
168
+
169
+ <h3>Output 1 — State / Events</h3>
170
+ <pre>
171
+ {
172
+ "topic": "dimmer_status",
173
+ "address": "0xA5",
174
+ "module": "GF Lights",
175
+ "outputType": "0-10V",
176
+ "dimCurve": "linear",
177
+ "channel": 1,
178
+ "state": "on",
179
+ "on": true,
180
+ "level": 187,
181
+ "percent": 73.6,
182
+ "inhibited": false,
183
+ "forcedOn": false,
184
+ "forcedOff": false,
185
+ "timestamp": 1719100000000
186
+ }
187
+ </pre>
188
+
189
+ <h3>VMB4LEDPWM-20 output grouping modes</h3>
190
+ <p>The four PWM channels can be grouped in three ways. This must match the physical wiring
191
+ and must be set before sending any colour commands.</p>
192
+ <ul>
193
+ <li><b>4 &times; single-colour</b> &mdash; four independent channels. Use <code>cmd: "set"</code> per channel.
194
+ Suitable for four separate LED circuits of any colour.</li>
195
+ <li><b>1 &times; RGB + 1 &times; single</b> &mdash; CH1=Red, CH2=Green, CH3=Blue form one RGB group; CH4 is independent.
196
+ Use <code>cmd: "rgbw"</code> for colour mixing; <code>cmd: "set"</code> for CH4.</li>
197
+ <li><b>1 &times; RGBW</b> &mdash; all four channels form one RGBW group (CH1=R, CH2=G, CH3=B, CH4=W).
198
+ Use <code>cmd: "rgbw"</code> for all colour control.</li>
199
+ </ul>
200
+ <p>The grouping mode is the emergent result of each channel's <b>Device Type</b> setting
201
+ (settings index 25): channel&nbsp;1 = <code>0x08</code> means RGBW, channel&nbsp;1 = <code>0xF0</code>
202
+ means RGB, any single-lamp type means independent. Use <code>cmd: "get_device_type"</code> below to
203
+ read this back from the module and confirm it matches this config setting &mdash; this node config
204
+ property documents which mode should be active but does <em>not</em> write to the module automatically.
205
+ Writing the mode is a deliberate commissioning-time decision, made by the installer or the VelbusAI
206
+ commissioning agent.</p>
207
+
208
+ <h3>Input — Commands</h3>
209
+ <pre>
210
+ { "channel": 1, "cmd": "set", "percent": 75 }
211
+ { "channel": 1, "cmd": "set", "level": 191, "fadeMode": 0 }
212
+ { "channel": 1, "cmd": "on" }
213
+ { "channel": 1, "cmd": "off" }
214
+ { "channel": 1, "cmd": "restore" }
215
+ { "channel": 1, "cmd": "timer", "duration": 60 }
216
+ { "channel": 1, "cmd": "scene", "scene": 0 }
217
+ { "channel": "all", "cmd": "off" }
218
+ { "channel": 1, "cmd": "forced_on", "duration": -1 }
219
+ { "channel": 1, "cmd": "forced_off", "duration": 3600 }
220
+ { "channel": 1, "cmd": "cancel_forced_on" }
221
+ { "channel": 1, "cmd": "cancel_forced_off" }
222
+ { "channel": 1, "cmd": "inhibit", "duration": 1800 }
223
+ { "channel": 1, "cmd": "cancel_inhibit" }
224
+ { "channel": 1, "cmd": "status" }
225
+ { "channel": 1, "cmd": "get_device_type" }
226
+ { "channel": "all", "cmd": "get_device_type" }
227
+ </pre>
228
+ <p>fadeMode: 0=direct, 1=rate, 2=time. Duration in seconds, -1=permanent.</p>
229
+
230
+ <h3>RGBW command &mdash; VMB4LEDPWM-20 (rgb / rgbw mode only)</h3>
231
+ <pre>
232
+ { "cmd": "rgbw", "r": 254, "g": 127, "b": 0, "w": 0, "fadeMode": 0 }
233
+ { "cmd": "rgbw", "r": 0, "g": 0, "b": 0, "w": 254, "fadeMode": 1 }
234
+ { "cmd": "rgbw", "r": 254, "g": 254, "b": 254, "w": 254, "group": 0xFF }
235
+ </pre>
236
+ <p>R, G, B, W values: 0&ndash;254 raw. <code>group</code>: 0xFF = all groups (default).
237
+ Node will warn and reject the command if <b>LED grouping mode</b> is set to
238
+ <em>4 &times; single-colour</em>.</p>
239
+
240
+ <p>Output 1 also emits <code>ledMode</code> in the <code>module_online</code> payload
241
+ (<code>"single"</code>, <code>"rgb"</code>, or <code>"rgbw"</code>) so downstream flows
242
+ can adapt their command set automatically.</p>
243
+
244
+ <h3>get_device_type &mdash; VMB4LEDPWM-20 grouping mode verification (read-only)</h3>
245
+ <p>Reads the actual Device Type setting from the module via the settings API (0xE7/0xE8) &mdash;
246
+ never writes. Useful to confirm the physical wiring matches this node's configured
247
+ <b>LED grouping mode</b> before commissioning colour control.</p>
248
+ <pre>
249
+ // Output 1
250
+ {
251
+ "topic": "device_type",
252
+ "address": "0x06",
253
+ "module": "Feature Wall",
254
+ "channel": 1,
255
+ "settingIndex": 25,
256
+ "deviceType": "0x08",
257
+ "deviceTypeName": "RGBW control (consumes all 4 channels)",
258
+ "detectedGroupMode": "rgbw"
259
+ }
260
+
261
+ // Output 2 — only if channel 1 reading disagrees with the configured ledMode
262
+ {
263
+ "topic": "ledmode_mismatch",
264
+ "address": "0x06",
265
+ "module": "Feature Wall",
266
+ "configuredLedMode": "single",
267
+ "detectedGroupMode": "rgbw",
268
+ "message": "Node config ledMode (\"single\") does not match Feature Wall (0x06)\u2019s actual Device Type setting (\"rgbw\"). Update the node config to match, or re-check the physical wiring."
269
+ }
270
+ </pre>
271
+ </script>