Add configuration visibility & control, robust multi-inverter energy, resilience, and a CLI (5.3.0) - #10
Open
bbarabe wants to merge 74 commits into
Open
Add configuration visibility & control, robust multi-inverter energy, resilience, and a CLI (5.3.0)#10bbarabe wants to merge 74 commits into
bbarabe wants to merge 74 commits into
Conversation
- Add new ac_relay_status sensor from workdata endpoint - Replace dy/store call with workdata in get_plant_data - Add --workdata and --workdata-fields CLI flags - AcRelayStatus: 1=Connected, 0=Disconnected (reliable during outages) - Keep existing grid_status sensor unchanged Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove number/switch/select/time entities (writable controls) - Add read-only config sensors for all settings (entity_category: CONFIG) - Add solark.configure_inverter action for atomic batched writes - Single API call for all changes, safer for dashboard visitors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Migration handler now handles v2 -> v3 transition when number/switch/select/time platforms were replaced with config sensors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove orphaned entities from old number/switch/select/time platforms when migrating to v3 (config sensors + service). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Logs settings_coordinator data and wraps sensor creation in try/except to catch any errors during config sensor setup. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Port from upstream HammondAutomationHub/HomeAssistant_SolArk commit d336443. Diagnostics downloads redacted only username/password; any token persisted on the entry or surfaced in options was emitted in the clear. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Port OBSOLETE_BASE_URLS / OBSOLETE_API_URLS / normalize_solark_urls and the new DEFAULT_BASE_URL from upstream HammondAutomationHub/HomeAssistant_SolArk commits 11072e0 and 6ed5b09, replacing this fork's narrower LEGACY_API_URLS tuple. Upstream reached the same p2 conclusion this fork did in d640e6d, but covers more ground: - also rewrites base_url (mysolark.com -> www.solarkcloud.com), which this fork left pointing at the retired portal host; base_url is what we send as Origin/Referer on every request - also covers ecsprod-api.solarkcloud.com, not just ecsprod-api-new - strips trailing slashes, so a hand-entered URL still matches The v4 migration now runs both URLs through normalize_solark_urls instead of matching api_url against a tuple. It stays idempotent, so entries already migrated by d640e6d are unaffected apart from picking up the corrected base_url. Verified www.mysolark.com and www.solarkcloud.com currently serve the identical portal bundle (app.d9466868.js), so nothing breaks today; this is about not depending on a retired host. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Port discovery.py verbatim from upstream HammondAutomationHub/HomeAssistant_SolArk commits 11072e0 and 6ed5b09, plus the surrounding wiring, adapted to this fork's allow_write option and its deferred-import style in __init__.py. The portal ships its API root as VUE_APP_BASE_API inside /static/js/app.<hash>.js. Reading it at setup means the next cluster migration self-heals instead of needing another hand-patched commit like d640e6d. A manual api_url remains as override (auto-discover off) and as fallback when discovery fails. Verified against the live portal: both www.solarkcloud.com and www.mysolark.com resolve to https://p2.api.solarkcloud.com, matching the host d640e6d set by hand. A dead portal host returns None and the configured api_url is kept. Divergences from upstream, all forced by this fork's shape: - options flow keeps allow_write_access and uses self._config_entry (this fork stores the entry on the private attribute to avoid assigning the read-only property) - discover_api_url is called from _resolve_urls with the HA client session imported inside the function, matching this module's pattern - no entry version bump is needed: auto_discover_api reads through .get(..., DEFAULT_AUTO_DISCOVER_API) everywhere and async_setup_entry persists it on first run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Port three fixes from upstream HammondAutomationHub/HomeAssistant_SolArk commit 62d0b49 ('5.2'): - PV power now adds minPower (microinverter / AC-coupled PV) from the energy flow endpoint when existsMin/microOn/minPower indicate it is present. minPower, existsMin, microOn, genPower and existsGen are added to the flow key passthrough in get_plant_data so they reach the parser. - _mppt_looks_like_placeholder rejects the fixed 0/1.5/3.0/4.5 ramp the portal returns in some dy/store payloads, so it is no longer summed into pv_power as if it were live string data. The MPPT sum is also now a true last resort, only used when the flow endpoint gave no PV figure at all. - Battery SOC only falls back to curCap/batteryCap when curCap is actually populated; before, a missing curCap reported a confident 0%. Verified against synthetic payloads: micro adds (3000+450), the placeholder ramp is rejected, real MPPT strings still sum, and a missing curCap no longer computes 0%. This fork already reached upstream's other 62d0b49 conclusions independently — signed battery power from toBat/batTo and the gridTo/toGrid import/export fallback — and keeps its own versions, which additionally gate on existsMeter and handle the both-flags-set ambiguity. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Port _get_realtime_data from upstream HammondAutomationHub/HomeAssistant_SolArk commit 62d0b49, but wire it as a gap-filler rather than an override -- the one deliberate behavioral divergence in this integration pass. Upstream prefers the plant realtime etoday/etotal over the inverter list. Probed against a live two-inverter plant on the p2 cluster, the two sources disagree: etoday matches exactly, so daily energy -- what the HA energy dashboard actually consumes -- is identical either way. etotal, however, reads roughly a quarter higher than the per-inverter sum. Adopting upstream's preference would push a one-time multi-MWh step into an existing TOTAL_INCREASING sensor and corrupt the recorder statistics behind the energy dashboard. So the per-inverter sum from 406ebf2 stays primary, and /realtime fills in only when the inverter list yields nothing -- which is the behavior upstream described in its own 5.0.1 notes ('falls back to the plant /realtime endpoint when inverter summary values are missing') before 62d0b49 changed it to a preference. The extra request is skipped entirely when both energy values are already present, so the normal poll cycle is unchanged. Verified live: energy_total is unchanged with no step, and all other sensors are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
….3.0 Docs are updated in the same pass as the behavior change: - README.md / QUICKSTART.md: document the auto-discover option and why it exists, and point at www.solarkcloud.com for finding the Plant ID - CLI.md + solark_secrets.template.json: base_url default updated - CHANGELOG.md: Unreleased section covering this integration pass, with the upstream commit for each item and an explicit note on the one deliberate divergence (/realtime as fallback, not preference) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The rationale in the fallback commit was a hypothesis stated as fact.
Investigated properly against the live API; the conclusion (fallback,
not preference) is unchanged but the reason was wrong.
What the earlier commit claimed: the plant figure counts production
from hardware no longer in the inverter list. That is contradicted by
the plant's own year-by-year PV history, which would have to contain
the surplus and does not.
What is actually true: /realtime etotal tracks the per-inverter sum
with a FIXED offset. Three samples across ~40 min of active
production, during which the counter advanced ~10 kWh, show the
difference constant to 0.1 kWh -- so it is the same underlying
quantity carrying a legacy constant, not a different metric, not
drift. The offset appears in no other endpoint. Cross-checked
against:
- the inverter list sum
- /api/v1/plants (also a lagging cache; its etoday read ~26% low
against the true mid-day value)
- /plant/energy/{id}/total lifetime PV, from the labelled
Load/PV/Export/Import/Charge/Discharge year-by-year series
All three agree within ~0.4%. The sibling realtime fields are sound
too: etoday matches the inverter sum exactly, emonth to within 1 kWh,
eyear to within 1.5%. Only etotal is an outlier, high by roughly a
quarter of lifetime production.
Origin of the constant remains unknown; it is recorded as unexplained
rather than guessed at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Module-level Tigo monitoring on the same array -- hardware entirely
separate from SolArk -- provides an independent lifetime production
reference:
Tigo (independent) baseline
history PV series -0.6%
inverter list sum (used) -0.9%
/realtime etotal about +26%
The three agreeing sources land within 1% of each other. The inverter
sum reading slightly under Tigo is expected physics, not error: Tigo
measures DC at the modules, inverter etotal is AC after conversion
losses. So the per-inverter sum is the correct AC production figure
and the right feed for the Energy Dashboard's solar source.
This closes the question the fallback commit was hedging. It is no
longer 'two sources disagree, prefer the safer one' -- /realtime
etotal is measurably wrong by many MWh against a physical meter.
Worth noting for the upstream report: the SolArk portal's overview
card displays etotal, so the portal contradicts both its own
production chart and the physical meter. Matching the card would
mirror a known-bad number.
No behavior change; energy_total already uses the per-inverter sum and
/realtime remains fallback-only.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Makes upstream's history an ancestor of this fork so a pull request diffs against a common base instead of appearing to delete upstream's newest work. Before this merge the merge-base was fe7546a, from before upstream's 11072e0..e7d0653. A PR opened from this fork would have rendered as -714 lines from api.py -- including the redaction and parse work upstream shipped in 5.0.2 -- because this fork had ported that content into solark_client.py / solark_auth.py / solark_logging.py rather than merging the branch. Conflict resolution, all in favor of this fork's module layout, which already contains upstream's 5.0.2 behavior: api.py ours (compat shim; upstream's monolith lives on in solark_client/solark_auth/solark_logging) __init__.py ours (upstream side was one comment we already have) config_flow.py ours (upstream side was self.config_entry vs our self._config_entry, deliberate -- avoids assigning the read-only property -- plus import paths) translations ours (superset; upstream side added nothing new) manifest.json ours (5.3.0) Docs were hand-merged rather than taken wholesale: CHANGELOG.md both histories kept; added a note that this fork's 5.1.x/5.2.x and upstream's 5.0.1/5.0.2 were parallel, so the version list is not chronological README.md upstream's clearer sensor descriptions (minPower, gridTo/toGrid, battery sign) adopted onto our fuller 18-sensor table; upstream's corrected plant-ID URL and redaction note adopted; energy_today/energy_total documented as summed across inverters, NOT plant realtime, per the measurements in fbadcb8 QUICKSTART.md same treatment Two things the auto-merge got wrong or surprising, both handled: - const.py silently duplicated OBSOLETE_BASE_URLS / OBSOLETE_API_URLS (both sides added equivalent blocks). Deduplicated; an AST scan of every module now reports no duplicate top-level definitions. - dashboards/README.md drops from 128 lines to upstream's 5-line stub. This is a correct merge -- we never modified it and upstream gutted it in 62d0b49 -- but note both repos still ship solark_flow.yaml and solark_dashboard.yaml, so those files are now undocumented upstream. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The docstring and changelog entry carried the raw investigation: plant id, inverter serials, exact sample tables and lifetime kWh figures. None of that is needed to justify the decision, and it does not belong in a repo that may be sent upstream. Reduced to what a reader actually needs: /realtime etoday agrees with the per-inverter sum, etotal does not (~26% high, a large fixed offset present in no other endpoint), and which side is correct was settled against an independent meter -- Tigo module-level monitoring agreed with the per-inverter sum, not with etotal. So the per-inverter sum is the figure we use. Also replaces a real inverter serial with a placeholder in two CLI.md examples. That predates this work but is the same class of leak. No behavior change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replace the parallel fork-version sections (5.1.0/5.1.1/5.2.0 plus an Unreleased block and a numbering apology) with one 5.3.0 entry that describes everything this branch adds relative to upstream's latest release. Upstream's own 5.0.x sections are kept verbatim below it. The entry describes the end state of this branch, including the resilience and lifecycle fixes landing in the following commits. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mysolark.com is retired; the CLI troubleshooting note and the repair issue description still told users to log in there. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_async_fix_grid_power_entity_id force-renamed the entity with unique_id
{entry_id}_grid_power to sensor.solark_grid_power on every setup. For
anyone upgrading from upstream (whose auto-generated id is
sensor.solark_grid_power_net) that silently breaks every automation,
dashboard, and statistics reference — and it also reverted any
user-chosen entity_id on each reload. Integrations should not rewrite
registry entity_ids; suggested_object_id already covers fresh installs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
get_plant_data swallowed every sub-fetch error and returned an empty dict, which parse_plant_data then padded with 0.0 defaults — so a cloud outage or login failure recorded hours of bogus 0 W / 0 % samples into long-term statistics while entities stayed 'available'. Now: if every sub-fetch failed, get_plant_data raises and the coordinator marks entities unavailable (restoring upstream's behavior). If only the flow fetch failed, the power/SOC keys are left absent so those entities report unknown rather than fake zeros, while energy and status values fetched from other endpoints stay fresh. Repair-issue health evaluation now runs on failed cycles too, so the prolonged- failure issue can actually appear during a full outage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
prime_inverters_cache() raised raw SolArkCloudAPIError before the coordinators ran, so a transient outage while HA was booting marked the entry 'Failed to set up' with no retry. The cache is lazy and refetches on demand, so priming is now best-effort. The settings coordinator's first refresh also gated the whole integration: accounts that can read telemetry but not settings (limited/installer logins, plants with no equipMode==1 master) went from working sensors to endless setup retries. Settings now refresh without raising; the config sensors just stay unavailable until a settings poll succeeds. The data coordinator still raises ConfigEntryNotReady, preserving retry-with-backoff for real outages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The service handler closed over the first entry's api object, settings coordinator and allow_write_access snapshot. Because it was registered once and never removed, toggling the Allow write access option (which reloads the entry) had no effect until HA restarted, writes kept using a pre-reload api object, and calling the service after removing the entry raised a KeyError. The handler now looks everything up in hass.data when called, and the service is unregistered when the last config entry unloads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The battery/grid/home energy sensors were built by instantiating homeassistant.components.integration.sensor.IntegrationSensor through inspect.signature probing and poking _attr_* privates — fragile against that integration's constructor changes, and its registry-miss fallback guessed source entity_ids that are wrong for a second config entry. SolArkIntegratedEnergySensor (previously the unreachable fallback) is now the only path: a plain coordinator entity doing the same trapezoidal integration, restoring its total across restarts. Unique IDs are unchanged, so existing entities keep their identity, history and last value. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_work_mode_to_str/_energy_mode_to_str returned 'Unknown (7)'-style strings for unmapped values, which are not in the descriptions' options lists — HA then raises 'value is not a valid option' and the entity errors out. Unmapped values now report 'Unknown' and log the raw value at debug level. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The config-entry migration calls async_update_entry(..., version=...), a kwarg that only exists since HA 2024.3. On older versions the migration raised TypeError and disabled the entry. 2024.3 also covers the suggested-object-id and repair-issue APIs this integration uses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
max_solar_power wrote solarMaxSellPower and max_sell_power wrote pvMaxLimit — crossed relative to what the API field names literally mean (pvMaxLimit caps solar input, solarMaxSellPower caps grid sell; consistent with the wider Deye-family cloud API). The read-only sensors carried the same swap, so 'Max Solar Power' displayed the sell limit and vice versa, and a configure_inverter write would have set the wrong register. Verified against a live system: after the swap the reported values (sell 16500 W, solar 18000 W on a two-inverter stack) line up with the portal's labels instead of exceeding the inverters' sell capability. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The post-write settings refresh burst (up to a minute of 15 s sleeps) was never cancelled on unload and kept refreshing a detached coordinator. Repair-issue IDs now include the entry_id so, with two config entries, one entry's recovery or unload no longer clears the other's outstanding issue. (Coordinators pick up their config entry automatically from the setup context, so no explicit kwarg is needed at our minimum supported HA version.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The data coordinator, settings coordinator, and service calls share one SolArkAuth; concurrent token expiry triggered parallel duplicate logins. ensure_token now double-checks validity under an asyncio.Lock so only one login runs at a time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The zero-filter cache for energy_today/energy_total had no TTL: after the provider's midnight reset it substituted yesterday's total until the first production of the morning, and through a multi-day outage it presented stale values indefinitely. Retention now uses the same 30-minute window as the status keys. After it expires, a zero the API actually reported is accepted for energy_today (that's the genuine daily reset); energy_total can never legitimately read zero, so it reports unknown instead of holding a stale number forever. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The number/select/switch/time platforms were removed in the v3 migration, and no sensor sets a translation_key (names are provided directly), so the whole entity section was unreferenced. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Setup no longer dumps the whole settings payload into the debug log or wraps plain constructor calls in try/except blocks that could only swallow programming errors. A rejected settings write logs the requested updates at ERROR and defers the full payload to debug — it is large and mostly unrelated settings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Submitting options unconditionally rewrote entry.data (reload via the update listener) and then finished the flow (second reload via the options update). The data write is now skipped when the resolved hosts are unchanged, which is the common case. Removed the unused _get_config_entry helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With no operation flags the CLI enabled every operation, then exited with 'Missing --inverter-sn for --settings' before printing anything. The default sweep now skips the SN-requiring operations (settings, workdata) unless --inverter-sn is given, matching the documented 'just run it with a secrets file' usage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ignoring a developer-environment directory is a personal preference, not a project rule; keep only the solark_secrets.json ignore the CLI needs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The troubleshooting sections told users a registry rename would run; the integration no longer rewrites entity ids, so describe the actual behavior (existing ids are kept, rename manually if preferred). README now states the HA 2024.3 minimum, and QUICKSTART's UTF-8 mojibake (arrows, menu glyphs, footer emoji) is repaired. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reverts the earlier 'un-swap': a live test settled it the other way. Changing 'Max Sell Power' on the SolArk portal to a distinctive value (also confirmed on the inverter's LCD) changed pvMaxLimit, while solarMaxSellPower held its old value. The cloud field names are misleading: the portal's Max Sell Power writes pvMaxLimit and its Max Solar Power writes solarMaxSellPower, exactly as this integration originally mapped them. Comments now document the measurement so the literal reading of the field names doesn't get 'fixed' back in. Co-Authored-By: Claude Fable 5 <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.
Add configuration visibility & control, robust multi-inverter energy, resilience, and a CLI (5.3.0)
This is the resubmission promised when I closed #5 ("found an issue with battery energy — will fix before resubmitting"). It has since been through several months of daily use on a live two-inverter (master/slave) plant, plus a full review pass to integrate everything you shipped in 5.0.1/5.0.2.
The branch merges your
main(5.0.2) as an ancestor, so the diff below is exactly what's new — nothing of yours is deleted or rewritten away. Where I diverge from a 5.0.2 behavior it's deliberate, called out below, and backed by measurements.24 files changed, ~3,800 insertions / ~990 deletions across 74 commits. I kept the commits small and focused rather than squashing, so the history documents why each change exists — several of them record measurements that are worth preserving. Happy to squash on merge if you prefer.
Highlights
1. Inverter configuration: read-only sensors + a gated write action
solark.configure_inverteraction writes those settings — but only when the "Allow write access" config option (default off) is enabled. With it off, the integration is strictly read-only, same as today.equipMode == 1, resolved once and cached). After a write, settings polling briefly accelerates (~15 s for up to a minute) because the cloud's/readendpoint lags its own/set— the read-back is eventually consistent.pvMaxLimit, and "Max Solar Power" writessolarMaxSellPower. The cloud field names are misleading. We verified empirically: setting Max Sell Power to a distinctive 17990 W on the portal (confirmed on the inverter LCD) changedpvMaxLimit;solarMaxSellPowerheld its old value. There are comments at both mapping sites so nobody "fixes" it back to the literal reading — including me; I tried once and the live test reverted me.2. Energy correctness on multi-inverter plants
energy_today/energy_totalnow sum across every inverter in the plant instead of reporting whichever inverter the API lists first. On this two-inverter system the old behavior under-reported daily production by roughly half./realtimeis a fallback, not the preferred source, foretoday/etotal. Itsetodayagrees with the per-inverter sum exactly, but itsetotalruns ~26% high on this (account-migrated) system, tracking the sum with a large fixed offset that appears in no other endpoint — the inverter list,/api/v1/plants, and the plant's own year-by-year PV history all agree within ~1%. We settled which side is right with an independent physical meter: module-level Tigo monitoring on the same array agrees with the per-inverter sum, not withetotal(the small remaining gap is DC-vs-AC conversion loss, as expected). Preferring/realtimewould mirror a known-bad number and inject a one-time step into aTOTAL_INCREASINGsensor, corrupting recorder statistics behind the Energy dashboard. The full reasoning lives in the_get_realtime_datadocstring. (Note: the portal's overview card displays the badetotal, so "matches the portal" is unfortunately not the same as "correct".)energy_todayis accepted as the genuine daily reset, andenergy_totalgoes unknown rather than serving stale data through a long outage.integrationhelper's internals).3. Resilience
ConfigEntryNotReadyinstead of a dead "Failed to set up"), and accounts that can read telemetry but not settings (installer/limited logins, or plants with no master) keep all their normal sensors — only the config sensors stay unavailable.4. New sensors beyond energy
/194), much more reliable than inferring from power flows. Matched by register number so the p2 cluster's column-name change (AcRelayStatus/194vs the oldAcRelayStatus(NA)/194) can't break it again.5. Architecture: HA-independent client + CLI
api.pymonolith is split:solark_client.py(API client, HA-independent),solark_auth.py(OAuth + legacy login),solark_logging.py(your redaction fromd336443, ported verbatim),solark_errors.py.api.pyremains as a compatibility shim re-exporting the same public names (SolArkCloudAPI,SolArkCloudAPIError,_redact_secrets,_redact_secret_text) with compatible signatures.python -m solark_cli) for poking the cloud API directly — plant data, raw endpoint dumps, settings read, gateway list, TOU slot writes. SeeCLI.md. Credentials come from a gitignoredsolark_secrets.json(template included). This is how most of the endpoint behavior above was measured.discovery.pyis byte-identical to yours), retired-host normalization, PVminPowerhandling, the MPPT placeholder-ramp guard, the SOCcurCapguard, battery sign flags, and the log redaction — applied at every log site in the split modules.6. Upgrade safety (please scrutinize — this was a priority)
{entry_id}_{key}unique_id format are unchanged.suggested_object_id; existing installs keep whatever ids (and customizations) they have, so no automation or dashboard breaks on upgrade.mysolark.com,ecsprod-api*) to current defaults — the old api_url still serves reads but rejects writes since the p2 account migration, so entries stuck on it look healthy until the first write fails.hacs.json): the migration usesasync_update_entry(..., version=...), which doesn't exist earlier — on older cores the old floor of 2023.5 would have bricked the entry mid-migration.7. Docs
CLI.mdadded, Energy-dashboard guide updated for the built-in energy sensors, QUICKSTART's UTF-8 mojibake repaired, and CHANGELOG rewritten so 5.3.0 reads as one release on top of your 5.0.2.Testing
/realtimebehavior, and endpoint cross-checks measured with the included CLI; the decisive numbers are recorded in the relevant commit messages and docstrings.Notes for review
git log --reversereads as a narrative. The commits touchingsolark_client.pyparsing carry the endpoint measurements inline./realtimeas fallback (§2) and meter-first grid import/export (explicit meter fields are trusted before direction flags whenexistsMeteris set).