fix(energysite): implement grid_import_export over local TEDAPI v1r - #5
Merged
Conversation
… export bug PowerwallEnergySite.grid_import_export previously raised NotImplementedError, so a per-command-failover router fell back to the cloud PATCH for it while operation() (mode) succeeded locally. That split coupled writes across two control planes: the cloud export-rule PATCH returned 200 but silently failed to actuate, leaving customer_preferred_export_rule stuck at "never" even though default_real_mode reached "autonomous" locally. Wire grid_import_export onto a local config.json write via PowerwallClient.set_grid_import_export, writing site_info.customer_preferred_export_rule and/or site_info.disallow_charge_from_grid_with_solar_installed in a single atomic read-modify-write (field names/enum confirmed against jasonacox/pypowerwall's v1r write path, which targets the same local gateway schema). This keeps operation and grid_import_export on the same control plane going forward.
Bre77
commented
Jul 19, 2026
| charge-from-grid keys) once the exact local fields are confirmed. Its | ||
| semantics differ enough from ``curtail`` that mapping to that would be | ||
| wrong, so this stays a placeholder and falls back to the cloud.""" | ||
| raise NotImplementedError( |
Member
Author
There was a problem hiding this comment.
Was this error causing the TeslaFleetApi Router to route to cloud, or was it swallowed silently?
Merged
Bre77
added a commit
that referenced
this pull request
Jul 31, 2026
The local setters and the merged set_grid_import_export (#5) write the same two site_info keys. Keep the ergonomic single-purpose wrappers, but make them delegate so validation and the read-modify-write live in one place; set_grid_import_export remains the way to set both atomically. Also corrects the EnergySite adapter docs, which still listed grid_import_export as unimplemented after #5 wired it up locally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
Restores evening battery→grid export by implementing
PowerwallEnergySite.grid_import_exportover the local TEDAPI path instead of leaving it aNotImplementedErrorplaceholder that fell back to the cloud.Every evening automation on a system running the local energy router issues two coupled commands:
operation(mode →autonomous) andgrid_import_export(export rule →battery_ok).operationis implemented locally and lands on the gateway;grid_import_exportwas not, so the router failed it over to the cloudEnergySite. That cloud PATCH returns200 {'response': ''}— success-shaped, but the export rule on the gateway never actually moves offnever, because the coupled mode change is now happening out-of-band on the local plane. Net effect:default_real_mode=autonomous+customer_preferred_export_rule=never→ the battery is allowed to run but forbidden from exporting.Read-back evidence (cloud
site_info, 07:30–09:30 UTC export window) shows the split lining up exactly with the local-router go-live:default_real_mode/customer_preferred_export_ruleautonomous/battery_okautonomous/never100% of evening export cycles have failed this way since the local router went live on 2026-07-14.
What changed
PowerwallClient.set_grid_import_export: writessite_info.customer_preferred_export_rule(GRID_EXPORT_RULES:battery_ok/pv_only/never) and/orsite_info.disallow_charge_from_grid_with_solar_installedviawrite_config. Both fields live in the sameconfig.jsondocument asbackup_reserve_percent, so passing both writes them in one atomic read-modify-write rather than two round-trips. Field names and the export-rule enum were confirmed against the siblingjasonacox/pypowerwallproject's v1r write path (set_grid_export/set_grid_charging), which targets the same local gateway schema — this repo has no way to read them back off real hardware itself.PowerwallEnergySite.grid_import_export: moved out of the placeholder section, maps onto the new client method, matches the cloud method's signature and_ok_response()return shape — no router changes needed.tests/test_grid_import_export.py(new) covers the client-level writer — enum validation, the atomic-both-fields write, and rejecting a no-argument call.tests/test_energysite.pymovesgrid_import_exportout of the placeholder-raises-NotImplementedErrortable into its own mapping tests.AGENTS.md: documents the field mapping and flags the router hazard this bug came from (don't splitoperation/backup/grid_import_exportacross local/cloud backends — they share oneconfig.jsondocument).Testing
uv run ruff check .,uv run mypy, anduv run pytest(100 passed) all pass. This has not been exercised against real Powerwall hardware — per the task's constraints, no commands were sent to any live gateway. The field names are cross-checked against a second independent local-API implementation (pypowerwall) rather than verified by a live write/read-back round trip; the customer's existing 17:30 AEST export automation will be the live verification once this is released and installed.