node-red-contrib-velbus-2026 0.10.1 → 0.10.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.
@@ -29,6 +29,98 @@ separate duplicate-table bugs took to fully resolve.
29
29
 
30
30
  ---
31
31
 
32
+ ## v0.10.3 — 09/07/2026
33
+
34
+ ### velbus-glass-panel — VMBEL edge colour control (set_edge_color)
35
+
36
+ - **Prompted by Stuart asking whether edge LED colours on `VMBEL` panels
37
+ could be controlled, or whether a raw-packet escape-hatch node (like the
38
+ original palette had) was needed instead.** Confirmed a real, well-defined
39
+ live-bus command exists — no raw-packet node needed for this case.
40
+ - **Scope deliberately split, per explicit decision:** `COMMAND_SET_PB_
41
+ BACKLIGHT` (`0xD4`) covers two different operations distinguished only by
42
+ DLC — "Set Custom Color" (`DLC=6`, *defines* a custom RGB palette slot)
43
+ and "Set Edge Color" (`DLC=4`, *applies* an already-defined colour).
44
+ Only the latter is implemented: defining new custom colours is
45
+ commissioning-time configuration, staying in VelbusLink's domain, same
46
+ reasoning as `VMB4LEDPWM-20`'s grouping mode and Program Step read/write
47
+ elsewhere in this project. See `HANDOVER.md` section 7.8b for the full
48
+ byte layout.
49
+ - **`hasEdgeLed` gated to the confirmed `VMBEL` family only** (12 of 29
50
+ glass panel types: `VMBEL1/2/4`, `VMBELO`, `VMBELPIR`, `VMBEL2PIR`, and
51
+ their `-20` siblings) — `VMBGP`-family panels have only a single-colour
52
+ front LED per button, genuinely different hardware, not just a missing
53
+ feature. Verified the command is byte-for-byte identical across every
54
+ `VMBEL` sub-family protocol PDF before implementing once. One vestigial
55
+ "Edge color inhibited" status bit found in `VMBGP1-20/2-20/4-20`'s own
56
+ `0xED` — confirmed as inherited documentation cruft, not evidence of real
57
+ edge-colour hardware on those panels.
58
+ - New `set_edge_color` command: `layers` (background/continuous/slow_blink/
59
+ fast_blink, any combination), `edges` (left/top/right/bottom, any
60
+ combination), `page` (1-8 or "all"), `palette` (default/custom), `index`
61
+ (0-31), `priority` (custom only), `blink`. Sending to a non-`VMBEL` panel
62
+ warns clearly and sends nothing, matching the established per-type
63
+ gating pattern used throughout this palette.
64
+ - Housekeeping: removed a stale "`VMBKP` has no node at all" entry from
65
+ `HANDOVER.md`'s known-open-issues — that was resolved back in v0.10.0
66
+ when `VMBKP` was folded into `velbus-button`, but the issue list was
67
+ never updated to reflect it.
68
+
69
+ Verified via the mock-RED harness: default palette, custom palette with
70
+ priority and blink flag, all-defaults shorthand, and rejection on a
71
+ non-`VMBEL` type — every case with hand-checked checksums and manually
72
+ verified bitfield encoding, not just visual review. Not yet sent to a real
73
+ bus.
74
+
75
+ ---
76
+
77
+ ## v0.10.2 — 09/07/2026
78
+
79
+ ### velbus-sensor — VMB4AN generic analogue reading (channels 9-12)
80
+
81
+ - **Prompted by Stuart noticing `isVMB4AN` was defined but never used** —
82
+ confirmed the sensor-input channels (Group 2 of VMB4AN's three functional
83
+ groups) produced literally no output at all beforehand; the packet
84
+ carrying that data wasn't handled anywhere in the file.
85
+ - **Real trap found and avoided:** the obviously-named `0xEA`
86
+ ("sensor status") packet does **not** carry a value at all — it's
87
+ operating-mode/sleep-timer/auto-send configuration only. The actual
88
+ reading lives in a separate, easy-to-miss packet earlier in the protocol
89
+ document: `0xA9` (`COMMAND_SENSOR_RAW_DATA`, "Transmit the sensor raw
90
+ value"), genuinely standalone with no need to cross-reference anything
91
+ else. Also confirmed `0xE8`/`0xE9` ("sensor settings") is preset
92
+ *configuration* storage, not a live value either — three different
93
+ packets that could each plausibly have been mistaken for "the reading."
94
+ - Deliberately generic, per explicit preference: emits `{ type: "analogue",
95
+ channel, mode, raw }` with no engineering-unit conversion attempted —
96
+ `mode` (voltage/current/resistance/period) and the PDF's resolution
97
+ table are exposed in the node's help so conversion can happen in the flow
98
+ instead.
99
+ - New `get_analogue` input command (`0xE5`, request-a-reading-now) — uses
100
+ the protocol's own "auto-send config byte = 0" option to request a value
101
+ without side-effecting the module's existing auto-send schedule.
102
+ **Priority byte corrected during verification:** the protocol PDF
103
+ explicitly states "SID10-SID9 = 11 (lowest priority)" for this specific
104
+ command — `0xFB`, not the `0xF8` most other commands in this file use.
105
+ Checked directly rather than assumed, since this is the one place it
106
+ genuinely differs from the file's established convention.
107
+ - Both confirmed absent from `VMB7IN`'s own protocol PDF before wiring
108
+ in — this file's switch statement handles both module types together,
109
+ so a collision would misfire for `VMB7IN` modules if either byte had
110
+ been reused there.
111
+ - **Full Group 2 configuration (presets, mode-switching, sleep time,
112
+ offset, auto-send interval config) and all of Group 3 (analog outputs)
113
+ remain parked** — see `coverage-roadmap.md` section 6. This is
114
+ deliberately just the minimal reading piece, not the full feature.
115
+
116
+ Verified via the mock-RED harness: voltage/period modes, both period
117
+ special-value cases (`0x000000` short-circuit, `0xFFFFFF` open-circuit),
118
+ `VMB7IN` isolation confirmed (same bytes produce no output on that type),
119
+ `get_analogue` checksum hand-verified, correctly rejected on non-VMB4AN
120
+ types. Not yet sent to a real bus.
121
+
122
+ ---
123
+
32
124
  ## v0.10.1 — 09/07/2026
33
125
 
34
126
  ### Two critical bugs found on real hardware — velbus-dimmer and velbus-glass-panel
package/HANDOVER.md CHANGED
@@ -6,7 +6,7 @@ you're a new contributor, a new maintainer, or an AI assistant starting a fresh
6
6
  with no memory of previous work — this document should be sufficient on its own, together
7
7
  with the source code in this repository, to continue development competently.
8
8
 
9
- Current state at time of writing: **v0.10.1, 19 nodes, published on npm.**
9
+ Current state at time of writing: **v0.10.3, 19 nodes, published on npm.**
10
10
 
11
11
  ---
12
12
 
@@ -300,13 +300,13 @@ or a string before assuming the parsing logic itself is wrong.
300
300
  | `velbus-relay-20` | Velbus (outputs) | V2 relays: VMB1RYS-20, VMB4RYLD-20, VMB4RYNO-20 |
301
301
  | `velbus-dimmer` | Velbus (outputs) | Original-series dimmers: VMBDMI, VMBDMI-R, VMB4DC |
302
302
  | `velbus-dimmer-20` | Velbus (outputs) | V2 dimmers: VMB2DC-20, VMB8DC-20, VMB4LEDPWM-20 (incl. RGB/RGBW grouping mode) |
303
- | `velbus-glass-panel` | Velbus (inputs) | All 29 glass panel types (original + V2), buttons/OLED/PIR/open-collector as applicable per type |
303
+ | `velbus-glass-panel` | Velbus (inputs) | All 29 glass panel types (original + V2), buttons/OLED/PIR/open-collector as applicable per type. `VMBEL` family only (12 of the 29 types): `set_edge_color` applies an already-defined colour across layer/edge/page combinations — deliberately not palette *definition*, which stays in VelbusLink |
304
304
  | `velbus-thermostat` | Velbus (inputs) | Thermostat function on any glass panel module that has one — same address as the corresponding glass-panel node, coexists without conflict |
305
305
  | `velbus-button` | Velbus (inputs) | 12 types across original and V2 series (VMB8PB, VMB8PBU, VMB6PBN, VMB2PBN, VMB4PB, VMB6PB-20, VMB8IR, VMB4PD, VMB4RF, VMBRFR8S, VMBVP01, VMBKP, VMBIN) — plain button events for all; lock/unlock and richer status decode for the 8 types confirmed to support them; fixed semantic channel labels for VMBVP01 (DoorBird) |
306
306
  | `velbus-pir` | Velbus (inputs) | Original-series PIR: VMBPIRO-10, VMBPIRM, VMBPIRC, VMBPIRO |
307
307
  | `velbus-pir-20` | Velbus (inputs) | V2 PIR: VMBPIR-20, VMBPIRO-20 |
308
308
  | `velbus-meteo` | Velbus (inputs) | Weather station: VMBMETEO |
309
- | `velbus-sensor` | Velbus (inputs) | Original-series input/analogue: VMB7IN, VMB4AN, VMB6IN |
309
+ | `velbus-sensor` | Velbus (inputs) | Original-series input/analogue: VMB7IN, VMB4AN (channels 1-8 alarm outputs + generic analogue reading on channels 9-12; full sensor/preset config and analog outputs on channels 13-16 remain parked), VMB6IN |
310
310
  | `velbus-sensor-20` | Velbus (inputs) | V2 input: VMB8IN-20 |
311
311
  | `velbus-blind` | Velbus (outputs) | VMB1BL, VMB2BL |
312
312
  | `velbus-blind-s` | Velbus (outputs) | VMB1BLS, VMB2BLE, VMB2BLE-10 |
@@ -589,6 +589,62 @@ Address assignment: allow 500ms, then rescan to confirm the new address took eff
589
589
  not the right trigger for an automatic write-back, both for the EEPROM-wear reason above
590
590
  and because it risks a feedback loop. Reads have no such constraint and are always safe.
591
591
 
592
+ ### 7.8a `VMB4AN` — three similar-sounding packets, only one has the actual value
593
+
594
+ A real trap, worth remembering for any future work on this module's
595
+ sensor channels (9-12): `0xEA` ("sensor status") sounds like it should be
596
+ the reading, but it's operating-mode/sleep-timer/auto-send configuration
597
+ only — no value field at all. `0xE8`/`0xE9` ("sensor settings") sounds
598
+ promising too, but that's stored preset *configuration*, not a live
599
+ reading. The actual value lives in `0xA9` (`COMMAND_SENSOR_RAW_DATA`,
600
+ labelled "Transmit the sensor raw value" in the PDF) — a completely
601
+ standalone packet with no need to cross-reference either of the other two.
602
+ ```
603
+ body[0] = 0xA9, body[1] = channel (9-12)
604
+ body[2] = operating mode (00=voltage, 01=current, 10=resistance, 11=period)
605
+ body[3-5] = 24-bit raw value, MSB first
606
+ (period mode: 0x000000 = short-circuited, 0xFFFFFF = open-circuit)
607
+ ```
608
+
609
+ ### 7.8b `0xD4` — one command byte, two very different operations, `VMBEL` family only
610
+
611
+ `COMMAND_SET_PB_BACKLIGHT` (`0xD4`) covers **two genuinely different commands**,
612
+ distinguished only by `DLC` (data length), confirmed identical across every
613
+ `VMBEL` sub-family protocol PDF checked (`VMBEL1/2/4`, `VMBELO`, `VMBELPIR`,
614
+ and their `-20` siblings):
615
+
616
+ - **"Set Custom Color" (`DLC=6`)** — *defines* one of 32 custom colour
617
+ palette slots as an actual RGB (or white+saturation) value:
618
+ `[0xD4, paletteIndex(0-31), rgbOrWhiteFlag+saturation, red, green, blue]`.
619
+ **Deliberately not implemented** — defining new custom colours is
620
+ commissioning-time configuration, and per an explicit decision, stays in
621
+ VelbusLink's domain. Same reasoning as `VMB4LEDPWM-20`'s grouping mode and
622
+ Program Step read/write elsewhere in this project.
623
+ - **"Set Edge Color" (`DLC=4`)** — *applies* an already-defined colour
624
+ (default palette, or a custom slot VelbusLink has already programmed)
625
+ across a chosen combination of layer/edge/page. This one **is**
626
+ implemented (`velbus-glass-panel`'s `set_edge_color` command) — a
627
+ genuine live/runtime action, not palette editing:
628
+ ```
629
+ body[0] = 0xD4
630
+ body[1] = layer bitmask (bit0=background, bit1=continuous-feedback,
631
+ bit2=slow-blink-feedback, bit3=fast-blink-feedback) + bit7
632
+ (0=default palette, 1=custom palette)
633
+ body[2] = edge bitmask (bit0=left, bit1=top, bit2=right, bit3=bottom) +
634
+ page nibble (bits4-7: 0-7=pages 1-8, 8-15=all pages — the
635
+ protocol PDF states the entire 1000-1111 range means "all",
636
+ not just one value)
637
+ body[3] = blink flag (bit7) + priority (bits5-6, custom palette only:
638
+ 01=low, 10=mid, 11=high) + palette index (bits0-4, 0-31)
639
+ ```
640
+ **Gated to `hasEdgeLed` types only** — the `VMBEL` family, confirmed
641
+ `VMBGP`-family panels genuinely lack this (they have a single-colour
642
+ front LED per button, not side-firing edge lighting — a real hardware
643
+ difference, not just a missing feature). One vestigial "Edge color
644
+ inhibited" status *bit* appears in `VMBGP1-20/2-20/4-20`'s `0xED` module
645
+ status — almost certainly inherited from shared documentation with the
646
+ `VMBEL` family, not evidence those panels have real edge-colour hardware.
647
+
592
648
  ### 7.9 Thermostat commands — always to the primary address
593
649
  ```
594
650
  0xDB = comfort mode, 0xDC = day mode, 0xDD = night mode, 0xDE = safe mode
@@ -872,15 +928,6 @@ orientation at the time of writing:
872
928
 
873
929
  ## 13. Known open issues
874
930
 
875
- - **`VMBKP` (0x42, "Keypad interface module") has no node at all.** Found scanning a
876
- real installation (Stuart's home) — confirmed present at address `0xFD`, a real
877
- module on a real bus, not a hypothetical. A genuinely new module type, not yet
878
- scoped. Its protocol PDF (`protocol_vmbkp.pdf`, 28 pages) is substantial: channel
879
- status, module status, and a full per-channel LED control layer
880
- (clear/set/slow-blink/fast-blink/very-fast-blink), similar in spirit to `velbus-button`
881
- but with LED feedback control `velbus-button` doesn't have. This needs the same
882
- "how much work would this involve" scoping pass `velbus-energy` got before it was
883
- built, not a quick bolt-on to an existing node.
884
931
  - **Open-collector support** on several glass panel types (marked "unconfirmed" in the
885
932
  registry in section 6) has not been verified against real hardware — the protocol PDFs
886
933
  are ambiguous or silent on some of these. Sending `0x01`/`0x02`/`0x03` open-collector
@@ -0,0 +1,186 @@
1
+ # Coverage Roadmap — Rationalized 09/July/2026
2
+
3
+ **STATUS: Implemented in v0.10.0 (09/July/2026).** Everything marked "confirmed
4
+ and ready to build" below has been built, tested, and shipped — including
5
+ the type-byte fixes and real protocol divergences caught along the way (see
6
+ `CHANGELOG_FORUM.md` v0.10.0 for the full list). The two genuinely deferred
7
+ items (dimmer family, bus error counter) and the OLED stretch goal are now
8
+ tracked directly in `HANDOVER.md` section 13 going forward — this document
9
+ remains as the record of the original rationalization discussion, not the
10
+ place to look for current status.
11
+
12
+ Synthesized from a full pass over every Velbus module type and feature not yet
13
+ covered by this palette. Organized by what actually needs to happen, not by the
14
+ order it came up in discussion.
15
+
16
+ ---
17
+
18
+ ## 1. New types to add to existing nodes' registries
19
+
20
+ No new nodes required — each of these is a straightforward registry addition
21
+ to a node that already exists and already handles this exact command family.
22
+
23
+ | Type | Module | Target node | Note |
24
+ |---|---|---|---|
25
+ | 0x07 | VMB1DM | `velbus-dimmer` | Old, likely rare in the field, minimal effort |
26
+ | 0x14 | VMBDME | `velbus-dimmer` | |
27
+ | 0x0F | VMB1LED | `velbus-dimmer` | Confirmed via protocol PDF: "PWM LED strip dimmer module" — genuinely dimmer-family, not a separate concept |
28
+ | 0x0A | VMB8IR | `velbus-button` | Confirmed — just a button-event module |
29
+ | 0x0B | VMB4PD | `velbus-button` | Reclassified from "not supported" — confirmed as a 4-button interface |
30
+ | 0x1A | VMB4RF | `velbus-button` | Presents 8 button events to the bus, nothing more |
31
+ | 0x30 | VMBRFR8S | `velbus-button` | Same mechanism as VMB4RF |
32
+ | 0x33 | VMBVP01 | `velbus-button` | DoorBird video intercom OEM module — 8 button events |
33
+ | 0x42 | VMBKP | `velbus-button` | Already agreed — plus lock/unlock and richer status decode (section 3) |
34
+ | 0x43 | VMBIN | `velbus-button` | Single-channel input, EOL but real installed base |
35
+ | 0x05 | VMB6IN | `velbus-sensor` | Sibling of VMB7IN |
36
+ | 0x3E | VMBGP4PIR-2 | `velbus-glass-panel` | Original-series-2 sibling of the -20 already covered |
37
+ | 0x25 | VMBGPTC | `velbus-glass-panel` | Confirmed — shares its actual protocol PDF with VMBGPO (0x21), genuine glass-panel sibling |
38
+
39
+ **Resolved — `VMB1TC`/`VMB1TS`: out of scope, not "super easy."** Checked the
40
+ actual protocol: `VMB1TC` has its own distinct command vocabulary ("Sensor
41
+ zone number," "sensor time statistics request," "lock/unlock sensor local
42
+ control") that doesn't overlay onto the existing `velbus-thermostat` decode —
43
+ this would be real new command handling, not a registry entry. Per the
44
+ stated condition, this stays out of scope. Nothing in Velbus is genuinely
45
+ wireless (confirmed) other than 433MHz/IR modules, which translate to the
46
+ bus rather than living on it — so no "W"-variant type IDs to separately track.
47
+
48
+ **Resolved — `VMBLCDWB` (0x13):** confirmed as a pre-glass-panel thermostat
49
+ controller (Velleman product page: 4-button control with LCD display, 32
50
+ functions, time/date backup). Out of scope, name-only in section 2.
51
+
52
+ ---
53
+
54
+ ## 2. Scan-table "see it, don't support it" additions
55
+
56
+ Named correctly in a scan (not `unknown_0xNN`), explicit "Not supported" note
57
+ in place of a suggested node — no live node built.
58
+
59
+ | Type | Module | Note |
60
+ |---|---|---|
61
+ | 0x45 / 0x5A | VMBDALI / VMBDALI-20 | Real prevalence (12 VLP projects) but DALI is its own protocol layer beyond the gateway — explicit "Not supported" label requested |
62
+ | 0x13 | VMBLCDWB | Pre-glass-panel thermostat controller (4-button + LCD + time/date backup) — confirmed out of scope, name only |
63
+ | 0x3F | VMCM3 | Custom corporate-client module, function unknown — name only |
64
+ | 0x39 / 0x5B / 0x60 | VMBSIG / VMBSIG-20 / VMBSIG-21 | Custom HomeAssistant-based master clock, not interactable — already named in the scanner; **worth aligning to the same explicit "Not supported" note style as VMBDALI for consistency**, rather than leaving these as a bare name with no note at all |
65
+ | 0x40 | VMBUSBIP | Already correctly handled as-is — no use in this context beyond potentially being what the bridge itself connects to |
66
+
67
+ ---
68
+
69
+ ## 3. Feature additions to existing nodes
70
+
71
+ - **`velbus-clock`: add sunrise/sunset enable/disable (`0xAE`)** — part of the
72
+ same shared system block as time/date/DST/alarm, genuinely missing.
73
+ - **`velbus-button`: lock/unlock (`0x12`/`0x13`) + richer `0xED` status decode**
74
+ (locked/enabled/inverted/program-selection) — already agreed, applies as a
75
+ general button-family capability, not scoped to VMBKP alone.
76
+ - **`velbus-button`: surface channel names in the event output** — using the
77
+ existing `0xF0`/`0xF1`/`0xF2` pattern, so named-channel modules (VMBKP, and
78
+ potentially others) show real names, not just channel numbers, while every
79
+ existing numbered-only setup keeps working unchanged.
80
+
81
+ - **Bus error counter (`0xDA`) — resolved design, scoped as a follow-up, not
82
+ built today.** Confirmed genuinely useful, but explicitly framed as an edge
83
+ case: "the once in a blue moon bus error message will clear itself within
84
+ a second or two — including it in normal payload traffic will cause
85
+ confusion." Resolved design: every node that registers for its own address
86
+ already receives every packet addressed to it, including an unsolicited
87
+ `0xDA` broadcast if one occurs — so no new "request" command is needed, just
88
+ passive decoding. Add `0xDA` handling to every existing node, emitting
89
+ **only on the secondary output** (never mixed into the main status payload
90
+ on output 1), and **only when at least one counter is non-zero** — silent
91
+ during normal operation, so it never appears unless there's genuinely
92
+ something to see. Nodes that currently only have a single output would need
93
+ a second one added. **Scoped as its own follow-up given it touches most or
94
+ all existing nodes — not part of today's build.**
95
+
96
+ ---
97
+
98
+ ## 4. Deferred — logged as a stretch goal, not being built now
99
+
100
+ - **OLED image writing** (pushing a custom B&W 1-bit bitmap to an OLED glass
101
+ panel) — genuine use case raised (swapping in a different-language greeting
102
+ for a visitor without opening VelbusLink). **Confirmed: no solid reason to
103
+ build it now** — parked in `HANDOVER.md` as an explicit stretch goal rather
104
+ than an open question.
105
+
106
+ **Resolved — calibration data:** no concrete current need. Stays out of
107
+ scope alongside generic memory read/write, parked for if/when a real need
108
+ comes up rather than built speculatively.
109
+
110
+ ---
111
+
112
+ ## 5. Confirmed out of scope — no action
113
+
114
+ - **Generic memory read/write** — the foundation Program Step would sit on;
115
+ out of scope as a consequence of Program Step being out of scope.
116
+ - **Program Step read/write** — explicitly parked pending VelbusLink's own
117
+ Delphi rewrite; a strong candidate to become its own separate project
118
+ rather than living in this palette at all.
119
+ - **Cross-module "linked" LED control** — out of scope.
120
+ - **IR learn-and-replay, reframed correctly:** this was mischaracterized in
121
+ the original list — it isn't third-party IR code learning at all. The
122
+ glass-panel IR variants simply respond to Velbus-defined HEX codes and
123
+ present them as button events (up to 32), with codes 33-39 specifically
124
+ translated into heating temperature mode changes that `velbus-thermostat`
125
+ would see. Genuinely out of scope as a project, but worth remembering this
126
+ detail if the IR-capable glass panel variants (`VMBGPOD_IR` etc.) ever get
127
+ addressed — the 33-39 range needs the same special handling as any other
128
+ thermostat mode change, not treatment as plain numbered buttons.
129
+ - **DALI bridging** — out of scope, name-only per section 2.
130
+ - **Signum integration** — out of scope, name-only per section 2.
131
+
132
+ ---
133
+
134
+ ## 6. VMB4AN — groups 2/3 parked, large commitment
135
+
136
+ **Status: parked 09/07/2026, not scoped for near-term work.** `velbus-sensor`
137
+ currently only implements Group 1 of VMB4AN's three functional groups (alarm
138
+ outputs, channels 1-8, via the standard `0x00` button-style event — the same
139
+ path VMB7IN uses). The `isVMB4AN` flag already in the code is a placeholder
140
+ that's never actually consumed anywhere.
141
+
142
+ - **Group 2 — sensor inputs (channels 9-12).** Each channel supports 4
143
+ configurable operating modes (voltage 0-10V / current 4-20mA / resistance
144
+ PT100-1000 / period measurement), 4 presets (safe/night/day/comfort — same
145
+ naming as thermostat presets), a sleep timer, and configurable auto-send
146
+ behaviour (fixed interval, or threshold-based at 3.125%/6.25%/12.5%/25%
147
+ change). Reading a value and reading full settings are two separate
148
+ packets (`0xEA` status, `0xE8`+`0xE9` two-part settings, confirmed from
149
+ `protocol_vmb4an.pdf`).
150
+ - **Group 3 — analog outputs (channels 13-16).** Effectively a full dimmer
151
+ control surface bolted onto VMB4AN — same command bytes as `velbus-dimmer`
152
+ itself (`0x07` set value, `0x11` restore last value), percentage or 12-bit
153
+ precision, timer/forced-on/inhibit, even reusing `0xB8` for its status
154
+ (same byte as `VMBDMI`'s dimmer status — no real conflict, different
155
+ module addresses, just worth knowing Velbus reuses this byte across
156
+ contexts). **Deprioritized further:** not yet configurable natively in
157
+ VelbusLink, so unlikely to appear in any real installation regardless of
158
+ whether this palette supports it — low priority until that changes.
159
+ - **Comparable in size to `velbus-energy`** — a genuine second build, not a
160
+ quick addition. Revisit if/when there's a concrete need.
161
+
162
+ ---
163
+
164
+ ## 7. Final scope — everything below is confirmed and ready to build
165
+
166
+ **New registry entries** (sections 1 and 2 above) — 13 types added to
167
+ existing nodes' registries, 5 types added as scan-visible "not supported"
168
+ entries, no new nodes required for any of it.
169
+
170
+ **Feature additions to existing nodes:**
171
+ - `velbus-clock`: sunrise/sunset enable/disable (`0xAE`)
172
+ - `velbus-button`: lock/unlock (`0x12`/`0x13`), richer `0xED` status decode,
173
+ channel names surfaced in event output
174
+
175
+ **Explicitly deferred to a later session, design already settled:**
176
+ - Bus error counter (`0xDA`) — touches most/all existing nodes, scoped above
177
+
178
+ **Confirmed out of scope, no action ever:**
179
+ - Generic memory read/write, calibration data (no concrete need), Program
180
+ Step read/write, cross-module linked LED control, IR learn-and-replay (as
181
+ a feature — though see the 33-39 thermostat-mode detail above if the
182
+ IR-capable glass panel variants ever get addressed), DALI bridging, Signum
183
+ integration, `VMB1TC`/`VMB1TS` (genuinely not "easy"), `VMBLCDWB`.
184
+
185
+ **Logged as a stretch goal in `HANDOVER.md`, not built now:**
186
+ - OLED image writing
@@ -140,6 +140,7 @@ const GLASS_PANEL_TYPES = {
140
140
  hasOled: false,
141
141
  hasPir: false,
142
142
  hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024
143
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
143
144
  minMapVer: 2,
144
145
  series: 'original',
145
146
  },
@@ -149,6 +150,7 @@ const GLASS_PANEL_TYPES = {
149
150
  hasOled: false,
150
151
  hasPir: false,
151
152
  hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024
153
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
152
154
  minMapVer: 2,
153
155
  series: 'original',
154
156
  },
@@ -158,6 +160,7 @@ const GLASS_PANEL_TYPES = {
158
160
  hasOled: false,
159
161
  hasPir: false,
160
162
  hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024
163
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
161
164
  minMapVer: 2,
162
165
  series: 'original',
163
166
  },
@@ -167,6 +170,7 @@ const GLASS_PANEL_TYPES = {
167
170
  hasOled: true,
168
171
  hasPir: false,
169
172
  hasOc: true, // OLED/output variant — OC confirmed by protocol family
173
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
170
174
  minMapVer: 4,
171
175
  series: 'original',
172
176
  },
@@ -176,6 +180,7 @@ const GLASS_PANEL_TYPES = {
176
180
  hasOled: false,
177
181
  hasPir: true,
178
182
  hasOc: true, // PIR variant of VMBEL family — OC present
183
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
179
184
  pirChannels: {
180
185
  1: 'button1', 2: 'button2', 3: 'button3', 4: 'button4',
181
186
  5: 'virtual', 6: 'dark', 7: 'light', 8: 'motion',
@@ -225,6 +230,7 @@ const GLASS_PANEL_TYPES = {
225
230
  hasOled: false,
226
231
  hasPir: true,
227
232
  hasOc: true, // PIR variant of VMBEL family — OC present
233
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
228
234
  pirChannels: {
229
235
  1: 'button1', 2: 'button2',
230
236
  3: 'virtual', 4: 'dark', 5: 'light', 6: 'motion',
@@ -241,6 +247,7 @@ const GLASS_PANEL_TYPES = {
241
247
  hasOled: false,
242
248
  hasPir: false,
243
249
  hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024 covers -20 variants
250
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
244
251
  minMapVer: null,
245
252
  series: 'v2',
246
253
  },
@@ -250,6 +257,7 @@ const GLASS_PANEL_TYPES = {
250
257
  hasOled: false,
251
258
  hasPir: false,
252
259
  hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024 covers -20 variants
260
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
253
261
  minMapVer: null,
254
262
  series: 'v2',
255
263
  },
@@ -259,6 +267,7 @@ const GLASS_PANEL_TYPES = {
259
267
  hasOled: false,
260
268
  hasPir: false,
261
269
  hasOc: true, // Confirmed: VMBEL1/2/4 protocol PDF ed2 Nov 2024 covers -20 variants
270
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
262
271
  minMapVer: null,
263
272
  series: 'v2',
264
273
  },
@@ -268,6 +277,7 @@ const GLASS_PANEL_TYPES = {
268
277
  hasOled: true,
269
278
  hasPir: false,
270
279
  hasOc: true, // OLED/output variant — OC confirmed by protocol family
280
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
271
281
  minMapVer: null,
272
282
  series: 'v2',
273
283
  },
@@ -277,6 +287,7 @@ const GLASS_PANEL_TYPES = {
277
287
  hasOled: false,
278
288
  hasPir: true,
279
289
  hasOc: true, // PIR variant of VMBEL family — OC present
290
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
280
291
  pirChannels: {
281
292
  1: 'button1', 2: 'button2', 3: 'button3', 4: 'button4',
282
293
  5: 'virtual', 6: 'dark', 7: 'light', 8: 'motion',
@@ -326,6 +337,7 @@ const GLASS_PANEL_TYPES = {
326
337
  hasOled: false,
327
338
  hasPir: true,
328
339
  hasOc: true, // PIR variant of VMBEL family — OC present
340
+ hasEdgeLed: true, // VMBEL family confirmed - own protocol PDF has 'Set Edge Color' (0xD4)
329
341
  pirChannels: {
330
342
  1: 'button1', 2: 'button2',
331
343
  3: 'virtual', 4: 'dark', 5: 'light', 6: 'motion',
@@ -316,6 +316,56 @@
316
316
  <dt><code>{ "cmd": "led_set", "channel": 1 }</code></dt>
317
317
  <dd>Control button LED. Commands: <code>led_set</code>, <code>led_clear</code>, <code>led_slow</code>, <code>led_fast</code>, <code>led_vfast</code>.</dd>
318
318
 
319
+ <dt><code>set_edge_color</code> — <strong>VMBEL family only</strong> (VMBEL1/2/4, VMBELO,
320
+ VMBELPIR, and their -20 siblings — not VMBGP-family panels, which have only a
321
+ single-colour front LED per button, not side-firing edge lighting)</dt>
322
+ <dd>
323
+ Applies an <em>already-defined</em> colour — the module's default palette, or a
324
+ custom slot VelbusLink has already programmed — across a chosen combination of
325
+ layer(s), edge(s), and button page. <strong>Does not define new custom colours</strong> —
326
+ that stays in VelbusLink; this node only ever applies/switches between colours that
327
+ already exist on the module.
328
+ <pre>
329
+ { "cmd": "set_edge_color", "index": 5 }
330
+ // Simplest form — every field below is optional and defaults as shown.
331
+ // Applies default palette colour #5 to the background layer, all four
332
+ // edges, all button pages.
333
+
334
+ { "cmd": "set_edge_color",
335
+ "layers": ["background", "continuous"],
336
+ "edges": ["left", "top", "right", "bottom"],
337
+ "page": 1,
338
+ "palette": "default",
339
+ "index": 5 }
340
+ // Same as above but explicit — background AND continuous-feedback layers,
341
+ // all edges, button page 1 only.
342
+
343
+ { "cmd": "set_edge_color",
344
+ "layers": ["fast_blink"],
345
+ "edges": ["left"],
346
+ "page": "all",
347
+ "palette": "custom",
348
+ "index": 12,
349
+ "priority": "high",
350
+ "blink": true }
351
+ // Custom palette slot #12 (must already be programmed via VelbusLink),
352
+ // applied only to the fast-blink-feedback layer, left edge only, every
353
+ // button page, at highest override priority, with the blink flag set.
354
+ </pre>
355
+ <table style="width:100%; font-size:0.85em; border-collapse:collapse; margin-top:4px;">
356
+ <tr style="text-align:left; border-bottom:1px solid #ccc;"><th>Field</th><th>Values</th><th>Default if omitted</th></tr>
357
+ <tr><td><code>layers</code></td><td>array, any of: <code>background</code>, <code>continuous</code>, <code>slow_blink</code>, <code>fast_blink</code></td><td><code>["background"]</code></td></tr>
358
+ <tr><td><code>edges</code></td><td>array, any of: <code>left</code>, <code>top</code>, <code>right</code>, <code>bottom</code></td><td>all four</td></tr>
359
+ <tr><td><code>page</code></td><td><code>1</code>-<code>8</code>, or <code>"all"</code></td><td><code>"all"</code></td></tr>
360
+ <tr><td><code>palette</code></td><td><code>"default"</code> or <code>"custom"</code></td><td><code>"default"</code></td></tr>
361
+ <tr><td><code>index</code></td><td><code>0</code>-<code>31</code> (which palette slot to apply)</td><td><code>0</code></td></tr>
362
+ <tr><td><code>priority</code></td><td><code>"low"</code>/<code>"mid"</code>/<code>"high"</code> — <em>custom palette only</em>, ignored for default</td><td><code>"low"</code></td></tr>
363
+ <tr><td><code>blink</code></td><td><code>true</code>/<code>false</code></td><td><code>false</code></td></tr>
364
+ </table>
365
+ <p>Sending this to a non-VMBEL panel (e.g. VMBGP2) sends nothing and logs a clear
366
+ warning rather than silently failing.</p>
367
+ </dd>
368
+
319
369
  <dt><code>{ "cmd": "output_on" }</code> / <code>"output_off"</code></dt>
320
370
  <dd>Open collector output on/off.</dd>
321
371
 
@@ -38,6 +38,33 @@ const LED_CMD = {
38
38
  led_vfast: 0xF9,
39
39
  };
40
40
 
41
+ // ─────────────────────────────────────────────────────────────────────────────
42
+ // Edge colour constants — VMBEL family only (hasEdgeLed), confirmed identical
43
+ // byte layout across every VMBEL sub-family protocol PDF checked (VMBEL1/2/4,
44
+ // VMBELO, VMBELPIR, and their -20 siblings). Command byte 0xD4
45
+ // (COMMAND_SET_PB_BACKLIGHT) is shared with 'Set Custom Color' (defining new
46
+ // custom RGB palette entries) — deliberately NOT implemented here. Defining
47
+ // custom colours is commissioning-time configuration and stays in
48
+ // VelbusLink's domain, same reasoning as VMB4LEDPWM-20's grouping mode and
49
+ // Program Step read/write elsewhere in this project. Only 'Set Edge Color'
50
+ // (applying an already-defined colour — default palette, or a custom slot
51
+ // VelbusLink has already programmed) is in scope: a genuine live/runtime
52
+ // action, not palette editing.
53
+ // ─────────────────────────────────────────────────────────────────────────────
54
+ const EDGE_LAYER_BITS = {
55
+ background: 0x01,
56
+ continuous: 0x02,
57
+ slow_blink: 0x04,
58
+ fast_blink: 0x08,
59
+ };
60
+ const EDGE_SIDE_BITS = {
61
+ left: 0x01,
62
+ top: 0x02,
63
+ right: 0x04,
64
+ bottom: 0x08,
65
+ };
66
+ const EDGE_PRIORITY = { low: 0x01, mid: 0x02, high: 0x03 };
67
+
41
68
  // ─────────────────────────────────────────────────────────────────────────────
42
69
  // Channel bitmask helpers
43
70
  // ─────────────────────────────────────────────────────────────────────────────
@@ -91,6 +118,7 @@ module.exports = function(RED) {
91
118
  const typeDesc = (node.typeId !== null) ? (GLASS_PANEL_TYPES[node.typeId] || null) : null;
92
119
  const hasOled = typeDesc ? typeDesc.hasOled : false;
93
120
  const hasPir = typeDesc ? typeDesc.hasPir : false;
121
+ const hasEdgeLed = typeDesc ? !!typeDesc.hasEdgeLed : false;
94
122
  const hasOc = typeDesc ? !!typeDesc.hasOc : false;
95
123
  const pirCh = typeDesc ? (typeDesc.pirChannels || {}) : {};
96
124
 
@@ -421,6 +449,50 @@ module.exports = function(RED) {
421
449
  return;
422
450
  }
423
451
 
452
+ // ── Set edge colour (VMBEL family only) ─────────────────────────────
453
+ // Applies an already-defined colour (default palette, or a custom
454
+ // slot VelbusLink has already programmed) across a chosen combination
455
+ // of layer(s), edge(s), and button page. Does NOT define new custom
456
+ // colours — that stays in VelbusLink, see the constants comment above.
457
+ if (cmd === 'set_edge_color') {
458
+ if (!hasEdgeLed) {
459
+ node.warn('velbus-glass-panel: ' + (typeDesc ? typeDesc.name : 'this module type') +
460
+ ' has no edge lighting (VMBEL family only) — sending nothing.');
461
+ return;
462
+ }
463
+
464
+ const layers = Array.isArray(msg.payload.layers) ? msg.payload.layers : ['background'];
465
+ const edges = Array.isArray(msg.payload.edges) ? msg.payload.edges : ['left', 'top', 'right', 'bottom'];
466
+ const palette = msg.payload.palette === 'custom' ? 'custom' : 'default';
467
+ const index = Math.max(0, Math.min(31, parseInt(msg.payload.index) || 0));
468
+ const blink = !!msg.payload.blink;
469
+
470
+ let db2 = 0;
471
+ for (const l of layers) db2 |= (EDGE_LAYER_BITS[l] || 0);
472
+ if (palette === 'custom') db2 |= 0x80;
473
+
474
+ let db3 = 0;
475
+ for (const e of edges) db3 |= (EDGE_SIDE_BITS[e] || 0);
476
+ // Page: 1-8 map to nibble 0-7; 'all' (or omitted) uses 0xF, matching
477
+ // the protocol's own stated range (1000xxxx through 1111xxxx all
478
+ // mean "all pages" — 0xF is simply the clearest, most explicit value
479
+ // in that range to use).
480
+ const page = msg.payload.page;
481
+ const pageNibble = (page === undefined || page === 'all')
482
+ ? 0x0F
483
+ : Math.max(0, Math.min(7, parseInt(page) - 1));
484
+ db3 |= (pageNibble << 4) & 0xF0;
485
+
486
+ let db4 = index & 0x1F;
487
+ if (blink) db4 |= 0x80;
488
+ if (palette === 'custom') {
489
+ db4 |= (EDGE_PRIORITY[msg.payload.priority] || EDGE_PRIORITY.low) << 5;
490
+ }
491
+
492
+ node.bridge.send(pkt(0xFB, node.address, [0xD4, db2, db3, db4]));
493
+ return;
494
+ }
495
+
424
496
  // ── Open collector output ──────────────────────────────────────────
425
497
  if (cmd === 'output_on') {
426
498
  // COMMAND_SWITCH_RELAY_ON (0x02), channel byte = don't care (0xFF)
@@ -137,12 +137,24 @@
137
137
  "enabled": [1,2,3,4,5,6,7,8], "locked": [], "progDisabled": [],
138
138
  "program": "summer", "alarms": { "alarm1Active": false, "alarm2Active": false } }</pre>
139
139
 
140
- <h3>Output 2 — Counter data (VMB7IN only)</h3>
140
+ <h3>Output 2 — Counter data (VMB7IN) / Analogue readings (VMB4AN)</h3>
141
141
  <pre>{ "type": "counter", "channel": 1, "count": 12345,
142
142
  "pulsesPerUnit": 1000, "periodMs": 3600, "overflow": false }</pre>
143
143
  <p>Engineering units: calculate in flow using pulsesPerUnit and periodMs.</p>
144
144
  <p>Power (W) = 3,600,000,000 / (periodMs × pulsesPerUnit)</p>
145
145
 
146
+ <p>VMB4AN sensor channels (9-12) — deliberately generic, no engineering-unit
147
+ conversion attempted here (mode-aware scaling, presets, and sleep-timer
148
+ configuration are a larger, separately-scoped piece of work — see
149
+ <code>coverage-roadmap.md</code>):</p>
150
+ <pre>{ "type": "analogue", "channel": 9, "mode": "voltage", "raw": 12345 }</pre>
151
+ <p><code>mode</code> is <code>"voltage"</code> (0-10V, 0.25mV/count),
152
+ <code>"current"</code> (4-20mA, 5µA/count), <code>"resistance"</code>
153
+ (PT100/PT1000/Ni1000, 0.25Ω/count), or <code>"period"</code> (0.5µs/count —
154
+ <code>raw</code> of <code>0</code> means short-circuited, <code>16777215</code>
155
+ means open-circuit). Apply the resolution yourself in the flow, e.g.
156
+ <code>volts = raw * 0.00025</code> for voltage mode.</p>
157
+
146
158
  <h3>Input commands</h3>
147
159
  <dl>
148
160
  <dt><code>{ "cmd": "get_status" }</code></dt><dd>Request module status.</dd>
@@ -150,6 +162,10 @@
150
162
  <dd>Request counter (VMB7IN). channels=bitmask of ch1-4 (default 15=all). interval=0 no change.</dd>
151
163
  <dt><code>{ "cmd": "reset_counter", "channel": 1 }</code></dt><dd>Reset pulse counter to zero.</dd>
152
164
  <dt><code>{ "cmd": "load_counter", "channel": 1, "value": 12345 }</code></dt><dd>Load counter with preset value (build 1426+).</dd>
165
+ <dt><code>{ "cmd": "get_analogue", "channel": 9 }</code></dt>
166
+ <dd>Request an analogue reading now (VMB4AN, channel 9-12). Doesn't change
167
+ the module's own auto-send interval configuration — it already sends
168
+ readings on its own schedule; this just asks for one immediately.</dd>
153
169
  <dt><code>{ "cmd": "get_name", "channel": 1 }</code></dt><dd>Request channel name from VelbusLink.</dd>
154
170
  </dl>
155
171
  </script>
@@ -172,6 +172,34 @@ module.exports = function(RED) {
172
172
  break;
173
173
  }
174
174
 
175
+ // ── 0xA9 Sensor raw value (VMB4AN channels 9-12 only) ────────────
176
+ // Confirmed from protocol_vmb4an.pdf, "Transmit the sensor raw
177
+ // value" — a genuinely standalone packet (COMMAND_SENSOR_RAW_DATA),
178
+ // not to be confused with 0xEA "sensor status" (that one carries
179
+ // operating mode / sleep timer / auto-send config, no actual value
180
+ // at all — a real trap if you assume "status" means "reading").
181
+ // body[0]=0xA9, body[1]=channel(9-12), body[2]=operating mode,
182
+ // body[3-5]=24-bit raw value (upper/high/low, MSB first).
183
+ // Deliberately generic per Stuart's own preference — engineering-
184
+ // unit conversion (voltage/current/resistance/period) is left to
185
+ // the flow, not attempted here. The PDF's resolution table (e.g.
186
+ // 0.25mV/count for voltage) is available in this node's help if
187
+ // conversion is ever wanted downstream.
188
+ case 0xA9: {
189
+ if (!isVMB4AN) return;
190
+ if (body.length < 6) return;
191
+
192
+ const channel = body[1];
193
+ const modeCode = body[2] & 0x03;
194
+ const mode = ['voltage', 'current', 'resistance', 'period'][modeCode];
195
+ const raw = (body[3] << 16) | (body[4] << 8) | body[5];
196
+
197
+ const payload = { type: 'analogue', channel, mode, raw };
198
+
199
+ node.send([null, { payload }]);
200
+ break;
201
+ }
202
+
175
203
  // ── 0xF0/F1/F2 Channel name parts ───────────────────────────────
176
204
  case 0xF0:
177
205
  case 0xF1:
@@ -222,6 +250,25 @@ module.exports = function(RED) {
222
250
  return;
223
251
  }
224
252
 
253
+ // Request an analogue reading now — VMB4AN channels 9-12 only.
254
+ // DATABYTE3=0 explicitly means "don't change the auto-send interval
255
+ // config" per the protocol PDF — this just asks for a value without
256
+ // side effects on the module's existing auto-send behaviour.
257
+ // Priority: protocol PDF explicitly states "SID10-SID9 = 11 (lowest
258
+ // priority)" for this specific command — 0xFB, not the 0xF8 used by
259
+ // most other commands in this file. Checked directly rather than
260
+ // assumed, since this is the one place it genuinely differs.
261
+ if (cmd === 'get_analogue') {
262
+ if (!isVMB4AN) { node.warn('velbus-sensor: this module has no analogue sensor channels'); return; }
263
+ const ch = parseInt(msg.payload.channel);
264
+ if (!ch || ch < 9 || ch > 12) {
265
+ node.warn('velbus-sensor: get_analogue requires "channel": 9-12');
266
+ return;
267
+ }
268
+ node.bridge.send(pkt(0xFB, node.address, [0xE5, ch, 0x00]));
269
+ return;
270
+ }
271
+
225
272
  // Load counter — DB2=channel number (0-3), DB3=don't care, DB4-7=32-bit value
226
273
  if (cmd === 'load_counter') {
227
274
  if (!hasCounter) { node.warn('velbus-sensor: this module has no pulse counter'); return; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-velbus-2026",
3
- "version": "0.10.1",
3
+ "version": "0.10.3",
4
4
  "description": "Node-RED palette for Velbus building automation \u2014 relay, dimmer, glass panel, PIR, blind, sensor, meteo, clock nodes. Original and V2 (-20) series.",
5
5
  "keywords": [
6
6
  "node-red",
Binary file