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
- Find the Backup Gateway IP.
- Tesla mobile app → Settings > Powerwall > Network.
- The router's DHCP table (look for
TeslaEnergyGateway-or the gateway hostname). arp -afrom the controller, looking for the gateway MAC.
- 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.
- 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 emailpassword— the gateway password from step 2 (stored encrypted)
- Verify. From the Script Console:
You should see a JSON response withawait gem.command({device: <device_id>, action: 'get_status'});
soe,aggregates,operation,grid_status, andsystem_status. After one polling cycle (~30 s) the device picks upbattery_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. - (Optional) Per-Powerwall zones. If the install has multiple Powerwall battery blocks and you want per-block tracking, run:
Note theawait gem.command({device: <device_id>, action: 'get_system_status'});
PackageSerialNumberof eachbattery_block, then create one zone per block withaddressset to that serial. The poller will populatebattery_soc_percentandpower_won each zone.
Attributes
Device
| Attribute | Required | Type | Notes |
|---|---|---|---|
ip | yes | string | Backup Gateway LAN IP. |
email | yes | string | Tesla account email tied to the install. |
password | yes | string (secure) | Gateway password — from the QR sticker inside the enclosure. |
port | no | int | HTTPS port. Default 443. |
polling_interval | no | int | Telemetry poll interval in ms. Default 30000, minimum 5000. |
request_timeout | no | int | HTTP request timeout in ms. Default 10000. |
Runtime-populated (read-only, written by the poller):
| Attribute | Type | Source |
|---|---|---|
battery_soc_percent | float | /api/system_status/soe |
site_power_w | int | /api/meters/aggregates (negative when exporting) |
load_power_w | int | /api/meters/aggregates |
solar_power_w | int | /api/meters/aggregates |
battery_power_w | int | /api/meters/aggregates (positive when discharging) |
grid_export_w | int | derived: -site_power_w (positive when exporting to grid) |
grid_status | string | /api/system_status/grid_status (SystemGridConnected / SystemIslandedActive / etc.) |
operating_mode | string | /api/operation (real_mode: self_consumption / backup / autonomous) |
backup_reserve_percent | int | /api/operation — reserve floor held for grid outages |
freq_shift_load_shed_soe | int | /api/operation — off-grid frequency-shift load-shed SoE threshold |
freq_shift_load_shed_delta_f | float | /api/operation — off-grid frequency-shift load-shed delta-frequency (Hz) |
solar_energy_exported_kwh | float | /api/meters/aggregates — lifetime cumulative solar production |
site_energy_imported_kwh | float | /api/meters/aggregates — lifetime cumulative grid import |
site_energy_exported_kwh | float | /api/meters/aggregates — lifetime cumulative grid export |
load_energy_imported_kwh | float | /api/meters/aggregates — lifetime cumulative home/load consumption |
energy_remaining_kwh | float | /api/system_status (nominal_energy_remaining) — energy stored right now |
full_pack_energy_kwh | float | /api/system_status (nominal_full_pack_energy) — usable pack capacity; trends down as packs age (degradation indicator) |
available_blocks | int | /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 suffix | Type | Source field | Notes |
|---|---|---|---|
serial | string | PackageSerialNumber | Block serial number. |
nominal_energy_remaining | int | nominal_energy_remaining | Energy stored in this block (Wh). |
nominal_full_pack_energy | int | nominal_full_pack_energy | Usable capacity of this block (Wh) — per-block degradation. |
p_out | int | p_out | Instantaneous block power output (W). |
pinv_state | string | pinv_state | Inverter state. |
pinv_grid_state | string | pinv_grid_state | Inverter grid-connection state. |
backup_ready | bool | backup_ready | Block is ready to carry backup load. |
wobble_detected | bool | wobble_detected | Power-quality wobble flag. |
disabled_reasons | string | disabled_reasons | Comma-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.
| Attribute | Type | Source |
|---|---|---|
battery_soc_percent | float | derived from nominal_energy_remaining / nominal_full_pack_energy |
power_w | int | battery_block.p_out |
state | string | convenience render of SOC% or watts |
Commands
| Name | Args | Description |
|---|---|---|
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_mode | mode | Mode = self_consumption | backup | autonomous. |
set_backup_reserve | percent | 0–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_modeandset_backup_reservewrite to/api/operationbut 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
| Symptom | Check |
|---|---|
login http 401 on first connect | Wrong 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 443 | Gateway is on a different VLAN than the GEM controller. Add a firewall rule permitting GEM → gateway on TCP/443. |
set_mode returns http 500 | Sitemaster 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 two | JWT auto-refresh failed. Check the device log for the re-auth attempt; if persistent, verify the gateway password has not been changed. |