Home Assistant - Zendure automation MQTT and QCELLs
YAML
alias: __Zendure_New_Automation v4
description: ""
triggers:
- trigger: time_pattern
seconds: /30
conditions: []
actions:
- sequence: []
- alias: HP ou HC ?
choose:
- conditions:
- condition: state
entity_id: sensor.linky_current_price
state:
- HEURE PLEINE
sequence:
- alias: Etat de QCELLS
choose:
- conditions:
- condition: state
entity_id: sensor.qcells_etat
state:
- Standby
sequence:
- alias: >-
Si il est à -10% (vide) alors on compense si nécessaire
avec SF2400
choose:
- conditions:
- condition: and
conditions:
- type: is_battery_level
condition: device
device_id: afda895cc1812b99521eb78710326b89
entity_id: f25a2fe7b99ed232a00fd0c82cb205e2
domain: sensor
below: 11
- condition: numeric_state
entity_id: sensor.te31njn8n387601_l3_p
above: 50
sequence:
- sequence:
- if:
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Input mode
then:
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: select_option
option: Output mode
alias: >-
SF2400 - Changement d'état IN to Out si
necessaire
- action: number.set_value
target:
entity_id: number.eeb4aep1p032084_outputlimit
data:
value: >
{% set current =
states('sensor.te31njn8n387601_l3_p') |
float(0) %} {% set output =
states('number.eeb4aep1p032084_outputlimit')
| float(0) %}
{% if -50 <= current <= 50 %}
{{ output | round(0) }}
{% else %}
{% set delta = 0.7 * current %}
{% set cible = output + delta %}
{% set result = cible - 50 if cible > 0 else cible %}
{{ [result, 900] | min | round(0) }}
{% endif %}
- sequence:
- if:
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Input mode
then:
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: select_option
option: Output mode
alias: >-
SF2400 - Changement d'état IN to Out si
necessaire
- action: number.set_value
target:
entity_id: number.eeb4aep1p032084_outputlimit
data:
value: >
{% set current =
states('sensor.te31njn8n387601_l3_p') |
float(0) %} {% set output =
states('number.eeb4aep1p032084_outputlimit')
| float(0) %}
{% if -50 <= current <= 50 %}
{{ output | round(0) }}
{% else %}
{% set delta = 0.7 * current %}
{% set cible = output + delta %}
{% set result = cible - 50 if cible > 0 else cible %}
{{ [result, 900] | min | round(0) }}
{% endif %}
enabled: false
alias: Si 3CT > 50W, on compense avec xW pour arriver à 50W
- conditions:
- condition: numeric_state
entity_id: sensor.te31njn8n387601_l3_p
below: -200
- type: is_battery_level
condition: device
device_id: afda895cc1812b99521eb78710326b89
entity_id: f25a2fe7b99ed232a00fd0c82cb205e2
domain: sensor
above: 90
alias: QCELLs > 90%
sequence:
- alias: >-
On demarre la charge de SF2400 avec 0.3 de ce
qu'on inject en trop
sequence:
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: select_option
option: Input mode
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: number
entity_id: f2984ee0e6ebcf40e208c5a0d56b90d8
type: set_value
value: 0
- action: number.set_value
target:
entity_id: number.eeb4aep1p032084_inputlimit
data:
value: >
{% set current =
states('sensor.te31njn8n387601_l3_p') |
float(0) %} {% set input =
states('number.eeb4aep1p032084_inputlimit')
| float(0) %}
{% if -50 <= current <= 50 %}
{{ input | round(0) }}
{% else %}
{% set current = current * -1 %}
{% set delta = 0.3 * current %}
{% set cible = input + delta %}
{% set result = cible - 50 if cible > 0 else cible %}
{{ [result, 900] | min | round(0) }}
{% endif %}
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: number
entity_id: 446540682f8a0142e01a5f8a2272926d
type: set_value
value: 200
enabled: false
alias: QCELLs full
alias: QCELLS en Standby
- conditions:
- condition: state
entity_id: sensor.qcells_etat
state:
- En charge
sequence:
- alias: Si surplus de production
choose:
- conditions:
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.qcells_inverter_production_2
below: 20
- type: is_power
condition: device
device_id: 181b299767530862f68a9d7f84c40146
entity_id: 04b888d40622ee63c389a1da5046b77f
domain: sensor
below: -50
sequence:
- alias: Faut-il lancer la charge du SF2400 ?
choose:
- conditions:
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Output mode
sequence:
- target:
entity_id: number.eeb4aep1p032084_outputlimit
data:
value: >
{% set current =
states('sensor.te31njn8n387601_l3_p') |
float %} {% set output =
states('number.eeb4aep1p032084_outputlimit')
| float %}
{% if -50 <= current <= 50 %}
{{ output | round(0) }}
{% else %}
{% set surplus = current * -1 %}
{% set delta = 0.7 * surplus %}
{{ (output - delta) | round(0) }}
{% endif %}
action: number.set_value
alias: >-
SF2400 est-il en output, auquel ca il faut
réduire son injection
- conditions: []
sequence:
- sequence:
- alias: >-
SF2400 - Changement d'état OUT to IN si
necessaire
if:
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Output mode
then:
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: select_option
option: Input mode
- target:
entity_id: number.eeb4aep1p032084_inputlimit
data:
value: >
{% set current =
states('sensor.te31njn8n387601_l3_p') |
float %} {% set input =
states('number.eeb4aep1p032084_inputlimit')
| float %}
{% if -50 <= current <= 50 %}
{{ input | round(0) }}
{% else %}
{% set surplus = current * -1 %}
{% set delta = 0.7 * surplus %}
{{ (input + delta) | round(0) }}
{% endif %}
action: number.set_value
alias: 3CT negatif < -50W vérification sur QCELLS inject
- conditions:
- condition: or
conditions:
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.qcells_inverter_production_2
below: 10
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Output mode
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.qcells_inverter_production_2
below: sensor.qcells_production_solaire
enabled: false
sequence:
- target:
entity_id: number.eeb4aep1p032084_outputlimit
data:
value: >
{% set current =
states('sensor.te31njn8n387601_l3_p') | float
%} {% set output =
states('number.eeb4aep1p032084_outputlimit') |
float %} {% set qcell =
states('sensor.qcells_inverter_production_2') |
float %}
{% if qcell <= 10 %}
{{ output | round(0) }}
{% else %}
{% set surplus = current * -1 %}
{% set delta = 0.1 * output %}
{{ (output - delta) | round(0) }}
{% endif %}
action: number.set_value
alias: QCELLS en Charge
- conditions:
- condition: state
entity_id: sensor.qcells_etat
state:
- En décharge
sequence:
- choose:
- conditions:
- alias: >-
QCELLs à plus de 90%, on réduit la charge de
SF2400 si en charge
condition: and
conditions:
- condition: and
conditions: []
- condition: numeric_state
entity_id: sensor.qcells_batterie_level
above: 90
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Input mode
sequence:
- action: number.set_value
target:
entity_id: number.eeb4aep1p032084_inputlimit
data:
value: >
{% set current =
states('sensor.te31njn8n387601_l3_p') | float(0)
%}
{% set input =
states('number.eeb4aep1p032084_inputlimit') |
float(0) %}
{% if -50 <= current <= 50 %}
{{ input | round(0) }}
{% else %}
{% set delta = 0.7 * current * -1 %}
{% set cible = input - delta %}
{% set result = cible - 50 if cible > 0 else cible %}
{{ [result, 900] | min | round(0) }}
{% endif %}
- conditions:
- condition: numeric_state
entity_id: sensor.te31njn8n387601_l3_p
above: 50
- type: is_power
condition: device
device_id: afda895cc1812b99521eb78710326b89
entity_id: ef2eee2418adb0259f2f29dbd7ed8206
domain: sensor
above: 1900
sequence:
- sequence:
- if:
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Input mode
then:
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: select_option
option: Output mode
alias: >-
SF2400 - Changement d'état IN to Out si
necessaire
- action: number.set_value
target:
entity_id: number.eeb4aep1p032084_outputlimit
data:
value: >
{% set current =
states('sensor.te31njn8n387601_l3_p') |
float(0) %} {% set output =
states('number.eeb4aep1p032084_outputlimit')
| float(0) %}
{% if -50 <= current <= 50 %}
{{ output | round(0) }}
{% else %}
{% set delta = 0.7 * current %}
{% set cible = output + delta %}
{% set result = cible - 50 if cible > 0 else cible %}
{{ [result, 900] | min | round(0) }}
{% endif %}
alias: >-
Qcells en Décharge mais on tire toujours de l'EDF, on
compense avec SF2400
- conditions:
- type: is_power
condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
entity_id: b220a07315cd6e8c253e8c771528fd3e
domain: sensor
above: 50
sequence:
- sequence:
- action: number.set_value
target:
entity_id: number.eeb4aep1p032084_outputlimit
data:
value: >
{% set current =
states('sensor.te31njn8n387601_l3_p') |
float(0) %}
{% set output =
states('number.eeb4aep1p032084_outputlimit')
| float(0) %}
{% set delta = 0.3 * output %}
{% set cible = output - delta %}
{% set result = cible - 50 if cible > 0 else
cible %}
{% if -50 <= result <= 50 %}
{{ output | round(0) }}
{% else %}
{{ [result, 900] | min | round(0) }}
{% endif %}
alias: QCELLs est en Decharge mais SF aussi, on reduit SF
alias: QCELLS en Decharge
alias: Si HP
- conditions:
- condition: state
entity_id: sensor.linky_current_price
state:
- HEURE CREUSE
sequence:
- alias: Faut-il lancer la charge du SF2400 ?
choose:
- conditions:
- condition: state
entity_id: sensor.qcells_etat
state:
- En décharge
sequence:
- sequence:
- target:
entity_id: number.eeb4aep1p032084_inputlimit
data:
value: "0"
action: number.set_value
alias: >-
QCELLs est en Décharge alors mettre le SF2400 en input mais à
0W !
- conditions:
- condition: and
conditions:
- condition: state
entity_id: sensor.qcells_etat
state:
- Standby
alias: Si Qcells est Standby
- condition: numeric_state
entity_id: sensor.qcells_batterie_level
below: 11
alias: Si QCells < 11%
- type: is_battery_level
condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
entity_id: 23f1ec717c4dcf6732d11bfaac92d296
domain: sensor
below: 35
alias: Si SF2400 < 35%
sequence:
- sequence:
- alias: SF2400 - Changement d'état OUT to IN si necessaire
if:
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Output mode
then:
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: select_option
option: Input mode
- target:
entity_id: number.eeb4aep1p032084_inputlimit
data:
value: "900"
action: number.set_value
- if:
- condition: numeric_state
entity_id: number.eeb4aep1p032084_inputlimit
below: 800
then:
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: number
entity_id: 446540682f8a0142e01a5f8a2272926d
type: set_value
value: 900
alias: Charge SF2400 Si Qcells vide et SF2400 <35%
- conditions:
- type: is_battery_level
condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
entity_id: 23f1ec717c4dcf6732d11bfaac92d296
domain: sensor
above: 89
sequence:
- sequence:
- alias: SF2400 - Changement d'état IN to OUT si necessaire
if:
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Input mode
then:
- device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: select_option
option: Output mode
- target:
entity_id: number.eeb4aep1p032084_outputlimit
data:
value: "300"
action: number.set_value
alias: SF2400 est à >=90%
- conditions:
- condition: and
conditions:
- condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
domain: select
entity_id: faa8306ea0f42cc0b510209a0e408c8e
type: selected_option
option: Output mode
alias: "SF2400 Mode : output"
- type: is_battery_level
condition: device
device_id: a9bed78637a3d4199888177480ba0f4b
entity_id: 23f1ec717c4dcf6732d11bfaac92d296
domain: sensor
below: 41
alias: SF2400 Bat <=41%
alias: SF2400 <=41% et AC Output
sequence:
- sequence:
- target:
entity_id: number.eeb4aep1p032084_outputlimit
data:
value: "0"
action: number.set_value
alias: "SF2400 Stop decharge : Output 0W"
alias: Stop decharge SF2400
alias: SF2400 est à <=40% et si il etait en décharge, on stop
alias: Si HC
mode: single
Présentation
Le but de ce process est le suivant :
- Pouvoir coupler à la fois le système Zendure sans HEMS, un système QCells, et optimiser les cycles !
- Eviter de donner le surplus à EDF au maximum possible.
Je vais essayer d'expliquer en détail ce que j'ai souhaité faire...
Mon installation :
- Solution QCells G1 avec 5.5kw en batterie et 2400Wc (installation toiture historique)
- Solution Zendure SolarFlow 800 avec 2 x 500Wc (Installation toiture Veranda)
- Solution Zendure SolarFlow 800 avec 2x 450Wc (Installation toiture Abris Bois)
- Solution Zendure SolarFlow 2400 Pro avec 4 x 450Wc (Future installation toiture Garage)
Pourquoi une si "grosse installation" ?
Habitant dans le sud (Aix en Provence), il est parfois nécessaire en été de se rafraichir... la climatisation de la maison (5 clims) et la piscine peuvent vite devenir un gouffre !
De même l'hiver, il est pas rare de devoir chauffer un peu !
Quand j'ai emménagé dans la maison, la première période (Aout 2023 - Aout 2024) a été un test, pas de clim et pas d'optimisation de la piscine
Résultat : 7.428kWh de consommés (HP 4.293kWh / HC 3.135kWh) : 1849€ TTC pour l'année, soit 154€ mensuel...
L'année suivante Aout 2024 - Aout 2025), 7.286kWh ( HP 3.942kWh / HC 3.344kWh) : 1772€ pour l'année, soit 148€ mensuel...
Cette seconde année a été marqué par le fait que j'ai optimisé un peu la piscine (variateur de pompe), qu'on a installé la Clim dans la maison, mais surtout que j'ai ajoutée un kit SF800, ce qui nous a pas réduis la conso, mais a rendu la maison plus confortable !
Bref c'était pas foufou...
Enfin cette troisième année Aout 2025 - Aout 2026, même si elle n'est pas fini, a été l'occasion de refaire un abris à bois et donc d'installer un second kit SF800 en guise de toiture en novembre 2025 !
Et à partir de ce moment, c'est devenu intéressant, on a tiré un peu moins d'EDF sur l'hiver, mais surtout, j'arrivais à charger le QCells a plus de 50%, ce qui était une bonne chose car avant ce n'était pas possible.
Depuis fin février et l'ajout d'une batterie AB2000, avec les beaux jours, cela c'est confirmé c'était intéressant.
Enfin depuis avril, les jours ensoleillés, le QCells se charge à plus de 80% et de temps à autre, je donne quelques kWh à EDF gratuitement.
L'idée était donc d'éviter de trop donner tout en essayer d'optimiser encore...
Ainsi depuis début mai, j'ai ajouter un kit SF2400 Pro avec 4 x 450Wc.
A ce jour, je suis à 4.425kWh (HP 1.192 / HC 2.513kWh)
Voici le tableau sur ces 3 an :
| 2023 - 2024 | 2024 - 2025 | 2025 - 2026 | ||||
| HP | HC | HP | HC | HP | HC | |
| juillet | 142 | 128 | 274 | 294 | 248 | 299 |
| aout | 375 | 257 | 399 | 309 | 284 | 285 |
| septembre | 331 | 227 | 286 | 223 | 171 | 184 |
| octobre | 548 | 274 | 296 | 267 | 246 | 280 |
| novembre | 566 | 309 | 470 | 358 | 322 | 329 |
| décembre | 768 | 513 | 589 | 392 | 426 | 443 |
| janvier | 529 | 350 | 432 | 283 | 230 | 326 |
| février | 195 | 209 | 348 | 283 | 127 | 277 |
| mars | 185 | 182 | 265 | 219 | 53 | 211 |
| avril | 113 | 106 | 196 | 190 | 53 | 178 |
| mai | 299 | 319 | 153 | 228 | ||
| juin | 242 | 261 | 234 | 298 | ||
| 4293 | 3135 | 3942 | 3344 | 2160 | 2812 | |
| 7428 | 7286 | 4972 | ||||
Il fallait donc un petit chef d'orchestre pour gerer tout ca, HEMS n'etant pas capable d'avoir une vision d'ensemble et surtout de s'adapter au cas particulier.
Ainsi est né cet automatisme sous HA...
Fonctionnement
Ce script se base sur plusieurs éléments et tourne toute les 30 secondes.
Dans mon cas, les heures creuses sont de 20h50 à 4h50.
Dans un premier temps il vérifie si on est en heure pleine ou en heure creuse.
Si on est en HC, il va avoir 4 options :
- Soit le QCells est en décharge
- => Dans ce cas le SF2400 va bloquer sa charge forcée.
- Soit le QCells est vide et le SF2400 a moins de 35%
- => On passe le SF2400 en mode "input" avec une charge forcée à 900W
- => Objectif le charger en HC quand l'EDF est moins cher pour venir le décharger en heure pleine
- Soit le SF2400 est à plus de 90% chargé
- => le SF2400 s'est chargé avec les PV ou le surplus au cours de la journée
- => on injecte 300W en HC pour "gommer" un peu la conso.
- Soit le SF2400 est en dessous de 40% (ou passe en dessous)
- => on stop sa décharge
- => on garde le reste pour les heures pleine.
Maintenant les HP !
Je me base en priorité sur l'état de mon QCells !
Il peut avoir 3 etats : Standby, en Charge, en Décharge
StandBy : c'est quand il est complètement vide ou complètement chargé
en Charge : il se charge avec le surplus de ses PV qu'il n'injecte pas
en Décharge : il balance ce que les PV produisent + sa batterie si nécessaire, dans la limite des 3500W
Il surveille ce que je tire d'EDF et compense de façon autonome et non pilotable !
J'ai pas le chois c'est comme ca...
Donc en StandBy :
Je demande au SF2400 de venir compenser uniquement si le QCells est vide.
En Décharge, 3 options :
- QCells est à plus de 90% alors je réduis ma charge sur le SF2400.
- QCells est en décharge mais on tire encore de l'EDF, alors SF2400 vient aider le QCells
- QCells est en décharge mais aussi SF2400, alors on va essayer d'adapter l'injection de SF2400 pour toujours privilégier une décharge de QCells
En Charge, 2 options :
- QCells se charge mais SF2400 injecte, alors on réduit l'injection de SF2400 au maximum
- QCells se charge mais on injecte encore vers EDF, on vient forcer la charge de SF2400 avec le surplus.
Ce qu'il va manquer maintenant c'est de limiter l'injection vers EDF si tout est full.
Au plus fort, il peut arriver d'envoyer 3000W vers EDF ce qui n'est pas supposer arriver...