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,120 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * dimmer-types-20.js
5
+ * Registry of V2 (-20 series) Velbus dimmer/output module types.
6
+ * Original series modules are in dimmer-types.js.
7
+ *
8
+ * ALL THREE MODULES SHARE IDENTICAL PACKET STRUCTURE.
9
+ * The only behavioural difference is the dim curve:
10
+ * linear — VMB2DC-20, VMB8DC-20 (0-10V DC output)
11
+ * exponential — VMB4LEDPWM-20 (PWM LED output, perceptual curve)
12
+ *
13
+ * WHAT THESE MODULES OUTPUT:
14
+ * VMB2DC-20 / VMB8DC-20: 0-10V DC control voltage per channel.
15
+ * No mains load. Drives dimmer packs (Finder, Mode Lighting etc.),
16
+ * linear actuators, variable valves, any 0-10V compatible input.
17
+ * VMB4LEDPWM-20: Direct PWM per channel for LED strips/drivers.
18
+ *
19
+ * VMB4LEDPWM-20 OUTPUT GROUPING MODES:
20
+ * The four PWM channels can be grouped in three ways. This is a
21
+ * hardware/memory configuration decision made at commissioning time.
22
+ * The mode must match the physical wiring.
23
+ *
24
+ * 'single' — 4 independent single-colour channels (default)
25
+ * CH1, CH2, CH3, CH4 each drive separate LED circuits.
26
+ * Use cmd: 'set' per channel.
27
+ *
28
+ * 'rgb' — CH1=Red, CH2=Green, CH3=Blue form one RGB group.
29
+ * CH4 remains independent.
30
+ * Use cmd: 'rgbw' for the group (W byte ignored).
31
+ * Use cmd: 'set' for CH4.
32
+ *
33
+ * 'rgbw' — All four channels form one RGBW group.
34
+ * CH1=Red, CH2=Green, CH3=Blue, CH4=White.
35
+ * Use cmd: 'rgbw' for all colour control.
36
+ *
37
+ * RESOLVED (protocol PDF ed.2, 01/07/2025): there is no single "mode byte".
38
+ * The mode is the emergent result of each channel's per-channel "Device Type"
39
+ * setting (settings index 25). Channel 1 = 0x08 (RGBW control) means the
40
+ * module is in 'rgbw' mode; channel 1 = 0xF0 (RGB control) means 'rgb' mode;
41
+ * any single-lamp type (typically 0x06, LED module) on every channel means
42
+ * 'single' mode. See DEVICE_TYPE_NAMES below for the full value table.
43
+ *
44
+ * Read via the settings API: 0xE7 [channel, 0x00, 25] request →
45
+ * 0xE8 [channel, 25, device_type] reply. This node's 'get_device_type'
46
+ * input command performs this read and reports the detected mode on
47
+ * output 1, warning on output 2 if it disagrees with the configured
48
+ * ledMode. Direct memory addresses also exist (0x066C/0x06D0/0x0734/0x0798,
49
+ * 0x64-byte stride per channel) but are for VLP-level analysis only —
50
+ * this node always uses the settings API, never a raw memory write.
51
+ *
52
+ * This node exposes the mode as a config property so the correct command
53
+ * set can be applied and documented. It does NOT write the mode to the
54
+ * module — that remains a deliberate commissioning-time decision, made by
55
+ * the installer or the VelbusAI commissioning agent, never automatically.
56
+ *
57
+ * CRITICAL: Sending individual channel dim commands to a module
58
+ * configured as 'rgb' or 'rgbw' bypasses colour coordination.
59
+ * The result will look wrong. Always confirm mode before commissioning.
60
+ *
61
+ * KEY PROTOCOL NOTES:
62
+ * Status packet: 0xEE (NOT 0xFB or 0xB8)
63
+ * Dim value: 0-254 raw (NOT 0-100%)
64
+ * Dim value pkt: 0xA5 (up to 4 channels packed per packet)
65
+ * Set dim: 0x07 + channel + value(0-254) + fade_mode
66
+ * fade_mode: 0=direct, 1=rate, 2=time
67
+ * RGBW: 0x1E + group + R + G + B + W + fade_mode
68
+ * Scenes: 16 scenes (S0-S15), command 0x1D
69
+ * 0xFF response: 8 bytes (properties byte: CAN FD, terminator, HW ver)
70
+ * Channel 0xFF: broadcast to all channels
71
+ *
72
+ * NOTE: VMB2DC-20, VMB8DC-20 and VMB4LEDPWM-20 PDFs contain DALI-referencing
73
+ * commands. These are documentation bleed-over from the shared VMBDALI
74
+ * codebase and are INVALID for all three modules. Ignore all DALI references.
75
+ *
76
+ * Confirmed on Toulouse site (26/06/2026):
77
+ * VMB8DC-20 type 0x4B — present on site, channels 1/4/8 verified live.
78
+ *
79
+ * Fields:
80
+ * name - Human-readable module name
81
+ * channels - Number of output channels
82
+ * outputType - '0-10V' or 'PWM'
83
+ * dimCurve - 'linear' or 'exponential'
84
+ * ledMode - VMB4LEDPWM-20 only: 'single'|'rgb'|'rgbw'|null
85
+ * null = not applicable (VMB2DC-20, VMB8DC-20)
86
+ * minMemoryMapVersion - minimum map version required (null = unverified)
87
+ */
88
+ const DIMMER_TYPES_20 = {
89
+ 0x24: { name: 'VMB2DC-20', channels: 2, outputType: '0-10V', dimCurve: 'linear', ledMode: null, minMemoryMapVersion: null },
90
+ 0x4B: { name: 'VMB8DC-20', channels: 8, outputType: '0-10V', dimCurve: 'linear', ledMode: null, minMemoryMapVersion: null },
91
+ 0x06: { name: 'VMB4LEDPWM-20', channels: 4, outputType: 'PWM', dimCurve: 'exponential', ledMode: 'single', minMemoryMapVersion: null },
92
+ };
93
+
94
+ const DIMMER_TYPE_IDS_20 = new Set(Object.keys(DIMMER_TYPES_20).map(Number));
95
+
96
+ // Type bytes for which ledMode configuration is applicable
97
+ const LEDPWM_TYPE_IDS = new Set([0x06]);
98
+
99
+ /**
100
+ * VMB4LEDPWM-20 per-channel "Device Type" setting (settings index 25).
101
+ * Read via 0xE7/0xE8 settings API — see doc comment above.
102
+ * Source: VMB4LEDPWM-20 protocol PDF ed.2 (01/07/2025).
103
+ */
104
+ const DEVICE_TYPE_NAMES = {
105
+ 0x00: 'Fluorescent lamps',
106
+ 0x01: 'Emergency lamps',
107
+ 0x02: 'Discharge lamps',
108
+ 0x03: 'Low voltage lamps',
109
+ 0x04: 'Dimmer for incandescent lamps',
110
+ 0x05: 'Conversion to DC voltage (1-10V)',
111
+ 0x06: 'LED module',
112
+ 0x07: 'Switching device (relay)',
113
+ 0x08: 'RGBW control (consumes all 4 channels)',
114
+ 0x09: 'Sequencer',
115
+ 0xF0: 'RGB control (consumes channels 1-3, channel 4 independent)',
116
+ 0xFE: 'Device present but type unknown',
117
+ 0xFF: 'Device not present (factory default)'
118
+ };
119
+
120
+ module.exports = { DIMMER_TYPES_20, DIMMER_TYPE_IDS_20, LEDPWM_TYPE_IDS, DEVICE_TYPE_NAMES };
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * dimmer-types.js
5
+ * Registry of ORIGINAL SERIES Velbus dimmer module types.
6
+ * V2 (-20) modules are in dimmer-types-20.js.
7
+ *
8
+ * KEY PROTOCOL NOTES:
9
+ * Status packet: 0xB8 (NOT 0xEE or 0xFB)
10
+ * Dim value: 0-100% (NOT 0-254)
11
+ * Dimspeed: 16-bit seconds
12
+ * 0xFF response: 7 bytes (no properties byte — original series)
13
+ * Channel: 0x01 fixed for VMBDMI/VMBDMI-R, bitmask for VMB4DC
14
+ *
15
+ * STATUS BYTE DIFFERENCES:
16
+ * VMBDMI / VMBDMI-R: mode(2b) + error(2b) + loadType(1b) + tempBand(3b)
17
+ * VMB4DC: mode(2b) only
18
+ *
19
+ * Fields:
20
+ * name - Human-readable module name
21
+ * channels - Number of dimmer channels
22
+ * hasThermal - true if thermal status in 0xB8 byte3 (VMBDMI/R only)
23
+ * channelModel - 'single' (fixed 0x01) or 'bitmask'
24
+ * minMemoryMapVersion - minimum map version required (null = unverified)
25
+ */
26
+ const DIMMER_TYPES = {
27
+ 0x15: { name: 'VMBDMI', channels: 1, hasThermal: true, channelModel: 'single', minMemoryMapVersion: null },
28
+ 0x2F: { name: 'VMBDMI-R', channels: 1, hasThermal: true, channelModel: 'single', minMemoryMapVersion: null },
29
+ 0x12: { name: 'VMB4DC', channels: 4, hasThermal: false, channelModel: 'bitmask', minMemoryMapVersion: null },
30
+ };
31
+
32
+ const DIMMER_TYPE_IDS = new Set(Object.keys(DIMMER_TYPES).map(Number));
33
+
34
+ module.exports = { DIMMER_TYPES, DIMMER_TYPE_IDS };
@@ -0,0 +1,296 @@
1
+ 'use strict';
2
+
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ // Glass panel type registry — full VMBEL / VMBGP family
5
+ // Single velbus-glass-panel node handles all entries in this table.
6
+ //
7
+ // hasOled: module has OLED display — DATABYTE8 of 0xED is page number,
8
+ // 0xAC memo text and 0xBE counter status are also emitted
9
+ // hasPir: module has PIR sensor — 0xA9 raw light value emitted
10
+ // hasOc: module has open collector output (channel 18 in protocol)
11
+ // Confirmed from PDF: VMBEL1/2/4 and -20 variants, VMBELO/-20,
12
+ // VMBGPO/-20, VMBELPIR/-20, VMBEL2PIR/-20, VMBGP4PIR/-20.
13
+ // VMBGP1/2/4 original and -2/-20 series: OC NOT confirmed from
14
+ // PDF — VMBGP1-2/2-2/4-2 protocol doc has no OC commands.
15
+ // TO VERIFY ON HARDWARE (UK, post July 4th 2026).
16
+ // hasOc: null = unverified, treat as false until confirmed.
17
+ // pirChannels: channel meanings for PIR variants (1-indexed)
18
+ // keys: channel number, value: semantic label
19
+ // channels: number of physical button channels
20
+ // minMapVer: minimum memory map version required (null = unverified/unknown)
21
+ // series: 'original' | 'v2'
22
+ // ─────────────────────────────────────────────────────────────────────────────
23
+
24
+ const GLASS_PANEL_TYPES = {
25
+
26
+ // ── Original series ───────────────────────────────────────────────────────
27
+
28
+ 0x1E: {
29
+ name: 'VMBGP1',
30
+ channels: 1,
31
+ hasOled: false,
32
+ hasPir: false,
33
+ hasOc: null, // TO VERIFY — no PDF read for original VMBGP1
34
+ minMapVer: null,
35
+ series: 'original',
36
+ },
37
+ 0x1F: {
38
+ name: 'VMBGP2',
39
+ channels: 2,
40
+ hasOled: false,
41
+ hasPir: false,
42
+ hasOc: null, // TO VERIFY — no PDF read for original VMBGP2
43
+ minMapVer: null,
44
+ series: 'original',
45
+ },
46
+ 0x20: {
47
+ name: 'VMBGP4',
48
+ channels: 4,
49
+ hasOled: false,
50
+ hasPir: false,
51
+ hasOc: null, // TO VERIFY — no PDF read, was incorrectly labelled VMB6PB-20
52
+ minMapVer: null,
53
+ series: 'original',
54
+ },
55
+ 0x21: {
56
+ name: 'VMBGPO',
57
+ channels: 32,
58
+ hasOled: true,
59
+ hasPir: false,
60
+ hasOc: true, // OLED/output variant — OC confirmed by protocol family
61
+ minMapVer: 2,
62
+ series: 'original',
63
+ },
64
+ 0x2D: {
65
+ name: 'VMBGP4PIR',
66
+ channels: 8,
67
+ hasOled: false,
68
+ hasPir: true,
69
+ hasOc: null, // TO VERIFY on hardware
70
+ pirChannels: {
71
+ 1: 'button1', 2: 'button2', 3: 'button3', 4: 'button4',
72
+ 5: 'virtual', 6: 'dark', 7: 'light', 8: 'motion',
73
+ },
74
+ minMapVer: null,
75
+ series: 'original',
76
+ },
77
+ 0x34: {
78
+ name: 'VMBEL1',
79
+ channels: 1,
80
+ hasOled: false,
81
+ hasPir: false,
82
+ hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024
83
+ minMapVer: 2,
84
+ series: 'original',
85
+ },
86
+ 0x35: {
87
+ name: 'VMBEL2',
88
+ channels: 2,
89
+ hasOled: false,
90
+ hasPir: false,
91
+ hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024
92
+ minMapVer: 2,
93
+ series: 'original',
94
+ },
95
+ 0x36: {
96
+ name: 'VMBEL4',
97
+ channels: 4,
98
+ hasOled: false,
99
+ hasPir: false,
100
+ hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024
101
+ minMapVer: 2,
102
+ series: 'original',
103
+ },
104
+ 0x37: {
105
+ name: 'VMBELO',
106
+ channels: 4,
107
+ hasOled: true,
108
+ hasPir: false,
109
+ hasOc: true, // OLED/output variant — OC confirmed by protocol family
110
+ minMapVer: 4,
111
+ series: 'original',
112
+ },
113
+ 0x38: {
114
+ name: 'VMBELPIR',
115
+ channels: 8,
116
+ hasOled: false,
117
+ hasPir: true,
118
+ hasOc: true, // PIR variant of VMBEL family — OC present
119
+ pirChannels: {
120
+ 1: 'button1', 2: 'button2', 3: 'button3', 4: 'button4',
121
+ 5: 'virtual', 6: 'dark', 7: 'light', 8: 'motion',
122
+ },
123
+ minMapVer: 2,
124
+ series: 'original',
125
+ },
126
+ 0x3A: {
127
+ name: 'VMBGP1-2',
128
+ channels: 1,
129
+ hasOled: false,
130
+ hasPir: false,
131
+ hasOc: false, // VMBGP1-2/2-2/4-2 PDF has no OC commands — TO VERIFY on hardware
132
+ minMapVer: null,
133
+ series: 'original',
134
+ },
135
+ 0x3B: {
136
+ name: 'VMBGP2-2',
137
+ channels: 2,
138
+ hasOled: false,
139
+ hasPir: false,
140
+ hasOc: false, // VMBGP1-2/2-2/4-2 PDF has no OC commands — TO VERIFY on hardware
141
+ minMapVer: null,
142
+ series: 'original',
143
+ },
144
+ 0x3C: {
145
+ name: 'VMBGP4-2',
146
+ channels: 4,
147
+ hasOled: false,
148
+ hasPir: false,
149
+ hasOc: false, // VMBGP1-2/2-2/4-2 PDF has no OC commands — TO VERIFY on hardware
150
+ minMapVer: null,
151
+ series: 'original',
152
+ },
153
+ 0x3D: {
154
+ name: 'VMBGPOD-2',
155
+ channels: 4,
156
+ hasOled: true,
157
+ hasPir: false,
158
+ hasOc: null, // OLED variant of GP series — TO VERIFY on hardware
159
+ minMapVer: 2,
160
+ series: 'original',
161
+ },
162
+ 0x47: {
163
+ name: 'VMBEL2PIR',
164
+ channels: 6,
165
+ hasOled: false,
166
+ hasPir: true,
167
+ hasOc: true, // PIR variant of VMBEL family — OC present
168
+ pirChannels: {
169
+ 1: 'button1', 2: 'button2',
170
+ 3: 'virtual', 4: 'dark', 5: 'light', 6: 'motion',
171
+ },
172
+ minMapVer: null,
173
+ series: 'original',
174
+ },
175
+
176
+ // ── V2 series ─────────────────────────────────────────────────────────────
177
+
178
+ 0x4F: {
179
+ name: 'VMBEL1-20',
180
+ channels: 1,
181
+ hasOled: false,
182
+ hasPir: false,
183
+ hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024 covers -20 variants
184
+ minMapVer: null,
185
+ series: 'v2',
186
+ },
187
+ 0x50: {
188
+ name: 'VMBEL2-20',
189
+ channels: 2,
190
+ hasOled: false,
191
+ hasPir: false,
192
+ hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024 covers -20 variants
193
+ minMapVer: null,
194
+ series: 'v2',
195
+ },
196
+ 0x51: {
197
+ name: 'VMBEL4-20',
198
+ channels: 4,
199
+ hasOled: false,
200
+ hasPir: false,
201
+ hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024 covers -20 variants
202
+ minMapVer: null,
203
+ series: 'v2',
204
+ },
205
+ 0x52: {
206
+ name: 'VMBELO-20',
207
+ channels: 4,
208
+ hasOled: true,
209
+ hasPir: false,
210
+ hasOc: true, // OLED/output variant — OC confirmed by protocol family
211
+ minMapVer: null,
212
+ series: 'v2',
213
+ },
214
+ 0x53: {
215
+ name: 'VMBELPIR-20', // Official list shows VMBBEL1PIR-20 — TO VERIFY via VelbusLink
216
+ channels: 8,
217
+ hasOled: false,
218
+ hasPir: true,
219
+ hasOc: true, // PIR variant of VMBEL family — OC present
220
+ pirChannels: {
221
+ 1: 'button1', 2: 'button2', 3: 'button3', 4: 'button4',
222
+ 5: 'virtual', 6: 'dark', 7: 'light', 8: 'motion',
223
+ },
224
+ minMapVer: null,
225
+ series: 'v2',
226
+ },
227
+ 0x54: {
228
+ name: 'VMBGP1-20',
229
+ channels: 1,
230
+ hasOled: false,
231
+ hasPir: false,
232
+ hasOc: null, // TO VERIFY — GP series OC status unconfirmed
233
+ minMapVer: null,
234
+ series: 'v2',
235
+ },
236
+ 0x55: {
237
+ name: 'VMBGP2-20',
238
+ channels: 2,
239
+ hasOled: false,
240
+ hasPir: false,
241
+ hasOc: null, // TO VERIFY — GP series OC status unconfirmed
242
+ minMapVer: null,
243
+ series: 'v2',
244
+ },
245
+ 0x56: {
246
+ name: 'VMBGP4-20',
247
+ channels: 4,
248
+ hasOled: false,
249
+ hasPir: false,
250
+ hasOc: null, // TO VERIFY — GP series OC status unconfirmed
251
+ minMapVer: null,
252
+ series: 'v2',
253
+ },
254
+ 0x57: {
255
+ name: 'VMBGPO-20',
256
+ channels: 32,
257
+ hasOled: true,
258
+ hasPir: false,
259
+ hasOc: true, // OLED/output variant — OC confirmed by protocol family
260
+ minMapVer: null,
261
+ series: 'v2',
262
+ },
263
+ 0x5C: {
264
+ name: 'VMBEL2PIR-20', // Official list shows VMBBEL2PIR-20 / VMBEL4PIR-20 conflict — TO VERIFY via VelbusLink
265
+ channels: 6,
266
+ hasOled: false,
267
+ hasPir: true,
268
+ hasOc: true, // PIR variant of VMBEL family — OC present
269
+ pirChannels: {
270
+ 1: 'button1', 2: 'button2',
271
+ 3: 'virtual', 4: 'dark', 5: 'light', 6: 'motion',
272
+ },
273
+ minMapVer: null,
274
+ series: 'v2',
275
+ },
276
+ 0x5F: {
277
+ name: 'VMBGP4PIR-20',
278
+ channels: 8,
279
+ hasOled: false,
280
+ hasPir: true,
281
+ hasOc: null, // TO VERIFY — GP4PIR OC status unconfirmed
282
+ pirChannels: {
283
+ 1: 'button1', 2: 'button2', 3: 'button3', 4: 'button4',
284
+ 5: 'virtual', 6: 'dark', 7: 'light', 8: 'motion',
285
+ },
286
+ minMapVer: null,
287
+ series: 'v2',
288
+ },
289
+ };
290
+
291
+ // Flat set of all type byte values — used for quick membership test in scan node
292
+ const GLASS_PANEL_TYPE_IDS = new Set(
293
+ Object.keys(GLASS_PANEL_TYPES).map(k => parseInt(k))
294
+ );
295
+
296
+ module.exports = { GLASS_PANEL_TYPES, GLASS_PANEL_TYPE_IDS };
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ // PIR module type registry — V2 (-20) series
5
+ // Covered by velbus-pir-20 node.
6
+ //
7
+ // hasTempSensor: module has temperature sensor — 0xE6 emitted, 0xE8 part1
8
+ // is full glass-panel-style multi-part format (only calibration
9
+ // and alarm fields meaningful — no thermostat presets).
10
+ // Temp alarm bits replace absence in 0x00 bitmask.
11
+ // lockStyle: 'number' — DB2 of lock/unlock is channel number 1-N
12
+ // channels: named channel numbers (1-indexed), keyed by channel number
13
+ // minMapVer: minimum memory map version (null = unverified)
14
+ // series: 'v2'
15
+ // ─────────────────────────────────────────────────────────────────────────────
16
+
17
+ const PIR_TYPES_20 = {
18
+ 0x4D: {
19
+ name: 'VMBPIR-20',
20
+ hasTempSensor: false,
21
+ lockStyle: 'number',
22
+ // Channels 1-7 by number; bitmask uses bit0=dark...bit6=absence
23
+ channels: {
24
+ 1: 'dark', 2: 'light', 3: 'motion1', 4: 'ldMotion1',
25
+ 5: 'motion2', 6: 'ldMotion2', 7: 'absence',
26
+ },
27
+ // 0x00 bitmask: bit0-bit6 as above, bit7 unused
28
+ bitmask: ['dark', 'light', 'motion1', 'ldMotion1', 'motion2', 'ldMotion2', 'absence'],
29
+ minMapVer: 1,
30
+ series: 'v2',
31
+ },
32
+ 0x59: {
33
+ name: 'VMBPIRO-20',
34
+ hasTempSensor: true,
35
+ lockStyle: 'number',
36
+ // Channels 1-6 lockable; bits 6-7 are temp alarms (read-only, not lockable)
37
+ channels: {
38
+ 1: 'dark', 2: 'light', 3: 'motion1', 4: 'ldMotion1',
39
+ 5: 'motion2', 6: 'ldMotion2',
40
+ },
41
+ // 0x00 bitmask: bits 0-5 motion channels, bit6=lowTempAlarm, bit7=highTempAlarm
42
+ bitmask: ['dark', 'light', 'motion1', 'ldMotion1', 'motion2', 'ldMotion2',
43
+ 'lowTempAlarm', 'highTempAlarm'],
44
+ minMapVer: null,
45
+ series: 'v2',
46
+ },
47
+ };
48
+
49
+ const PIR_TYPE_IDS_20 = new Set(Object.keys(PIR_TYPES_20).map(k => parseInt(k)));
50
+
51
+ module.exports = { PIR_TYPES_20, PIR_TYPE_IDS_20 };
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ // PIR module type registry — original and -10 series
5
+ // Covered by velbus-pir node.
6
+ //
7
+ // hasTempSensor: module has temperature sensor — 0xE6 emitted, 0xE8 settings
8
+ // compact format (7 bytes). Alarms replace absence in bitmask.
9
+ // lockStyle: 'bitmask' — DB2 of lock/unlock/enable/disable is channel bitmask
10
+ // channels: named channel bits in bitmask order (bit0=index0)
11
+ // minMapVer: minimum memory map version (null = unverified)
12
+ // series: 'original' | '-10'
13
+ // ─────────────────────────────────────────────────────────────────────────────
14
+
15
+ const CHANNEL_BITS_WITH_ABSENCE = [
16
+ 'dark', 'light', 'motion1', 'ldMotion1', 'motion2', 'ldMotion2', 'absence',
17
+ ];
18
+
19
+ const CHANNEL_BITS_WITH_TEMP = [
20
+ 'dark', 'light', 'motion1', 'ldMotion1', 'motion2', 'ldMotion2',
21
+ 'lowTempAlarm', 'highTempAlarm',
22
+ ];
23
+
24
+ const PIR_TYPES = {
25
+ 0x2A: {
26
+ name: 'VMBPIRM',
27
+ hasTempSensor: false,
28
+ lockStyle: 'bitmask',
29
+ channels: CHANNEL_BITS_WITH_ABSENCE,
30
+ minMapVer: null,
31
+ series: 'original',
32
+ },
33
+ 0x2B: {
34
+ name: 'VMBPIRC',
35
+ hasTempSensor: false,
36
+ lockStyle: 'bitmask',
37
+ channels: CHANNEL_BITS_WITH_ABSENCE,
38
+ minMapVer: null,
39
+ series: 'original',
40
+ },
41
+ 0x2C: {
42
+ name: 'VMBPIRO',
43
+ hasTempSensor: true,
44
+ lockStyle: 'bitmask',
45
+ channels: CHANNEL_BITS_WITH_TEMP,
46
+ minMapVer: null,
47
+ series: 'original',
48
+ },
49
+ 0x23: {
50
+ name: 'VMBPIRO-10',
51
+ hasTempSensor: true,
52
+ lockStyle: 'bitmask',
53
+ channels: CHANNEL_BITS_WITH_TEMP,
54
+ minMapVer: null,
55
+ series: '-10',
56
+ },
57
+ };
58
+
59
+ const PIR_TYPE_IDS = new Set(Object.keys(PIR_TYPES).map(k => parseInt(k)));
60
+
61
+ module.exports = { PIR_TYPES, PIR_TYPE_IDS };
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * relay-types-20.js
5
+ * Registry of V2 (-20 series) Velbus relay module types.
6
+ * Original series modules are in relay-types.js.
7
+ *
8
+ * KEY PROTOCOL DIFFERENCES FROM ORIGINAL SERIES:
9
+ * 0xFF response: 8 bytes (byte 8 = properties: CAN FD, terminator, HW ver)
10
+ * 0xFB status: One packet = full module state (bitmask per byte, not per-channel)
11
+ * byte 2: channel 1-8 on/off status bitmask
12
+ * byte 3: channel 1-8 inhibited bitmask
13
+ * byte 4: channel 1-8 forced_on bitmask
14
+ * byte 5: channel 1-8 forced_off (locked) bitmask
15
+ * byte 6: channel 1-8 program disabled bitmask
16
+ * byte 7: channel 1-8 interval timer running bitmask
17
+ * byte 8: alarm & program selection
18
+ * Commands use channel NUMBER (1-8) or 0xFF for all — NOT bitmask
19
+ * Full scheduling engine: programs, alarms, sunrise/sunset
20
+ * CAN FD support (flag in 0xFF properties byte)
21
+ * Program steps: 122 (original series: 72)
22
+ *
23
+ * Fields:
24
+ * name - Human-readable module name
25
+ * channels - Number of physical relay channels
26
+ * virtual - Number of virtual channels
27
+ * hasInput - true if module has physical input channel (VMB1RYS-20 ch8)
28
+ * minMemoryMapVersion - minimum map version required (null = unverified)
29
+ *
30
+ * Confirmed type bytes (25/06/2026, from PDF edition 3 Jun 2025):
31
+ * VMB1RYS-20: 0x0D (previously TBD — now confirmed)
32
+ * VMB4RYLD-20: 0x26
33
+ * VMB4RYNO-20: 0x27
34
+ */
35
+ const RELAY_TYPES_20 = {
36
+ 0x0D: { name: 'VMB1RYS-20', channels: 1, virtual: 0, hasInput: true, minMemoryMapVersion: null },
37
+ 0x26: { name: 'VMB4RYLD-20', channels: 4, virtual: 4, hasInput: false, minMemoryMapVersion: null },
38
+ 0x27: { name: 'VMB4RYNO-20', channels: 4, virtual: 4, hasInput: false, minMemoryMapVersion: null },
39
+ };
40
+
41
+ const RELAY_TYPE_IDS_20 = new Set(Object.keys(RELAY_TYPES_20).map(Number));
42
+
43
+ module.exports = { RELAY_TYPES_20, RELAY_TYPE_IDS_20 };
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * relay-types.js
5
+ * Registry of ORIGINAL SERIES Velbus relay module types only.
6
+ * V2 (-20) modules are in relay-types-20.js.
7
+ *
8
+ * Fields:
9
+ * name - Human-readable module name
10
+ * channels - Number of physical relay channels
11
+ * virtual - Number of virtual channels
12
+ * hexSwitch - true if address set by physical hex switches
13
+ * hasForcedInhibit - true | 'build1105' | false
14
+ * hasInput - true if module has physical input button (VMB1RYS)
15
+ * minMemoryMapVersion - minimum map version required (null = unverified)
16
+ *
17
+ * Confirmed map versions (Toulouse, 25/06/2026):
18
+ * VMB1RYS 0x41: map version 0, build 2436
19
+ * All other original series assumed 0 pending confirmation.
20
+ *
21
+ * NOTE: Build number bytes in 0xFF response are NOT year/week.
22
+ * They are build number high byte and low byte.
23
+ * (byte6_decimal * 100) + byte7_decimal = build number as shown in VelbusLink.
24
+ */
25
+ const RELAY_TYPES = {
26
+ 0x02: { name: 'VMB1RY', channels: 1, virtual: 0, hexSwitch: true, hasForcedInhibit: false, hasInput: false, minMemoryMapVersion: 0 },
27
+ 0x08: { name: 'VMB4RY', channels: 4, virtual: 0, hexSwitch: true, hasForcedInhibit: 'build1105', hasInput: false, minMemoryMapVersion: 0 },
28
+ 0x10: { name: 'VMB4RYLD', channels: 4, virtual: 1, hexSwitch: false, hasForcedInhibit: 'build1105', hasInput: false, minMemoryMapVersion: 0 },
29
+ 0x11: { name: 'VMB4RYNO', channels: 4, virtual: 1, hexSwitch: false, hasForcedInhibit: 'build1105', hasInput: false, minMemoryMapVersion: 0 },
30
+ 0x1B: { name: 'VMB1RYNO', channels: 1, virtual: 4, hexSwitch: false, hasForcedInhibit: 'build1105', hasInput: false, minMemoryMapVersion: 0 },
31
+ 0x29: { name: 'VMB1RYNOS', channels: 1, virtual: 4, hexSwitch: false, hasForcedInhibit: true, hasInput: false, minMemoryMapVersion: 0 },
32
+ 0x41: { name: 'VMB1RYS', channels: 1, virtual: 4, hexSwitch: false, hasForcedInhibit: true, hasInput: true, minMemoryMapVersion: 0 },
33
+ 0x48: { name: 'VMB4RYLD-10', channels: 4, virtual: 1, hexSwitch: false, hasForcedInhibit: true, hasInput: false, minMemoryMapVersion: 0 },
34
+ 0x49: { name: 'VMB4RYNO-10', channels: 4, virtual: 1, hexSwitch: false, hasForcedInhibit: true, hasInput: false, minMemoryMapVersion: 0 },
35
+ };
36
+
37
+ const RELAY_TYPE_IDS = new Set(Object.keys(RELAY_TYPES).map(Number));
38
+
39
+ module.exports = { RELAY_TYPES, RELAY_TYPE_IDS };
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ // Sensor/input module type registry — V2 series
5
+ // Covered by velbus-sensor-20 node.
6
+ //
7
+ // VMB8IN-20 (0x4E): 8 digital inputs + 8 energy counters + up to 24 alarm
8
+ // channels via 3 subaddresses (channels 9-16, 17-24, 25-32)
9
+ // lockStyle: 'number' — DB2 of lock/unlock is channel number 1-32
10
+ // nameStyle: 'number' — DB2 of 0xF0/F1/F2 is channel number 1-32
11
+ // hasCounter: true — 0xA4 energy counter on all 8 channels (Wh / litres / ml)
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+
14
+ const SENSOR_TYPES_20 = {
15
+ 0x4E: {
16
+ name: 'VMB8IN-20',
17
+ channels: 8, // primary digital input channels
18
+ alarmCh: 24, // additional alarm channels via subaddresses (9-32)
19
+ hasCounter: true,
20
+ lockStyle: 'number',
21
+ nameStyle: 'number',
22
+ minMapVer: null,
23
+ series: 'v2',
24
+ },
25
+ };
26
+
27
+ const SENSOR_TYPE_IDS_20 = new Set(Object.keys(SENSOR_TYPES_20).map(k => parseInt(k)));
28
+
29
+ module.exports = { SENSOR_TYPES_20, SENSOR_TYPE_IDS_20 };