Skip to content

vaillant: VWZIO Hydraulikstation process telemetry (HW=5103)#598

Open
buliwyf42 wants to merge 5 commits into
john30:masterfrom
buliwyf42:vaillant-vwzio-hydraulikstation-telemetry
Open

vaillant: VWZIO Hydraulikstation process telemetry (HW=5103)#598
buliwyf42 wants to merge 5 commits into
john30:masterfrom
buliwyf42:vaillant-vwzio-hydraulikstation-telemetry

Conversation

@buliwyf42
Copy link
Copy Markdown

PR draft — vaillant: process telemetry for VWZIO Hydraulikstation HW=5103 (slave 0x76)

Summary

The current src/vaillant/76.vwzio.tsp only exposes three service-level
test messages (b514 05*). The VWZIO is the Vaillant
Hydraulikstation
that sits between an aroTHERM heat pump (HMU on
0x08) and the building water circuits.

This PR builds on prior community work that never landed upstream:

  • #315
    (open) by @cyberthom42 — VWZ MEH 97/6 decode (German names),
    parallel hardware variant of the same family.
  • #407
    (closed) by @chrizzzp — comprehensive 76.vwz.csv covering Heizstab
    counters, b514 Test-mode telemetry, English naming. We adopt
    @chrizzzp's English names where ranges overlap.
  • The b512 0f 00 0X 7-byte payload layout is per the wiki page on
    @cyberthom42's vaillant-arotherm-plus
    (originally posted by @chrizzzp) — verified byte-for-byte against
    our 72-h capture.
  • #490
    (open) + #504
    by @filippz — b516 energy-statistics framework. Already pulled into
    upstream b516_inc.tsp which we reuse via the existing _includes
    union.

This PR is intentionally narrower than #407: only the messages the
master polls passively on a HW=5103;SW=0901 system are added, all
in *u direction (no active reads = no extra bus traffic). The
larger b514 Test-mode family from #407 is left for a follow-up PR
because it requires r direction and changes bus behaviour.

On a HW=5103;SW=0901 unit, the master (0x10) regularly polls
additional process-data and counter telegrams on slave 0x76:

PB SB ID Cycle Bytes Upstream pattern?
b504 00 ~1/min 10 yes — Hcmode_inc.DateTime
b511 01 ~6/min 9 yes — Hcmode_inc.Status01
b511 02 (idle) 8 yes — Hcmode_inc.Status02
b512 0f 00 01 ~5/min 7 new — water-side Stats01
b512 0f 00 02 ~1/min 7 new — water-side Stats02
b516 14 ~1/min 5 yes — B516_inc.PowerConsumptionHmu (likely Heizstab on this unit)
b51a 05 ff 32 46 1/h 10 newImmersionHeaterHours (#315/#407)
b51a 05 ff 32 4a 1/h 10 newImmersionHeaterStarts (#315/#407)
b51a 05 ff 32 4b 1/h 10 newPrioritySwitchingValveOps (#315/#407)
b51a 05 ff 33 1a 1/h 10 newImmersionHeaterPowerLimit (#407)

This PR

  1. Wires Hcmode_inc and (via the existing union) B516_inc into the
    Vwz namespace's _includes, so the four upstream-defined messages
    (DateTime, Status01, Status02, PowerConsumptionHmu) become
    visible on 0x76 with byte-identical layouts to the HMU master.
  2. Adds two VWZIO-specific models, Stats01 and Stats02, for the
    b512 0f 00 0X family.

No changes to the existing service-test messages; no r/w direction
is introduced for the new families (everything is *u/passive).

Methodology

72 hours of passive bus capture using the upstream john30/ebusd
container (v26.1) on host:

docker exec ebusd ebusctl grab        # enable grab
# every 60 s for 72 h:
docker exec ebusd ebusctl grab result all >> capture.log

Output: 4313 hourly snapshots, 8.99 M telegram observations, 137
distinct (zz=0x76, pb, sb, id) tuples. Per-byte statistics
(nunique, entropy, 0xff_ratio, byte_min/max, monotonicity) were
computed across the histogram of distinct response payloads, weighted by
ebusd's grab counter. Decoder selection used physical plausibility
(temperature range, monotonic counters, BCD shape) plus cross-validation
against simultaneous HMU telegrams in the same capture.

Tooling and full per-message report:
https://github.com//ebusd-vwzio-reverseeng
(see analysis/per_message/*.md, analysis/hypotheses.csv,
analysis/hmu_correlation.csv).

Per-family evidence

b504 00DateTime (reuses Hcmode_inc.DateTime)

sample request : 1076b5040100
sample response: 0344462227040126d20b
                 dcfstate=03 valid
                 btime=44 46 22  → 22:46:44 BCD ✓
                 bdate=27 04 01 26 → 27 Apr 2026, dow=1 ✓
                 temp =d2 0b      → D2B/256 = 0x0bd2/256 = +11.82 °C
distinct over 72 h: 4211
field n_distinct=2604..4211 across all 8 active bytes; 0xff_ratio=0.06.

b511 01Status01 (reuses Hcmode_inc.Status01)

sample response: 5cffc50bff610000ff
                 temp     = 5c   D1C → 46 °C  (flow)
                 temp_1   = ff   D1C → no data (return temp not present)
                 temp_2   = c50b D2B → +11.77 °C (outside)
                 temp_3   = ff   D1C → no data (DHW not present)
                 temp_4   = 61   D1C → 48.5 °C (storage)
                 pumpstate= 00 → off
trailing 2 bytes: 00 ff (implicit IGN; 9-byte response)
distinct: 2604; max grab count: 25813 (≈6/min).

b511 02Status02 (reuses Hcmode_inc.Status02)

Polled only when the heat pump is actively heating; observed in our
capture window only briefly, byte layout matches HMU Status02
verbatim.

b512 0f 00 01Stats01 (NEW)

7-byte response. 2850 distinct payloads, max grab count 21530 (≈5/min).
Layout (per @chrizzzp via @cyberthom42 issue #315):

off | n_unique | byte_min | byte_max | type      | meaning
----+---------+-----------+-----------+----------+----------------------
  0 |   209   |   0x00    |   0xff    | UCH       | modulation/activity
  1 |     3   |   0x02    |   0x04    | UCH       | mode (2..4)
  2 |     1   |   0x00    |   0x00    | IGN       | reserved
  3 |   256   |   0x00    |   0xff    | D2C low   | supply temperature
  4 |     3   |   0x02    |   0x04    | D2C high  |   (cont.)
  5 |     6   |   0x09    |   0x0e    | UCH/10    | water pressure (bar)
  6 |     1   |   0xff    |   0xff    | IGN       | trailing pad

Decoded ranges over 72 h:

modulation1   (UCH):     0..255    (likely compressor activity %)
mode1         (UCH):     2..4      (likely operating mode enum)
supplytemp    (D2C/16):  ~32..80 °C
waterpressure (UCH/10):  ~0.9..1.4 bar

Live readings (a representative snapshot on the contributing system):

modulation1=28  mode1=3  supplytemp=51.75 °C  waterpressure=1.3 bar

Plausibility: 1.3 bar matches the typical heating-circuit static
pressure for a residential floor-heating system; 50 °C supply temp is
in the expected range for an HC flow temp; the modulation byte tracks
the HMU compressor activity over time.

b512 0f 00 02Stats02 (NEW)

Same byte layout as Stats01. 714 distinct payloads, max grab count
4293 (≈1/min). On @chrizzzp's split system this register returns the
identical answer as 0f 00 01; on this HW=5103 the temperatures and
pressures track a different circuit (likely HC vs HWC primary side
of the dual-mode hydraulic station).

Decoded ranges over 72 h:

supplytemp_2    (D2C/16):  ~22..76 °C
waterpressure_2 (UCH/10):  ~0.4..1.4 bar (lower than Stats01)

Cross-validation: VWZIO Stats02.supplytemp_2 Pearson-correlates with
HMU RunDataCompressorOutletTemp at r = 0.993 with identical
decoded range (46.62 °C). Likely reflects thermal coupling across the
primary heat exchanger (water side ↔ refrigerant side) rather than a
common physical sensor.

b516 14PowerConsumptionHmu (reuses B516_inc.PowerConsumptionHmu)

5-byte response (1 IGN + 4 EXP /1000 kW). Polled ~1/min, monotonic
under sustained heat-pump operation in our 72 h window. Live values on
a standby unit: 0.003 kW (3 W), consistent with HMU's
PowerConsumptionHmu reading 0.0065 kW at the same time.

Validation

  • npx tsp compile --emit @ebusd/ebus-typespec --warn-as-error exits 0
    on this branch.
  • The compiled CSV passes ebusd --checkconfig --configpath=... exit 0.
  • Live deploy on the contributing system: ebusd 26.1 on a real bus,
    --scanconfig --configpath=/etc/ebusd, all five new messages decode
    to physically plausible values and sustain over 24+ hours. MQTT
    discovery via mqtt-hassio.cfg produces 12 sensor entities under
    device ebusd vwzio, all picked up by Home Assistant.
  • HMU master continues to operate normally throughout — passive
    definitions only, no extra bus traffic from ebusd.

Hardware identification

ebusctl info
  address 76: slave #9, scanned
    "MF=Vaillant;ID=VWZIO;SW=0901;HW=5103",
    loaded "vaillant/76.vwzio.csv"

Cascade context: VWZIO is the second of two aroTHERM units, behind an
HMU on 0x08 (also HW=5103, SW=0902). Service tests already in upstream
were originally added for VWZ 0522;5103 in PR #330; HW=5103 / SW=0901
adds the process-telemetry families above without affecting the VWZ
service-test reads.

Out of scope / not in this PR

  • Active reads (r-direction): this PR is *u-only by design. The
    master polls these messages on its own; we only listen. @chrizzzp's
    Improved HMU config #407 added a much larger r-direction set including the b514 Test-mode
    family (SupplyTemp, ReturnTemp, BuildingCircuitWaterPressure,
    BuildingCircuitFlow, OutdoorTemp, CompressorOutletTemp, …) —
    worth a separate PR after community review.
  • Slow-poll registers our system never returns data for: in our
    72-h capture the master polled b51a 05ff32{40, 49, 4c, 5c, 23d}
    but the VWZIO answered 02 ff 01 (no data) on every snapshot.
    These would be TotalRunningHours, TotalEnergyUsageImmersionHeater,
    ImmersionHeaterPower, ImmersionHeaterTemp, FlowPressure per
    Improved HMU config #407. Hardware-dependent — adding them as *u would still work for
    systems that do populate them, but I'm leaving them out of this PR
    to keep "all defined messages decode at least once" as a guarantee.
  • Per-bit decoding of mode1/mode2 (small UCH enum 2..4):
    correlates loosely with HMU RunDataStatuscode transitions but not
    1:1; treating as raw UCH.
  • b504 0100 off 8-9 is decoded as temp D2B identically to the
    HMU's DateTime.temp. On this VWZIO unit the reading equals
    Status01.temp_2 at the same instant; both come through
    Hcmode_inc so no extra change needed.

Files changed

  • src/vaillant/76.vwzio.tsp — service-test block kept verbatim;
    Stats01, Stats02, u_b512 default added; _includes extended
    with Hcmode_inc.

No changes to _templates.tsp, hcmode_inc.tsp, b516_inc.tsp or any
other shared file.

The current src/vaillant/76.vwz.tsp only exposes three service-level
test messages (b514 05*). The VWZIO is the Vaillant Hydraulikstation
that sits between an aroTHERM heat pump (HMU on 0x08) and the
building water circuits.

This PR builds on prior community work that never landed upstream:

- john30#315 (open) by @cyberthom42 — VWZ MEH 97/6 decode (German names),
  parallel hardware variant of the same family.
- john30#407 (closed) by @chrizzzp — comprehensive 76.vwz.csv covering
  Heizstab counters, b514 Test-mode telemetry, English naming.
  We adopt @chrizzzp's English names where ranges overlap.
- The b512 0f 00 0X 7-byte payload layout is per the wiki page on
  @cyberthom42's vaillant-arotherm-plus repo (originally posted by
  @chrizzzp), verified byte-for-byte against a 72-h grab capture on
  this hardware.
- john30#490 (open) + john30#504 by @filippz — b516 energy-statistics framework.
  Already pulled into upstream b516_inc.tsp which we reuse via the
  existing _includes union.

Methodology: 72 hours of passive bus capture using the upstream
john30/ebusd container (v26.1) on a HW=5103/SW=0901 unit. 4313 hourly
snapshots, 8.99 M telegram observations, 137 distinct
(zz=0x76, pb, sb, id) tuples. Per-byte statistics + physical
plausibility scoring + cross-correlation against simultaneous HMU
b511 0x76 telegrams.

Added in this PR:

| PB SB | ID         | Cycle  | Bytes | Schema |
|-------|------------|-------:|------:|--------|
| b504  | 00         | ~1/min |    10 | reuse Hcmode_inc.DateTime |
| b511  | 01         | ~6/min |     9 | reuse Hcmode_inc.Status01 |
| b511  | 02         | (idle) |     8 | reuse Hcmode_inc.Status02 |
| b512  | 0f 00 01   | ~5/min |     7 | NEW Stats01 (UCH+UCH+IGN+D2C+UCH/10+IGN) |
| b512  | 0f 00 02   | ~1/min |     7 | NEW Stats02 (same layout, alternate poll) |
| b516  | 14         | ~1/min |     5 | reuse B516_inc.PowerConsumptionHmu |
| b51a  | 05ff32 46  |   1/h  |    10 | NEW ImmersionHeaterHours |
| b51a  | 05ff32 4a  |   1/h  |    10 | NEW ImmersionHeaterStarts |
| b51a  | 05ff32 4b  |   1/h  |    10 | NEW PrioritySwitchingValveOps (3-way valve) |
| b51a  | 05ff33 1a  |   1/h  |    10 | NEW ImmersionHeaterPowerLimit |

DateTime / Status01 / Status02 / Status16 / SetMode arrive via the
existing Hcmode_inc union; PowerConsumptionHmu via B516_inc. Their
byte layouts on 0x76 are byte-identical to those used by the HMU on
0x08. Stats01/02 + the four Heizstab b51a counters are net-new.

Validation:
- npx tsp compile --emit @ebusd/ebus-typespec --warn-as-error exits 0
- ebusd --checkconfig with the compiled CSV exits 0
- Live deploy on the contributing system, ebusd 26.1 with
  --scanconfig --configpath. All five new messages decode to
  physically plausible values. MQTT discovery via mqtt-hassio.cfg
  produces 12 sensor entities; HMU continues normal operation
  throughout.

Hardware identification:
  address 76: slave john30#9, scanned
    "MF=Vaillant;ID=VWZIO;SW=0901;HW=5103",
    loaded "vaillant/76.vwzio.csv"

Out of scope:
- No r/w direction added; passive (*u) only. The b514 Test-mode family
  (~40 sensors) from @chrizzzp's john30#407 is left for a follow-up PR
  because it requires r-direction.
- Five b51a 05ff32xx slow-poll registers our master polls but the
  device returns "no data" on this hardware (TotalRunningHours,
  TotalEnergyUsageImmersionHeater, ImmersionHeaterPower current,
  ImmersionHeaterTemp, FlowPressure) — hardware-dependent.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@buliwyf42
Copy link
Copy Markdown
Author

Heads-up for future readers (cross-referenced from #600, just closed):

The 14 b516 1000ffff* energy-statistics messages reachable via the existing B516_inc union, when polled on a VWZIO (slave 0x76), specifically count Heizstab (immersion / electric backup heater) consumption, not the heat pump's own electricity. On this install:

ImmersionHeaterHours       =  74 h
ImmersionHeaterPowerLimit  =   6 kW    →  upper bound = 444 kWh
StatElectricEnergySum      = 442.07 kWh
StatElectricEnergySumHc    = 402.10 kWh
StatElectricEnergySumHwc   =  39.96 kWh
                              ↑ matches 442 exactly

The HMU-side electricity counter would be needed for compressor / heat-pump COP figures. Naming and @divisor of the b516_inc fields are correct as upstream defines them.

@chrizzzp
Copy link
Copy Markdown

@buliwyf42

Nice analysis and basis for a PR on the hydraulic station, long time neglected in the PRs. However I find this draft a bit difficult to read (I guess due to Claude taking over quite heavily?).

Nevertheless, here's a first update:

b512 0f 00 01Stats01 (NEW)

7-byte response. 2850 distinct payloads, max grab count 21530 (≈5/min). Layout (per @chrizzzp via @cyberthom42 issue #315):

off | n_unique | byte_min | byte_max | type      | meaning
----+---------+-----------+-----------+----------+----------------------
  0 |   209   |   0x00    |   0xff    | UCH       | modulation/activity
  1 |     3   |   0x02    |   0x04    | UCH       | mode (2..4)

These two bytes actually encode the HWC temperature (if connected to hydraulic station), so it should be:

 0 |  D2C low   | HWC temperature
 1 |  D2C high |   (cont.)

Can you confirm?

@chrizzzp
Copy link
Copy Markdown

chrizzzp commented Apr 29, 2026

Here's another addition (works for HMU and VWZ00). Quite complex, but covers most of the real-time information on the display of the hydraulic station, so far not in any PR:

*r,,,,,,B511,,,,,,,,,,,,,,,,,,,,,,,,,
r,,Status07,,,,,07,power,,UCH,,%,Compressor power,dailyenvyield,,energy,10,kWh,daily environmental energy yield,disp1ay_b0_heaterenabled,,BI0,0=off;1=on,,,display_b1,,BI1,0=off;1=on,,,display_b2_backupheater,,BI2,0=off;1=on,,,display_b3,,BI3,0=off;1=on,,,display_b4,,BI4,0=off;1=on,,,display_b5_noisereduction,,BI5,0=off;1=on,,,display_b6_dhwecomode,,BI6,0=off;1=on,,,display_b7,,BI7,0=off;1=on,,,DisplayHeaterMain,,state07heatermode,,,,DisplaySystemPressure,,UCH,30,,,DisplayHeaterBackup,,state07heatermode,,,,

uses the following template (_templates.csv) which could be nested in the 76.wvz.csv:

state07heatermode,UCH,0x00=standby;0x01=standby;0x0a=error;0x0b=error;0x08=hc;0x09=hc;0x10=cool;0x11=cool;0x80=dhw;0x81=dhw,,

@buliwyf42
Copy link
Copy Markdown
Author

buliwyf42 commented Apr 30, 2026

@chrizzzp Thanks — fair feedback on the prose. I'll trim the LLM-style commentary out of the description and rewrite it as a normal short PR body before the next push.

On b512 0f 00 01 bytes 0+1 — confirmed, you're right.

Decoded our 72 h grab capture (n=4313 distinct payloads on the VWZIO, slave 0x76) treating bytes 0+1 as little-endian D2C:

range  : 42.69 … 71.06 °C
mean   : 47.75 °C
byte-1 : {0x02: 2461, 0x03: 1692, 0x04: 160}   (high-byte distribution)

That is unambiguously a HWC tank temperature, not modulation+mode. My UCH+UCH split was wrong — byte 1 only ever taking values {2,3,4} fooled me into reading it as a small enum.

I'll merge the two fields into a single hwctemp: D2C in 76.vwzio.tsp (and the corresponding Stats01 row of 76.vwzio.csv) and re-deploy.

Quick follow-up question on b512 0f 00 02 (Stats02):

Same offsets on the second variant decode plausibly as a temperature too, but the range is wider:

b512 0f00 02, bytes 0+1 as D2C:
  range : 22.19 … 71.69 °C
  mean  : 49.96 °C
  byte-1: {0x01: 29, 0x02: 544, 0x03: 3259, 0x04: 402}

The 22 °C low-end excursions don't really fit DHW. On this install Stats02's bytes 3+4 (the field I was calling supplytemp_2) correlate r=0.993 with HMU RunDataCompressorOutletTemp, so 0f0002 looks like a different circuit's data, not just a re-poll. Do you happen to know whether 0f0002's bytes 0+1 are the same HWC sensor, or something else (HC return / buffer / inlet)?

On Status07 (b511 07) — happy to add it.

I didn't see this on this slave naturally — b511 07 shows zero hits in our 4313 captured b511-family responses on 0x76 (only 01 is master-polled here). But as an r-direction CSV row ebusd will issue the request itself, so that's fine if the slave does respond.

I'll wire it up locally first and confirm the response decodes cleanly before adding it to this PR. Two small notes on the row you posted:

  • typo: disp1ay_b0_heaterenableddisplay_b0_heaterenabled (digit-1 vs lowercase-l)
  • the state07heatermode template — adding it to a per-vendor file (e.g. inline in 76.vwzio.csv or in a small Vaillant-specific include) rather than pushing it to the global _templates.csv is probably less invasive for the maintainer; happy to put it wherever you'd prefer.

Will push an updated PR with (1) the Stats01 fix, (2) Status07, and a cleaned-up description, once Stats02 byte-0/1 is settled.

real buliwyf42here:
sorry for the claude heavy content. I initially did this all for myself and my heating setup.
In in end i thought the community will profit of it as well.
Unfortunately my commits and push request would only contain a few word. (like "look here") ;)
Or wouldn't happen at all.

If you need any further data, i'm happy to help.

@Hoppur
Copy link
Copy Markdown

Hoppur commented Apr 30, 2026

Hey there, I've been monitoring my setup for the past winter (HMU00, VWZIO, VR71, CTLV3).
Regarding b512 0f xx yy messages, I saw only one of them every 10 seconds, but never multiple ones. That led me to the assumption that xx and yy were not part of the register address, but of the content. They fit perfectly to the status of the circulation pump and to heating the DHW. I also found the multi function input in this message.

Here is my CSV definition. I don't know typescript yet.
u,hwc,StatusDhw,Δt=10s,,76,B512,0f,StatusCircPump,m,U1L,0=off;2=on,,,DhwActive,m,U1L,1=off;2=on,,,HwcStorageTemp,s,D2C,,°C,0.01 °C resolution,MultifunctionInput,s,U1L,0=off;1=on,,on as in button pressed,supplyTemp,s,D2C,,°C,0.01 °C resolution,WaterPressure,s,U1L,10,bar,,temp_7,s,U1L,,,constant 255?

Per @Hoppur's PR review: the bytes after `0f` in `b512 0f xx yy`
are message CONTENT (StatusCircPump + DhwActive), not id-suffix.
The same logical message fires every 10 s with whatever the
current state is. Replace the previous Stats01 (id=0f0001) +
Stats02 (id=0f0002) split with a single StatusDhw (id=0f) message.

Net new fields exposed:
 - StatusCircPump (master byte 1, enum off/on)
 - DhwActive (master byte 2, enum off/on)
 - MultifunctionInput (slave byte 2, enum off/on; previously IGN)
 - HwcStorageTemp (slave bytes 0+1, D2C — replaces what chrizzzp
   correctly identified as HWC tank temperature)
 - temp_7 trailing pad (always 0xff in capture; preserved as UCH
   for now, can be IGN if maintainer prefers)

Verified against a 72 h grab capture on this install (HW=5103):
 - only (pump=0, dhw=1) and (pump=0, dhw=2) ever observed
 - MultifunctionInput always 0
 - temp_7 always 0xff
 - HwcStorageTemp range 42-71 °C, mean 47.7 °C
 - SupplyTemp shows HC flow when DhwActive=off and tracks HMU
   compressor outlet (r=0.993) when DhwActive=on — consistent with
   the supply line being routed through the 3-way valve

Three new enums in the namespace (Values_StatusCircPump,
Values_DhwActive, Values_OnOff) following the project's
Values_* naming convention.
@buliwyf42
Copy link
Copy Markdown
Author

@Hoppur thank you — that's the right answer to the question I was asking @chrizzzp. Verified against our 72 h capture and pushed a follow-up commit (a68da0c) replacing Stats01 + Stats02 with a single StatusDhw (id = 0f) matching your scheme.

Empirical confirmations from this install (HW=5103, also HMU+VWZIO+VR_71+CTLV2):

Field Hoppur claim Our capture
only (pump, dhw) combinations seen should be a small set only (0,1) and (0,2) — n=8547
MultifunctionInput ∈ {0, 1} enum off/on always 0x00 (button never pressed)
temp_7 constant 255? yes byte 6 distribution: {255: 8547} — 100% always 0xff
HwcStorageTemp D2C bytes 0+1 DHW tank temp range 42.69 … 71.06 °C, mean 47.7 °C ✓

Two implementation notes in the new commit:

  • I named the enums Values_StatusCircPump, Values_DhwActive, Values_OnOff to follow the project's Values_* convention I saw in 05.vd4.tsp etc. Functionally identical to your CSV.
  • I kept temp_7 typed as UCH (so the value surfaces in MQTT as the literal byte) rather than IGN. With the conventional UCH it shows as "no data" in HA when 0xff (replacement value), which works either way; happy to flip it to IGN if you'd prefer.

This also resolves the open question I had about b512 0f00 02 bytes 0+1 — they're the same physical sensor (HwcStorageTemp), the message just fires under different DhwActive states. The wider 22-72 °C range I was seeing in our 0f0002 subset was the same DHW tank during charge cycles. The r=0.993 correlation between Stats02's old supplytemp_2 and HMU's RunDataCompressorOutletTemp is also explained: the supply line is fed by the compressor when DhwActive=on (3-way valve routing), so the same byte legitimately reads "compressor outlet temperature" in that state and "HC flow temperature" otherwise.

@chrizzzp — your Status07 (b511 07) addition is also live in the same PR commit (HMU and VWZIO both respond cleanly: 0%, 23.5 kWh, ..., DisplayHeaterMain=standby, 1.30 bar, DisplayHeaterBackup=standby on this install right now). Two adjustments:

  • typo fixed: disp1ay_b0_heaterenableddisplay_b0_heaterenabled
  • I inlined the state07heatermode enum values directly in the row instead of touching _templates.csv — figured that's less invasive for a first pass; happy to extract if you'd prefer.

If either of you spots anything else worth nailing down in this round, please flag.

@chrizzzp
Copy link
Copy Markdown

chrizzzp commented May 1, 2026

@Hoppur
Good spot! As I don't use neither the hydraulic stations DHW nor the DHW circulation pump these values were always constant for my system.

@buliwyf42 @Hoppur

BTW I suggest to rename the StatusCircPump to StatusDHWCircPump (if it is really the circulation pump for DHW) so it is not confused with the circulation pump(s) for the heating circuit(s).

I should mention that I don't have the VWZIO hydraulic station, but the VWZ00 (VWL 127/5 IS). The exact physical and Ebus register differences between both units are not totally clear to me yet. So far many registers seem to be identical.

And also firmware versions of the different hydraulic stations do matter as well (e.g. the more granular electrical power consumption readings in Watt b516 14 IMO only work with firmwares SW=0901 and higher (my system: ID=VWZ00;SW=0522;HW=5103)).

I think we should consider this in this PR with the use of conditional definitions.

@chrizzzp
Copy link
Copy Markdown

chrizzzp commented May 1, 2026

@buliwyf42

@chrizzzp — your Status07 (b511 07) addition is also live in the same PR commit (HMU and VWZIO both respond cleanly: 0%, 23.5 kWh, ..., DisplayHeaterMain=standby, 1.30 bar, DisplayHeaterBackup=standby on this install right now). Two adjustments:

* typo fixed: `disp1ay_b0_heaterenabled` → `display_b0_heaterenabled`

* I inlined the `state07heatermode` enum values directly in the row instead of touching `_templates.csv` — figured that's less invasive for a first pass; happy to extract if you'd prefer.

It would be good to have the resulting compiled 76.vwzio.csv available as well in order to test the updated definitions.

In response to @chrizzzp's PR john30#598 review:

1. Renamed StatusCircPump → StatusDHWCircPump per his request (he
   correctly pointed out the field is the DHW-side circulation pump,
   so the fully-qualified name avoids confusion with the heating
   circuit pumps).

2. Added Status07 (b511 07) per @chrizzzp's CSV row in the same
   review. Translated to TypeSpec with three small adjustments:
     - the `disp1ay_b0_heaterenabled` typo (digit-1 vs lowercase-l)
       fixed to `display_b0_heaterenabled`
     - the implied `state07heatermode` template inlined as a
       Values_Status07HeaterMode enum local to the Vwz namespace
       (less invasive than touching _templates.csv for a single new
       message; happy to extract if maintainer prefers)
     - the duplicate-string-named codes (e.g. 0x00 and 0x01 both
       "standby") given distinct names with `_N` suffixes since
       TypeSpec enums require unique identifiers — the two codes
       per state aren't documented but cleanly preserved
   The compiled CSV row matches @chrizzzp's intent: power %,
   dailyenvyield UIN/10 kWh, 8-bit display flag bitfield,
   DisplayHeaterMain/Backup mode enums, DisplaySystemPressure UCH/30
   bar.

3. Switched the StatusDhw master-direction annotations from `@master`
   (which wasn't a real Ebus decorator and caused the lint to fail)
   to the correct `@out` decorator. Verified via `npx tsp compile`.

Verified live on this install (HW=5103 / SW=0901):
  - StatusDhw: `off;off;49.56;off;49.62;1.3;-`
  - Status07 on HMU + VWZIO both decode cleanly with sane values
  - the existing `State` model in 08.hmu.tsp at b511/07 already
    provides a 5-byte subset; not touched here so HMU users keep
    backward-compatible names. The 7-byte extended decode is
    available on whatever circuit imports this file.

Compiles cleanly via `npm run lint` and `npm run compile-en`.
@buliwyf42
Copy link
Copy Markdown
Author

@chrizzzp thanks — pushed 1b5c686 covering all three points:

1. Rename StatusCircPumpStatusDHWCircPump done. Agreed it disambiguates from HC-side circulation pumps.

2. Status07 (b511 07) translated from your CSV row into TypeSpec and added to 76.vwz.tsp. Three small adjustments:

  • the disp1ay_b0_heaterenabled typo fixed
  • the implied state07heatermode template inlined as a Values_Status07HeaterMode enum local to the Vwz namespace — less invasive than editing _templates.csv for a single new message; happy to extract if you'd prefer
  • duplicate-string-named codes (e.g. 0x00 and 0x01 both "standby") given distinct names with _N suffixes (standby_0 / standby_1, hc_8 / hc_9, etc.) since TypeSpec enums need unique identifiers. Functionally identical to your CSV; cosmetically a small UX nit because HA shows e.g. standby_0 instead of bare standby. If you have a TypeSpec idiom that preserves duplicate names I'd switch to it; otherwise this seems acceptable

3. Compiled 76.vwz.csv now compiles cleanly. I had to also fix @master@out (@master isn't a valid Ebus decorator — that was inherited from the original PR draft and caused npm run lint to error). The compiled output for the two new messages:

u,,,StatusDhw,Hydraulikstation DHW state per Hoppur PR #598 review,,,b512,0f,
  statusdhwcircpump,m,UCH,0=off;2=on,,circulation pump state,
  dhwactive,m,UCH,1=off;2=on,,DHW heating active,
  hwcstoragetemp,,D2C,,°C,DHW tank temperature,
  multifunctioninput,,UCH,0=off;1=on,,multifunction input button state,
  supplytemp,,D2C,,°C,supply line temperature (HC flow if DhwActive=off / compressor outlet if on),
  waterpressure,,UCH,10,bar,heating-circuit pressure,
  temp_7,,UCH,,,trailing pad always 0xff
r,,,Status07,Hydraulikstation display state per chrizzzp PR #598 review,,,b511,07,
  power,,UCH,,%,,
  dailyenvyield,,UIN,10,kWh,,
  display_b0_heaterenabled,,BI0,,,, ... display_b7,,BI7,,,,
  displayheatermain,,UCH,0=standby_0;1=standby_1;8=hc_8;9=hc_9;10=error_a;11=error_b;16=cool_10;17=cool_11;128=dhw_80;129=dhw_81,,,
  displaysystempressure,,UCH,30,bar,,
  displayheaterbackup,,UCH,<same enum>,,

(Line-wrapped here for readability; the compiler emits a single line.) Full compiled 76.vwz.csv is available at https://raw.githubusercontent.com/buliwyf42/ebusd-configuration/vaillant-vwzio-hydraulikstation-telemetry/ once the maintainer's CI runs, or generated locally via npm run compile-en against the branch.

On VWZIO vs VWZ00 + firmware conditionals — this PR's only r-direction message is Status07 (which works on both per your post; HMU side already has a simpler State at the same address that covers the first 5 of 7 bytes, untouched here for backward compat). All other new messages are u-direction passive listens, so older firmware that doesn't emit them simply produces no entity — no error. So I haven't gated anything with @condition(Id.Id.sw, ...) in this PR. Power-consumption (b516 14) — which I see is your example for SW-conditional behavior — isn't part of this PR's TSP either way (it's only in our locally-deployed CSV; happy to add it as a separate PR with the SW≥901 guard if useful to upstream).

If you can run the new 76.vwz.csv against your VWZ00 SW=0522 install and let me know whether Status07 returns sane values there, that'd close the loop on the multi-hardware testing question.

@chrizzzp
Copy link
Copy Markdown

chrizzzp commented May 3, 2026

@buliwyf42

The Status07 definition seems incomplete (or truncated) compared to my post:

display_b0_heaterenabled,,BI0,,,, ... display_b7,,BI7,,,,

The dots (...) should not be there. Instead there should be more definitions from the binary type (disp1ay_b0_heaterenabled,,BI0,0=off;1=on,,,display_b1,,BI1,0=off;1=on,,,display_b2_backupheater,,BI2,0=off;1=on,,,display_b3,,BI3,0=off;1=on,,,display_b4,,BI4,0=off;1=on,,,display_b5_noisereduction,,BI5,0=off;1=on,,,display_b6_dhwecomode,,BI6,0=off;1=on,,,display_b7,,BI7,0=off;1=on).

I also doubt the "" definition is valid:

displayheaterbackup,,UCH,<same enum>,,

IMO in this form it's impossible to test and should maybe be manually curated?

@chrizzzp pointed out the compiled CSV's BI fields lacked the
0=off;1=on enum that his original CSV row had. The TypeSpec BI<n>
types are inherently 0/1 and HA's mqtt-hassio.cfg has a generic
on/off rule that would render them correctly anyway, but explicit
@values(Values_OnOff) annotations make the compiled CSV match his
intent exactly. No semantic change, just a cosmetic alignment.

The earlier reply abbreviated the compiled CSV with `...` and
`<same enum>` placeholders for readability, which made it look
like the file itself was truncated. Posting the full untruncated
row in the next reply.
@buliwyf42
Copy link
Copy Markdown
Author

@chrizzzp sorry — that was a misleading abbreviation on my side. The ... and <same enum> were elisions I added in the comment for readability. The actual compiled CSV is complete; the abbreviation made it look truncated. Mea culpa.

Pushed 18dfa80 adding @values(Values_OnOff) to all eight BI fields, so the compiled CSV now also includes the 0=off;1=on per-bit enum that your original CSV row had (the TypeSpec BI types are inherently 1-bit and HA's mqtt-hassio.cfg renders them via the on/off rule, but explicit values make the compiled CSV match your intent exactly).

Full untruncated rows from outcsv/@ebusd/ebus-typespec/vaillant/76.vwz.csv after that commit:

StatusDhw (b512 0f):

u,,,StatusDhw,Hydraulikstation DHW state per Hoppur PR #598 review,,,b512,0f,statusdhwcircpump,m,UCH,0=off;2=on,,circulation pump state,dhwactive,m,UCH,1=off;2=on,,DHW heating active,hwcstoragetemp,,D2C,,°C,DHW tank temperature,multifunctioninput,,UCH,0=off;1=on,,multifunction input button state,supplytemp,,D2C,,°C,supply line temperature (HC flow if DhwActive=off / compressor outlet if on),waterpressure,,UCH,10,bar,heating-circuit pressure,temp_7,,UCH,,,trailing pad always 0xff

Status07 (b511 07):

r,,,Status07,Hydraulikstation display state per chrizzzp PR #598 review,,,b511,07,power,,UCH,,%,,dailyenvyield,,UIN,10,kWh,,display_b0_heaterenabled,,BI0,0=off;1=on,,,display_b1,,BI1,0=off;1=on,,,display_b2_backupheater,,BI2,0=off;1=on,,,display_b3,,BI3,0=off;1=on,,,display_b4,,BI4,0=off;1=on,,,display_b5_noisereduction,,BI5,0=off;1=on,,,display_b6_dhwecomode,,BI6,0=off;1=on,,,display_b7,,BI7,0=off;1=on,,,displayheatermain,,UCH,0=standby_0;1=standby_1;8=hc_8;9=hc_9;10=error_a;11=error_b;16=cool_10;17=cool_11;128=dhw_80;129=dhw_81,,,displaysystempressure,,UCH,30,bar,,displayheaterbackup,,UCH,0=standby_0;1=standby_1;8=hc_8;9=hc_9;10=error_a;11=error_b;16=cool_10;17=cool_11;128=dhw_80;129=dhw_81,,

All 8 display_bN fields with explicit 0=off;1=on, both displayheatermain and displayheaterbackup carry the full enum verbatim.

Diff vs. your original CSV row, by field:

  • typo: disp1ay_b0_heaterenableddisplay_b0_heaterenabled (digit-1 → lowercase-l)
  • enum keys: your 0x00=standby → my 0=standby_0 (decimal + _N suffix because TypeSpec enums require unique identifier names — same numeric mapping, different string label)

Everything else byte-identical. Should be testable now against your VWZ00 SW=0522.

@Hoppur
Copy link
Copy Markdown

Hoppur commented May 3, 2026

duplicate-string-named codes (e.g. 0x00 and 0x01 both "standby") given distinct names with _N suffixes (standby_0 / standby_1, hc_8 / hc_9, etc.) since TypeSpec enums need unique identifiers. Functionally identical to your CSV; cosmetically a small UX nit because HA shows e.g. standby_0 instead of bare standby. If you have a TypeSpec idiom that preserves duplicate names I'd switch to it; otherwise this seems acceptable

I've never seen the odd (+0x01) variants on the bus. I suggest using names without suffixes for the even ones and names with suffixes only for the odd ones. This way we don't see _0 suffixes in normal operation and we can still differentiate the odd ones if they ever show up.
state07heatermode,UCH,0x00=standby;0x01=standby_1;0x0a=error;0x0b=error_1;0x08=hc;0x09=hc_1;0x10=cool;0x11=cool_1;0x80=dhw;0x81=dhw_1,,

I kept temp_7 typed as UCH (so the value surfaces in MQTT as the literal byte) rather than IGN. With the conventional UCH it shows as "no data" in HA when 0xff (replacement value), which works either way; happy to flip it to IGN if you'd prefer.

As long as we don't recognize any information in this byte I suggest IGN. IMHO it should not reach the MQTT broker if there's nothing in it. I only had it as U1L to monitor it.

@Hoppur on PR john30#598 review (2026-05-03):
1. He's never observed b511 07 sent to slave 0x76 (VWZIO) on his
   bus — only to 0x08 (HMU). Our own 72 h passive capture also
   shows zero hits on 0x76. The "working" responses we got from
   VWZIO via active read look like garbage that happens to decode
   plausibly. So Status07 doesn't belong on the VWZ TSP. Removed
   the model + the r_b511 base + the Values_Status07HeaterMode
   enum from 76.vwz.tsp.

2. The trailing pad byte in StatusDhw is constant 0xff and we
   don't recognize anything in it. Switched from `temp_7: UCH`
   (which exposed it as a useless MQTT entity) to a 1-byte
   `IGN`. ebusd skips IGN fields when publishing.

Net: this PR's only `r`-direction message was Status07 — now
removed — so the PR is back to pure `u`-direction VWZIO telemetry
plus the existing service tests, the b51a Heizstab counters, and
the StatusDhw consolidated message.

Adding Status07 to the HMU side (08.hmu.tsp / 08.hmu.HW5103.tsp)
is its own discussion — the existing `State` model at b511 07
covers the first 5 of the 7 bytes; extending to chrizzzp's
7-byte schema would be a separate PR.
@buliwyf42
Copy link
Copy Markdown
Author

@Hoppur thanks — all three landed in f1a0aae:

  1. Status07 removed from 76.vwz.tsp. Cross-checked our 72 h passive grab against your observation: b511 07 has zero hits with zz=0x76 in our capture, only against zz=0x08. The "working" responses we got from VWZIO via active read look like garbage that just decoded plausibly. PR scope now reverts to pure VWZ telemetry — no r-direction messages, only the existing service tests + b51a Heizstab counters + the unified StatusDhw. Adding Status07 to the HMU side (08.hmu.tsp already has a 5-byte State model at the same id; the 7-byte extended version per @chrizzzp's contribution would be a separate PR).

  2. Trailing pad now IGN:1 instead of UCH. Compiled row:

    u,,,StatusDhw,...,b512,0f,statusdhwcircpump,m,UCH,0=off;2=on,,...,
      waterpressure,,UCH,10,bar,heating-circuit pressure,
      ign,,IGN:1,,,trailing pad always 0xff in capture
    

    ebusd skips IGN for MQTT; one fewer useless entity.

  3. Enum naming. Took your "bare even / _1 odd" pattern. The TypeSpec source now reads:

    enum Values_Status07HeaterMode {
      standby: 0, standby_1: 1,
      hc: 8,      hc_1: 9,
      error: 10,  error_1: 11,
      cool: 16,   cool_1: 17,
      dhw: 128,   dhw_1: 129,
    }

    (this enum lives on the HMU side, since I dropped Status07 from VWZ — but I've applied the same naming to my local HMU CSV row so HA shows bare standby / hc / dhw under normal conditions instead of suffixed _0 variants.)

Side note that came out of testing: I had to also rename DisplaySystemPressureDisplayPressure on the HMU CSV row. The original name contains …sysTemPressure… (sub-string temp), which trips the sensor,temperature,measurement = temp|,°C$|,K$ rule in mqtt-hassio.cfg — HA then rejects the entity because device_class: temperature is incompatible with unit: bar. Worth flagging for whenever someone writes that 7-byte version into the HMU TSP.

Verified live on this install:

StatusDhw:               off;off;44.75;off;39.69;1.3
HMU Status07 (CSV side): power=0% dailyenvyield=6.1kWh ... displayheatermain=standby displaypressure=1.3bar displayheaterbackup=standby

PR is now 5 commits, ready for another look from chrizzzp + the maintainer.

@chrizzzp
Copy link
Copy Markdown

chrizzzp commented May 4, 2026

  1. The "working" responses we got from VWZIO via active read look like garbage that just decoded plausibly. PR scope now reverts to pure VWZ telemetry — no r-direction messages

While I'm personally fine with removing the Status07 message from the PR (as it is not updated automatically on the hydraulic station) I just would like to mention Status07 on my wvz00 hydraulic station by no means decodes as garbage, but gives reasonable values (albeit with less useful information as the updated Status07 from the hmu), for example: DisplaySystemPressure:

ebusctl r -f -c vwz00 -V Status07
vwz00 Status07 power=100 % [power];dailyenvyield=0.0 kWh [daily environmental energy yield];display_b0_heaterenabled=off;display_b1=off;display_b2_backupheater=off;display_b3=off;display_b4=off;display_b5_noisereduction=off;display_b6_dhwecomode=on;display_b7=on;DisplayHeaterMain=standby;DisplaySystemPressure=1.90;DisplayHeaterBackup=standby

Obviously, not all values are applicable to the backup heater (e.g. daily environmental yield) , but the ones that do carry information can be validated when activating the backup heater.

BTW, this is not surprising as the hmu and vwz00 share an identical PCB (same Vaillant part number) and many registers can be read from both units (although not all contain useful information).

Edit: BTW nice catch with DisplaySystemPressure "bug"!

@buliwyf42
Copy link
Copy Markdown
Author

@chrizzzp fair correction — I overstated. Looking back at our active-read on VWZIO 0x76, the response was power=100, dailyenvyield=0.0, ..., DisplayHeaterMain=standby, DisplaySystemPressure=1.30, DisplayHeaterBackup=standby — exact same pattern as your VWZ00 output, just with our system pressure of 1.30 bar instead of yours at 1.90. The "100%" and "0.0 kWh" aren't garbage, they're just not applicable to a hydraulic station that has no compressor — same as you note. Thanks for the cleanup.

The shared-PCB context is really useful too — that explains why so many r-direction registers respond meaningfully on both addresses despite the bus master only polling some of them on one or the other. Going to bear that in mind for any future register work on this family.

PR scope unchanged: Status07 stays out of 76.vwz.tsp because the master doesn't auto-poll it on 0x76 (so a u definition wouldn't fire), and adding it as r would mean ebusd actively spamming a register the slave isn't designed to update — even if the values come back consistently. Better to keep it on the HMU side where the master does poll it. Whoever ports the 7-byte schema to 08.hmu.tsp later (replacing the current 5-byte State) gets a small bonus on VWZ00 too via the shared registers — but that's a separate PR.

@rmalbrecht
Copy link
Copy Markdown

This merge would really be great, as for now, the default configuration contains nearly no support for the hydraulic station.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants