Skip to main content

Tesla Powerwall (Local Gateway API)

The Tesla Powerwall driver polls a Tesla Backup Gateway over its on-LAN HTTPS API (TEG firmware) and surfaces state of charge, instantaneous site/load/solar/battery power flow, lifetime energy counters, pack capacity/degradation, online block count, grid status, and operating mode as device attributes. Per-Powerwall-block diagnostics (state-of-charge, inverter state, backup readiness) are flattened onto the device as well. It can also switch the operating mode (self-consumption / backup-only / autonomous) and adjust the backup reserve percentage. All traffic stays on the customer LAN — there is no Tesla cloud round-trip at runtime.

Status​

Built against TEG 21.x / 22.x / 26.x — verified against firmware 26.10.3 on a 5-Powerwall / 3-inverter install. Read paths (/api/system_status/soe, /api/meters/aggregates, /api/system_status/grid_status, /api/operation, /api/system_status) and write paths (/api/operation for mode + reserve) are implemented. Older Backup Gateway 1 units running pre-TEG firmware used basic-auth without JWT and are not supported here.

Prerequisites​

  • Backup Gateway 1 or Backup Gateway 2 on TEG firmware, on the same LAN as the GEM controller, reachable on TCP/443.
  • Tesla account email associated with the Powerwall installation.
  • Gateway password — the last 5 characters of the gateway code printed on the QR-code sticker inside the Backup Gateway enclosure (e.g. code HAKJGVLFCZ → password VLFCZ), or a custom password set during commissioning by the installer. This is NOT the Tesla account password. You may paste the full gateway code — the driver falls back to its last 5 characters if the full value is rejected.

Setup​

  1. Find the Backup Gateway IP.
    • Tesla mobile app → Settings > Powerwall > Network.
    • The router's DHCP table (look for TeslaEnergyGateway- or the gateway hostname).
    • arp -a from the controller, looking for the gateway MAC.
  2. Locate the gateway password. Open the Backup Gateway enclosure (front cover, no power-down required) — there is a QR-code sticker on the inside that lists the gateway code. The gateway password is the last 5 characters of that code (e.g. code HAKJGVLFCZ → password VLFCZ). If you are unsure which part to enter, paste the full code — the driver tries it as entered and falls back to the last 5 characters if the full value is rejected. If the installer set a custom password during commissioning, use that instead (it is taken exactly as entered, never clipped).
  3. Create the device. From Open Devices (System → Devices), add a device with driver tesla_powerwall. Set:
    • ip — the gateway LAN IP (e.g., 192.168.1.60)
    • email — the customer's Tesla account email
    • password — the gateway password from step 2 (stored encrypted)
  4. Verify. From the Script Console:
    await gem.command({device_id: <device_id>, command: 'get_status'});
    You should see a JSON response with soe, aggregates, operation, grid_status, and system_status. Running get_status also refreshes the device attributes right away — battery_soc_percent, site_power_w, load_power_w, solar_power_w, battery_power_w, grid_export_w, grid_status, the lifetime energy counters, pack capacity, online block count, operating mode, and per-block diagnostics — and the background poller then keeps them current every ~30 s (set by polling_interval).
  5. (Optional) Per-Powerwall zones. If the install has multiple Powerwall battery blocks and you want per-block tracking on a Zone, run:
    await gem.command({device_id: <device_id>, command: 'get_system_status'});
    Note the PackageSerialNumber of each battery_block, then create one zone per block, bound to this device, with the zone Address set to that serial. New Powerwall zones default to the Power subsystem (the zone editor pre-selects it). The driver does not create these zones for you — it only fills in ones you have already created — and they populate on the next background poll (~30 s), not from a manual get_status. Each block zone then carries battery_soc_percent and power_w.

Energy dashboards​

Once the device is reporting, its power and energy values flow straight onto GEM's generic energy widgets — no extra configuration, no per-site build. The driver already publishes the standard solar / grid / battery / load / state-of-charge values (and the lifetime energy counters) the dashboards look for, so a Powerwall is a first-class source for every widget on the Power Management dashboard:

  • Power Flow — a live energy-flow diagram with Home in the center and Solar, Grid and Battery around it. Add it to any UI page (UIs → Pages → add widget → Power Flow) and pick this device under Power Device. Flow lines animate in the direction power is moving; the battery node fills to show state of charge and shows a bolt while charging.
  • Battery Backup — state-of-charge ring, energy remaining (kWh), and estimated backup runtime at the current load. Flips to "On Backup" when grid_status reports the grid is down.
  • Self-Sufficiency — share of the current home load being met locally (solar + battery) versus the grid.
  • Energy Today — today's consumed / produced / net energy, accumulated since local midnight from the lifetime counters.

To combine several gateways — or a Powerwall plus separate solar/meter devices — into one diagram, enable Aggregate All Power Devices on the Power Flow widget instead of picking a single device; the aggregate state of charge is energy-weighted across the fleet. See Power Management for the full widget catalog and settings.

Attributes​

Device​

AttributeRequiredTypeNotes
ipyesstringBackup Gateway LAN IP.
emailyesstringTesla account email tied to the install.
passwordyesstring (secure)Gateway password — the last 5 characters of the gateway code on the QR sticker inside the enclosure, or a custom commissioning password. The full gateway code may be pasted; the driver falls back to its last 5 characters if the full value is rejected. A value pasted with a stray leading/trailing space or newline is also tried trimmed if the value as entered is rejected.
portnointHTTPS port. Default 443.
polling_intervalnointTelemetry poll interval in ms. Default 30000, minimum 5000.
request_timeoutnointHTTP request timeout in ms. Default 10000.

Runtime-populated (read-only, written by the poller):

AttributeTypeSource
battery_soc_percentfloat/api/system_status/soe
site_power_wint/api/meters/aggregates (negative when exporting)
load_power_wint/api/meters/aggregates
solar_power_wint/api/meters/aggregates
battery_power_wint/api/meters/aggregates (positive when discharging)
grid_export_wintderived: -site_power_w (positive when exporting to grid)
grid_statusstring/api/system_status/grid_status (SystemGridConnected / SystemIslandedActive / etc.)
operating_modestring/api/operation (real_mode: self_consumption / backup / autonomous)
backup_reserve_percentint/api/operation — reserve floor held for grid outages
freq_shift_load_shed_soeint/api/operation — off-grid frequency-shift load-shed SoE threshold
freq_shift_load_shed_delta_ffloat/api/operation — off-grid frequency-shift load-shed delta-frequency (Hz)
solar_energy_exported_kwhfloat/api/meters/aggregates — lifetime cumulative solar production
site_energy_imported_kwhfloat/api/meters/aggregates — lifetime cumulative grid import
site_energy_exported_kwhfloat/api/meters/aggregates — lifetime cumulative grid export
load_energy_imported_kwhfloat/api/meters/aggregates — lifetime cumulative home/load consumption
energy_remaining_kwhfloat/api/system_status (nominal_energy_remaining) — energy stored right now
full_pack_energy_kwhfloat/api/system_status (nominal_full_pack_energy) — usable pack capacity; trends down as packs age (degradation indicator)
available_blocksint/api/system_status — count of online Powerwall blocks (a drop below install size means a unit went offline)

Per-Powerwall-block diagnostics​

The poller flattens the highest-signal fields from each entry of system_status.battery_blocks onto the device as battery_block_<n>_<attr>, where <n> is the 1-based block index. These are populated every poll regardless of whether per-block zones exist.

Attribute suffixTypeSource fieldNotes
serialstringPackageSerialNumberBlock serial number.
nominal_energy_remainingintnominal_energy_remainingEnergy stored in this block (Wh).
nominal_full_pack_energyintnominal_full_pack_energyUsable capacity of this block (Wh) — per-block degradation.
p_outintp_outInstantaneous block power output (W).
pinv_statestringpinv_stateInverter state.
pinv_grid_statestringpinv_grid_stateInverter grid-connection state.
backup_readyboolbackup_readyBlock is ready to carry backup load.
wobble_detectedboolwobble_detectedPower-quality wobble flag.
disabled_reasonsstringdisabled_reasonsComma-joined list of reasons the block is disabled (empty when healthy).

History is trended on the low-churn diagnostic fields (capacity, inverter/grid state, backup/wobble flags, disabled reasons); the high-churn electrical values (nominal_energy_remaining, p_out, block serial) are live-only to avoid flooding attribute history.

Zone​

Zone address is optional. If set, it must match a battery_blocks[*].PackageSerialNumber from /api/system_status.

AttributeTypeSource
battery_soc_percentfloatderived from nominal_energy_remaining / nominal_full_pack_energy
power_wintbattery_block.p_out
statestringconvenience render of SOC% or watts

Commands​

NameArgsDescription
get_status—Snapshot of SOC + meter aggregates + operation + grid status + system status. Refreshes device attributes (including energy counters, pack capacity, online block count, and per-block diagnostics).
get_soc—State-of-charge percent.
get_power_flow—Site/load/solar/battery instantaneous power.
get_operation—Mode + backup reserve.
get_grid_status—Grid connected/islanded.
get_system_status—Full system payload (battery blocks, inverters, etc.).
set_modemodeMode = self_consumption | backup | autonomous. The current backup reserve is read back and resent unchanged; if that read fails or the reply carries no reserve, the command is refused rather than resetting the reserve.
set_backup_reservepercent0–100. The current operating mode is read back and resent unchanged; if that read fails or the reply carries no mode, the command is refused rather than switching the mode.

Known limitations​

  • The local API exposes raw cell SOC. The Tesla mobile app applies a 5–10% reserved-capacity offset, so the two readings will differ by design.
  • set_mode and set_backup_reserve write to /api/operation but the gateway commits the change on the next sitemaster cycle (~5 s). Reading back immediately may still show the old value.
  • Each write sends both the mode and the reserve, so the driver reads the current values first and resends the one you didn't change. If that read fails the command returns an error and writes nothing, rather than resetting the other setting.
  • Lifetime energy counters are surfaced at the meter level (solar_energy_exported_kwh, site_energy_imported_kwh, site_energy_exported_kwh, load_energy_imported_kwh). Per-Powerwall-block lifetime charge/discharge throughput counters are not surfaced — only per-block live energy/capacity is.
  • Time-of-use scheduling (cost / rate plans) is not implemented.
  • If the gateway refuses the login (a wrong password or email, or its lockout after too many failed attempts), the driver stops posting the login for 60 seconds, doubling after each further refusal up to 15 minutes. Editing the email or password, or reloading the device, retries straight away. While it is holding off, commands answer with the refusal and (retrying in Ns).
  • If the gateway stops answering, the poller skips the rest of that cycle instead of waiting out a timeout on every endpoint, and after consecutive missed cycles it doubles the wait between polls, up to 5 minutes (or your polling_interval, if that is longer). The first answered poll puts it back on the normal interval.
  • When nothing answers, get_status returns an error alongside its usual soe / aggregates / … fields, so a macro set to stop on error stops.

Troubleshooting​

SymptomCheck
login http 401 on first connectWrong gateway password or wrong email. The gateway password is the last 5 characters of the gateway code on the QR sticker inside the Backup Gateway (e.g. HAKJGVLFCZ → VLFCZ), NOT the Tesla account password. Entering the full code is tolerated — the driver retries with its last 5 characters automatically — so a persistent 401 means the email, the code itself, or a custom commissioning password is wrong.
connect timeout on 443Gateway is on a different VLAN than the GEM controller. Add a firewall rule permitting GEM → gateway on TCP/443.
set_mode returns http 500Sitemaster is not running (gateway in commissioning mode or post-firmware-update reboot). Wait 5 minutes and retry.
Battery SOC stays at 100% but Tesla app shows ~95%Expected — the app applies a reserved-capacity offset. Local API exposes raw SOC.
auth rejected (401) after a day or twoJWT auto-refresh failed. Check the device log for the re-auth attempt; if persistent, verify the gateway password has not been changed.
No data after a brief gateway/network outage at startupThe driver logs ... login failed, will retry on poll (or ... connect soe failed/connect error, will retry on poll) but keeps the poller running, re-authenticating on each cycle — so a momentary failure at connect time recovers on its own, no reload needed: within one poll interval (~30 s) after a short outage, and within 5 minutes after a long one (the poller backs off while the gateway is unreachable). If data never appears after the gateway is reachable again, the cause is configuration (wrong IP/email/password) or reachability, not a transient hiccup.
login http 401 (retrying in …s)The gateway refused the login and the driver is holding off before trying again (see Known limitations). Fix the password or email; saving the change or reloading the device retries straight away.