You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

521 lines
20 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

######################################################################
# @CCOSTAN - Follow Me on X
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Tesla Powerwall - Monitoring and outage automations
# Related Issue: 1490
# Related Issue: 1550
# Related Issue: 1571
# Related Issue: 1598
# Related Issue: 1936
# Powerwall status sensors, outage alerting, and load-shedding helpers.
# -------------------------------------------------------------------
# Video: https://youtu.be/hR_0lFEE2bA
# Blog: https://www.vcloudinfo.com/2026/06/tesla-powerwall-home-assistant-watchdog.html
# Notes: Read more https://www.vcloudinfo.com/2018/01/going-green-to-save-some-green-in-2018.html | Existing Issue #272
# Tesla Powerwall added via UI Integration
# Notes: Home Assistant owns staged resilience policy; recommendations remain monitor-only until coordinated actions are approved.
# Notes: The outage anchor uses one restoring input_datetime so stage timing survives Home Assistant restarts.
######################################################################
# Binary Sensors:
# - binary_sensor.powerwall_charging ............. battery_charging (on=charging)
# - binary_sensor.powerwall_connected_to_tesla ... connectivity (on=connected)
# - binary_sensor.powerwall_grid_services_active .. power (on=active)
# - binary_sensor.powerwall_grid_status .......... power (on=grid up)
# - binary_sensor.powerwall_status ............... power (on=system ok)
#
# Power & Energy Sensors:
# - sensor.powerwall_charge ...................... battery %, 0–100
# - sensor.powerwall_backup_reserve .............. reserve %, 0–100
# - sensor.powerwall_battery_power ............... kW (+discharge / -charge)
# - sensor.powerwall_load_power .................. kW house load
# - sensor.powerwall_site_power .................. kW (+export / -import)
# - sensor.powerwall_solar_power ................. kW solar production
#
# Lifetime kWh Meters:
# - sensor.powerwall_battery_export .............. kWh -> grid/load
# - sensor.powerwall_battery_import .............. kWh <- battery charged
# - sensor.powerwall_load_export ................. kWh exported from loads
# - sensor.powerwall_load_import ................. kWh imported to loads
# - sensor.powerwall_site_export ................. kWh site -> grid
# - sensor.powerwall_site_import ................. kWh site <- grid
# - sensor.powerwall_solar_export ................ kWh solar -> site/battery/grid
# - sensor.powerwall_solar_import ................ kWh solar <- grid (rare)
#
# Switches:
# - switch.powerwall_off_grid_operation .......... island mode toggle
######################################################################
input_datetime:
power_resilience_outage_started:
name: Power Resilience Outage Started
icon: mdi:transmission-tower-off
has_date: true
has_time: true
template:
- sensor:
- name: "Power Resilience Recommended Stage"
unique_id: power_resilience_recommended_stage
icon: mdi:home-assistant
state: >-
{% set grid = states('binary_sensor.powerwall_grid_status') %}
{% set charge_raw = states('sensor.powerwall_charge') %}
{% set anchor_raw = states('input_datetime.power_resilience_outage_started') %}
{% set anchor_valid = anchor_raw not in ['unknown', 'unavailable', 'none', ''] and anchor_raw != '2000-01-01 00:00:00' %}
{% set outage_seconds = (as_timestamp(now()) - as_timestamp(anchor_raw, as_timestamp(now()))) if anchor_valid else 0 %}
{% if grid not in ['on', 'off'] or not is_number(charge_raw) %}
TELEMETRY_UNAVAILABLE
{% elif grid == 'on' %}
NORMAL
{% elif not anchor_valid %}
TELEMETRY_UNAVAILABLE
{% elif outage_seconds < 600 %}
OBSERVE
{% elif charge_raw | float(0) <= 25 %}
PRESERVE
{% elif charge_raw | float(0) <= 50 %}
SHED_2
{% elif charge_raw | float(0) <= 75 %}
SHED_1
{% else %}
OBSERVE
{% endif %}
attributes:
control_owner: Home Assistant
execution_mode: monitor_only
actions_armed: "{{ false }}"
grid_status: "{{ states('binary_sensor.powerwall_grid_status') }}"
powerwall_charge: >-
{% set charge = states('sensor.powerwall_charge') %}
{{ charge | float if is_number(charge) else none }}
outage_started_at: >-
{% set anchor = states('input_datetime.power_resilience_outage_started') %}
{{ anchor if is_state('binary_sensor.powerwall_grid_status', 'off') and anchor not in ['unknown', 'unavailable', 'none', '', '2000-01-01 00:00:00'] else none }}
outage_elapsed_seconds: >-
{% set anchor = states('input_datetime.power_resilience_outage_started') %}
{% if is_state('binary_sensor.powerwall_grid_status', 'off') and anchor not in ['unknown', 'unavailable', 'none', '', '2000-01-01 00:00:00'] %}
{{ [0, as_timestamp(now()) - as_timestamp(anchor, as_timestamp(now()))] | max | round(0) | int }}
{% else %}
0
{% endif %}
observation_seconds: "{{ 600 }}"
shed_1_threshold: "{{ 75 }}"
shed_2_threshold: "{{ 50 }}"
preserve_threshold: "{{ 25 }}"
garage_ups_status: "{{ states('sensor.garage_ups_status') }}"
office_ups_status: "{{ states('sensor.office_ups_eaton_550_status') }}"
office_cyberpower_ups_status: "{{ states('sensor.office_ups_cyberpower_status') }}"
#-------------------------------------------
script:
powerwall_turn_off_nonessential_lights:
alias: "Powerwall - Turn Off Non-Essential Lights"
sequence:
- service: homeassistant.turn_off
target:
entity_id:
- group.interior_lights
- group.exterior_lights
- switch.kitchen_accent_2
- switch.master_bathroom_accent_2
powerwall_outage_live_activity_update:
alias: "Powerwall - Outage Live Activity Update"
description: Refresh the tagged outage live activity using native grid-status state metadata.
mode: queued
sequence:
- variables:
grid_entity: "{{ states.binary_sensor.powerwall_grid_status if states.binary_sensor.powerwall_grid_status is defined else none }}"
outage_started_ts: >-
{% if grid_entity is not none and grid_entity.state == 'off' %}
{{ as_timestamp(grid_entity.last_changed, 0) | int(0) }}
{% else %}
0
{% endif %}
charge_pct: >-
{% set charge = states('sensor.powerwall_charge') %}
{% if charge not in ['unknown', 'unavailable', 'none', ''] %}
{{ charge | float(0) | round(0) | int(0) }}
{% else %}
0
{% endif %}
outage_started_label: >-
{% if outage_started_ts > 0 %}
{{ as_datetime(outage_started_ts) | as_local | strftime('%I:%M %p') }}
{% else %}
unknown
{% endif %}
- condition: template
value_template: "{{ outage_started_ts > 0 }}"
- service: script.notify_live_activity
data:
who: parents
tag: powerwall_grid_outage
title: "Power outage"
message: "Grid outage since {{ outage_started_label }}. Powerwall charge at {{ charge_pct }}%."
icon: mdi:transmission-tower-off
color: "#E65100"
progress: "{{ charge_pct }}"
progress_max: 100
chronometer: true
when: "{{ outage_started_ts }}"
when_relative: false
automation:
- alias: "Power Resilience - Track Outage Anchor"
id: 0d2400c4-6e2e-4f17-9ca5-b899ad80f056
description: Persist the active grid outage start for restart-safe, Home Assistant-owned stage recommendations.
mode: queued
max: 3
triggers:
- trigger: homeassistant
event: start
id: startup
- trigger: state
entity_id: binary_sensor.powerwall_grid_status
to: "off"
id: grid_off
- trigger: state
entity_id: binary_sensor.powerwall_grid_status
to: "on"
id: grid_on
actions:
- choose:
- conditions:
- condition: state
entity_id: binary_sensor.powerwall_grid_status
state: "off"
- condition: or
conditions:
- condition: template
value_template: >-
{{ trigger.id == 'grid_off' and trigger.from_state is not none and
trigger.from_state.state == 'on' }}
- condition: template
value_template: >-
{{ states('input_datetime.power_resilience_outage_started') in
['unknown', 'unavailable', 'none', '', '2000-01-01 00:00:00'] }}
sequence:
- action: input_datetime.set_datetime
target:
entity_id: input_datetime.power_resilience_outage_started
data:
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
- conditions:
- condition: trigger
id:
- startup
- grid_on
- condition: state
entity_id: binary_sensor.powerwall_grid_status
state: "on"
sequence:
- action: input_datetime.set_datetime
target:
entity_id: input_datetime.power_resilience_outage_started
data:
datetime: "2000-01-01 00:00:00"
- alias: "Powerwall outage - Refresh live activity"
id: 5b5fb370-c6cb-4c74-95d4-264737e4456f
description: Start the tagged outage live activity when the grid drops and refresh it when charge changes.
mode: restart
trigger:
- id: grid_off
platform: state
entity_id: binary_sensor.powerwall_grid_status
to: 'off'
- id: charge_change
platform: state
entity_id: sensor.powerwall_charge
action:
- choose:
- conditions:
- condition: trigger
id: grid_off
- condition: template
value_template: >-
{{ trigger.to_state is not none and trigger.from_state is not none and
trigger.to_state.state not in ['unknown', 'unavailable', 'null'] and
trigger.from_state.state not in ['unknown', 'unavailable', 'null'] }}
sequence:
- service: script.powerwall_outage_live_activity_update
- conditions:
- condition: trigger
id: charge_change
- condition: state
entity_id: binary_sensor.powerwall_grid_status
state: 'off'
- condition: template
value_template: "{{ trigger.to_state is not none and trigger.to_state.state not in ['unknown', 'unavailable', 'none', ''] }}"
sequence:
- service: script.powerwall_outage_live_activity_update
- alias: "Powerwall outage - Clear live activity"
id: 1d998f72-50df-46eb-b335-7f0d9974f89a
description: Clear the tagged outage live activity after the grid is stable again.
mode: single
trigger:
- platform: state
entity_id: binary_sensor.powerwall_grid_status
from: 'off'
to: 'on'
for:
minutes: 2
action:
- service: script.notify_live_activity
data:
who: parents
tag: powerwall_grid_outage
clear: true
- alias: Notify if Grid is down
id: 56a32121-5725-4510-a1fa-10f69a5c82ef
trigger:
- platform: state
entity_id: binary_sensor.powerwall_grid_status
condition:
- condition: template
# Check that both current and previous states are not 'unavailable', 'unknown', or 'null'.
value_template: >-
{{ states('binary_sensor.powerwall_grid_status') not in ['unavailable', 'unknown', 'null'] and
trigger.from_state.state not in ['unavailable', 'unknown', 'null'] }}
action:
- service: script.send_to_logbook
data:
topic: "POWER"
message: >-
Grid status is {{ (trigger.to_state.state)|replace('on', 'up')|replace('off', 'down') }}.
- service: script.notify_engine
data:
title: "Electrical Grid Status {{ (trigger.to_state.state)|replace('on', 'up')|replace('off', 'down') }}."
value1: "Please verify with the Tesla App."
who: 'parents'
group: 'information'
# https://github.com/CCOSTAN/Home-AssistantConfig/issues/779
- alias: Shut Things down if running on PowerWall
mode: restart
id: 37288a96-5f49-46c2-85c8-7e8794d23612
trigger:
- platform: state
entity_id: binary_sensor.powerwall_grid_status
to: 'off'
action:
- repeat:
count: 3
sequence:
- choose:
- conditions:
- condition: template
value_template: "{{ repeat.index == 2 }}"
sequence:
- delay:
minutes: 1
- conditions:
- condition: template
value_template: "{{ repeat.index == 3 }}"
sequence:
- delay:
minutes: 3
- service: script.powerwall_turn_off_nonessential_lights
- choose:
- conditions:
- condition: template
value_template: "{{ repeat.index == 1 }}"
sequence:
- service: script.notify_engine
data:
title: "Electrical Grid Status {{ (trigger.to_state.state)|replace('True', 'up')|replace('False', 'down') }}."
value1: "Taking actions to turning off the House Lights to preserve Battery Power."
who: 'family'
group: 'information'
- conditions:
- condition: template
value_template: "{{ repeat.index == 2 }}"
sequence:
- service: script.speech_engine
data:
value1: "Because of the Power Outage, the Lights will be recycled for 3 minutes. Lights may turn on and off during this time."
- conditions:
- condition: template
value_template: "{{ repeat.index == 3 }}"
sequence:
- service: script.speech_engine
data:
value1: "Automatic light recycling has been completed. Any abnormalities will have to be addressed in the Hue App most likely. "
- alias: "Powerwall Low Charge Monitoring and Recovery"
id: fda6116b-b2a5-4198-a1ce-4cf4bb3254b2
mode: single
trigger:
- id: low_24h
platform: numeric_state
entity_id: sensor.powerwall_charge
below: 60
for:
hours: 24
- id: recovered
platform: numeric_state
entity_id: sensor.powerwall_charge
above: 60
for:
minutes: 10
condition:
- condition: state
entity_id: binary_sensor.powerwall_grid_status
state: 'on'
action:
- choose:
- conditions:
- condition: trigger
id: low_24h
sequence:
- service: script.send_to_logbook
data:
topic: "POWER"
message: "Powerwall charge below 60% for 24h (current: {{ states('sensor.powerwall_charge') }}%)."
- service: repairs.create
data:
issue_id: "powerwall_low_charge_60_24h"
title: "Powerwall charge low for 24h"
severity: "warning"
persistent: true
description: >-
Powerwall has been below 60% for 24 hours while the grid is online.
Current charge: {{ states('sensor.powerwall_charge') }}%.
- service: script.notify_engine
data:
title: "Powerwall Low Charge Alert - Current Charge: {{ states('sensor.powerwall_charge') }}"
value1: "The Powerwall has been below 50% charge for more than 24 hours while the grid is online. This may indicate an issue."
who: 'parents'
group: 'information'
- conditions:
- condition: trigger
id: recovered
sequence:
- service: repairs.remove
continue_on_error: true
data:
issue_id: "powerwall_low_charge_60_24h"
- service: script.send_to_logbook
data:
topic: "POWER"
message: "Powerwall charge recovered above 60%. Cleared repair issue."
- alias: "Powerwall outage - Rheem WH mode control"
id: d686f650-65ad-4cc6-8e27-8b5ee76b5338
description: "During outages, switch Rheem between off and electric mode while the compressor is under warranty repair"
mode: single
trigger:
- id: sunrise
platform: sun
event: sunrise
- id: charge_above_50
platform: numeric_state
entity_id: sensor.powerwall_charge
above: 50
for:
minutes: 5
- id: sunset
platform: sun
event: sunset
- id: grid_off_1m
platform: state
entity_id: binary_sensor.powerwall_grid_status
to: 'off'
for:
minutes: 1
condition:
- condition: state
entity_id: binary_sensor.powerwall_grid_status
state: 'off'
action:
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.id in ['sunset', 'grid_off_1m'] }}"
- condition: or
conditions:
- condition: sun
after: sunset
- condition: sun
before: sunrise
sequence:
- service: water_heater.set_operation_mode
target:
entity_id: water_heater.rheem_wh
data:
state: off
- conditions:
- condition: template
value_template: "{{ trigger.id in ['sunrise', 'charge_above_50', 'grid_off_1m'] }}"
- condition: numeric_state
entity_id: sensor.powerwall_charge
above: 50
- condition: sun
after: sunrise
before: sunset
sequence:
- service: water_heater.set_operation_mode
target:
entity_id: water_heater.rheem_wh
data:
state: electric
# https://github.com/CCOSTAN/Home-AssistantConfig/issues/547
- alias: Turn off the Nest Thermostats during a Power Outage
id: 022bd9da-ab7c-4c1b-9e95-2ff4ffe5f9cc
trigger:
- platform: state
entity_id: binary_sensor.powerwall_grid_status
to: 'off'
for:
minutes: 6
condition:
- condition: template
value_template: "{{ states('binary_sensor.powerwall_grid_status') == 'off' }}"
action:
- service: script.reconcile_hvac_state
data:
reason: "grid outage"
- service: script.notify_engine
data:
title: "Electrical Grid Status {{ (trigger.to_state.state)|replace('True', 'up')|replace('False', 'down') }}."
value1: "Taking actions to turning off the AC units to preserve Battery Power."
who: 'family'
group: 'information'
- alias: Turn back on the Nest Thermostats after a Power Outage
id: 14faf84b-cb24-4391-a97f-b83479a67fc7
trigger:
- platform: state
entity_id: binary_sensor.powerwall_grid_status
from: 'off'
to: 'on'
for:
minutes: 10
action:
- service: script.reconcile_hvac_state
data:
reason: "grid restored"
- service: script.notify_engine
data:
title: "Electrical Grid Status {{ (trigger.to_state.state)|replace('True', 'up')|replace('False', 'down') }}."
value1: "HVAC settings were reconciled with grid, entry-point, and occupancy conditions."
who: 'family'
group: 'information'

Powered by TurnKey Linux.