github jasonacox/pypowerwall v0.18.1
pyPowerwall v0.18.1 - Tesla Tariff API and v1r WiFi Failover

latest release: v0.18.2
4 hours ago
  • feat(tariff): read the site's utility tariff and update Time-of-Use settings - pw.get_tariff() / pw.set_tariff(tou_settings) (endpoints /api/tesla/tariff_rate and /api/tesla/time_of_use_settings). Cloud reads the Owner API tariff and FleetAPI reads tariff_content from site info; writes follow Tesla's time_of_use_settings contract (tariff_content_v2). Responses are normalized (e.g. {"Message": "Updated", "Code": 201}), a successful write invalidates the cached tariff, and TEDAPI (mock read, None on write) and local mode (None) fail cleanly. Thanks @nesys (#382)
  • fix(cloud): recover when Tesla replaces or re-provisions a site - after a 404 for a site ID that's gone from the account, switch to the site with the same gateway_id (or unique site_name), or the only site on a single-site account, persist it to .pypowerwall.site, and retry once; with several sites and no match, leave the site unchanged and log an error. Serialized, rate-limited to one attempt per minute. Long-running processes no longer need a restart after a site change. (#382)
  • fix(tedapi): v1r failover to the WiFi fallback host (wifi_host) now actually happens when the wired LAN is down. The v1r LAN and WiFi-fallback sessions retry once instead of 2-3 times, so a dead host fails in ~2x timeout instead of holding the per-method API lock long enough for callers' outer timeouts to abandon it (later polls then only saw Timeout waiting for API lock and never reached WiFi). And a start or LAN recovery probe with the LAN down now adopts the DIN over WiFi and serves data there, where it used to return None and never come up; the LAN retry backoff still doubles on each failed probe. Both hosts down still returns None. Basic (WiFi) and bearer TEDAPI sessions are unchanged. Thanks @erikgieseler, requested by @jasonacox-sam (#394).
  • feat: new failover option on Powerwall, PyPowerwallTEDAPI and TEDAPI (default True, unchanged behavior — what the proxy and pypowerwall-server use). failover=False is strict: connect() tries only the configured mode (no fallback to FleetAPI/Cloud; with retry_modes it retries that mode), and in v1r mode the leader always uses the wired LAN — no switch to the WiFi host, no startup on WiFi, and get_native_api() stays on the gateway host — so a failed request returns None instead of being rerouted. Followers still use wifi_host, their only route in v1r mode. For scripts that test which modes and transports work. (#395)
  • fix(tedapi): the v1r LAN recovery probe runs once per window. When the window opened, every concurrent getter (each holds its own per-method API lock) passed the unguarded check and reconnected at once — with the LAN down and a WiFi host up, N LAN logins and N WiFi DIN fetches per probe — and the failure counter could be bumped once per racing thread, pushing the next probe out extra backoff steps. One request now claims the probe while the others keep routing via WiFi; the failover state (including the #394 startup-on-WiFi path) changes only in a few small methods under one lock, never held across a network call; and LAN failures of requests already in flight when the failover tripped no longer escalate it. Backoff schedule unchanged (8 minutes, doubling to ~2 hours). (#395)
  • fix(tedapi): a known v1r leader DIN is no longer cleared by a failed reconnect (recovery probe or connect(force=True); connect() still returns None). Clearing it opened a window where concurrent requests saw DIN None — starting a second concurrent reconnect or signing LAN requests with None — and, when the WiFi host was also briefly unreachable, left every getter running a full reconnect each poll (ignoring the backoff) and returning "Not Connected" instead of serving via WiFi once it answered again. (#395)
  • fix(tedapi): v1r without a wifi_host no longer stops trying the LAN after 3 failures. With nowhere to fail over to, the tripped state only returned None without trying — for 8 minutes, even after the LAN came back. Every request now tries the LAN, and data resumes on the first one that succeeds. (#395)
  • fix(tedapi): connect() is single-flight. Getters under different per-method locks that all found no DIN each ran connect() at once: in WiFi/basic mode each cleared the DIN and closed the session other threads were mid-request on; in v1r mode each ran a full login + DIN + key probe. A caller that finds a connect in flight now gets the current DIN (None if still unknown) instead of starting another. (#395)
  • fix(tedapi): get_pw3_vitals() no longer raises. It calls the transport directly, so a gateway timeout, connection reset or malformed payload propagated into vitals(), get_blocks() and /api/meters/aggregates; it now logs and returns None, and a transport error on one Powerwall skips only that Powerwall. Its result is now cached for the data-cache window, pwcacheexpire (the cache was read but never written, so one proxy poll re-queried every Powerwall three or more times). (#395)
  • fix(tedapi): @uses_api_lock getters accept positional arguments: get_battery_block("DIN"), as the module docstring documents it, raised TypeError (multiple values for self_function). Keyword calls are unchanged. (#395)
  • fix(tedapi): the cache read no longer raises KeyError when another thread (config-write invalidation) pops the entry mid-read, and treats an entry stamped in the future (wall clock stepped back) as expired instead of fresh. (#395)
  • fix(tedapi): a successful WiFi TEDAPI request clears a WiFi cooldown another thread set while it was in flight. (#395)
  • fix(v1r): transport read/write methods called without a DIN return their failure value (None/False) instead of raising from the signing code; the retry after a 401/403 re-login is re-signed (the signature expires 12 s after signing, which the first attempt plus the login can exceed); write_config_v1r() no longer raises internally on a non-filestore reply (MessageEnvelope has no error field) and reports failure for a filestore reply that isn't an update acknowledgement; get_din() handles a gzip-compressed body (as /tedapi/din sends on firmware 25.42.2+); concurrent key-auth failures emit their UserWarning once. (#395)
  • docs: README "v1r WiFi Fallback" now documents the leader failover (it described only follower routing) and failover=False; API.md lists tedapi_auth_mode and failover. (#395)
  • tests: new test_failover_option.py, test_v1r_lan_probe.py, test_tedapi_hardening.py, test_v1r_transport_hardening.py, and proxy/tests/test_transport_health.py. (#395)

Contributors

Thanks to the community members who made this release possible:

  • @nesys — Tesla tariff and Time-of-Use API support (get_tariff() / set_tariff()) and the cloud site-replacement recovery (PR #382)
  • @erikgieseler — reported and diagnosed the v1r WiFi failover that never triggered when the wired LAN was down (issue behind #394)
  • @jasonacox-sam — independent hardware validation of the v1r failover, strict-mode failover option, and TEDAPI hardening on a live two-Powerwall-3 fleet (PR #395)

Don't miss a new pypowerwall release

NewReleases is sending notifications on new releases.