feat(solax): cache plant and device info in storage and refresh it by data age - #4366
Merged
Conversation
… data age Every restart re-read the full plant list and every device from the cloud before Predbat could do anything, even though that information describes the hardware and barely changes. Both are now kept in storage, as gecloud does with its settings, and restored on startup when the data is still current. The refresh is driven by the age of the data rather than the time since startup: plant info is re-read once it is more than 8 hours old, device info once it is more than 30 minutes old. Restoring from the cache carries the age over, so a restart neither re-reads data that is still good nor waits a full interval to refresh data that was already nearly due. Entries are written with a 24 hour expiry so an abandoned cache is discarded by the storage layer rather than lingering. A plant info refresh that fails now keeps the previously known plants instead of aborting the run, which only remains fatal when nothing has ever been read. Realtime data is deliberately not cached, it is polled every 60 seconds, but it is no longer published when the read failed. Previously a failed poll republished the last known values, and before any successful read the plant totals were published as zero, which corrupts the energy totals Predbat consumes. Plant totals, battery sensors and per-device sensors are each held back independently, so one failing device does not suppress the rest, and static sensors such as capacity are always published. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves the SolaX cloud integration startup/runtime behaviour by persisting largely-static “plant” and “device” metadata in the Storage component and by preventing Home Assistant sensor updates from republishing stale/zero realtime data when polls fail.
Changes:
- Cache and restore SolaX
plant_infoanddevice_infovia Storage, refreshing based on cached data age (8h / 30m) and writing with a 24h expiry. - Track per-plant and per-device realtime poll failures and gate sensor publishing so failed reads do not republish stale/zero totals.
- Extend the SolaX test suite to cover static-info caching/ageing and realtime publish gating behaviour.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| apps/predbat/solax.py | Implements storage-backed caching/age-based refresh of static info and failure-gated publishing of realtime sensors. |
| apps/predbat/tests/test_solax.py | Adds/updates tests validating cache restore/refresh semantics and publish gating on failed realtime reads. |
…eded query_device_info returns None on an API failure, but the run loop advanced device_info_updated and wrote the cache regardless. A transient failure therefore marked the data fresh for 30 minutes, and on a cold start with no cache it locked in an empty device list for that long, leaving the plant with no inverters or batteries to control. The read time is now only advanced, and the cache only written, when every query_device_info call succeeded and there was at least one plant to read. Gating on success alone would have replaced one problem with another, since run is called every few seconds and a failing read would then retry on every cycle. Both paths now also record when a read was last attempted and hold off for five minutes before trying again. That applies to plant info too, which had the same retry storm because a failed refresh left plant_info_updated unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Battery and PV capacity come from the plant record, so adding hardware changes both the device list and the plant info. Device info is re-read every 30 minutes but plant info only every 8 hours, which left the capacities stale for hours after a battery was added. A device list that differs from the one held before the read now clears the plant info read and attempt times, so the plant record is re-read on the next cycle rather than waiting out its maximum age or the retry interval. The comparison is skipped when there was no previous device list, so the first read after a cold start does not trigger a pointless re-read of plant info that was just fetched. It detects devices appearing rather than disappearing, since device_info is only ever added to, which is the case that matters for capacity. 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
Every restart re-read the full plant list and every device from the SolaX cloud before Predbat could do anything, even though that information describes the hardware and barely changes. Other integrations already avoid this —
gecloud.pyrestores its settings from storage on startup.Separately, realtime data was published to Home Assistant whether or not the read succeeded.
realtime_device_datais only written on a successful read, so a failed poll republished the previous values, and before any successful read the plant totals were published as0.0from the.get(..., 0.0)defaults. Those totals feedimport_today/export_today/load_today, so publishing zeros or stale values corrupts what Predbat consumes.Change
Plant and device info are cached in storage under
solax/plant_infoandsolax/device_info, and restored on startup. Two entries rather than one, because a single file's timestamp would be refreshed by every 30 minute device save, and the plant info age rule would then never fire.The refresh is driven by the age of the data, not the time since startup. Previously
seconds % (30 * 60) == 0meant a restart began a fresh 30 minute interval regardless of how old the data actually was. Now:plant_infodevice_infoRestoring from the cache carries the age over, so a restart neither re-reads data that is still current nor waits a full interval to refresh data that was already nearly due — 25 minute old device info refreshes 5 minutes later, not 30. Entries are written with a 24 hour expiry so an abandoned cache is discarded by the storage layer.
Realtime data is not published when the read failed. Failures are recorded per plant and per device and cleared on the next success. Tracking failures rather than successes matters: it distinguishes "never polled" from "poll failed", and a missing update simply leaves the entity at its last value in HA, which is the desired outcome. Plant totals, battery sensors and per-device sensors are gated independently, so one failing device does not suppress the rest. Static sensors — capacity, PV capacity, max power — are always published.
Realtime data is deliberately still not cached to storage, as it is polled every 60 seconds.
Behaviour notes
Falseon an 8 hourly refresh blip would take out an otherwise healthy cycle. It remains fatal when nothing has ever been read.plant_listis now recomputed whenever plant info is successfully refreshed, not only on the first run, so a plant added to the account is picked up within 8 hours.Testing
./run_all --test solaxand./run_pre_commitboth pass.test_static_info_cache— fresh cache restored with no polling, each entry ageing out independently, the age carrying across a restart rather than restarting the interval, missing cache, no storage configured, a failed refresh keeping the previous data, and the 24 hour expiry.test_realtime_publish_gating— successful reads publish, a failed plant read holds back the totals while static sensors still publish, a failed battery read holds back the battery sensors, per-device publishing skips only the failed device, and failures are recorded on a bad read and cleared on a good one.plant_info_updated/device_info_updated, since "subsequent run" no longer implies "never polls plant info".🤖 Generated with Claude Code