Skip to content

feat(switch): ✨ add manual EVU2 input for models that do not report SG2 - #812

Merged
rhammen merged 10 commits into
mainfrom
feat/500-evu2-manual-switch
Sep 24, 2026
Merged

rhammen merged 10 commits into
mainfrom
feat/500-evu2-manual-switch

Conversation

@rhammen

@rhammen rhammen commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

🔍 What this fixes

The last open piece of #500 (step 3 of the plan agreed with @siedi in that thread).

On the MSW2-9S, the SmartGrid status can never show a state that needs the SG2 contact closed. The controller reacts to SG2 normally but reports it nowhere the integration can read:

The gate is an explicit model list rather than a data test. calc 185 reads 0 in all 84 dumps in the diagnostics corpus, SmartGrid on or not, so nothing in the data separates "contact open" from "contact not reported".

✨ Changes

Manual SG2 input

  • New config switch EVU2 input (manual), on MSW2-9S units only (EVU2_MANUAL_INPUT_MODELS) and only while Smart Grid mode is not Off, the same rule as the SG offset numbers. Nothing is written to the heat pump.
  • SG2 is now resolved in this order: RFV sign (Lux 2.0 wiring) > manual switch > calc 185. The SmartGrid status sensor and the EVU2 binary sensor share that resolver, so they always agree.
  • The value lives on the coordinator and goes into each poll's data (LuxtronikCoordinatorData.evu2_manual, None when not applied). The switch shows the stored setting, not the applied value, so turning Smart Grid off and reloading does not store it as off.
  • Restore across restarts is done by the coordinator before the platforms are set up, looked up by unique id. Restoring in the switch itself let the status publish a value computed from the default and then flip, a spurious state change on every restart. The restore is gated on the model alone, so turning SG on through the select (no reload) applies the stored setting. A disabled switch counts as SG2 open.

Diagnostics

  • New derived section: evu2_manual (the setting) and evu2_manual_applied (what the status used, null when not applied). An MSW2-9S report is explainable from the dump alone now.

Entity attributes

  • Entities without a register no longer carry luxtronik_key: "NSET UNSET" (the UNSET enum name sliced as if it were an index), and neither do luxtronik_key_* fields left at UNSET. On a live install that was six attributes across the water heater and both climate entities.
  • The heating climate entity's luxtronik_key_current_temperature now names the key it actually reads (C0227, or the configured indoor sensor); base built the attribute before climate swapped the key in.

Docs

  • ADVANCED_FEATURES.md: a how-to section for the switch (what on/off mean, the two wiring cases, Smart Grid on/off and reload behaviour, an example automation that copies an SG2 relay onto the switch), linked from the Smart Grid mode entry and the README.
  • Translations in en/de/nl/cs/pl, icon.

🧪 Tests

  • Resolver order, the model + SmartGrid gate, the per-poll apply, restore (including a renamed entity id, a disabled switch, SG off at startup then on at runtime, and asserting on the data the entities read rather than private state), setup-order before the platforms, the switch entity, the diagnostics section, and the attribute changes.
  • Full run: 1370 passed, 1 skipped, coverage 100%. ruff check / format clean, basedpyright 0 errors, codespell clean, translation coverage complete.
  • Reviewed in three rounds; the findings (restore applied one poll late, disabled switch still steering the status, runtime SG-on using the default, doc accuracy) are fixed in the commits above.
  • Not tested on a real MSW2-9S: confirmation from @Zaschii / @mhuiskes on hardware is still needed, so this PR refers to Enhancement request: Smart Grid #500 rather than closing it.

Refs #500

🤖 Generated with Claude Code

rhammen and others added 10 commits September 25, 2026 00:03
- add an "EVU2 input (manual)" config switch on the MSW2-9S, whose SG2
  contact no register reports (calc 185 stays 0, RFV reads 0.0)
- resolve EVU2 as RFV sign > manual value > calc 185, so the SmartGrid
  status and the EVU2 binary sensor both follow the switch
- carry the value on the coordinator into each poll's data; nothing is
  written to the heat pump
- restore it from the switch's last state before the platforms are set
  up, so no entity computes a status from the default on a restart
- gate on an explicit model list: calc 185 reads 0 in every corpus dump,
  so no data test separates "contact open" from "not reported"
- document the switch in ADVANCED_FEATURES.md, translate it in all five
  languages

Refs #500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- the first refresh runs before the restore and applies the default, so
  the restored value only reached the entities at the next poll: the
  status still flipped on every restart, and stopping HA inside that
  window stored the switch as off and lost the setting
- apply the restored value to the current data straight away
- skip the restore for a disabled switch, which otherwise kept steering
  the status with no visible entity until the restore cache expired it
- assert the restore tests on the data the entities read, seeded the way
  setup seeds it, instead of the coordinator's private copy
- document enabling the switch after turning Smart Grid on later

Refs #500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…id is on

- fold the SmartGrid check into evu2_manual_input_required, so the switch
  setup and the coordinator share one rule: with SG off the switch is not
  created and its value is not applied, like the SG offset numbers
- enable the switch by default, now that it only exists when it matters
- show the stored setting on the switch rather than the applied value:
  with SG off the applied value is None, which would have been stored as
  off on the next reload and lost the setting

Refs #500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- turn the EVU2 input paragraph into a how-to: what on and off mean, the
  two wiring cases, and an automation that copies an SG2 relay's state
- explain that the switch only exists while Smart Grid is on, what a
  reload does when Smart Grid is turned on or off, and the 7-day restore
- link the section from the Smart Grid mode entry and the README

Refs #500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- the restore was gated on SmartGrid being on, so a unit started with SG
  off and then switched on through the select (no reload) applied the
  default instead of the stored setting until the next reload
- gate the restore on the model alone; whether the setting is applied
  stays the per-poll decision in _apply_evu2_manual

Refs #500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- say that turning Smart Grid off takes effect at the next poll, and
  that Home Assistant keeps the setting about a week after the reload,
  counted once it has restarted
- say that disabling the switch takes effect after the reload that
  follows it
- re-sync the example automation when the Luxtronik switch comes up
  instead of at Home Assistant start, which also covers a slow or
  retried integration setup

Refs #500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- entities without a register (luxtronik_key UNSET) got the attribute
  luxtronik_key "NSET UNSET": the enum name sliced as if it were a P/C
  index, e.g. the SmartGrid status sensor and the manual EVU2 switch
- optional luxtronik_key_* fields left at UNSET did the same; on a live
  install that is six attributes across the water heater and both
  climate entities
- skip both when the key is UNSET; nothing reads these attributes back

Refs #500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- add a "derived" section for values the integration derives rather than
  reads, which the register sections cannot explain
- carry the manual SG2 switch setting and the value the SmartGrid status
  used (None when not applied: SmartGrid off, or not a listed model)
- an MSW2-9S report showing calc 185 at 0 and a status with SG2 closed
  is explainable from the dump alone now

Refs #500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- base builds the luxtronik_key attributes before climate swaps in the
  current temperature key (C0227, or the configured indoor sensor), so
  the attribute showed "NSET UNSET" and, after the previous commit,
  nothing at all
- move the attribute building into LuxtronikEntity's
  _set_luxtronik_key_attributes and call it again after the swap

Refs #500

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- the switch comes up before automations arm their triggers on a normal
  restart, so the "from: unavailable" trigger alone never re-synced then
- keep the Home Assistant start trigger for restarts and the unavailable
  one for reloads and slow or retried setups

Refs #500

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% (3955 / 3955) ✔

@rhammen
rhammen merged commit db17d0e into main Sep 24, 2026
8 checks passed
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.

1 participant