Skip to content

fix(fox): don't model Feed-in First on FoxCloud, which never selects it (#5015) - #5023

Closed
chalfontchubby wants to merge 1 commit into
mainfrom
fix/foxcloud-feedin-first-5015
Closed

chalfontchubby wants to merge 1 commit into
mainfrom
fix/foxcloud-feedin-first-5015

Conversation

@chalfontchubby

Copy link
Copy Markdown
Collaborator

(Claude wrote this PR, posted under my account.)

Fixes #5015.

The bug

FoxCloud declares support_feedin_first: True, justified in config.py by a comment saying it is the same hardware as FoxESS. That flag tells the planner the inverter's freeze export is a real "Feed-in First" mode — load, then export, then battery — so PV past the export limit is recaptured into the battery rather than clipped (prediction.py:1146).

The Cloud path never selects that mode:

  • it drives the inverter through the Fox API scheduler, and apply_battery_schedule only ever emits ForceCharge / ForceDischarge groups — Feedin appears in fox.py only in enum lists, never in a written schedule
  • adjust_inverter_mode hardcodes new_inverter_mode = "SelfUse" for any has_fox_inverter_mode inverter (inverter.py:2443)
  • fox_cloud.yaml has no discharge_freeze_service (no *_service stanzas at all), so the freeze hook finds nothing configured and no-ops

So the inverter sits in Self Use for the whole slot: PV covers house load, then charges the battery, and exports only once the battery is full — exactly the Demand behaviour the reporter observes.

Same silicon, different control surface. FoxESS (modbus) reaches the mode through select.foxess_work_mode, wired up by the discharge_freeze_service stanza in templates/fox.yaml.

Why now

Two commits that are individually fine but mutually inconsistent:

The old hardcode was harmless until the recent planner change started relying on the mode it blocks. That matches the reporter's "until a few weeks ago freeze export slots were never planned".

Worth noting #4864 (GivTCP) is not implicated despite also shipping in v9.0.1 — it touched no Fox files, and the hardcode predates it by a year.

The change

One flag, plus the two tests that asserted the old value.

support_discharge_freeze stays True — freeze export is still attempted, it is only the Feed-in First recapture modelling that was wrong.

The shared opt-in test's docstring claimed all five other types genuinely select the mode, so I checked before shrinking the set: they do (solax.py:833 calls set_default_work_mode(mode="feedin"), sunsynk/deye have dedicated Selling First freeze handling, solis.py has the feed-in priority bit). FoxCloud was the only false member.

Effect for users

Freeze export slots on FoxCloud go back to being Demand slots, as they were before 24 Aug. That is the correct plan for what the inverter actually does, but it will look like a regression if unexplained — the plan stops promising something the hardware was never being told to do.

Adding the mode properly to the Cloud path is tracked in #5022, which records the implementation route I verified (the Cloud component does publish a writable work-mode select whose enum includes Feedin; the blocker is that adjust_inverter_mode would revert it).

Testing

  • ./run_all --test inverter passes
  • full ./run_all --quick suite green (1015 passed, exit 0)
  • all 12 pre-commit hooks pass

Known follow-up, deliberately not in this PR

support_feedin_first is a type-level constant asserting something that is really user config — call_service_template resolves discharge_freeze_service from self.base.args with no inverter-type gate anywhere. A modbus user who trimmed that stanza from their apps.yaml has this same silent mismatch today.

Gating the flag on the service actually being configured would fix that, but it changes behaviour for all five remaining opt-in types on a path nobody has reported a problem with, and it needs a design call on when the gate is evaluated (apps.yaml changes at runtime; the kernel context carries the flag). Kept out of a targeted bug fix — happy to file it separately if you want it tracked.

…it (#5015)

FoxCloud declared support_feedin_first: True on the grounds that it is the
same hardware as FoxESS, so the planner modelled its freeze export slots as
a real "Feed-in First" mode - load, then export, then battery, with PV past
the export limit recaptured into the battery rather than clipped.

The Cloud path never actually selects that mode. It drives the inverter
through the Fox API scheduler, which only ever emits ForceCharge and
ForceDischarge groups (apply_battery_schedule), and adjust_inverter_mode
pins the work mode to SelfUse for any has_fox_inverter_mode inverter. So a
freeze export slot leaves the inverter in Self Use: PV covers the house
load, then charges the battery, and only exports once the battery is full -
exactly the Demand behaviour the reporter observes.

Same silicon, different control surface: FoxESS (modbus) reaches the mode
through a work-mode select, wired up by the discharge_freeze_service stanza
in templates/fox.yaml. fox_cloud.yaml has no service stanzas at all.

The planner was therefore picking freeze export slots on a premise the
execute path could not deliver, inventing energy that never reached the
battery. Turning the flag off makes the plan honest; those slots go back to
being Demand slots, as they were before the flag was added.

support_discharge_freeze stays True - freeze export itself is still
attempted, it is only the Feed-in First recapture modelling that was wrong.

Adding the mode to the Cloud path is tracked separately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chalfontchubby

Copy link
Copy Markdown
Collaborator Author

In theory not needed now since #5022 is merged.

@springfall2008

Copy link
Copy Markdown
Owner

Yes, I fixed fox :)

@chalfontchubby

Copy link
Copy Markdown
Collaborator Author

A better solution. Figured I'd put the quick fix in in case.

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.

Fox Cloud integration - Freeze Export planned but has no effect

2 participants