Na flink wat iteraties ben ik op deze Home Assistant automation uitgekomen voor het aansturen van mijn thuisbatterij. Het doel is simpel: de batterij slim laden en ontladen op basis van dynamische energieprijzen (Nordpool/Gielz), zonne-opwek en SoC (State of Charge).
@
gielz zijn repo =
https://github.com/Gielz1986/Dynamisch-HA
Wat doet deze automation?
In de kern zorgt de logica ervoor dat de batterij niet 'dom' op- en ontlaadt, maar kijkt naar de meest rendabele momenten.
De belangrijkste features:
Anti-flap beveiliging: Voorkomt dat de batterij constant switcht tussen modi door een instelbare minimale tijd tussen acties (batterij_anti_flap_minuten). Staat nu op 15 minuten met een helper
Prijs-optimalisatie: Gebruikt een sensor voor de goedkoopste en duurste periodes van de dag. Gebruikt sensoren van Gielz
Spread-check: Er wordt alleen geladen als het prijsverschil (spread) groot genoeg is om de verliezen (round-trip efficiency) te rechtvaardigen. Gebruikt sensoren van Gielz. Ik wil minimaal 100% spread, geen rekening houden met belasting.
Zon-prioriteit: Bij voldoende PV-opwek wordt de batterij op zero_charge_only gezet om de eigen stroom op te slaan zonder het net te belasten.
Nacht/Dag logica: Voor zonsopkomst mag de batterij geforceerd laden (to_full), terwijl hij na zonsopkomst conservatiever laadt om ruimte te houden voor eigen PV-stroom. En als de laatste prijs na zonsopgang is, maar er nog minder verwachte opbrengst is dan 2kWh de rest van de dag, gaat die gewoon geforceerd laden (blijkbaar is er heel veel wind)
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
| alias: "Dynamisch-HA: Batterij Controle V4.1"
description: ""
triggers:
- entity_id:
- sensor.plug_in_battery_state_of_charge
- sensor.dynamisch_nordpool
- sensor.solaredge_i1_ac_power
trigger: state
- minutes: /10
trigger: time_pattern
enabled: false
conditions:
- condition: or
conditions:
- condition: template
value_template: |
{{
(as_timestamp(now()) -
as_timestamp(states('input_datetime.batterij_laatste_actie')))
>
(states('input_number.batterij_anti_flap_minuten') | float * 60)
}}
- condition: numeric_state
entity_id: sensor.plug_in_battery_state_of_charge
below: input_number.batterij_soc_min
- condition: numeric_state
entity_id: sensor.plug_in_battery_state_of_charge
above: input_number.batterij_soc_max
actions:
- choose:
- conditions:
- condition: state
entity_id: sensor.dynamisch_goedkoopste_periode
state: Ja
- condition: numeric_state
entity_id: sensor.plug_in_battery_state_of_charge
below: input_number.batterij_soc_max
- condition: numeric_state
entity_id: sensor.dynamisch_spread_indicatie_nom
above: input_number.dynamisch_minimale_spread
- condition: or
conditions:
- condition: sun
before: sunrise
- type: is_energy
condition: device
device_id: d2edb3ee8dd9b8193940ffd7c053ca44
entity_id: 469d7849f2a40d90fabca5df112427f6
domain: sensor
below: 2
sequence:
- data:
option: to_full
action: select.select_option
target:
entity_id: select.p1_meter_battery_group_mode
- conditions:
- condition: state
entity_id: sensor.dynamisch_goedkoopste_periode
state: Ja
- condition: numeric_state
entity_id: sensor.plug_in_battery_state_of_charge
below: input_number.batterij_soc_max
- condition: numeric_state
entity_id: sensor.dynamisch_spread_indicatie_nom
above: input_number.dynamisch_minimale_spread
- condition: sun
after: sunrise
sequence:
- data:
option: zero_charge_only
action: select.select_option
target:
entity_id: select.p1_meter_battery_group_mode
- conditions:
- condition: and
conditions:
- condition: state
entity_id: sensor.dynamisch_duurste_periode
state: Ja
- condition: numeric_state
entity_id: sensor.plug_in_battery_state_of_charge
above: input_number.batterij_soc_min
sequence:
- action: select.select_option
target:
entity_id: select.p1_meter_battery_group_mode
data:
option: zero_discharge_only
- conditions:
- condition: numeric_state
entity_id: sensor.solaredge_i1_ac_power
above: input_number.solaredge_minimale_waarde_laden
- condition: numeric_state
entity_id: sensor.plug_in_battery_state_of_charge
below: input_number.batterij_soc_max
sequence:
- data:
option: zero_charge_only
action: select.select_option
target:
entity_id: select.p1_meter_battery_group_mode
default:
- data:
option: standby
action: select.select_option
target:
entity_id: select.p1_meter_battery_group_mode
mode: restart |
Benodigdheden:
Nordpool integratie: Ik gebruik die van Gielz, waarin goedkoopste en duurste periode worden opgehaald
Helper entities: Verschillende input_number en input_datetime velden om drempelwaardes (SoC min/max, minimale spread) in de UI aan te kunnen passen zonder de YAML te editen.
Verwachte zon: Solcast integratie
Modbus SolarEdge : Voor het direct nuitlezen van je omvormer. Kan met elke andere omvormer op een andere manier.
Ik gebruik 2 dashboarden. De standaard versie van Gielz om dynamisch de laatste/hoogste prijzen te bepalen. En heb een eigen batterij dashboard.
Het rendement per dag reken ik uit door power * huidige prijs te doen. En daar wat helpers omheen die dagelijks resetten voor het dashboard.
Ik reken de RTE dus meerdere keren per dag uit zoals ik paar posts geleden heb beschreven.
En heb in de SOC/Power graph (ik dacht ook van Gielz) de resterende energie uitgerekend. Dat doe ik de SOC * RTE * 2830 (lijkt het totale energie wat ik erin moet stoppen van 0 naar 100%
code:
1
2
3
4
| {% set soc = states('sensor.plug_in_battery_state_of_charge') | float(0) %}
{% set rte = states('sensor.gemiddelde_batterij_rte') | float(0) %}
{% set full_kwh = 2.830 * (rte / 100) %}
{{ ((soc / 100) * full_kwh) | round(3) }} |
Ik ben benieuwd hoe jullie dit aangepakt hebben! Gebruiken jullie vergelijkbare drempelwaardes voor de spread, of sturen jullie puur op de laagste drempel van de dag?
Vragen of verbetersuggesties zijn altijd welkom!
[
Voor 10% gewijzigd door
Utrechtduurzaam op 04-04-2026 10:58
]