Skip to content

fix(inverter): treat a failing ge_cloud_direct as transient, and name the source - #4642

Merged
springfall2008 merged 2 commits into
mainfrom
fix/gecloud-charge-window-transient
Aug 22, 2026
Merged

springfall2008 merged 2 commits into
mainfrom
fix/gecloud-charge-window-transient

Conversation

@mgazza

@mgazza mgazza commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

The branch that can't catch its own use case

inverter.py:1495 gates the transient path on self.rest_api, and its comment names the case it exists for:

A REST data source (givtcp_rest) is configured but hasn't returned anything this cycle — genuinely transient (a fetch hiccup, or before the first poll on a fresh start, e.g. GivEnergy cloud "no devices")

But that case can never reach it. ge_cloud_direct sets neither rest_api nor charge_start_time — it auto-configures the charge window at runtime from whatever device the cloud reports. So a cloud-backed instance whose fetch fails falls straight past into the permanent-setup-gap branch and raises ValueError, killing the plan.

Observed live: three causes, one message

Three instances on a cloud deployment, three different upstream failures:

HTTP 402 from GE Cloud on every endpoint          (lapsed entitlement)
"No devices found, check your GE Cloud credentials" (stale credential)
HTTP 401 on both GE Cloud and Octopus              (user had revoked their keys)

All three reported:

Error: Inverter 0 unable to read charge window time as neither REST,
       charge_start_time or charge_start_hour are set

Every one had apps.yaml identical to healthy instances — ge_cloud_direct set, no givtcp_rest, no charge_start_time. The message asserts a configuration fault that doesn't exist. Diagnosing it meant ruling out config, then pod networking, then node placement, before arriving at a 401.

Changes

1. ge_cloud_direct joins rest_api in the transient branch — a cloud failure now gets the existing safe-defaults/retry-next-update handling instead of raising.

2. Both messages name the source. The retry warning matters most: it's what a user actually sees once (1) stops these raising, and charge_start_time is None would send them to the one file that's fine. It now says which source returned nothing and suggests checking credentials and that the account still has the inverter attached. Predbat can't tell 402 from 401 from no-devices here, but it can say where the data should have come from.

The no-source-configured error also now lists what to set, rather than naming three internals the reader has to map back to config keys.

Tests

  • charge_window_no_source_configured — assertion updated for the new wording. Its docstring asks for "a clear, informative ValueError", so the assertion followed the message rather than pinning the old text.
  • New charge_window_ge_cloud_configured_but_no_data_yet — mirrors the existing REST transient test, and pins that the status names GE Cloud. Without that, change (1) could silently regress to blaming apps.yaml again.
unit_test.py --test inverter --test inverter_multi \
             --test multi_inverter_status --test find_charge_window
-> All tests passed

black --check and ruff --select F401 clean, no trailing whitespace.

Note: pre-commit couldn't run in my environment (ModuleNotFoundError: yaml in the local install), and cspell couldn't resolve its en-gb dictionary there either — so the commit used --no-verify and both are left to pre-commit.ci. Everything else in the hook list I ran by hand.

Context

Relates to the maintainer discussion on #4288 / #4179 that produced this branch. The permanent-gap behaviour is unchanged and still correct — this only stops a configured cloud source being misclassified as no source.

mgazza and others added 2 commits August 22, 2026 00:36
The transient branch at inverter.py:1495 gates on self.rest_api, and its
comment names GivEnergy cloud "no devices" as the case it exists to catch
-- but that case can never reach it. ge_cloud_direct sets neither rest_api
nor charge_start_time, because it auto-configures the charge window at
runtime from whatever device the cloud reports. So a cloud-backed instance
whose fetch fails falls straight past into the permanent-setup-gap branch
and raises ValueError, killing the plan for something that is not a setup
gap at all and is often not even the user's to fix.

Observed live on three instances, three different upstream causes, one
identical error:

  HTTP 402 from GE Cloud on every endpoint (lapsed entitlement)
  "No devices found, check your GE Cloud credentials" (stale credential)
  HTTP 401 on both GE Cloud and Octopus (user had revoked their keys)

All three read:

  Error: Inverter 0 unable to read charge window time as neither REST,
         charge_start_time or charge_start_hour are set

Every one of those instances had apps.yaml identical to healthy ones --
ge_cloud_direct set, no givtcp_rest, no charge_start_time. The message
asserts a configuration fault that does not exist, and diagnosing it meant
ruling out config, then pod networking, then node placement, before
arriving at a 401.

Two changes:

1. ge_cloud_direct joins rest_api in the transient branch, so a cloud
   failure gets the existing safe-defaults/retry-next-update handling
   instead of raising.

2. Both messages now name the source. The retry warning matters most: it
   is what a user actually sees once (1) stops these raising, and
   "charge_start_time is None" would send them to the one file that is
   fine. It now says which source returned nothing and suggests checking
   credentials and that the account still has the inverter attached --
   Predbat cannot tell 402 from 401 from no-devices here, but it can say
   where the data should have come from.

The no-source-configured error also now lists what to set, rather than
naming three internals the reader must map back to config keys.

Tests: existing charge_window_no_source_configured updated for the new
wording (its own docstring asks for "a clear, informative ValueError", so
the assertion followed the message). New
charge_window_ge_cloud_configured_but_no_data_yet mirrors the existing
REST transient test and pins that the status names GE Cloud -- without
that, (1) would silently regress back to blaming apps.yaml.

  unit_test.py --test inverter --test inverter_multi
               --test multi_inverter_status --test find_charge_window
  -> All tests passed

black --check and ruff F401 clean, no trailing whitespace. Committed with
--no-verify because the pre-commit install in this environment is broken
(ModuleNotFoundError: yaml, unrelated to this change); cspell likewise
could not run locally as its en-gb dictionary does not resolve here. Both
are left to pre-commit.ci.
@springfall2008
springfall2008 merged commit 3d27f9d into main Aug 22, 2026
2 checks passed
@springfall2008
springfall2008 deleted the fix/gecloud-charge-window-transient branch August 22, 2026 12:44
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