Add restore, and separate the produce box from add-ons
Validate / hassfest (push) Failing after 8s
Validate / HACS (push) Failing after 17s
Validate / pytest (push) Failing after 8s

Restore is POST /s/submit/restore-delivery, wired to switch.turn_off. Its
popup only exists once an order is actually skipped, which is why it could not
be found earlier; verified end to end by skipping Aug 18 and restoring it.

Fixes two bugs found while testing that. async_fetch treated popup and AJAX
fragments as full pages, so the signed-in heuristic read them as logged out and
skip could never run. And the to-do list mixed box produce with add-ons, which
would have invited deletes with no endpoint behind them: only add-ons are
add/removable, so the entity is now scoped to those.
This commit is contained in:
flan
2026-08-03 20:20:15 +00:00
parent 551d3240a2
commit 5e9d721a0f
7 changed files with 122 additions and 38 deletions
+8 -3
View File
@@ -366,18 +366,23 @@ class FreshHarvestClient:
"""
return "sign out" in body.lower()
async def async_fetch(self, path: str) -> str:
"""Fetch any portal page, re-authenticating once if the session lapsed.
async def async_fetch(self, path: str, *, is_page: bool = True) -> str:
"""Fetch from the portal, re-authenticating once if the session lapsed.
The shared primitive for reads and for the token-scraping that every
write action in `actions.py` has to do first.
Set ``is_page=False`` for popup bodies and AJAX replies. Those are HTML
*fragments* with no navigation, so they never contain a Sign Out control
and the signed-in heuristic would read every one of them as logged out —
re-authenticating pointlessly and then failing.
"""
if not self._authenticated:
await self.async_login()
try:
body = await self._get(path)
if not self._signed_in(body):
if is_page and not self._signed_in(body):
self._authenticated = False
await self.async_login()
body = await self._get(path)