Skip to content

feat(myenergi): drive the Zappi from Predbat's car charging plan - #4674

Merged
springfall2008 merged 3 commits into
mainfrom
feat/myenergi-zappi-control
Aug 23, 2026
Merged

feat(myenergi): drive the Zappi from Predbat's car charging plan#4674
springfall2008 merged 3 commits into
mainfrom
feat/myenergi-zappi-control

Conversation

@springfall2008

Copy link
Copy Markdown
Owner

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.py established in #4665 — gate, read plan, correct drift, release — with the differences a Zappi brings.

Behaviour

  • Inside a planned window: Fast. Outside: Stopped. Fast because the window was chosen for its electricity 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. Car 0 reads binary_sensor.predbat_car_charging_slot, car 1 reads ..._car_charging_slot_1, and so on.
  • Drift is corrected. The mode already polled is compared against what was asked for and re-applied. Purely edge-triggered control diverges silently the moment anything else touches the charger.
  • Nothing happens until a plan exists. With no slot sensor published yet the loop does nothing, so a restart cannot stop a car Predbat has not yet decided anything about.
  • The plan's planned attribute 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_control appears once myenergi_zappi_control is set. It starts on, and turning it off hands the charger back without editing apps.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 the predbat_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_mode is implemented on both transports to support this — direct GET /cgi-zappi-mode-Z{serial}-{index}-0-0-0000, cloud POST /devices/{id}/mode. It is therefore no longer one of the reserved stubs, and is now abstract on MyEnergiTransport since every transport must provide it. The docs' not-implemented list shrinks accordingly.
  • A refused mode command is a warning, not a failed cycle. myenergi can refuse for reasons Predbat cannot see (nothing plugged in, a charger fault). Letting that escape would skip the success timestamp and eventually report the component unhealthy even though monitoring is fine. Nothing is recorded as set, so the next cycle retries.
  • Two consequences documented for users: the manual boost switch will refuse while control is on, since myenergi only accepts a boost in Eco or Eco+; and Stopped outside a window means surplus solar no longer goes to the car, so anyone who wants diversion should leave this off.
  • Control requires myenergi_automatic (it is auto-config that maps Zappis to cars) and myenergi_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_mode on 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 --quick passes.

🤖 Generated with Claude Code

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>
Copilot AI lite review requested due to automatic review settings August 23, 2026 18:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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_mode across myenergi transports and introduce Zappi plan-driven control logic with release/restore semantics.
  • Add myenergi_zappi_control configuration plus a persisted switch.predbat_myenergi_zappi_control entity.
  • 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.

Comment thread apps/predbat/myenergi.py
springfall2008 and others added 2 commits August 23, 2026 19:34
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>
@springfall2008
springfall2008 merged commit cfc6518 into main Aug 23, 2026
2 checks passed
@springfall2008
springfall2008 deleted the feat/myenergi-zappi-control branch August 23, 2026 18:54
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.

2 participants