Skip to content

feat(sensor): ✨ expose compressor-2 heat meters on twin units - #816

Merged
rhammen merged 2 commits into
mainfrom
feat/815-twin-heat-meters
Sep 25, 2026
Merged

rhammen merged 2 commits into
mainfrom
feat/815-twin-heat-meters

Conversation

@rhammen

@rhammen rhammen commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

🔍 What this fixes

Resolves #815, requested in discussion #782 by @Sebbel1990.

A Luxtronik controller that drives two compressors (twin / master-slave) keeps a separate set of heat meters for each one. The integration only exposed compressor 1: calculations 151–154 equal P854/P878/P852 ÷ 100 exactly, so they are not totals. Compressor 2's figures were visible on the controller display and nowhere else.

Evidence comes from the #782 unit, a Novelan LD7 on V4.81.3. We have its diagnostics dump plus photos of the controller's Wärmemenge pages 1 and 2, taken at the same time:

Display (page 2) Register Raw → display
Heizung P1015 ID_Waermemenge_Hz_2 10802976 → 108029.7 kWh
Warmwasser P1016 ID_Waermemenge_BW_2 6194614 → 61946.1 kWh
seit 2.10.20 P1018 ID_Waermemenge_Seit_2 15528261 → 155282.6 kWh

P1010 ID_Einst_isTwin reads 1 on that unit. It reads 0 on every other pump in the diagnostics corpus (about 30), and all of those also return 1015–1018 reading 0. The registers exist everywhere, so the gate has to be the twin flag, not whether the register is present.

✨ Changes

  • New description field entity_active_key (model.py, coordinator.py). When it is set, entity_active_formula judges that register instead of the entity's own. A description can then declare "this entity only exists if register X says so", and the coordinator needs no per-register special case. That keeps niche entities off every other install entirely, rather than creating them disabled. coordinator.py changes by one line. A register the controller does not return still fails the gate (fix(coordinator): 🐛 entity_active creates entities for registers the controller does not expose #738 behaviour).
  • lux_overrides.py: 1010 is registered as Bool, and 1015–1018 as Energy2 (0.01 kWh, the same family as 852/854/878/879). The library's own names are kept.
  • sensor_entities_predefined.py: four total_increasing energy sensors mirror C151–C154, on the same devices:
    • Heating, DHW and the "seit" counter are gated by entity_active_key=P1010, entity_active_formula="!= 0". A twin whose counter still reads 0 still gets the entity.
    • The pool counter uses only its own != 0.0 gate, like C153. A non-twin always reads 0 there too.
    • Two decimals, like the other 0.01 kWh counters (1135–1139).
  • AUX_HEATER_ENERGY_FACTOR_BY_SERIES gains 4: 1. The Heat Meters Master/Slave Mode #782 unit is the first series-4 controller we have seen, and its P1059 reads 1579.8 kWh on the display. Over 176.3 h of ZWE1 run time that is 8.96 kW, against a 9.0 kW element (P1025). The fallback already produced this value, so behaviour does not change; the entry records the measurement.
  • Translations (en/de/nl/cs/pl) and icons.

🧪 Tests

  • test_coordinator.py: gate tests for entity_active_key covering P1010 off, on and absent, a twin whose own counter reads 0, and a counter that counts on a single unit.
  • test_sensor.py:
    • scaling of 1015/1016/1018, using the Heat Meters Master/Slave Mode #782 raw values against the display
    • the gate declarations and the energy-dashboard attributes
    • P1010 decoding raw 0/1 through its Bool datatype
    • series-4 aux-heater scale checked against the physics (energy ÷ run time ≈ the element's power)
  • test_predefined_entities.py: a guard that fails when a description sets entity_active_key without a formula, which would leave it ungated without any warning.

Full run: 1396 passed, 1 skipped, coverage 100%. ruff check, ruff format --check, basedpyright (0 errors) and codespell are clean.

🤖 Generated with Claude Code

rhammen and others added 2 commits September 25, 2026 20:10
- add four total_increasing energy sensors for compressor 2 of a twin
  (master/slave) unit: heating (P1015), DHW (P1016), pool (P1017) and the
  "seit" counter (P1018), mirroring calculations 151-154, which count
  compressor 1 only
- register P1015-P1018 as Energy2 (0.01 kWh) and P1010 ID_Einst_isTwin as
  Bool in lux_overrides; scale verified against the controller display of
  the #782 LD7 (V4.81.3), e.g. 10802976 counts = 108029.7 kWh
- add the entity_active_key description field: entity_active_formula then
  judges that register instead of the entity's own, so a description can
  declare that it only exists when another register says so, without a
  special case in the coordinator
- gate the twin sensors on P1010 != 0 (every single unit returns the block
  reading 0); the pool counter keeps its own != 0.0 gate, like C153
- add series 4 to AUX_HEATER_ENERGY_FACTOR_BY_SERIES at 0.1 kWh, measured
  on the same unit: 1579.8 kWh over 176.3 h of ZWE1 run time = 8.96 kW
  against a 9.0 kW element (P1025)
- translations (en/de/nl/cs/pl) and icons

Resolves #815

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- fail when a description sets entity_active_key without an
  entity_active_formula, which would leave it silently ungated
- assert P1010 decodes raw 0/1 to False/True through its Bool datatype
- clarify that only a plain visibility flag is limited to enabled-by-default

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage

Package Line Rate Health
custom_components.luxtronik2 100% ✔
Summary 100% (3971 / 3971) ✔

@rhammen
rhammen merged commit e33ff4d into main Sep 25, 2026
8 checks passed
@rhammen
rhammen deleted the feat/815-twin-heat-meters branch September 25, 2026 19:46
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.

feat(sensor): ✨ expose compressor-2 heat meters on twin (master/slave) units

1 participant