v1.11.1 - Hardening and cleanup pass
Six small refactors landed across separate commits, none of them user-visible. The integration behaves identically — this release is purely about code quality and laying ground-work for #12 (token caching).
Changes
- Stricter cookie scoping — drop
unsafe=Trueon theaiohttp.CookieJar. We only ever talk to portal hostnames, so refusing cookies that would only resolve via IP literals is defence-in-depth at zero cost. - Case-insensitive host comparison in redirect handler — yarl already normalises today, but lowering both sides explicitly protects future callers from a silent regression if they pass a capitalised
allowed_host. - Drop dead branches in
_http.py— after switching tenacity toreraise=True, theRetryErrorand trailingraisepaths became unreachable. Replaced with a singleAssertionErrorsentinel. - Drop redundant runtime
isinstancecheck in_auth_call—request_with_retryis typeddict[str, Any]and mypy strict enforces it. - Split
self.tokeninto_app_token+_ael_token— the same attribute previously carried two semantically different bearer tokens (short-lived app token vs long-lived AEL session token). Splitting them makes the lifecycle obvious and unblocks token caching across coordinator polls (#12). - Move credentials into
PortalAuth— login/password/provider were duplicated on the client and the auth helper. Now they live on the helper that actually uses them; smaller surface for accidental leaks.
Verified
- Full local validation (ruff, mypy strict, pytest 30 passed)
- All 5 CI jobs green at every step (Lint, Typecheck, Pytest, Hassfest, HACS)
- Live smoke test on a Raspberry Pi against SEM (Ventabren contract) — auth + fetch + statistics path all green
Closes #15.