I've seen a couple of different variants of Power saving scripts for Home Assistant. This is my take on it built using a template entity, a couple of input_number fields and the Nordpool integration.
My first step is to setup 3 input_number helper entities.
- power_min_hours_on (0-24, number of hours minimum required on)
- power_max_price (if price is above this the power will always be off, regardless of power_min_hours)
- power_percent_over_min (How many percent of the daily minimum price to count as cheap and allow power to be on)
Then I implement the following binary template sensor. Be sure to change the nordpool sensor id to match yours!
- binary_sensor:
- name: Power saver
unique_id: powersaver
device_class: problem
state: '{{ not (this.attributes.schedule | selectattr("current") | first | default({"onOff":false})).onOff }}'
attributes:
schedule: >
{% set data = namespace(sorted=[],activated=10) %}
{% set data.activated = states("input_number.power_min_hours_on") | int(10) %}
{% set minperc = (states("input_number.power_percent_over_min") | int(15) + 100.0) / 100.0 %}
{% set maxprice = states("input_number.power_max_price") | float(0) %}
{% set sorted = state_attr("sensor.nordpool_kwh_se4_sek_3_10_025", "raw_today") | sort(attribute="value") %}
{% set breakprice = minperc * sorted[0].value %}
{% for s in sorted %}
{% set onOff = (s.value <= breakprice or data.activated > 0) and s.value < maxprice %}
{% set current = s.start < now() and s.end > now() %}
{% set data.sorted = data.sorted + [{"price": s.value, "time": as_timestamp(s.start) | timestamp_local, "onOff": onOff, "current": current }] %}
{% set data.activated = data.activated - (1 if onOff else 0) %}
{% endfor %}
{% set sorted = state_attr("sensor.nordpool_kwh_se4_sek_3_10_025", "raw_tomorrow") | sort(attribute="value") %}
{% if sorted | length > 0 %}
{% set data.activated = states("input_number.power_min_hours_on") | int(10) %}
{% set breakprice = minperc * sorted[0].value %}
{% for s in sorted %}
{% set onOff = (s.value <= breakprice or data.activated > 0) and s.value < maxprice %}
{% set data.sorted = data.sorted + [{"price": s.value, "time": as_timestamp(s.start) | timestamp_local, "onOff": onOff, "current": false }] %}
{% set data.activated = data.activated - (1 if onOff else 0) %}
{% endfor %}
{% endif %}
{{ data.sorted | sort(attribute="time") | list }}
As a bonus I've created a nice little chart using the apexcharts-card.
type: custom:apexcharts-card
update_interval: 11m
graph_span: 1d
header:
show: true
title: Today
apex_config:
chart:
type: area
legend:
show: false
fill:
type: gradient
gradient:
shadeIntensity: 0.1
opacityFrom: 0.55
opacityTo: 1
inverseColors: true
stops:
- 0
- 90
- 100
span:
start: day
offset: "-0d"
now:
show: true
color: rgb(138,43,226)
label: Now
yaxis:
- id: price
decimals: 2
min: "|-0.1|"
max: "|+0.1|"
apex_config:
title:
text: kr/kWh
forceNiceScale: true
- id: powersaver
show: false
min: 0
max: 1
series:
- entity: sensor.nordpool_kwh_se4_sek_3_10_025
yaxis_id: price
type: line
color: rgb(128,128,128)
data_generator: |
return entity.attributes.raw_today.map((entry) => {
const then = new Date(entry.start.split('+')[0]);
return [then, parseFloat(entry.value)];
});
name: Elpris
curve: stepline
stroke_width: 3
- entity: binary_sensor.powersaver
data_generator: |
return entity.attributes.schedule.map((entry) => {
return [new Date(entry.time), entry.onOff ? 1 : 0];
});
yaxis_id: powersaver
name: " "
type: area
color: rgba(0, 255, 0, 0.2)
opacity: 0.1
stroke_width: 0
curve: stepline
group_by:
func: min
show:
legend_value: false
in_header: false
name_in_header: false
datalabels: false