Skip to content

fix(load): add switch-load off-hysteresis and remove phantom first-cycle feedforward#11

Merged
bvweerd merged 1 commit into
devfrom
fix/switch-load-hysteresis
Jul 4, 2026
Merged

fix(load): add switch-load off-hysteresis and remove phantom first-cycle feedforward#11
bvweerd merged 1 commit into
devfrom
fix/switch-load-hysteresis

Conversation

@bvweerd

@bvweerd bvweerd commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Problem

  1. Zero off-hysteresis. Switch loads turned off at residual ≥ 0. After turn-on the residual sits at ~0 W by construction (the load consumes exactly the surplus that triggered it), which is precisely the off threshold — any noise beyond the deadband flapped the load off again. With the default 30 s debounce that is up to two relay actions per minute on a boiler or pump.
  2. Phantom first-cycle feedforward. The numeric-load absorption fed forward to switch decisions was computed from a snapshot taken before loads are initialised from their entity state. On the first cycle a running load is seen (e.g. an EV already charging at 16 A after a restart), the snapshot default of setpoint_min produced a fictitious absorption of thousands of watts, suppressing or mis-triggering switch-load decisions in that cycle.

Fix

  • Switch loads now turn off only when grid import exceeds 10% of the load's fixed power (SWITCH_LOAD_OFF_FRACTION), giving a real hysteresis band. Turn-on behaviour is unchanged (surplus ≥ power_w).
  • The load absorption is derived from the distribution return values instead of a pre-initialisation snapshot, so entity-state initialisation can no longer leak into the feedforward. This also removes the _load_sp_before bookkeeping entirely.

Tests

New: load stays on at 150 W import (below the margin), turns off at 400 W (beyond it), and the restart scenario — EV at 16 A unseen by the engine, 2500 W export — now correctly turns the boiler on where the phantom absorption previously suppressed it. Full suite: 242 passed.

https://claude.ai/code/session_01RUWpwxbGsgR3PoLHLq4Djz


Generated by Claude Code

@github-actions github-actions Bot added enhancement New feature or request bug Something isn't working labels Jul 4, 2026
…cle feedforward

Two switch-load correctness fixes:

1. Switch loads turned off at residual >= 0. After turn-on the residual
   sits at ~0 W by construction (the load consumes the surplus that
   triggered it), which is exactly the off threshold — any noise beyond
   the deadband flapped the load off again, up to two switch actions
   per minute with the default 30 s debounce. The off threshold is now
   10% of the load's fixed power (SWITCH_LOAD_OFF_FRACTION), giving a
   real hysteresis band.

2. The numeric-load absorption fed forward to switch decisions was
   computed from a snapshot taken before loads are initialised from
   their entity state. On the first cycle that a running load is seen
   (e.g. an EV already charging at 16 A after a restart), the snapshot
   default of setpoint_min produced a fictitious absorption of
   thousands of watts, suppressing or mis-triggering switch loads.
   Absorption is now derived from the distribution return values, so
   initialisation cannot leak into the feedforward.

https://claude.ai/code/session_01RUWpwxbGsgR3PoLHLq4Djz
@bvweerd bvweerd force-pushed the fix/switch-load-hysteresis branch from e3261e7 to a9d6ba9 Compare July 4, 2026 09:48
@bvweerd bvweerd merged commit ca7b9da into dev Jul 4, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant