# Predbat template: Tesla Powerwall controlled locally through the Teslemetry Home Assistant integration # # Everything Predbat reads and every command it sends goes through Home Assistant: the Teslemetry # integration's sensors and its teslemetry.dispatch_battery_power / teslemetry.resume_battery_operation # actions. No Teslemetry token goes in this file, and Predbat's built-in Teslemetry component is not used. # # Requirements: # - The Teslemetry integration (https://github.com/Teslemetry/hass-teslemetry) v2026.10.24 or later, from the # Marketplace (or HACS on Home Assistant 2026.10 or older), which provides teslemetry.dispatch_battery_power and teslemetry.resume_battery_operation. The per-pack # energy sensors used for the state of charge below need v2026.10.26 or later. # - The energy site paired with its local Powerwall gateway in the Teslemetry integration, so the # dispatch actions and live values go over the local network. # # How this differs from teslemetry.yaml: that template uses Predbat's built-in Teslemetry component, which # talks to the Teslemetry cloud API directly with your Teslemetry token and drives the Powerwall through # its operation mode, backup reserve and tariff. This template needs no token and never changes the # operation mode, export rule, grid charging switch or backup reserve: Predbat sets the battery power # directly, and the Powerwall returns to its normal operation on its own when a dispatch ends. pred_bat: module: predbat class: PredBat # Sets the prefix for all created entities in HA - only change if you want to run more than one instance prefix: predbat # Timezone to work in timezone: Europe/London # XXX: Template configuration, delete this line once you have set up for your system template: True # If you are using Predbat outside of HA then set the HA URL and Key (long lived access token here) #ha_url: 'http://homeassistant.local:8123' #ha_key: 'xxx' # Currency, symbol for main currency second symbol for 1/100s e.g. $ c or £ p or e c currency_symbols: - '£' - 'p' # Number of threads to use in plan calculation # Can be auto for automatic, 0 for off or values 1-N for a fixed number threads: auto # --- Tesla Powerwall via Teslemetry local battery dispatch --- # The entity names below assume your Teslemetry energy site is called "My home", so its entities are # sensor.my_home_*. Replace my_home with your own site's name as it appears in Home Assistant. inverter_type: TESLA inverter: name: "Tesla Powerwall via Teslemetry local battery dispatch" has_rest_api: False has_mqtt_api: False # Predbat writes the rate it chose (W) to charge_rate / discharge_rate and passes it to the # service hooks below as {power} output_charge_control: "power" # Charging and exporting are started and stopped by the service hooks, not by time windows, # target SoC or backup reserve has_charge_enable_time: False has_discharge_enable_time: False has_target_soc: False has_reserve_soc: False charge_time_format: "S" charge_time_entity_is_option: False soc_units: "%" num_load_entities: 1 time_button_press: False clock_time_format: "%Y-%m-%d %H:%M:%S" write_and_poll_sleep: 2 has_time_window: False # A 0 W dispatch holds the battery (no charge, no discharge) support_charge_freeze: True support_discharge_freeze: True has_idle_time: False # ---- Live power (energy site sensors) ---- # Tesla sign convention: battery positive is discharging, grid positive is importing battery_power: - sensor.my_home_battery_power battery_power_invert: - False pv_power: - sensor.my_home_solar_power load_power: - sensor.my_home_load_power grid_power: - sensor.my_home_grid_power grid_power_invert: - True # ---- Daily energy (kWh, energy site totals for today) ---- load_today: - sensor.my_home_home_usage import_today: - sensor.my_home_grid_imported export_today: - sensor.my_home_grid_exported pv_today: sensor.my_home_solar_generated # ---- State of charge (kWh, real basis) ---- # Each Powerwall and expansion pack paired through the local gateway has its own "Energy remaining" and # "Full pack energy" sensors (sensor.powerwall_energy_remaining, sensor.powerwall_expansion_energy_remaining, # sensor.powerwall_expansion_2_energy_remaining and so on). Predbat needs the totals across all packs, so # create two template sensors that sum them, e.g. in configuration.yaml (list every pack you have): # # template: # - sensor: # - name: "Powerwall energy remaining total" # unique_id: powerwall_energy_remaining_total # unit_of_measurement: kWh # device_class: energy_storage # state_class: measurement # state: > # {% set packs = ['sensor.powerwall_energy_remaining', 'sensor.powerwall_expansion_energy_remaining'] %} # {% set values = packs | map('states') | select('is_number') | map('float') | list %} # {{ (values | sum) | round(2) if values | count == packs | count else this.state }} # - name: "Powerwall full pack energy total" # unique_id: powerwall_full_pack_energy_total # unit_of_measurement: kWh # device_class: energy_storage # state_class: measurement # state: > # {% set packs = ['sensor.powerwall_full_pack_energy', 'sensor.powerwall_expansion_full_pack_energy'] %} # {% set values = packs | map('states') | select('is_number') | map('float') | list %} # {{ (values | sum) | round(2) if values | count == packs | count else this.state }} # # These hold the last total while a pack sensor is unavailable, which happens briefly whenever a poll of the # local gateway times out. A "Combine the state of several sensors" (min_max) helper set to sum is simpler # to set up, but it does not hold the last value, so the total is wrong or unavailable during those gaps. # The full pack energy total tracks the battery's real capacity as it degrades. soc_kw: - sensor.powerwall_energy_remaining_total soc_max: - sensor.powerwall_full_pack_energy_total # Without the per-pack sensors you can use the energy site's percentage instead, with soc_max set to your # usable capacity, and battery_min_soc set to 0 as that percentage already hides the bottom buffer: #soc_percent: # - sensor.my_home_percentage_charged #soc_max: # - 13.5 # Hard floor on the real basis: the per-pack sensors include the bottom buffer Tesla keeps below the 0% the # app shows, which is about 5% real. Raise input_number.predbat_best_soc_keep for a soft planning floor. battery_min_soc: - 5 # ---- Rates ---- # Set these to what your system can do, e.g. 10000 W for a Powerwall 3 limited to 10 kW battery_rate_max: - 10000 inverter_limit: - 10000 # ---- Charge and discharge rate (written by Predbat, passed to the service hooks as {power}) ---- # Create two Number helpers (Settings > Devices & services > Helpers > Create helper > Number), each with # minimum 0, maximum 10000, step 100 and unit W (raise the maximum if your battery_rate_max is higher): # input_number.predbat_powerwall_charge_rate # input_number.predbat_powerwall_discharge_rate # Predbat writes the rate it chose into these and sends the current value in the dispatch below. The rate # is written after the dispatch is sent, so the first dispatch of a charge or export window can run at the # previous rate (after idle, the maximum) for one cycle until the next dispatch replaces it. charge_rate: - input_number.predbat_powerwall_charge_rate discharge_rate: - input_number.predbat_powerwall_discharge_rate # Device id of the Teslemetry energy site, passed to the service hooks as {device_id}. Find it under # Settings > Devices & services > Teslemetry > your energy site device: it is the last part of the page URL # (/config/devices/device/). device_id: 'xxxx' # --- Predbat service hooks: local battery dispatch --- # Charge: dispatch -{power} W. Export: dispatch {power} W. Charge and export freeze (hold): dispatch 0 W. # Stop: resume the Powerwall's normal operation. # Each dispatch lasts 600 seconds and repeat: True re-sends it every Predbat cycle (5 minutes), so the # Powerwall follows the plan continuously and returns to its normal operation on its own within 10 minutes # if Predbat stops. The hold power must be the string "0": Predbat drops a hook value that is 0 or empty. charge_start_service: - service: teslemetry.dispatch_battery_power device_id: "{device_id}" power: "-{power}" duration: 600 repeat: True charge_freeze_service: - service: teslemetry.dispatch_battery_power device_id: "{device_id}" power: "0" duration: 600 repeat: True charge_stop_service: - service: teslemetry.resume_battery_operation device_id: "{device_id}" discharge_start_service: - service: teslemetry.dispatch_battery_power device_id: "{device_id}" power: "{power}" duration: 600 repeat: True discharge_freeze_service: - service: teslemetry.dispatch_battery_power device_id: "{device_id}" power: "0" duration: 600 repeat: True discharge_stop_service: - service: teslemetry.resume_battery_operation device_id: "{device_id}"