Repository navigation
09. Voorbeelden AlphaESS
Bron
Doopy-X- op Tweakers Forum 1, Tweakers Forum 2
TheMystery Tweakers Forum 3
Ik heb voor DAO 2 grafieken gemaakt:
✏️ Click to expand HA configuration details
type: custom:apexcharts-card
header:
show: true
title: Frank prijs + DAO batterij SoC + PV forecast
show_states: false
graph_span: 24h
span:
start: day
now:
show: true
label: Nu
yaxis:
- id: price
min: '|-1|'
max: '|+1|'
decimals: 0
opposite: false
apex_config:
offsetX: -45
tickAmount: 10
title:
text: PRIJS
labels:
formatter: |
EVAL:function(value) {
return value.toFixed(0) + " ct";
}
- id: soc
min: 0
max: 100
decimals: 0
opposite: true
apex_config:
title:
text: SOC
labels:
formatter: |
EVAL:function(value) {
return value.toFixed(0) + "%";
}
- id: zon
min: 0
decimals: 1
opposite: true
apex_config:
offsetX: 35
title:
text: ZON
labels:
formatter: |
EVAL:function(value) {
return value.toFixed(1) + " kW";
}
series:
- entity: sensor.frank_prices_today
name: Frank prijs
yaxis_id: price
type: line
curve: stepline
color: '#00B7FF'
stroke_width: 2
opacity: 1
unit: ct/kWh
float_precision: 2
data_generator: |
return entity.attributes.prices.map(p => [
new Date(p.from).getTime(),
p.total_price_eur_kwh * 100
]);
show:
legend_value: false
- entity: sensor.dao_battery_soc
name: DAO SoC
yaxis_id: soc
type: line
curve: stepline
color: '#A0C770'
stroke_width: 2
opacity: 1
unit: '%'
float_precision: 0
data_generator: |
return entity.attributes.data.map(p => [
new Date(p.time).getTime(),
parseFloat(p.value)
]);
show:
legend_value: false
- entity: sensor.solcast_pv_forecast_voorspelling_vandaag
name: Zon voorspelling
type: line
yaxis_id: zon
color: '#FABF00'
opacity: 0.7
stroke_width: 2
curve: stepline
data_generator: |
const data = entity.attributes.detailedForecast;
const result = [];
data.forEach(p => {
const t = new Date(p.period_start).getTime();
const value = Number(p.pv_estimate);
result.push([t, value]);
result.push([t + 15 * 60 * 1000, value]);
});
return result;
show:
legend_value: false
apex_config:
chart:
height: 400px
tooltip:
shared: true
intersect: false
grid_options:
columns: full✏️ Click to expand HA configuration details
type: custom:apexcharts-card
update_interval: 30sec
graph_span: 24h
span:
start: day
now:
show: true
color: red
header:
show: true
title: Energie vandaag
show_states: false
colorize_states: true
apex_config:
chart:
height: 600px
tooltip:
intersect: false
custom: |
EVAL:function({series, seriesIndex, dataPointIndex, w}) {
const hoverX = w.globals.seriesX[seriesIndex][dataPointIndex];
function nearestIndex(xValues, target) {
if (!xValues || xValues.length === 0) {
return -1;
}
let bestIndex = 0;
let bestDiff = Math.abs(xValues[0] - target);
for (let i = 1; i < xValues.length; i++) {
const diff = Math.abs(xValues[i] - target);
if (diff < bestDiff) {
bestDiff = diff;
bestIndex = i;
}
}
return bestIndex;
}
function previousIndex(xValues, target) {
if (!xValues || xValues.length === 0) {
return -1;
}
let index = 0;
for (let i = 0; i < xValues.length; i++) {
if (xValues[i] <= target) {
index = i;
} else {
break;
}
}
return index;
}
function formatValue(name, value) {
if (
value === null ||
value === undefined ||
Number.isNaN(Number(value))
) {
return '—';
}
if (name === 'Battery') {
return `${Number(value).toFixed(0)} %`;
}
if (name === 'Frank prijs') {
return `${Number(value).toFixed(2)} ct/kWh`;
}
return `${Number(value).toFixed(2)} kW`;
}
const rows = [];
for (let i = 0; i < w.globals.seriesNames.length; i++) {
const name = w.globals.seriesNames[i];
const xValues = w.globals.seriesX[i];
let index;
if (name === 'Frank prijs') {
index = previousIndex(xValues, hoverX);
} else {
index = nearestIndex(xValues, hoverX);
}
const value =
index >= 0 && w.globals.series[i]
? w.globals.series[i][index]
: null;
let markerColor = '#999999';
if (name === 'Battery') {
markerColor = '#A0C770';
} else if (name === 'Huis') {
markerColor = '#9B59B6';
} else if (name === 'PV') {
markerColor = '#FABF00';
} else if (name === 'Netimport') {
markerColor = '#C18349';
} else if (name === 'Netexport') {
markerColor = '#FF5722';
} else if (name === 'Frank prijs') {
markerColor = '#00B7FF';
}
rows.push(`
<div style="
display:flex;
align-items:center;
justify-content:space-between;
gap:18px;
margin:4px 0;
white-space:nowrap;
">
<span style="
display:flex;
align-items:center;
gap:6px;
">
<span style="
width:9px;
height:9px;
border-radius:50%;
background:${markerColor};
display:inline-block;
"></span>
${name}
</span>
<strong>
${formatValue(name, value)}
</strong>
</div>
`);
}
const date = new Date(hoverX);
const timeText = date.toLocaleString('nl-NL', {
day: '2-digit',
month: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
return `
<div style="
padding:10px 12px;
min-width:210px;
">
<div style="
font-weight:600;
margin-bottom:8px;
border-bottom:1px solid rgba(128,128,128,0.25);
padding-bottom:6px;
">
${timeText}
</div>
${rows.join('')}
</div>
`;
}
grid:
padding:
left: 10
right: 10
bottom: 20
stroke:
width: 2
dataLabels:
enabled: false
legend:
show: true
position: bottom
horizontalAlign: center
offsetY: 5
annotations:
yaxis:
- 'y': 0
strokeDashArray: 0
borderWidth: 2
all_series_config:
type: area
stroke_width: 2
opacity: 0.9
curve: smooth
series:
- entity: sensor.alphaess_inverter_battery_state_of_charge
name: Battery
yaxis_id: battery
type: area
color: '#A0C770'
extend_to: now
curve: smooth
fill_raw: last
stroke_width: 3
opacity: 0.18
group_by:
func: avg
duration: 5min
show:
legend_value: false
- entity: sensor.alphaess_inverter_current_house_load
name: Huis
yaxis_id: power
type: area
color: '#9B59B6'
unit: kW
float_precision: 2
transform: return x / 1000;
extend_to: now
curve: smooth
fill_raw: last
stroke_width: 2.5
opacity: 0.15
group_by:
func: avg
duration: 5min
show:
legend_value: false
- entity: sensor.alphaess_inverter_current_pv_production
name: PV
yaxis_id: power
type: area
color: '#FABF00'
unit: kW
float_precision: 2
transform: return x / 1000;
extend_to: now
curve: smooth
fill_raw: last
stroke_width: 2.5
opacity: 0.15
group_by:
func: avg
duration: 5min
show:
legend_value: false
- entity: sensor.alphaess_inverter_grid_power
name: Netimport
yaxis_id: power
type: area
color: '#C18349'
unit: kW
float_precision: 2
transform: 'return x > 0 ? x / 1000 : 0;'
extend_to: now
curve: smooth
fill_raw: last
stroke_width: 1.5
opacity: 0.22
group_by:
func: min
duration: 5min
show:
legend_value: false
- entity: sensor.alphaess_inverter_grid_power
name: Netexport
yaxis_id: power
type: area
color: '#FF5722'
unit: kW
float_precision: 2
transform: 'return x < 0 ? Math.abs(x) / 1000 : 0;'
extend_to: now
curve: smooth
fill_raw: last
stroke_width: 1.5
opacity: 0.22
group_by:
func: avg
duration: 5min
show:
legend_value: false
- entity: sensor.frank_prices_today
name: Frank prijs
yaxis_id: price
type: line
curve: stepline
color: '#00B7FF'
stroke_width: 4
opacity: 1
unit: ct/kWh
float_precision: 2
data_generator: |
return entity.attributes.prices.map(p => [
new Date(p.from).getTime(),
p.total_price_eur_kwh * 100
]);
show:
legend_value: false
yaxis:
- id: power
min: 0
max: 15
decimals: 2
apex_config:
tickAmount: 15
title:
text: POWER
labels:
formatter: |
EVAL:function(value) {
return value.toFixed(2) + " kW";
}
- id: price
min: '|-1|'
max: '|+1|'
decimals: 0
opposite: false
apex_config:
offsetX: -45
tickAmount: 10
title:
text: PRIJS
labels:
formatter: |
EVAL:function(value) {
return value.toFixed(0) + " ct";
}
- id: battery
min: 0
max: 100
decimals: 0
opposite: true
apex_config:
tickAmount: 10
title:
text: BAT
labels:
formatter: |
EVAL:function(value) {
return value.toFixed(0) + " %";
}
grid_options:
columns: fullRondom de config:
✏️ Click to expand DAO configuration details
"battery": [
{
"name": "AlphaESS SMILE G3-T10",
"capacity": 18.6,
"entity actual level": "sensor.alphaess_inverter_battery_state_of_charge",
"upper limit": 98,
"lower limit": 15,
"optimal lower level": 20,
"charge stages": [
{ "power": 0, "efficiency": 1 },
{ "power": 10000, "efficiency": 0.95 }
],
"discharge stages": [
{ "power": 0, "efficiency": 1 },
{ "power": 10000, "efficiency": 0.95 }
],
"dc_to_bat efficiency": 1.0,
"bat_to_dc efficiency": 1.0,
"minimum power": 200,
"cycle cost": 0.0175,
"entity set power feedin": "input_number.simulatie_batterij_vermogen",
"entity set operating mode": "input_select.dao_batterij_operating_mode"
}
]input_number.simulatie_batterij_vermogen
-10000
10000
stapgrootte 1
Meeteenheid W
input_select.dao_batterij_operating_mode
Aan
Uit
Modbus-integratie toegevoegd: https://github.com/senalse/ha-alphaess-modbus
(Hillview is de bron van veel AlphaESS modbus-integraties, deze modbus-aansturing is onder water ook hetzelfde. Senalse heeft deze doorontwikkeld naar een echte HA-integratie die te installeren is via HACS waarbij je out-of-box een grote set aan entiteiten krijgt en deze reageren ook erg snel, wat bijvoorbeeld weer goed is voor de grafieken.)
Deze voegt deze entiteiten toe:
- switch.alphaess_inverter_dispatch
- select.alphaess_inverter_dispatch_mode
- number.alphaess_inverter_dispatch_power
- number.alphaess_inverter_dispatch_duration
- number.alphaess_inverter_dispatch_stop_at_soc
Entiteiten die meetwaarden opleveren:
- sensor.alphaess_inverter_battery_state_of_charge
- sensor.alphaess_inverter_battery_power
- sensor.alphaess_inverter_grid_power
- sensor.alphaess_inverter_current_house_load
- sensor.alphaess_inverter_current_pv_production
Automatisering die de batterij aanstuurt:
✏️ Click to expand HA automation details
alias: DAO naar AlphaESS
description: Stuurt DAO operating mode en vermogenssetpoint naar AlphaESS Dispatch
triggers:
- trigger: state
entity_id:
- input_select.dao_batterij_operating_mode
- input_number.simulatie_batterij_vermogen
conditions: []
actions:
- choose:
- conditions:
- condition: state
entity_id: input_select.dao_batterij_operating_mode
state: Uit
sequence:
- action: switch.turn_off
target:
entity_id: switch.alphaess_inverter_dispatch
- conditions:
- condition: state
entity_id: input_select.dao_batterij_operating_mode
state: Aan
sequence:
- action: number.set_value
target:
entity_id: number.alphaess_inverter_dispatch_duration
data:
value: 480
- action: select.select_option
target:
entity_id: select.alphaess_inverter_dispatch_mode
data:
option: State of Charge Control (2)
- action: number.set_value
target:
entity_id: number.alphaess_inverter_dispatch_power
data:
value: >
{% set dao = states('input_number.simulatie_batterij_vermogen')
| float(0) %} {% set power = dao * -0.001 %} {{ [10, [-10,
power] | max] | min }}
- action: number.set_value
target:
entity_id: number.alphaess_inverter_dispatch_stop_at_soc
data:
value: >
{% set dao = states('input_number.simulatie_batterij_vermogen')
| float(0) %} {% if dao > 0 %}
100
{% else %}
15
{% endif %}
- action: switch.turn_on
target:
entity_id: switch.alphaess_inverter_dispatch
mode: restartDe trigger kijkt naar:
input_select.dao_batterij_operating_mode
input_number.simulatie_batterij_vermogen
en de automatisering zorgt ervoor dat wanneer DAO zijn planning aanpast, de AlphaESS opnieuw wordt aangestuurd.
AlphaESS Self-Consumption setting:
AlphaESS in de Self-Consumption mode gezet, zodat wanneer DAO niet specifiek aan het laden of ontladen is, de batterij gewoon gebruikt wordt voor huisgebruik en opgeladen kan worden via zonnepanelen.
In het kort:
DAO stuurt de 2 helpers aan, de automatisering reageert op de helpers, de automatisering stuurt de batterij aan via modbus
bovenstaande stuurt de alpha aan via dao via de power feedin (grid) optie, maar de hillview integratie stuurt de inverter aan met laden en ontladen en niet op grid niveau. Je kunt ook aansturen met "from battery" om hem volledig aan de dao planning te kunnen houden.
Deze configuration en automations:
✏️ Click to expand DAO configuration details
"battery": [
{
"name": "AlphaEss G3 Smile T10",
"entity actual level": "sensor.alphaess_soc_battery",
"capacity": 46.5,
"upper limit": "input_number.dao_upper_limit",
"lower limit": "input_number.dao_lower_limit",
"optimal lower level": "input_number.dao_optimal_lower_level",
"entity min soc end opt": "input_number.dao_min_soc_einde",
"entity max soc end opt": "input_number.dao_max_soc_einde",
"charge stages": [
{"power": 0, "efficiency": 1.0},
{"power": 2500, "efficiency": 0.945},
{"power": 5000, "efficiency": 0.958},
{"power": 7500, "efficiency": 0.955},
{"power": 10000, "efficiency": 0.950}
],
"discharge stages": [
{"power": 0, "efficiency": 1.0},
{"power": 2500, "efficiency": 0.945},
{"power": 5000, "efficiency": 0.958},
{"power": 7500, "efficiency": 0.955},
{"power": 10000, "efficiency": 0.945}
],
"minimum power": 100,
"dc_to_bat efficiency": 1.0,
"dc_to_bat max power" : 10000.0,
"bat_to_dc efficiency": 1.0,
"bat_to_dc max power" : 10000.0,
"cycle cost": 0.015,
"entity set power feedin": "input_number.dao_set_power_feedin",
"entity set operating mode": "input_select.dao_set_operating_mode",
"entity stop inverter": "input_datetime.dao_stop_inverter",
"entity from battery": "input_number.dao_from_battery",
"entity from ac": "input_number.dao_from_ac",
"entity from pv": "input_number.dao_from_pv",
"entity calculated soc": "input_number.dao_calculated_soc"
}
],✏️ Click to expand HA automation 1 details
alias: 'DAO: AlphaESS Mode 2'
description: Elk kwartier DAO planning doorsturen naar Hillview dispatch mode 2
triggers:
- minutes: /15
seconds: '15'
trigger: time_pattern
conditions:
- condition: template
value_template: |
{{ states('input_select.dao_set_operating_mode') != 'Uit' }}
- condition: template
value_template: |
{{ not (states('input_select.dao_set_operating_mode') == 'Aan'
and states('input_boolean.dao_balance_switch') == 'on') }}
actions:
- variables:
dao_power: '{{ states(''input_number.dao_from_battery'') | float(0) }}'
stop_time: '{{ states(''input_datetime.dao_stop_inverter'') }}'
duration: |-
{% if stop_time == '2000-01-01 00:00:00' %}15 {% else %}
{% set stop = strptime(stop_time, '%Y-%m-%d %H:%M:%S') | as_local %}
{% set now_dt = now().replace(second=0, microsecond=0) %}
{% set diff = (stop - now_dt).total_seconds() / 60 %}
{% if diff < 1 or diff > 15 %}15
{% else %}{{ diff | int }}
{% endif %}
{% endif %}
hillview_power: |-
{% set dur = duration | int(15) %} {% if dur < 15 %}
{% set power = (dao_power / 1000 * 15 / dur) | round(1) %}
{{ [[power, 10.0] | min, -10.0] | max }}
{% else %}
{{ (dao_power / 1000) | round(1) }}
{% endif %}
hillview_soc: >-
{% set base_soc = states('input_number.dao_calculated_soc') | int(0) %}
{% set p = hillview_power | float(0) %} {% if p > 0 %}
{{ [base_soc - 2, 15] | max }}
{% elif p < 0 %}
{{ [base_soc + 2, 100] | min }}
{% else %}
{{ base_soc }}
{% endif %}
- target:
entity_id: input_number.alphaess_helper_dispatch_duration
data:
value: 1
action: input_number.set_value
- delay: '00:00:01'
- target:
entity_id: number.alphaess_template_dispatch_power
data:
value: '{{ hillview_power }}'
action: number.set_value
- target:
entity_id: input_number.alphaess_helper_dispatch_cutoff_soc
data:
value: '{{ hillview_soc }}'
action: input_number.set_value
- target:
entity_id: input_select.alphaess_helper_dispatch_mode
data:
option: State of Charge Control (2)
action: input_select.select_option
- delay: '00:00:01'
- target:
entity_id: input_number.alphaess_helper_dispatch_duration
data:
value: '{{ duration }}'
action: input_number.set_value
- condition: state
entity_id: input_boolean.alphaess_helper_dispatch
state: 'off'
- target:
entity_id: input_boolean.alphaess_helper_dispatch
action: input_boolean.turn_on
mode: single✏️ Click to expand HA automation 2 details
alias: 'Dao: Dispatch off'
description: Mode 2 met power 0 als DAO dispatch uit gaat
triggers:
- trigger: state
entity_id:
- input_boolean.alphaess_helper_dispatch
to:
- 'off'
conditions:
- condition: template
value_template: >-
{% set trigger_id = trigger.to_state.context.id %} {% set veroorzaker =
states.automation
| selectattr('context.id', 'eq', trigger_id)
| map(attribute='entity_id') | first %}
{{ veroorzaker != 'automation.dao_alphaess_mode_2'
and veroorzaker != 'automation.dao_dao_off' }}
- condition: not
conditions:
- condition: state
entity_id: input_select.dao_set_operating_mode
state: Uit
- condition: state
entity_id: input_boolean.dao_balance_switch
state: 'off'
actions:
- target:
entity_id: number.alphaess_template_dispatch_power
data:
value: 0
action: number.set_value
- target:
entity_id: input_number.alphaess_helper_dispatch_duration
data:
value: 15
action: input_number.set_value
- target:
entity_id: input_number.alphaess_helper_dispatch_cutoff_soc
data:
value: '{{ states(''input_number.dao_calculated_soc'') | int(0) }}'
action: input_number.set_value
- target:
entity_id: input_select.alphaess_helper_dispatch_mode
data:
option: State of Charge Control (2)
action: input_select.select_option
- target:
entity_id: input_boolean.alphaess_helper_dispatch
action: input_boolean.turn_on
mode: single✏️ Click to expand HA automation 3 details
alias: 'Dao: DAO off'
description: Mode 2 met power 0 als DAO uitgeschakeld is
triggers:
- minutes: /15
seconds: '15'
trigger: time_pattern
conditions:
- condition: template
value_template: |
{{ states('input_select.dao_set_operating_mode') == 'Uit' }}
- condition: template
value_template: |
{{ states('input_number.dao_from_pv') | float(0) < 50 }}
actions:
- target:
entity_id: input_number.alphaess_helper_dispatch_duration
data:
value: 1
action: input_number.set_value
- delay: '00:00:01'
- target:
entity_id: number.alphaess_template_dispatch_power
data:
value: 0
action: number.set_value
- target:
entity_id: input_number.alphaess_helper_dispatch_cutoff_soc
data:
value: '{{ states(''input_number.dao_calculated_soc'') | int(0) }}'
action: input_number.set_value
- target:
entity_id: input_select.alphaess_helper_dispatch_mode
data:
option: State of Charge Control (2)
action: input_select.select_option
- delay: '00:00:01'
- target:
entity_id: input_number.alphaess_helper_dispatch_duration
data:
value: 15
action: input_number.set_value
- condition: state
entity_id: input_boolean.alphaess_helper_dispatch
state: 'off'
- target:
entity_id: input_boolean.alphaess_helper_dispatch
action: input_boolean.turn_on
mode: singleUitleg:
DAO → AlphaESS Mode 2 via Hillview — automatiseringstoelichting
Achtergrond:
De Day Ahead Optimizer (DAO) berekent elk kwartier een optimaal laad/ontlaadplan voor de batterij op basis van day-ahead stroomprijzen. De AlphaESS wordt aangestuurd via de Hillview integratie in Home Assistant, specifiek Mode 2: State of Charge Control, waarbij je Power, Duration en Cutoff SoC instelt.
Automatisering 1: DAO → Mode 2 Trigger: Elke 15 minuten op :00:15 (15 seconden na het kwartier, zodat DAO zijn berekening heeft afgerond).
Condities:
DAO moet actief zijn (operating_mode != Uit)
Als DAO aan staat én de balance switch aan staat, wordt niks gedaan — de omvormer regelt dan zelf nul-op-de-meter
Vermogen (dao_from_battery):
DAO geeft het gewenste batterijvermogen als kwartiergemiddelde. Bij een korte dispatch (bijv. 8 minuten) wordt dit vermogen omgerekend: vermogen × 15 / duration, zodat de juiste hoeveelheid energie in de kortere tijd wordt geleverd. Begrensd op ±10 kW.
Duration:
Berekend op basis van dao_stop_inverter. Als DAO geen stoptijd heeft ingesteld staat deze op 2000-01-01, dan wordt 15 minuten gebruikt als fallback.
Cutoff SoC:
DAO's berekende SoC ±2% marge, zodat de omvormer iets verder laadt/ontlaadt dan het exacte target. Minimaal 15% bij ontladen.
Dispatch herstart:
De duration wordt eerst op 1 minuut gezet, dan na een korte delay op de echte waarde. Dit forceert altijd een state change waardoor Hillview de dispatch herstart — ook als de duration niet verandert. De boolean wordt alleen aangezet als hij uit staat.
Automatisering 2: Dispatch off → batterij stilzetten
Trigger: Als alphaess_helper_dispatch op off gaat.
Doel: Als de dispatch afloopt (target SoC bereikt of timer verlopen) valt de omvormer terug naar zijn standaard modus. Als de balance switch uit staat betekent dit vrij laden/ontladen zonder sturing. Door Mode 2 opnieuw in te stellen met power 0 blijft de batterij stil totdat de volgende DAO-berekening op het kwartier een nieuwe opdracht geeft.
Conditie: Alleen als de dispatch niet door de DAO-automatisering zelf of de DAO off-automatisering werd uitgeschakeld, DAO actief is, en de balance switch uit staat — als de balance switch aan staat regelt de omvormer zelf nul-op-de-meter en hoeft er niets te gebeuren.
Automatisering 3: DAO uit → batterij stilzetten
Trigger: Elke 15 minuten.
Doel: Als DAO uitgeschakeld is wil je voorkomen dat de omvormer de batterij vrij laadt of ontlaadt. Power 0 houdt de batterij stil zodat PV ongestoord naar het net of huis gaat.
Conditie op PV: dao_from_pv < 50W — bij hoge PV-productie op DC-installaties kan dit anders interfereren.