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 — printed on the QR-code sticker inside the Backup Gateway enclosure, or set during commissioning by the installer. This is NOT the Tesla account password.

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 password as a 5–8 character alphanumeric string. If the installer set a custom password during commissioning, use that instead.
  3. Create the device under System > Devices 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: <device_id>, action: 'get_status'});
    You should see a JSON response with soe, aggregates, operation, grid_status, and system_status. After one polling cycle (~30 s) the device picks up 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 as attributes.
  5. (Optional) Per-Powerwall zones. If the install has multiple Powerwall battery blocks and you want per-block tracking, run:
    await gem.command({device: <device_id>, action: 'get_system_status'});
    Note the PackageSerialNumber of each battery_block, then create one zone per block with address set to that serial. The poller will populate battery_soc_percent and power_w on each zone.

Attributes

Device

AttributeRequiredTypeNotes
ipyesstringBackup Gateway LAN IP.
emailyesstringTesla account email tied to the install.
passwordyesstring (secure)Gateway password — from the QR sticker inside the enclosure.
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_statusSnapshot 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_socState-of-charge percent.
get_power_flowSite/load/solar/battery instantaneous power.
get_operationMode + backup reserve.
get_grid_statusGrid connected/islanded.
get_system_statusFull system payload (battery blocks, inverters, etc.).
set_modemodeMode = self_consumption | backup | autonomous.
set_backup_reservepercent0–100.

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.
  • 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.

Troubleshooting

SymptomCheck
login http 401 on first connectWrong gateway password or wrong email. The gateway password is on the QR sticker inside the Backup Gateway, NOT the Tesla account password.
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.