feat(myenergi): drive the Zappi from Predbat's car charging plan - #4674
Merged
Conversation
Adds myenergi_zappi_control. Inside a planned charging window Predbat puts the Zappi in Fast, and outside one in Stopped. Fast because the window was chosen for its rate rather than for sunshine - Eco or Eco+ would only charge from surplus and the car would not get what the plan assumed. Each Zappi follows its own car, matched in serial order, the same order automatic_config wires car_charging_energy and car_charging_planned in, so the two cannot disagree about which Zappi is which car. The mode already polled is compared against what was asked for and re-applied, since purely edge-triggered control diverges the moment anything else touches the charger. A component-level switch.predbat_myenergi_zappi_control hands the charger back without editing apps.yaml, on by default and persisted through Storage so a restart does not quietly resume control. That release, and Predbat's own read only mode, restore the mode the Zappi had before Predbat took over, falling back to Eco+ when nothing was saved - simply going quiet would strand a car Predbat had left Stopped. set_mode is implemented on both transports to support this, so it is no longer one of the reserved stubs and is now abstract on the transport interface. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The new Zappi control switch handling is incorrectly gated by myenergi_enable_controls, making the switch non-functional/persistence-inaccessible in monitor-only mode even though toggling it doesn’t require issuing device control calls.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds Predbat-led charge control for myenergi Zappi chargers, driving Zappi supply mode from Predbat’s existing per-car charging plan (Fast in planned windows, Stopped outside), including drift correction and a persistent runtime control switch.
Changes:
- Implement
set_modeacross myenergi transports and introduce Zappi plan-driven control logic with release/restore semantics. - Add
myenergi_zappi_controlconfiguration plus a persistedswitch.predbat_myenergi_zappi_controlentity. - Extend documentation and add extensive unit tests for mode setting, window parsing, gating, release behavior, and persistence.
File summaries
| File | Description |
|---|---|
| docs/components.md | Documents myenergi_zappi_control and the Zappi charge control behavior/switch. |
| docs/apps-yaml.md | Adds myenergi_zappi_control to apps.yaml reference with behavioral notes. |
| apps/predbat/tests/test_myenergi.py | Adds tests for set_mode and Predbat-led Zappi control logic and persistence. |
| apps/predbat/myenergi.py | Implements mode setting, plan window parsing, control loop, release logic, and switch persistence. |
| apps/predbat/config.py | Adds myenergi_zappi_control config schema entry. |
| apps/predbat/components.py | Wires myenergi_zappi_control into myenergi component args. |
| .cspell/custom-dictionary-workspace.txt | Adds “eddis” / “zappis” for spell checking. |
Review details
- Files reviewed: 7/7 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Exercises the same three supply-mode commands Predbat-led charge control issues, so the API can be tested against a real Zappi without enabling the feature. --release is included because without it --stop-charge would leave the charger sitting in Stopped with no way back from the command line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot review: the switch handler is gated behind myenergi_enable_controls, so the switch cannot be toggled in monitor-only mode. The symptom is real but wider than reported - switch_event() returns before the handler is ever reached, and publish_data gated only on the config key, so the switch appeared reading "on" for a feature enable_control() had already refused to start. Making it respond to a toggle would leave it live but still lying, so gate publishing on control_active instead: no switch appears unless control could actually act, and it reappears with its remembered state once controls are allowed. Also restore the saved state before the first publish rather than after. A restart with control switched off previously showed the switch on for one cycle and then flipped, which reads like Predbat taking control back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #4667. Adds
myenergi_zappi_control, letting Predbat drive a Zappi from the car charging plan it has already worked out, instead of the charge being scheduled on the Zappi itself.Follows the shape
ohme.pyestablished in #4665 — gate, read plan, correct drift, release — with the differences a Zappi brings.Behaviour
automatic_config()wirescar_charging_energyandcar_charging_plannedin, so the two cannot disagree about which Zappi is which car. Car 0 readsbinary_sensor.predbat_car_charging_slot, car 1 reads..._car_charging_slot_1, and so on.plannedattribute is evaluated against the live clock each minute rather than the sensor's own on/off state, which only refreshes on Predbat's five minute cycle — otherwise every window boundary would be acted on up to five minutes late.The control switch
switch.predbat_myenergi_zappi_controlappears oncemyenergi_zappi_controlis set. It starts on, and turning it off hands the charger back without editingapps.yaml. The state is persisted through the Storage component, so a restart does not quietly resume control of a Zappi that was deliberately released.It is a component-level switch rather than a
CONFIG_ITEM, and carries thepredbat_myenergi_prefix because event routing matches on that substring — named otherwise it would publish but never receive its own turn-on/turn-off events.Release
Predbat releases the Zappi when the switch is turned off, or when Predbat is put in read only mode. Releasing restores the mode the Zappi was in before Predbat first changed it, falling back to Eco+ when nothing was saved (after a restart, or a device reporting a mode neither API accepts back).
It deliberately does not just stop sending commands: Predbat may have left the Zappi Stopped, and walking away would leave the car unable to charge until someone noticed.
Notes for reviewers
set_modeis implemented on both transports to support this — directGET /cgi-zappi-mode-Z{serial}-{index}-0-0-0000, cloudPOST /devices/{id}/mode. It is therefore no longer one of the reserved stubs, and is now abstract onMyEnergiTransportsince every transport must provide it. The docs' not-implemented list shrinks accordingly.myenergi_automatic(it is auto-config that maps Zappis to cars) andmyenergi_enable_controls. Each refusal logs which prerequisite is missing rather than going quiet.Testing
23 new tests in
apps/predbat/tests/test_myenergi.py, built test-first:set_modeon both transports including bad mode and wrong device kind, window parsing (per car, malformed entries, missing plan, the New Year rollover), Fast/Stopped selection, per-car mapping, drift correction, the three gating refusals, release to saved mode and to the Eco+ fallback, read-only release and resume, the switch publishing and toggling, persistence across a restart, and a refused command not failing the poll.Full
./run_all --quickpasses.🤖 Generated with Claude Code