Skip to content

foxess_h3_smart 0.9.4: night grid charging never worked — accept the charge and wait for the BMS to wake - #96

Merged
frahlg merged 1 commit into
mainfrom
foxess-night-charge-wake
Aug 19, 2026
Merged

foxess_h3_smart 0.9.4: night grid charging never worked — accept the charge and wait for the BMS to wake#96
frahlg merged 1 commit into
mainfrom
foxess-night-charge-wake

Conversation

@Leitet

@Leitet Leitet commented Aug 19, 2026

Copy link
Copy Markdown
Member

Found by the operator on live hardware (2026-09-08): the planner scheduled cheap night charging; the driver refused every slot for five hours with "battery is not accepting charge now" — at 10% SoC with a 20 °C pack. PV-surplus charging worked; grid charging did not, and never had.

Mechanism. The BMS-ceiling guard reads Pwr_limit_Bat_up (46018) immediately after writing the remote-control enable. At night the inverter sleeps in standby, and its master samples the RC block slowly — the same hardware fact behind the 60 s timeout floor — so the register still reports the sleeping value. The guard refuses, the host reverts the driver to default, the release write puts the inverter back to sleep, and the next retry repeats the cycle. Daylight charging always worked because the inverter was already awake. Two corroborations: the unguarded v0.2.0 build night-imported happily (the infamous Day-2 runaway ran all night), and nathanmarlor/foxess_modbus drives its session continuously every poll rather than gating on a first read.

Fix. A night charge that meets a floor-level limit is accepted as pending: the driver holds AC = 0 — no import, no discharge, the active session keeps the inverter awake — re-reads the limit on every poll refresh, applies the true setpoint the moment the BMS wakes, and gives up only after a 3-minute patience window (a genuine cold/fault hold). Daylight behaviour is unchanged: an awake inverter reporting a floor-level limit is a real refusal, and native self-use handles surplus better than a fight. The v2 entrypoint reports the pending state as accepted / charge_pending_bms_wake — the contract's word for "heard you, not done yet".

Observability. New metric foxess_bms_charge_limit_w emitted every poll — the night of refusals was undiagnosable precisely because this register was only read inside command paths.

Five new harness tests: hold-and-wait, apply-on-wake, patience expiry, daylight refusal unchanged, and the v2 pending result. 68 total pass. Hardware validation happens tonight on the discovering site's cheap slots.

🤖 Generated with Claude Code

frahlg commented Aug 19, 2026

Copy link
Copy Markdown
Member

Automated review pass: make check and make test-driver ID=foxess_h3_smart are green (68 passed, 5 skipped; full suite 3639 passed with no artifact drift). The fix itself looks sound — the daylight refusal path is byte-for-byte unchanged (test_daylight_low_limit_still_refuses still covers it), the night-hold path correctly keeps the session alive without importing/discharging, re-reads the BMS limit every poll, and the 3-minute patience window before giving up is tested (test_night_charge_gives_up_after_patience_window). driver_command_v2 correctly reports this as accepted/charge_pending_bms_wake rather than applied, which is the honest v2 contract call. Version/manifest/package bumps to 0.9.4 are consistent everywhere.

Holding on one thing: this driver's manifest still declares verification_status: "production" with verification_notes describing what was verified on the 1K5-HI-10-V1 in charge/discharge/hold — unchanged by this diff. But the PR body says hardware validation of this specific night-charge-accept path was happening "tonight," i.e. after this PR was opened. Right now the "production, hardware-verified" claim covers code this driver didn't have yet when that verification happened. Per this repo's own precedent (0.9.1/0.9.3 both recorded exactly what was verified and when in verification_notes), could you confirm whether last night's on-site test came back clean, and if so update verification_notes (and the CHANGELOG entry) to say so explicitly? If it didn't come back clean yet, the manifest claim is currently overstating what's been proven on hardware for a live control driver already on customer sites.

Not merging until that's confirmed — flagging for a maintainer to close the loop on the hardware result.


Generated by Claude Code

@Leitet

Leitet commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

The hold is correct — thank you for applying the repo's own standard. Confirming the timeline honestly: the night-charge-accept path has not yet run on hardware; last night was the failure that motivated the fix, and 0.9.4 did not exist yet. The production notes as they stand would overstate the moment this merges.

Plan to close the loop, running today on the discovering site (the 1K5-HI-10-V1):

  1. 0.9.4 is now deployed there as a local operator override (the sanctioned test mechanism) — done this morning, driver healthy.
  2. Tonight after dark, a bounded manual charge hold exercises the exact path: sleeping BMS limit → accepted-as-pending → 0 W hold with session alive → setpoint applied on wake, with foxess_bms_charge_limit_w recording the register's behaviour as a time series.
  3. verification_notes and the CHANGELOG entry get updated in this PR with the measured result — date, SoC, observed wake time — before merge. If the test fails, the PR gets reworked instead.

Will report back with data.

🤖 Generated with Claude Code

@frahlg
frahlg force-pushed the foxess-night-charge-wake branch from 7fae749 to cc0d002 Compare August 19, 2026 08:28
@frahlg

frahlg commented Aug 19, 2026

Copy link
Copy Markdown
Member

Rebased onto current main (d277460, through Blixt migrations #91#95). Catalog conflict is gone: CHANGELOG Unreleased keeps both the Blixt bullets and the foxess 0.9.4 fix; foxess version/hash from this branch, every other driver from main.

New head: cc0d002. Driver logic unchanged. make test-driver ID=foxess_h3_smart: 68 passed, 5 skipped.

Still held for tonight's hardware result on the night-charge-accept path before merge. verification_status: production left as Johan set it.

Note: the PR text and commit say the failure was observed 2026-09-08, which is in the future (today is 2026-08-19). Commit message left as written.

… wake (0.9.4)

Grid charging at night never worked. The BMS-ceiling guard read
Pwr_limit_Bat_up milliseconds after writing the session enable, but
the inverter sleeps in standby at night and its master samples the RC
block slowly, so the register still reported the sleeping value; the
guard refused, the host reverted the driver, and the release put the
inverter straight back to sleep -- every retry slot, all night.
Observed 2026-09-08: five hours of refusals at 10% SoC with a 20 C
pack. The unguarded v0.2.0 had night-imported happily, and the
reference implementation drives the session continuously rather than
gate on a first read.

A night charge that meets a floor-level limit is now accepted as
pending: hold AC = 0 (no import, no discharge, session keeps the
inverter awake), re-read the limit every poll, apply the true
setpoint the moment the BMS wakes, give up after a 3 min patience
window. Daylight behaviour unchanged: an awake inverter reporting a
floor-level limit is a real refusal. The v2 entrypoint returns
accepted/charge_pending_bms_wake for the pending state. New metric
foxess_bms_charge_limit_w every poll makes the BMS ceiling a time
series instead of a mystery.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Leitet <johan@sourceful-labs.com>
@frahlg
frahlg force-pushed the foxess-night-charge-wake branch from cc0d002 to 41be46a Compare August 19, 2026 08:32
@frahlg
frahlg merged commit 7c275b7 into main Aug 19, 2026
6 checks passed
@frahlg
frahlg deleted the foxess-night-charge-wake branch August 19, 2026 11:01
@Leitet

Leitet commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

Hardware result, as promised — night grid charging validated on the discovering 1K5-HI-10-V1 tonight:

  • 2026-09-08 21:22 CEST, fully dark (PV 0 W), SoC 54%, battery discharging −774 W on plan
  • Bounded manual hold: charge +2000 W for 600 s
  • Battery ramped −774 → −250 → +478 → +1265 → +1939 W in 65 s (FTW's slew), then held steady within the usual tolerance; grid import ~2.7 kW (battery + house)
  • Hold cleared after the measurement; battery returned to plan

Honest scoping of what tonight proved: the inverter was awake (actively discharging) when commanded, so the BMS limit read live immediately and the setpoint applied at once — the standby wake-pending path did not need to engage in this run. It remains covered by the five harness tests, and the new foxess_bms_charge_limit_w metric will record the limit's behaviour whenever the standby scenario (battery idle at the SoC floor, the discovery night's condition) naturally recurs.

verification_notes, verified_by/verified_at and the CHANGELOG entry are updated on the branch with exactly the above — the production claim now describes only what has been observed. Ready for merge from my side.

🤖 Generated with Claude Code

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