Compare commits

..

16 Commits

Author SHA1 Message Date
feef76b9b5 Fix mqtt API 2024-12-13 16:20:12 +01:00
758bfff35c Set the example file with PROWL key 2023-12-18 21:08:57 +01:00
a9443b2121 Fix paho_mqtt name in README 2023-12-18 21:07:06 +01:00
ae5b887f33 Added requirements.txt and updated README 2023-12-18 21:06:02 +01:00
93b6b08fda Fix typo (thk @dvb) 2023-12-14 13:53:55 +01:00
b07bcb5f47 Format 2023-12-13 21:24:49 +01:00
b81f2345e2 Update Doc 2023-12-13 21:22:28 +01:00
099aa1b849 Docs 2023-12-13 20:46:46 +01:00
6f49ba2ae9 Print the real values from configuration file and fix some issue
from Rouge -> Bleu
2023-11-29 20:53:39 +01:00
9608cf02be Fix TYPO in tarif BLANC using eco devices 2023-11-29 20:47:30 +01:00
3f411c7113 Adding debug. Since it doesn't seems to work after for night settings. 2023-11-29 14:55:32 +01:00
b71c02fcca 98% is working...
Not tested on venus OS < 3.12
2023-11-28 20:11:06 +01:00
bd60a67402 Fix typo 2023-11-21 17:24:46 +01:00
d5c6b4ae5b Fix typo 2023-11-21 10:42:02 +01:00
220fefc1ad Fixes 2023-11-08 20:53:42 +01:00
ebd6172329 PoC (pas testes en utilisant les API EDF) 2023-11-08 20:47:53 +01:00
7 changed files with 301 additions and 68 deletions

174
README.md
View File

@ -1,7 +1,175 @@
# TEMPO ESS : dynamic ESS for EDF TEMPO
(French only)
This code is for French Users that have an installation
with Multiplus-II, ESS, Battery AND have access to EDF TEMPO
electricity contract.
Since this mostly for French users only the rest of the doc is only
in French
*French only*
Ce code permet de régler de façon semi dynamique votre installation
Victron Energy avec un (ou des) Multiplus-II, ESS et EDF TEMPO (ou
équivalent ex EDF ZenFlex, à voir si les jours ZenFlex correspondent
au jours rouges de TEMPO).
*ATTENTION*: Code en cours de developpement.
## Installation
Vous avez besoin d'un installation qui fonctionne avec :
- Victron Energy Multiplus-II
- Batterie correctement configurée
- Des panneaux solaires (pas nécessaire mais ça aide a passer quand il y a du soleil)
- Un GX quelconque (soit intégré, soit Gerbo, soit home made)
- Une machine Linux/FreeBSD (l'installation sur le Venus OS sera a *vos propres risques*).
## Trucs en plus
- Un EcoDevice v1 (pas le RT2, pas conseillé)
Cet outil permet de trouver les informations de téléinformation de
façon indépendante de l'internet.
- Un compte prowl pour envoyer les notifications de changement de tarifs
A noter que le code se basant sur l'API EDF et/ou Enedis *n'est pas* fini.
## Fonctionnement / Idée
Dans tempo nous avons 3 type de jours où les Heure Pleines qui sont de 6h à 22h
sont variablement plus ou moins chères (a voir s'il existe des abo TEMPO où
les heures pleines sont en dehors de la plage horaire 6h -> 22h).
L'idée est d'utiliser le chargeur du Multiplus pour recharger la batterie et
utiliser l'ESS associé pour moins manger d'heures pleines dans les plages
BLANCHES et ROUGES.
### Jour BLEU
C'est le tarif moins cher donc on est en mode "openbar" :
- Recharge de la batterie si SOC < 80% en heure creuse (nécessite un schedule 1 dans le GX)
- Battery Life : On, car lorsque la batterie n'est pas rechargée souvent il y a une charge lente qui peux arriver
- Min Soc : 30% (configurable)
### Jour BLANC
Le tarifs est un peu plus cher en heures pleines donc on vas limiter la conso
le matin (machine a café, grille pain,...)
- Recharge de la batterie à 90% sur schedule 1
- Batterie Life : On
- Min Soc : 30%
Pareil que les jours blanc, j'ai dans la todo d'ajouter un plage horaire
on peux fermer un relais, pour informer ma pompe à chaleur dois passer
sur le backup Gaz pour la préchauffe du circuit d'eau chauffage.
*Pour l'instant*: le code en question n'existe pas.
### Jour ROUGE
- Recharge de la batterie à 100% sur schedule 1
- Batterie Life : Off
- Min Soc : 25%
Dans ce avoir la batterie à 100% avec battery life off, permet de
rester sur batterie jusqu'au SoC Minimal. En pratique sans soleil
chez moi, je suis de 6h à 16h sans toucher un seul kWh HP rouge.
S'il y a du soleil, j'arrive largement à rester toute les HP sans consommer
du réseau.
Le Battery Life à Off évite aussi de "charger lentement la batterie" quand
on est à Battery Life à On.
## Configuration du GX
Je ne vais pas expliquer comment configurer un Multiplus avec un GX en ESS mais les points
*IMPORTANT* a mettre en place.
### Activer le broker MQTT sur GX
Dans Settings -> Services, activez MQTT on LAN (SSL), _puis_ MQTT on LAN (Plaintext) :
![MQTT Settings](/img/gx-mqtt.png)
Pour l'instant le code ne prévois pas de se connecter en MQTT over SSL.
### Activer un schedule 1 sur le GX
Utilisé pour recharger les batteries en heures creuse, la valeur de recharge sera modifiée
selon les jours Bleu, Blanc et Rouge.
*Si vous ne voulez pas utiliser cette feature* il _suffit juste_ de laisser ce schedule a inactif.
Dans Settings -> ESS -> Scheduled charge levels -> Schedule 1 (capture prise en veille de jour Blanc) :
![Schedule 1](/img/schedule1.png)
![Schedule 1 Settings](/img/schedule1-settings.png)
## Configuration du code
### Prerequis
Il vous faut trouver les points suivants :
- L'ip ou le nom de votre eco device
- L'ip ou le nom de votre GX
- Le numéro de série du GX
*NOTE IMPORTANTE*: le code API EDF n'est pas encore fonctionnel.
Sur les deux premier points, vous avez ces informations sur votre routeur, box, ou ailleurs.
Je ne détaillerais pas comment retrouver ces point.
Pour le numéro de série c'est assez facile a coup de MQTT Explorer, vous le trouverez dans l'arbre `N/`, le
première serie de numeros est le numéro de série de votre GX.
### Configuration
Copiez le fichier `secret.py.exemple` en `secret.py` et remplissez les variables présentes.
### Ajout des modules python nécessaires
Le code a besoin des modules python suivants :
- urllib3
- paho_mqtt
- pyprowl
Ces dependances peuvent être installées :
```
pip3 install -r requirements.txt
```
Cette partie est a voir avec votre distribution linux.
## Comment faire un test ?
C'est assez simple vu tout se configure via MQTT, au lieu de mettre votre GX dans la varible `gx`de `secret.py`
utilisez un mosquitto de test pour voir si les valeur sont celles attendus.
Une fois que vous êtes sûr alors vous pouvez mettre en crontab le fonctionnemnt de code.
Exemple :
```
#
# EDF TEMPO
#
# Lors de la recup jour demain
5 20 * 1-3,9-12 * test -x $HOME/git/tempo-ess/tempo-ess-dynamic.py && $HOME/git/tempo-ess/tempo-ess-dynamic.py
# Lors du passage HC
2 22 * 1-3,9-12 * test -x $HOME/git/tempo-ess/tempo-ess-dynamic.py && $HOME/git/tempo-ess/tempo-ess-dynamic.py
```
Donc le code est executé 2 fois:
- une fois a 20:05, pour récuperer la couleur du lendemain et la valeur du Schedule 1
- une fois a 22:02, pour éventuellement changer la valeur du Min Soc et si on active/desactive Battery Life
Ce dépôt permet de régler de façon dynamique des Victron Energy Multiplus-II en ESS pour permettre de faire des économies d'énergie dans le cadre d'un abonnement EDF TEMPO
*WARNING* : Work in progress

BIN
img/gx-mqtt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
img/schedule1-settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
img/schedule1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
paho_mqtt==1.6.1
urllib3==1.26.18
pyprowl>=3.0.0

View File

@ -3,6 +3,12 @@
# EcoDevice (not the RT one) can be ip address or FQDN
ecodevice = "192.0.2.1"
# Use EcoDevice or EDF "API"
# True : EcoDevice
# False: EDF "API"
# WARNING not completly tested
use_eco = True
# MQTT Broker (or GX with MQTT enabled)
gx = "192.0.2.2"
@ -14,10 +20,13 @@ gxsn = "123456789abc"
# Max Charge according to colors
chgbleu = 80 # Jours Bleus
chgblanc = 90 # Jours Blanc
chgrouge = 95 # Jours Rouge / On VenusOS < 3.10 set this to 95%
# A 95% a cause des histoires de charges fantomes qui sont a corriger.
chgrouge = 98 # Jours Rouge / On VenusOS < 3.10 set this to 95%
# Min Charge according to colors
minbleu = 30 # Jours Bleus
minblanc = 30 # Jours Blanc
minrouge = 25 # Jours Rouge
# Your PROWL API Key
# set it to 'NO' if you don't want PROWL support
prowkey = 'NO'

View File

@ -7,8 +7,11 @@ import time
import pyprowl
#import syslog
from datetime import date,datetime
# Settings
from secret import ecodevice,gx,gxsn,chgbleu,chgblanc,chgrouge,minbleu,minblanc,minrouge,prowlkey
from secret import ecodevice,use_eco,gx,gxsn,chgbleu,chgblanc,chgrouge,minbleu,minblanc,minrouge,prowlkey
# Lancer ceci du 01 Oct au 31 mai
# une fois avant 22h / une fois apres 22h
@ -21,7 +24,7 @@ ESSwoBL = 10 # ESS "Optimized without BatteryLife)
p = pyprowl.Prowl(prowlkey)
def sendprowl(foo):
p.notify(event='EDF TEMPO', description='Attention EDF Tarif: '+foo,
p.notify(event='EDF TEMPO', description='Attention EDF Tarif: '+foo,
priority=0, appName='EDF TEMPO')
# Log !
@ -49,7 +52,8 @@ def on_disconnect(client, userdata, rc):
flagConntected = 0
loggerinfo("Broker disconnected.")
client = mqtt.Client("clientdynTEMPO")
##client = mqtt.Client("clientdynTEMPO")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)
flagConntected = 0
client.on_connect = on_connect
client.on_message = on_message
@ -70,7 +74,7 @@ def setChargeSetpoint(chargepoint):
# Wait for connecting
while not flagConntected:
time.sleep(1)
client.publish("W/" + gxsn + "/settings/0/Settings/CGwacs/BatteryLife/Schedule/Charge/O/Soc", '{"value":' + str(chargepoint) + '}')
client.publish("W/" + gxsn + "/settings/0/Settings/CGwacs/BatteryLife/Schedule/Charge/0/Soc", '{"value":' + str(chargepoint) + '}')
client.loop_stop()
# Set the Minimum Soc Limit
@ -115,64 +119,113 @@ except Exception as e:
print("Error verifying Prowl API key: {}".format(e))
exit()
# Setup EcoDevice
http = urllib3.PoolManager()
resp = http.request("GET", "http://"+ecodevice+"/api/xdevices.json?cmd=10")
if use_eco:
# Setup EcoDevice
resp = http.request("GET", "http://"+ecodevice+"/api/xdevices.json?cmd=10")
if resp.status == 200:
teleinfo = json.loads(resp.data)
if resp.status == 200:
teleinfo = json.loads(resp.data)
# Value of var today daynight
# HPJB = Bleu 0 1
# HCJB = Bleu 0 0
# HPJW = Blanc 1 1
# HCJW = Blanc 1 0
# HPJR = Rouge 2 1
# HCJR = Rouge 2 0
curtarif = teleinfo['T1_PTEC']
if curtarif == "HPJB":
# Value of var today daynight
# HPJB = Bleu 0 1
# HCJB = Bleu 0 0
# HPJW = Blanc 1 1
# HCJW = Blanc 1 0
# HPJR = Rouge 2 1
# HCJR = Rouge 2 0
curtarif = teleinfo['T1_PTEC']
if curtarif == "HPJB":
today = 0
daynight = 1
elif curtarif == "HCJB":
today = 0
daynight = 0
elif curtarif == "HPJW":
today = 1
daynight = 1
elif curtarif == "HCJW":
today = 1
daynight = 0
elif curtarif == "HPJR":
today = 2
daynight = 1
elif curtarif == "HCJR":
today = 2
daynight = 0
else:
today = 0
daynight = 0
# Value of var tomorrow
# ---- = Bleu 0
# BLEU = Bleu 0
# BLAN = Blanc 1
# ROUG = Rouge 2
demaintarif = teleinfo['T1_DEMAIN']
if demaintarif == "----":
tomorrow = 0
elif demaintarif == "BLEU":
tomorrow = 0
elif demaintarif == "BLAN":
tomorrow = 1
elif demaintarif == "ROUG":
tomorrow = 2
else:
tomorrow=0
loggerinfo("Current tarif: " + curtarif + " ("+str(today)+" / "+str(daynight) + ")")
loggerinfo("Demain tarif: " + demaintarif + " ("+str(tomorrow)+")")
else:
# Use EDF "API"
today=date.today()
resp = http.request("GET", "https://particulier.edf.fr/services/rest/referentiel/searchTempoStore?dateRelevant="+str(today.strftime("%Y-%m-%d")))
if resp.status == 200:
edfapi = json.loads(resp.data)
curcouleur = edfapi['couleurJourJ']
demaincouleur = edfapi['couleurJourJ1']
if curcouleur == "TEMPO_BLEU":
today = 0
daynight = 1
elif curtarif == "HCJB":
today = 0
daynight = 0
elif curtarif == "HPJW":
elif curcouleur == "TEMPO_BLANC":
today = 1
daynight = 1
elif curtarif == "HCJW":
today = 1
daynight = 0
elif curtarif == "HPJR":
elif curcouleur == "TEMPO_ROUGE":
today = 2
else:
today = -1
if demaincouleur == "TEMPO_BLEU":
tomorrow = 0
elif demaincouleur == "TEMPO_BLANC":
tomorrow = 1
elif demaincouleur == "TEMPO_ROUGE":
tomorrow = 2
elif demaincouleur == "NON_DEFINI":
tomorrow = -1
else:
tomorrow = -1 # trop tot.
now = datetime.now()
#Heures creuses: 22h00 -> 06h00
loggerinfo("Current hour : "+str(now.hour))
if now.hour >= 6 and now.hour <=22:
loggerinfo("We are DAY")
daynight = 1
elif curtarif == "HCJR":
today = 2
daynight = 0
else:
today = 0
else:
loggerinfo("We are NIGHT")
daynight = 0
# Value of var tomorrow
# ---- = Bleu 0
# BLEU = Bleu 0
# BLAN = Blanc 1
# ROUG = Rouge 2
demaintarif = teleinfo['T1_DEMAIN']
if demaintarif == "----":
tomorrow = 0
elif demaintarif == "BLEU":
tomorrow = 0
elif demaintarif == "BLANC":
tomorrow = 1
elif demaintarif == "ROUG":
tomorrow = 2
else:
tomorrow=0
loggerinfo("La couleur aujourd'hui "+str(curcouleur))
loggerinfo("La couleur demain "+str(demaincouleur))
if tomorrow == -1:
loggerinfo("La couleur de demain n'est pas definie -> exit")
quit()
loggerinfo("Current tarif: " + curtarif + " ("+str(today)+" / "+str(daynight) + ")")
loggerinfo("Demain tarif: " + demaintarif + " ("+str(tomorrow)+")")
# daynight : 1 = jour / 0 nuit
# Today Tomorrow Daynight Action
# 0 0 O Rien
@ -202,55 +255,55 @@ elif today == 0: # Bleu
if tomorrow == 1: # Blanc
if daynight == 1:
sendprowl('BLANC')
loggerinfo ("Charge 90%")
loggerinfo ("Charge "+str(chgblanc)+"%")
setChargeSetpoint(chgblanc)
elif tomorrow == 2: # Rouge
if daynight == 1:
loggerinfo ("Charge 95%")
loggerinfo ("Charge "+str(chgrouge)+"%")
sendprowl('ROUGE')
setChargeSetpoint(chgrouge)
else:
loggerinfo ("SocMin 25%")
loggerinfo ("SocMin "+str(minrouge)+"%")
setMinSocSetpoint(minrouge)
loggerinfo ("Battery life OFF")
setESSstate(ESSwoBL)
elif today == 1: # Blanc
if tomorrow == 0: # Bleu
if daynight == 1:
loggerinfo ("Charge 80%")
loggerinfo ("Charge "+str(chgbleu)+"%")
setChargeSetpoint(chgbleu)
sendprowl('BLEU')
else:
loggerinfo ("SocMin 30%")
loggerinfo ("SocMin "+str(minbleu)+"%")
setMinSocSetpoint(minbleu)
elif tomorrow == 2: # Rouge
if daynight == 1:
sendprowl('ROUGE')
loggerinfo ("Charge 95%")
loggerinfo ("Charge "+str(chgrouge)+"%")
setChargeSetpoint(chgrouge)
else:
loggerinfo ("SocMin 25%")
loggerinfo ("SocMin "+str(minrouge)+"%")
setMinSocSetpoint(minrouge)
loggerinfo ("Battery life OFF")
setESSstate(ESSwoBL)
elif today == 2: # Rouge
if tomorrow == 0: # Bleu
if daynight == 1:
loggerinfo ("Charge 80%")
loggerinfo ("Charge "+str(chgbleu)+"%")
setChargeSetpoint(chgbleu)
sendprowl('BLEU')
else:
loggerinfo ("SocMin 30%")
setChargeSetpoint(chgbleu)
loggerinfo ("SocMin "+str(minbleu)+"%")
setMinSocSetpoint(minbleu)
loggerinfo ("Battery life ON")
setESSstate(ESSwBL)
elif tomorrow == 1: # Blanc
if daynight == 1:
sendprowl('BLANC')
loggerinfo ("Charge 90%")
loggerinfo ("Charge "+str(chgblanc)+"%")
setChargeSetpoint(chgblanc)
else:
loggerinfo ("SocMin 30%")
loggerinfo ("SocMin "+str(minblanc)+"%")
setMinSocSetpoint(minblanc)
loggerinfo ("Battery life ON")
setESSstate(ESSwBL)