feat: expand energy gateway authorized-client support - #107
Conversation
…support Live-verified against a real Powerwall 3: AuthorizedClientState was mislabelled (a terminal timeout state was named as in-progress, which hangs a poll-while-not-VERIFIED pairing flow forever). Corrects the enum and adds the missing INVALID/REMOVED members. BREAKING: AuthorizedClientState.PENDING -> PENDING_VERIFICATION, and the old PENDING_VERIFICATION (value 2) -> PENDING_VERIFICATION_TIMEOUT (a terminal failure, not in-progress). Also: - AuthorizedClientKeyType gains INVALID/ECC (both live-verified to register and list back correctly); drop the "not published" caveat. - Tesla.ec_public_der_spki/_b64 mirror the RSA public-key helpers - ECC keys must be SPKI DER, not a raw point, or the gateway rejects them with an asn1 structure error. - EnergySite.remove_authorized_client added to the base Fleet-API class (previously cloud-missing); TeslemetryEnergySite's variant is now typed to match add_authorized_client's encoding behavior. - New AuthorizationRole/AuthorizedVerificationType enums for typing list_authorized_clients output. - Docstrings document the ~9-minute presence-proof window, the removal security asymmetry (no presence proof required), and that ECC keys cannot authenticate the LAN TEDapi v1r protocol.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6ec883ba5a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return await self._command( | ||
| "authorization", | ||
| "remove_authorized_client_request", | ||
| {"public_key": public_key_b64}, | ||
| ) |
There was a problem hiding this comment.
Accept empty successful removal responses
When the gateway returns an empty body—the new method contract explicitly allows this for some firmware—this call still passes through TeslaFleetApi._request, which rejects non-JSON responses at tesla/fleet.py:208-210 and attempts JSON decoding otherwise. A successful removal with a 204 or empty response will therefore raise ResponseError instead of returning success; normalize an empty successful response to {} before completing this command.
AGENTS.md reference: AGENTS.md:L151-L151
Useful? React with 👍 / 👎.
Intent
Implement the energy-gateway authorized-client findings from a live-verified aiopowerwall/Powerwall-3 test session (findings.md, relayed by the captain): (1) BREAKING rename of AuthorizedClientState - PENDING->PENDING_VERIFICATION, old PENDING_VERIFICATION(2)->PENDING_VERIFICATION_TIMEOUT (a terminal presence-proof-window-expired state, not in-progress), plus INVALID=0/REMOVED=4; (2) AuthorizedClientKeyType gains INVALID=0/ECC=2, drop the 'not published' caveat; (3) Tesla.ec_public_der_spki/_b64 helpers mirroring the existing rsa_public_der_pkcs1 pair, since ECC keys must be SPKI DER not a raw X9.62 point; (4) typed remove_authorized_client(public_key: bytes|str) on TeslemetryEnergySite (was an untyped params passthrough) and a new implementation on the base tesla EnergySite via the same _command('authorization', ...) route add_authorized_client uses - the base-class route is explicitly documented as inferred/unverified (only local v1r removal was hardware-verified) and does not assert on response fields since the response message is empty; (5) add_authorized_client docstring documents the ~9-minute presence-proof window, that PENDING_VERIFICATION_TIMEOUT is terminal, that re-registering the same key resets the window as the correct retry, the 59s physical-toggle observation, and no cloud auto-verify observed; (6) remove_authorized_client docstring documents the security asymmetry - removal needs no physical presence proof, so any paired key can revoke every other key including the owner's; (7) deliberately do NOT offer ECC as an RSA alternative in any TEDAPI-aimed keygen/pairing helper (ECC registers fine but cannot authenticate the LAN TEDapi v1r protocol - its SignatureData oneof has no ECDSA member) - Tesla.rsa_private_key's RSAPrivateKey assertion is kept as-is; (8) new AuthorizationRole and AuthorizedVerificationType enums for typing list_authorized_clients output. Scope is deliberately KISS: enum/docstring/method-signature changes plus two small helper properties, no new abstractions. Added tests pin the corrected enum values against the gateway's real numbers (so a regression to the old mislabelled names fails), the SPKI helper's output shape/length, and remove_authorized_client's bytes-to-base64/str-passthrough encoding on both the base and Teslemetry energy site classes. Updated tests/test_teslemetry_authorized_clients.py's existing assertions to the corrected enum names/values (this is the intentional breaking change, not a regression). Also added an AGENTS.md entry (CLAUDE.md is a symlink to it) summarizing this durable knowledge for future sessions. No CHANGELOG.md file exists in this repo - the project's release-notes convention is the PR/commit message plus the GitHub Release generated from the version-bump tag, not a tracked changelog file, so the breaking-rename callout belongs in the commit message and PR body rather than a new changelog file.
What Changed
Risk Assessment
✅ Low: The follow-up correctly adds typed roles and verification fields with per-value enum normalization, unknown-value preservation, absent-field handling, and focused coverage, resolving the prior intent-conformance issue without widening scope.
Testing
Inspected the authorized-client changes, ran both focused test modules successfully, and captured a concrete API/cryptographic transcript demonstrating corrected enum values, valid EC SPKI output, bytes-to-base64 Fleet removal, and string-passthrough Teslemetry removal; no working-tree artifacts or actionable failures remained.
Evidence: Authorized-client API behavior demonstration
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed ✅
tesla_fleet_api/teslemetry/energysite.py:73- Required intent says the newAuthorizationRoleandAuthorizedVerificationTypeenums are “for typing list_authorized_clients output,” butAuthorizedClientstill explicitly models onlypublic_keyandstate; capturedrolesandverificationvalues remain accessible only through untypedraw. Please either add typedrolesandverificationfields (including enum normalization that preserves unknown values) or confirm that merely defining otherwise-unused enums satisfies the intended output typing.🔧 Fix: Type authorized-client roles and verification
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
Inspected the target diff against base commit95daa121816c1b70937da94b8b250a6dbb80dd2a.uv run pytest -q tests/test_energysite_authorized_clients.py tests/test_teslemetry_authorized_clients.pyRan an end-to-end mocked API demonstration that generated a P-256 SPKI key, verified its 91-byte DER/base64 representation, and captured Fleet and Teslemetry removal request payloads.git status --shortconfirmed testing created no working-tree changes.✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.