feat(gecloud): use the customer account timezone for the start/end time registers - #4460
Merged
Conversation
…me registers The GivEnergy charge/export slot start and end times are held in the timezone set on the customer's GivEnergy account, which is not necessarily the timezone Predbat is running in. Predbat was reading and writing them as if they were already in its own timezone, so the slots landed at the wrong time of day for anyone whose two timezones differ. Fetch the account details from /v1/account and translate the time registers at the API boundary: values are published to the select entities in the Predbat timezone and converted back to the account timezone on write, leaving the rest of Predbat unchanged. When the account timezone is unknown no shift is applied, so existing behaviour is preserved. The account details are cached in storage and only re-fetched once a day, and a stale cache is kept as a fallback so a failed fetch still leaves the timezone usable. Also publish two sensors: predbat_gecloud_account (account name, with the remaining values in a data attribute) and predbat_gecloud_timezone (the IANA timezone name, with the raw values and the live offset from the Predbat timezone in a data attribute). Bump version to v8.47.7. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adjusts the GivEnergy GE Cloud integration to interpret and present charge/export slot time registers using the customer account timezone, while keeping Predbat’s internal logic operating in the configured Predbat timezone.
Changes:
- Added GE Cloud
/v1/accountfetch with timezone parsing and daily caching in storage. - Translated
date_format:H:iregister values at the GE Cloud API boundary when publishing selects and when writing back register values. - Published new GE Cloud account/timezone sensors and updated docs; bumped version to
v8.47.7.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| docs/inverter-setup.md | Documents that GE Cloud slot times are stored in the account timezone and may display shifted in Predbat. |
| apps/predbat/tests/test_ge_cloud.py | Adds coverage for account fetch/caching, timezone parsing, shifting logic, sensors, and round-trip register conversions. |
| apps/predbat/predbat.py | Version bump to v8.47.7. |
| apps/predbat/gecloud.py | Implements account timezone fetch/cache, time shifting helpers, boundary conversions, and new sensors. |
Suppressed comments (2)
apps/predbat/gecloud.py:503
- publish_account() sets the timezone sensor state from the raw account fields even if the timezone name was unknown/ignored (and therefore no conversion is applied). This can mislead users into thinking conversion is active when offset_minutes is 0; prefer publishing the resolved timezone (self.account_timezone_name) and fall back to "unknown" when not resolved.
timezone_name = self.account_timezone_name or account.get("standard_timezone", None) or account.get("timezone", None)
timezone_data = {
"timezone": account.get("timezone", None),
"standard_timezone": account.get("standard_timezone", None),
"predbat_timezone": str(self.local_tz) if self.local_tz else None,
apps/predbat/gecloud.py:1687
- The async_get_account() docstring embeds a full example response including email/address/telephone-like fields. Even as an example, it’s better to avoid including personal-data-shaped content in code comments; a minimal subset (id/name/timezone fields) conveys the intent without that risk.
{'id': 2, 'name': 'francesca.holmes.285', 'first_name': 'Maisie', 'surname': 'Walker', 'role': 'VIEWER',
'email': 'joshua94@martin.com', 'address': '18 Hunt Landing', 'postcode': 'CT6 9AR', 'country': 'UNITED_KINGDOM',
'telephone_number': '+44(0)7559 260236', 'timezone': 'GMT', 'standard_timezone': 'Europe/London',
'company': None, 'flags': []}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
update_account() advanced account_stamp before the fetch, so if there was no cached account and the first fetch failed, nothing retried for 24 hours and the timezone conversion stayed disabled for that whole period. Only mark the details fresh once a fetch actually returns something. A failure now retries after ACCOUNT_RETRY_MINUTES, tracked separately from the freshness stamp so a sustained API outage does not turn the 60 second run() tick into a poll loop against the account endpoint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The GivEnergy charge/export slot start and end times are held in the timezone set on the customer's GivEnergy account, which is not necessarily the timezone Predbat is running in. Predbat read and wrote those registers as if they were already in its own timezone, so for anyone whose two timezones differ the slots landed at the wrong time of day.
Changes
Fetch the account details — new
/v1/accountcall (async_get_account), recording the timezone fromstandard_timezone(IANA, e.g.Europe/London), falling back totimezone. Unknown or missing names are logged and ignored.Translate the time registers at the API boundary, so the rest of Predbat is untouched:
publish_registers()convertsdate_format:H:iregisters from account time into Predbat time before publishing the select entityselect_event()converts back into account time before writingget_timezone_offset_minutes()is evaluated at call time, so DST is handled on both sidesshift_time_string()wraps correctly at midnightWhen the account timezone is unknown the offset is 0 and nothing shifts, so existing behaviour is preserved.
Cache the account in storage — restored on startup and only re-fetched once a day (
ACCOUNT_MAX_AGE_MINUTES). A stale cache is still loaded as a fallback, so if the API call then fails the timezone conversion keeps working with the last known values rather than silently reverting to no conversion.Publish two sensors:
dataattributesensor.predbat_gecloud_accountnamesensor.predbat_gecloud_timezonetimezone,standard_timezone,predbat_timezone,offset_minutesoffset_minutesmakes it visible at a glance whether any shift is being applied.enable_default_options()is deliberately untouched — the00:00it writes to unused slots is a disable sentinel (start == end), so shifting it would serve no purpose.Version bumped to v8.47.7.
User-visible change
For anyone whose GivEnergy account timezone differs from their Predbat
timezone, the HA select entities will now show times shifted relative to the GivEnergy portal. That is the point of the change, but it is a visible difference when cross-checking the two. Documented indocs/inverter-setup.md.Testing
Ten new sub-tests cover the account fetch and its failure path, timezone parsing and fallbacks, the time-string shift (including midnight wrap and malformed input), the round-trip conversion through publish/select with and without a known timezone, both sensors, and the four storage-cache paths (save, fresh restore with no API call, stale re-fetch, no cache).
🤖 Generated with Claude Code