fix(energy): raise SignedCommandRequired from cloud-only island mode commands#84
Merged
Conversation
added 3 commits
July 20, 2026 09:56
…commands set_island_mode/go_off_grid/reconnect_grid sent an unsigned grpc_command that gateways can acknowledge without actuating the grid contactor, shipping as a silent no-op with a success-shaped response. Raise a specific TeslaFleetError instead of returning that false success, and point callers at the signed local control path (add_authorized_client + EnergySiteRouter).
Docs still described the cloud-only island mode methods as silent no-ops; update AGENTS.md, energy_local_control.md, and fleet_api_energy_sites.md to reflect that they now raise SignedCommandRequired instead.
…ode after unconditional raise
Merged
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.
Intent
Cloud-only EnergySite.set_island_mode()/go_off_grid()/reconnect_grid() send an unsigned grpc_command that gateways can acknowledge without actuating the grid contactor - they shipped as silent no-ops with success-shaped responses (see PR 78, which added honest docstrings pointing to the router/local signed path but left the cloud methods as no-ops). Captain decision 2026-07-20 explicitly chose 'raise a clear error from the cloud-only methods' over a docs-only fix and over auto-routing to the local path. Implemented: a new SignedCommandRequired(TeslaFleetError) exception in exceptions.py; set_island_mode() now unconditionally raises it (go_off_grid/reconnect_grid inherit the raise since they delegate to set_island_mode - no new raise machinery, no capability probing). The router/local/signed EnergySite paths (router/, aiopowerwall local backend) are untouched - only the cloud-only no-op path changed. This is a deliberate BREAKING behavior change for any caller that relied on these methods as silent no-ops: nothing they returned ever actually actuated the contactor, so surfacing that as an error rather than fixing the underlying no-op is the intended outcome, not an oversight. Added tests/test_energysite_island_mode.py asserting each of the three methods raises SignedCommandRequired and never calls the underlying _request. Updated AGENTS.md, docs/energy_local_control.md, and docs/fleet_api_energy_sites.md to describe the new hard-stop behavior instead of the old silent-no-op wording, including how it interacts with EnergySiteRouter failover (local primary still tried first; on total failure the cloud secondary now raises SignedCommandRequired instead of returning a false success).
What Changed
SignedCommandRequired(TeslaFleetErrorsubclass) intesla_fleet_api/exceptions.py.EnergySite.set_island_mode()intesla_fleet_api/tesla/energysite.pynow unconditionally raisesSignedCommandRequiredinstead of sending an unsignedgrpc_commandthat gateways can ack without actuating the contactor;go_off_grid()/reconnect_grid()inherit the raise since they delegate toset_island_mode(). Fixed a stale docstring left over from the prior no-op behavior.AGENTS.md,docs/energy_local_control.md, anddocs/fleet_api_energy_sites.mdto document the hard-stop behavior and how it interacts withEnergySiteRouterfailover (local primary still tried first; cloud secondary now raises instead of returning a false success).tests/test_energysite_island_mode.pyasserting each of the three methods raisesSignedCommandRequiredand never calls_request.Risk Assessment
✅ Low: Small, self-contained change: a new exception type plus three methods that now unconditionally raise it, matching the explicit captain decision; tests directly cover the new behavior and assert no underlying request is sent, docs are updated consistently, and the EnergySiteRouter/Router failover interaction requires no code change since Router's existing exception handling already surfaces SignedCommandRequired correctly.
Testing
All existing/new automated tests pass (3 new island-mode tests plus the full 365-test suite), and a manual end-to-end script exercising the EnergySiteRouter local-primary-fails -> cloud-secondary-raises scenario described in the user intent confirms SignedCommandRequired propagates correctly with the documented message and that no network call is made before the raise; no issues found.Evidence: E2E router failover script (local primary fails, cloud secondary raises SignedCommandRequired)
OK: router raised SignedCommandRequired as expected exception message: This command cannot actuate via the unsigned cloud API - gateways acknowledge it without operating the contactor. Pair a key with add_authorized_client and issue it through a signed local control path (EnergySiteRouter) instead. OK: cloud EnergySite._request was never invoked (raise happens pre-network-call)Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
uv run pytest tests/test_energysite_island_mode.py -vuv run pytest tests -q (full suite)Manual e2e script: EnergySiteRouter(FailingLocalEnergySite, EnergySite).go_off_grid() with a failing local primary -> confirms SignedCommandRequired is raised through router failover instead of a false-success no-op, and that the cloud _request mock is never calleduv run python -c check: SignedCommandRequired is a TeslaFleetError subclass, not a bare Exception subclass✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.