github nicois/foxess-control v1.0.22-beta.5

latest release: v1.0.22-beta.6
pre-release2 months ago

Fixed

  • The overview card presented a disconnected browser as stale inverter data (C-020). If the Home Assistant frontend loses its WebSocket — tab suspended, laptop asleep, network blip — the page's copy of last_update freezes while the card keeps computing its age as Date.now() - last_update, so the displayed age grows without bound. A user reported "API - last updated 45 minutes ago" with solar reading zero; the integration had in fact polled successfully every 5 minutes throughout (every poll success: True, server-side age_seconds never above 300 s across 22 consecutive intervals). The card now checks hass.connected and distinguishes the two causes, because they need opposite responses: a disconnected frontend means check the browser or network, whereas genuinely old data means check the inverter or the cloud API. When either applies the readings are dimmed and desaturated behind a full-strength amber banner naming the cause and the age, so a stale card cannot be mistaken for a live one at a glance. Styled entirely from theme variables, so it reads correctly in light and dark themes, and translated into all nine languages the card ships.
  • The stale badge cried wolf, which is why the real signal was missed. Its threshold was a flat 30 s against a 300 s REST poll interval, so a perfectly healthy install rendered the stale badge for roughly 90% of every interval — "API - 2m" almost always, making the genuinely broken "API - 45m" indistinguishable from normal. Thresholds are now anchored to each source's actual cadence: 60 s for the WebSocket (which pushes every ~5 s) and 900 s for REST polling (three missed polls). The badge shows a bare API when healthy and only appends an age when something is actually wrong. Regression class tests/test_overview_card_stale.py (13 tests driving the shipped card in a real Chromium DOM, asserting computed opacity and filter rather than just class names); the E2E test that previously asserted the 30 s behaviour now guards the inverse.

Changed

  • Test infrastructure: the Flaky Test Detection workflow's chronic panel-load failures are fixed (C-031) — one of the workflow's two independent causes of redness; see the job-timeout entry below for the other. No user-visible change — the integration is untouched; only the E2E harness changed. The workflow had never passed since v1.0.21-beta.2 (June 2026): on roughly 1 in 100 dashboard loads a test errored at setup with E2EConditionTimeout: lovelace-stage:ha-panel-lovelace: pass_check not satisfied within 74991ms, always at setup, never as an assertion failure, on random tests and in both connection modes. The captured screenshot showed the reason the message did not: a fully-booted Home Assistant with "Error while loading page lovelace." in the content area. Home Assistant loads its Lovelace panel as a Promise.all over 51 separately-fetched JavaScript chunks; if any one of those requests is lost — a starved GitHub runner drops one occasionally — hass-router-page catches the rejection, replaces the panel with <hass-error-screen> and never re-issues the import. The document is dead from that moment, so the harness's 75-second wait could never succeed; it was waiting for a page that had already failed, and then reporting a timeout, which sent three months of investigation towards slow-boot theories. The harness now (a) recognises hass-error-screen in its fail-fast check — previously it knew only ha-panel-error / hui-error-card, neither of which HA uses here — so the failure surfaces in about a second instead of 75, and (b) recovers by re-navigating, bounded to three navigations, because a rejected dynamic import is only ever retried by a new document; a plain timeout still fails on the first attempt, so this is recovery from an identified terminal state rather than a blind retry. Reproduced deterministically by fault injection against a real container (tests/e2e/test_ui.py::TestPanelLoadFailureRecovery aborts exactly the chunk that defines ha-panel-lovelace), which produced a pixel-identical failure capture and the verbatim CI error before the fix, and passes after it — setup time for that test fell from 87.3s to 14.0s. The trigger itself is environmental and does not occur locally: 820 consecutive local dashboard loads (500 unconstrained, 320 pinned to four cores, both at high xdist parallelism) produced zero failures.

  • Test infrastructure: E2E failure captures now say why the browser failed (C-020's spirit applied to the harness). Each page records console errors, uncaught page errors and failed network requests; on a wait failure the tail goes into the pytest message and the whole buffer is written next to the existing HTML and PNG as a .log artefact. The DOM summary is also now a recursive shadow-DOM search that reports the error element and its message — previously it used a flat document.querySelector for tags Home Assistant nests several shadow roots deep, which is why every capture said only elements present: ['home-assistant', 'home-assistant-main']. Had either of these existed in June, the failure above would have named the lost asset and the net error on day one. Regression class tests/test_e2e_lovelace_panel_load_error.py (11 tests, three of which drive a real Chromium shadow DOM).

  • Test infrastructure: a third of the Flaky Test Detection matrix was cancelled on every run, independently of test health, and no longer is. The workflow gave each of its 20 jobs "a random half of the tests", but selected at file granularity — and there are only three E2E files (76 + 90 + 8 tests), so one of the three equally-likely pairs draws the two heaviest, 166 of 174 tests. That overruns the job's 30-minute limit and is cancelled: 6 of 20 jobs on the last run, 10 on 2026-06-19. Selection is now per test, so every job runs 87 tests and finishes in the same time (~13 min against a 30-minute limit), while keeping the randomised test co-location the workflow exists to probe. The timeout was deliberately not raised — the jobs were not legitimately slow, they were unevenly sized. matrix.run also moved from ${{ }} interpolation inside run: into env:, following GitHub's workflow-injection guidance; it was never untrusted input, but the pattern is worth not modelling badly.

Don't miss a new foxess-control release

NewReleases is sending notifications on new releases.