diff --git a/config-default.yaml b/config-default.yaml index 6d311f8..eca38bd 100644 --- a/config-default.yaml +++ b/config-default.yaml @@ -1,222 +1,237 @@ ###################################################################### # PvMonit - By David Mercereau : http://david.mercereau.info/contact/ # Licence BEERWARE # Version 1.0 ###################################################################### ############################## # # NE MODIFIER PAS CE FICHIER ! # copier config-default.yaml dans config.yaml et modifier config.yaml (supprimer tout ce qui ne vous intéresse pas de modifier) # ############################## # Niveau d'affichage des messages printMessage: 0 # 0=0 5=debug printMessageLogfile: false # path or fase # URL data urlDataXml: http://192.168.1.X/data-xml.php # N'utilisez pas 127.0.0.1 mais plutôt l'ip local si vous restez en local (192.168.1.2 ? commande "ip a s" pour l'obtenir, vous ne pouvez pas être en DHCP, mais en IP fixe donc), ou un nom de domain résolu en local et à l'exterieur si c'est publié sur internet tmpFileDataXml: /tmp/PvMonit_data-xml.php.tmp dir: bin: /opt/PvMonit/bin/ bin_enabled: /opt/PvMonit/bin-enabled/ lcd: /opt/PvMonit/lcd/ domo: /opt/PvMonit/domo/ data: ppv_total: false # production total des régulateurs (utilisé si vous avez plusieurs régulateur) conso_calc: false # Calculé avec : la puissance instantané (P du BMV) - ppv_total ppv_total at true for use this cache: dir: /tmp/PvMonit_cache # in tmpfs file_prefix: time: 60 # in second # Methode de récupération des données VE DIRECT (par USB - vedirect OU serial par Arduino) vedirect: by: usb # usb OR arduino usb: # Binaire de vedirect.py USB bin: /usr/bin/sudo /usr/bin/python /opt/PvMonit/bin/vedirect.py arduino: # Fichier de data YAML enregistré par le script vedirectOnArduinoRemote.py cohérence avec config-vedirectOnArduinoRemote.yaml data_file: /tmp/PvMonit_getSerialArduino.data.yaml data_file_expir: 300 # Expiration serial: port: /dev/ttyAMA0 # ttyAMA0 pour le serial via GPIO, ttyUSB0 pour le port USB... timeout: 0 # Débit du serial 0 qui va vers l'Arduino (doit être cohérent entre les 2, diffère selon la distance de câble) # Débit Longueur (m) # 2400 60 # 4 800 30 # 9 600 15 # 19 200 7,6 # https://fr.wikipedia.org/wiki/RS-232#Limites baudRate: 4800 whileSleep: 0.001 whileSleepAfterStop: 3 # donnée récolté (voir la doc victron sur le protocole VE.Direct) data_ok: mppt: - CS - PPV - V - ERR - I - VPV - H19 - H20 - H21 - H22 - H23 - Relay bmv: - V - VS - VM - DM - I - T - P - CE - SOC - TTG - AR - H1 - H2 - H3 - H4 - H5 - H6 - H7 - H8 - H9 - H10 - H11 - H12 - H13 - h14 - H15 - H16 - H17 - H18 - Relay phoenix: - P - CS - MODE - AC_OUT_V - AC_OUT_I - WARN # Numéro de série (champs SER#) en correspondance avec des nom buvables deviceCorrespondance: HQXXXXXXXX: MpttGarage HQYYYYYYYY: MpttToit # Plafont de consommation en W impossible à dépasser (techniquement, sinon c'est une erreur de sonde) consoPlafond: 1500 # Tension standard du réseau (110V ou 230V) tensionNorme: 230 ### Export vers Emoncms emoncms: # Test la connexion internet testInternetHost: emoncms.org testInternetPort: 80 # emoncms URL du post.json & API key urlInputJsonPost: https://emoncms.org/input/post.json apiKey: XXXXXXXXXXXXXXXXXXXXXXXX # Répertoire de collecte de données dataCollecte: /tmp/PvMonit_collecteData # Dossier ou ranger les erreurs dataCollecteError: /tmp/PvMonit_collecteDataError # Attente entre deux requête OK sleepOk: 1 # Attente entre deux requête échoué sleepNok: 3 # Fichier de lock pour éviter les doublons lockFile: /tmp/PvMonit_sendToEmoncms.lock ### Page Web : www: - # Délais de raffraichissement de la page (en seconde) 300000 = 5 minutes - refreshTime: 300000 + # Délais de raffraichissement de la page (en seconde) 300 = 5 minutes + refreshTime: 300 # Max de la jauge voltage batterie (en V) vbatMax: 30 # Max de la jauge puissance PV (en W) PpvMax: 500 # max Jauge puissance PV (en W) # Max de la jauge puissance PV total (si plusieurs régulateur) (en W) PpvtMax: 500 # max Jauge puissance PV (en W) dataPrimaire: - V - PPV - ERR - CS - SOC - AR - P - TTG - MODE - AC_OUT_V - AC_OUT_I - WARN - PPVT - CONSO dataPrimaireSmallScreen: - SOC - P - PPVT menu: -
  • EmonCMS (historique)
  • -
  • PvMonit projet
  • -
  • Soutenir l'auteur
  • checkUpdate: 43200 # false for disable, or seconds checks + domo: false # Enable domo on web interface ? + domoRefreshTime: 5 # Refresh time domo in second # Ecran LCD (avec script PvMonit/lcd lcd: rafraichissement: 0.1 # en seconde pour les boutons dataUpdate: 45 # en seconde pour le rafraichissement des données onTimer: 60 # en seconde le temps que l'écran reste allumé si par défaut éteind estCeLaNuitTimer: 600 # détection de la nuit tout les x secondes dataPrint: - SOC - P - PPVT onAt: 8 # heure d'allumage du LCD offAt: 21 # heure d'extinction du LCD # Domotique (avec script PvMonit/domo) domo: dataCheckTime: 30 # Check du XML dbFile: /tmp/PvMonit_domo.sqlite3 - i2c: - adress: 0x04 - device: 1 - heartbeatTime: 4 # Fréquence du bâtement de coeur (en seconde) + jsonFile: + modPath: /tmp/PvMonit_domo_mod + etatPath: /tmp/PvMonit_domo_etat + binGpio: /usr/bin/sudo /usr/bin/gpio fileExpir: 180 # Age en seconde après laquel le fichier n'est plus considéré comme ok fileCheckError: 5 # nombre d'erreurs sur le xml xmlDataExpir: 500 # Age en seconde avant expiration des données XML (et donc arrêt du heartbeat) valueUse: SOC: ^-?[0-9]+\.?[0-9]+?$|^[0-9]$ P: ^-?[0-9]+\.?[0-9]+?$|^[0-9]$ PPVT: ^-?[0-9]+\.?[0-9]+?$|^[0-9]$ CS: ^Off$|Fault$|Bulk|Faible|Abs|Float|On$ relay: - nb: 5 # 14 max ! dataFreq: 30 # Délais de récupération des données depuis l'arduino (en secondes) scriptDir: /opt/PvMonit/domo/relay.script.d scriptExecInterval: 120 # Interval d'execution des script de relais - #~ relayCorrespondance: # A commencer par 0 - #~ 0: Pompe de relevage - #~ 1: Box Internet - #~ 2: Téléphone - #~ 3: Disque dur externe - #~ 4: Chargeur outil électroportatif # Sonde courant ! - #~ #5: Chauffe eau - #~ #6: Batterie de vélo életrique - #~ #7: Surpresseur + relayNb: 8 + relayName: # A commencer par 1 + 1: aDefinir + 2: aDefinir + 3: aDefinir + 4: aDefinir + 5: aDefinir + 6: aDefinir + 7: aDefinir + 8: aDefinir + relayWiringPi: # WiringPI is no GPIO number : https://fr.pinout.xyz/pinout/ + 1: 4 + 2: 5 + 3: 10 + 4: 21 + 5: 22 + 6: 27 + 7: 28 + 8: 29 + relayActionRefresh: 2 # delay (in second) to refresh etat relay (script relay-action.php) + tm1638: + refresh: 0.3 + actionButtonDelay: 2 # délais (en seconde) avant que les actions boutton n'est un effet diff --git a/domo/README.md b/domo/README.md index 6c66315..e89ecf9 100644 --- a/domo/README.md +++ b/domo/README.md @@ -1,67 +1,83 @@ # PvMonit Domotique - Ou comment utilisé le surplus d'une installation solaire autonome + + +Conserver install v1.0 + +faire fichier upgrade : 1.0 to 2.0 + +​ changement user pvmonit pour simplifier les cron / et permissions https://redmine.lighttpd.net/boards/2/topics/6247 + +domo : relay-action : wiringpi + + + Dans le cas d'une installation solaire autonome (non raccordé au réseau EDF), une fois que les batteries sont rechargé (ce qui se produit au alentour de 11h-12h pour moi 80% du temps) il y a de l'énergie potentiel de perdu. Plus précisément si je n'utilise pas cette énergie au moment ou il y a du soleil (de la production) cette énergie n'est pas utilisé. On peut augmenter le stockage mais c'est infini, coûteux en argent en ressource environnementale. Du coup m'a semblé pertinent de réfléchir à un moyen d'automatisé certaine tâche qui me permette d'utilisé ce surplus d'électricité quand il est là. Actuellement je le fait de façon tout à fait manuel : quand les batteries sont pleine et qu'il y a du soleil, je lance une machin à laver, je lance la pompe de relevage de la phyto, je recharge mes batterie d'outil portatif…. Cette automatisation va aussi me permettre d'aller plus loin & d'envisagé d'installé un petit chauffe eau électrique de camion (~10L) ou autres… Grâce à [PvMonit](https://david.mercereau.info/pvmonit-v1-0-monitoring-de-mon-installation-photovoltaique-autonome/) j'avais déjà une remonté d'information sur l'état du système solaire, des batteries, de la production qui m'arrivait sur un Raspbery PI. il ne me restait plus qu'a "piloter des prises électrique" en fonction de l'état du système solaire et de conditions que je donne au programme. Le cahier des charges c'était : - De pouvoir piloter ce que je veux, mon choix c'est donc porté vers un système de contrôle de relais (en gros des interrupteur contrôlé de façon électronique) - Que le système consomme très peu. C'est réussi le système consomme ~0,153W (tout les relais d'éteint), 0,4W avec 1 relais d'allumé (hors PvMonit…) - Que je puisse passé certain appareil en "marche forcé" ou en "stop forcé" - Que le système soit résilient, qu'il puisse encore fonctionné sans l'apport d'information du raspbery pi en cas de panne ## Câblage / Matériel Matériel : - Le raspbery pi (zéro ça suffit) sur lequel est installé PvMonit (expliqué [ici](https://david.mercereau.info/pvmonit-v1-0-monitoring-de-mon-installation-photovoltaique-autonome/)) - Un [arduino](https://fr.wikipedia.org/wiki/Arduino) UNO qui reçois de potentiel ordre du Raspbery PI avec le protocole i2c. (6€) - [Un afficheur 8 chiffres + 8 leds + 8 boutons (tm1638)](https://os.mbed.com/components/TM1638-LED-controller-80-LEDs-max-Keyboa/) nous permet d'interagire avec le système (forcé l'alumage, interdir l'allumage…) (~6€) - Une plaque de 8 relais (mais vous pouvez envisagez en avoir autant que vous voulez… ça correspond à mon besoin…) qui allume tel ou tel appareil pour (9€) https://david.mercereau.info/wp-content/uploads/2019/11/PvMonit-Domo_bb.png ## Arduino Avec l'Arduino IDE, uploader le firmware "arduinoDomoRelayi2c.ino" contenu dans le dossier "firmware" ## Installation Dépendance du script : ```bash aptitude install python3 python3-yaml pip3 pip3 install lxml pysqlite3 json smbus2 wget ``` Lancement du script à la main ```bash cd /opt/PvMonit/domo/ python3 domo.py ``` Pour le lancement au démarrage, ajouter avant "exit 0" dans le fichier /etc/rc.local la ligne suivant ```bash screen -A -m -d -S domo /opt/PvMonit/domo/domo-launch.sh ``` ## Configuration La configuration ce fait dans le même fichier que pvmonit /opt/PvMonit/config.yaml, vous avez une secion "domo". Ensuite il vous faut faire les scripts qui correspondent à vos usages, c'est en Python que ça ce joue. Les scripts sont contenu dans /opt/PvMonit/domo/relay.script.d/NUMEROduRELAIS.py ## Explication Les interupteur on 3 modes : - Off forcé - Mode Automatique (uniquement possible si le PI est présent) - On forcé L'affichange des 8 segments change quand le PI est présent et que le script domo.py est lancé. -Les leds au dessus des 8 segments sont synchronisé avec l'état des relaisre \ No newline at end of file +Les leds au dessus des 8 segments sont synchronisé avec l'état des relaisre + +------------------ + +Max 16 relay (contrainte 2x tm1638 diff --git a/domo/domo.php b/domo/domo.php new file mode 100644 index 0000000..3c23edb --- /dev/null +++ b/domo/domo.php @@ -0,0 +1,134 @@ += $HEARTBEAT_FREQ) { + $serial->sendMessage("H \n"); + trucAdir(5, 'Heardbeat !'); + $heartBeatCount=0; + } else { + $heartBeatCount++; + } + + // + // Lecture des données + // + $read = $serial->readPort(); + if ($read) { + + $serialDatas=explode("\n",$read); + + foreach ($serialDatas as $serialData) { + if (preg_match('/^ARDOMO DEBUG/', $serialData)) { + trucAdir(5, $serialData); + } else if (preg_match('/^ARDOMO/', $serialData)) { + trucAdir(2, $serialData); + } else if (preg_match('/^E\|/', $serialData)) { + trucAdir(5, "Seri2 : $serialData"); + $serialDataExplode = explode("|", $serialData); + // Vérification du nombre d'information (sinon c'est une erreur) + if (count($serialDataExplode)-1 == $NBRELAY) { + trucAdir(3, "Etat des relays : $serialData"); + $i=0; + foreach($serialDataExplode as $relayEtatValue) { + if ($relayEtatValue != "E") { + $relayEtatJustValue = explode(":", $relayEtatValue); + $relayEtat[$i]=$relayEtatJustValue[1]; + $i++; + } + } + } + } else if (preg_match('/^M\|/', $serialData)) { + trucAdir(5, "Seri3 : $serialData"); + $serialDataExplode = explode("|", $serialData); + // Vérification du nombre d'information (sinon c'est une erreur) + if (count($serialDataExplode)-1 == $NBRELAY) { + trucAdir(3, "Mode des relays : $serialData"); + $i=0; + foreach($serialDataExplode as $relayModValue) { + if ($relayModValue != "M") { + $relayModJustValue = explode(":", $relayModValue); + $relayMod[$i]=$relayModJustValue[1]; + $i++; + } + } + } + /*} else { + trucAdir(5, "Serial ? : $serialData");*/ + } + } + + } + // + // Traitement, ordre... + // + if ($relay_script_last_exec+$GLOBALS['RELAY_SCRIPT_EXEC_INTERVAL'] < time()) { + trucAdir(3, "Traitement des ordres"); + if (isset($relayMod) && isset($relayEtat)) { + foreach ($relayMod as $relay => $Mod) { + // On s'occupe uniquement de ceux qui sont en mode auto + if ($Mod == 2) { + if (is_file($GLOBALS['RELAY_SCRIPT_DIR'].$relay.'.php')) { + $r['etat']=$relayEtat[$relay]; + $r['id']=$relay; + $d=$xml_data_get; + $script_return = (include $GLOBALS['RELAY_SCRIPT_DIR'].$relay.'.php'); + if ($relayEtat[$relay] != $script_return) { + trucAdir(3, "Changement d'état pour le relay ".$relay." (vers ".$script_return.")"); + $serial->sendMessage('RO:'.$relay.'='.$script_return."\n"); + } else { + trucAdir(3, "Aucun changement d'état pour le relay ".$relay); + } + } + } + } + } else { + trucAdir(3, "Aucune donnée sur les relay exploitable pour pouvoir lancer des actions"); + } + $relay_script_last_exec=time(); + } + sleep(0.5); + } else { + trucAdir(1, 'Trop d\'erreur ('.$xml_check_error.') sur le xml, on stop le heartbeat.'); + sleep(10); + } +} + +?> diff --git a/domo/relay-actions-launch.sh b/domo/relay-actions-launch.sh new file mode 100755 index 0000000..67ef06d --- /dev/null +++ b/domo/relay-actions-launch.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +while (true); do + su - pvmonit -c '/usr/bin/php /opt/PvMonit/domo/relay-actions.php' + sleep 10 +done diff --git a/domo/relay-actions.php b/domo/relay-actions.php new file mode 100644 index 0000000..5a348eb --- /dev/null +++ b/domo/relay-actions.php @@ -0,0 +1,98 @@ + $lastRefreshMod) { + trucAdir(4, 'Changement détecté'); + + $relay_mods = json_decode(file_get_contents($config['domo']['jsonFile']['modPath']), true); + $lastRefreshMod=time(); + + foreach ($relay_mods as $id_relay => $relay_mod) { + if ($relay_mod == 2 || $relay_mod == 3) { + if (etatRelayRead($id_relay) == $etatDown) { + trucAdir(2, 'On allume le relay '.$id_relay); + etatRelayWrite($id_relay, $etatUp); + } + } else { + if (etatRelayRead($id_relay) == $etatUp) { + trucAdir(2, 'On éteind le relay '.$id_relay); + etatRelayWrite($id_relay, $etatDown); + } + } + } + foreach ($relay_mods as $id_relay => $relay_mod) { + if ($premierLancement==true){ + trucAdir(1, 'Premier lancement : '.$id_relay); + $cmd = $config['domo']['binGpio'].' mode '.$config['domo']['relayWiringPi'][$id_relay].' out'; + trucAdir(5, 'cmd : '.$cmd); + exec($cmd, $output, $return_var); + } + } + $premierLancement=false; + + + } + + sleep($config['domo']['relayActionRefresh']); +} + +?> diff --git a/domo/tm1638-launch.sh b/domo/tm1638-launch.sh new file mode 100755 index 0000000..b459b29 --- /dev/null +++ b/domo/tm1638-launch.sh @@ -0,0 +1,5 @@ +#!/bin/bash +while (true); do + python3 /opt/PvMonit/domo/tm1638.py + sleep 10 +done diff --git a/domo/tm1638.py b/domo/tm1638.py new file mode 100644 index 0000000..f9a010a --- /dev/null +++ b/domo/tm1638.py @@ -0,0 +1,212 @@ +# coding=utf-8 +from rpi_TM1638 import TMBoards +# https://github.com/thilaire/rpi-TM1638 +import json +import yaml +import sys +import time +import os +import hashlib +from pwd import getpwnam +import grp + +def file_as_bytes(file): + with file: + return file.read() + + +# my GPIO settings (two TM1638 boards connected on GPIO19 and GPIO13 for DataIO and Clock; and on GPIO06 and GPIO26 for the STB) +DIO = 19 +CLK = 13 +STB = 26, # S'il y a plusieurs TM1638 à la suite, vous pouvez les indiquer après la virgules + +### aptitude install python-yaml + +## for debug : +import pprint + +with open('/opt/PvMonit/config-default.yaml') as f1: + config = yaml.load(f1, Loader=yaml.FullLoader) +with open('/opt/PvMonit/config.yaml') as f2: + config_perso = yaml.load(f2, Loader=yaml.FullLoader) + +TM = TMBoards(DIO, CLK, STB, 0) + +TM.clearDisplay() + +def configGet(key1, key2=None, key3=None, key4=None): + if key4 != None: + try: + return config_perso[key1][key2][key3][key4] + except: + return config[key1][key2][key3][key4] + elif key3 != None: + try: + return config_perso[key1][key2][key3] + except: + return config[key1][key2][key3] + elif key2 != None: + try: + return config_perso[key1][key2] + except: + return config[key1][key2] + else: + try: + return config_perso[key1] + except: + return config[key1] + +def ledAction(idRelay, action): + idRelay=idRelay-1 + TM.leds[idRelay] = action + +def modChangeTo(idRelay, mod): + idRelay=idRelay-1 + TM.segments[idRelay] = ' ' + if (mod == 0): + TM.segments[idRelay,3] = True + elif (mod == 3): + TM.segments[idRelay,0] = True + +# Function for log +def logMsg(level, msg): + if level <= configGet('printMessage') : + print(time.strftime ('%m/%d/%Y %H:%M') + " - " + str(msg)) + return -1 + +def waitLcd(timeSleep): + wait=0.2 + nombreDeTour=timeSleep/wait + etat=0 + for i in range(int(nombreDeTour)): + if (etat == 0): + TM.segments[7,7] = True + etat = 1 + else: + TM.segments[7,7] = False + etat = 0 + time.sleep(wait) + TM.segments[7,7] = False + + +logMsg(3, "Lancement du script ") + + +TM.segments[0] = 'boot' + +for i in range(configGet('domo', 'relayNb')): + TM.leds[i] = True + +######## waitLcd(3) + +def refreshEtat(): + logMsg(3, "Rafraichissement de l'état") + with open(configGet('domo', 'jsonFile', 'etatPath')) as f3: + JsonEtatRelay = json.load(f3) + pprint.pprint(JsonEtatRelay); + idRelay=1 + for etat in JsonEtatRelay: + if (JsonEtatRelay[etat] == 1): + logMsg(2, "UP relay " + str(idRelay+1)) + ledAction(idRelay, True) + else: + logMsg(2, "DOWN relay " + str(idRelay)) + ledAction(idRelay, False) + idRelay=idRelay+1 + return t + +def refreshMod(JsonModRelay): + logMsg(3, "Rafraichissement des mods") + idRelay=1 + for mod in JsonModRelay: + logMsg(5, "Mod relay " + str(idRelay) + " = " + str(JsonModRelay[mod])) + modChangeTo(idRelay, JsonModRelay[mod]) + idRelay=idRelay+1 + return t + +def genDefaultJsonFile(): + logMsg(1, "Init des fichiers json") + data={} + for i in range(configGet('domo', 'relayNb')): + data[str(i+1)] = 1 + with open(configGet('domo', 'jsonFile', 'etatPath'), 'w') as etatFile: + json.dump(data, etatFile) + os.chown(configGet('domo', 'jsonFile', 'etatPath'), getpwnam('pvmonit').pw_uid, grp.getgrnam('pvmonit')[2]) + with open(configGet('domo', 'jsonFile', 'modPath'), 'w') as modFile: + json.dump(data, modFile) + os.chown(configGet('domo', 'jsonFile', 'etatPath'), getpwnam('pvmonit').pw_uid, grp.getgrnam('pvmonit')[2]) + waitLcd(2) + +# Init des json si inexistant +if (not os.path.isfile(configGet('domo', 'jsonFile', 'modPath')) or not os.path.isfile(configGet('domo', 'jsonFile', 'etatPath'))): + genDefaultJsonFile() + +t=int(time.time()) +lastRefreshEtat = refreshEtat() +lastHashEtat = hashlib.md5(file_as_bytes(open(configGet('domo', 'jsonFile', 'etatPath'), 'rb'))).hexdigest() +with open(configGet('domo', 'jsonFile', 'modPath')) as f4: + JsonModRelay = json.load(f4) +lastRefreshMod = refreshMod(JsonModRelay) +lastHashMod = hashlib.md5(file_as_bytes(open(configGet('domo', 'jsonFile', 'modPath'), 'rb'))).hexdigest() + + +logMsg(3, "Début de la boucle") +JsonModRelayChange=False; +while True: + t=int(time.time()) + + # Etat + if (os.path.getctime(configGet('domo', 'jsonFile', 'etatPath')) > lastRefreshEtat): + logMsg(5, "Fichier etat modifié") + lastRefreshEtat=t + if (lastHashEtat != hashlib.md5(file_as_bytes(open(configGet('domo', 'jsonFile', 'etatPath'), 'rb'))).hexdigest()): + logMsg(4, "Fichier etat est différent (hash md5)") + lastRefreshEtat = refreshEtat() + lastHashEtat = hashlib.md5(file_as_bytes(open(configGet('domo', 'jsonFile', 'etatPath'), 'rb'))).hexdigest() + waitLcd(1) + + # Mod + if (os.path.getctime(configGet('domo', 'jsonFile', 'modPath')) > lastRefreshMod): + logMsg(5, "Fichier mod modifié") + lastRefreshMod=t + if (lastHashMod != hashlib.md5(file_as_bytes(open(configGet('domo', 'jsonFile', 'modPath'), 'rb'))).hexdigest()): + logMsg(4, "Fichier mod est différent (hash md5)") + with open(configGet('domo', 'jsonFile', 'modPath')) as f4: + JsonModRelay = json.load(f4) + lastRefreshMod = refreshMod(JsonModRelay) + lastHashMod = hashlib.md5(file_as_bytes(open(configGet('domo', 'jsonFile', 'modPath'), 'rb'))).hexdigest() + waitLcd(1) + + # Gestion boutton (en attente de la lib ?) + bouttonArray =TM.getData(0) + idButton=0 + for etatBoutton in bouttonArray: + if (etatBoutton != 0): + if (etatBoutton == 16): + idButtonReal=idButton+4+1 + else: + idButtonReal=idButton+1 + # Etat Suivant : + modNow=JsonModRelay[str(idButtonReal)] + if modNow == 0: + modNext=1 + elif modNow == 3: + modNext=0 + else: + modNext=3 + logMsg(2, "Boutton relay " + str(idButtonReal) + " pressé pour un passage sur le mod " + str(modNext)) + modChangeTo(idButtonReal, modNext) + JsonModRelay[str(idButtonReal)] = modNext + JsonModRelayChange=t+configGet('domo', 'tm1638', 'actionButtonDelay'); + idButton=idButton+1 + + if (JsonModRelayChange != False and JsonModRelayChange < t): + logMsg(2, "Enregistrement / prise en compte des changements de mod") + with open(configGet('domo', 'jsonFile', 'modPath'), 'w') as modFile: + json.dump(JsonModRelay, modFile) + os.chown(configGet('domo', 'jsonFile', 'etatPath'), getpwnam('pvmonit').pw_uid, grp.getgrnam('pvmonit')[2]) + waitLcd(2) + JsonModRelayChange=False + + time.sleep(configGet('domo', 'tm1638', 'refresh')) + diff --git a/function.php b/function.php index 67c396d..34aa269 100644 --- a/function.php +++ b/function.php @@ -1,781 +1,800 @@ $perso1) { if ($key1 == 'deviceCorrespondance') { $config[$key1]=$perso1; } elseif (is_array($perso1)) { foreach($perso1 as $key2=>$perso2) { if (is_array($perso2)) { foreach($perso2 as $key3=>$perso3) { if (isset($config[$key1][$key2][$key3])) { $config[$key1][$key2][$key3]=$perso3; } } }elseif (isset($config[$key1][$key2])) { $config[$key1][$key2]=$perso2; } } } elseif (isset($config[$key1])) { $config[$key1]=$perso1; } } return $config; } # Victron : détermine le type d'appareil # Source doc Victron "VE.Direct Protocol" function ve_type($ve_pid) { if (substr($ve_pid, 0, -1) == '0x20') { $ve_type_retour='BMV'; } else if (substr($ve_pid, 0, -2) == '0xA0' || $ve_pid == '0x300') { $ve_type_retour='MPTT'; } else if (substr($ve_pid, 0, -2) == '0xA2') { $ve_type_retour='PhoenixInverter'; } else { $ve_type_retour='Inconnu'; } return $ve_type_retour; } # Victron : détermine le modèle de l'appareil # Source doc Victron "VE.Direct Protocol" function ve_modele($ve_pid) { switch ($ve_pid) { case '0x203': $ve_modele_retour='BMV-700'; break; case '0x204': $ve_modele_retour='BMV-702'; break; case '0x205': $ve_modele_retour='BMV-700H'; break; case '0xA381': $ve_modele_retour='BMV-712 Smart'; break; case '0xA04C': $ve_modele_retour='BlueSolar MPPT 75/10'; break; case '0x300': $ve_modele_retour='BlueSolar MPPT 70/15'; break; case '0xA042': $ve_modele_retour='BlueSolar MPPT 75/15'; break; case '0xA043': $ve_modele_retour='BlueSolar MPPT 100/15'; break; case '0xA044': $ve_modele_retour='BlueSolar MPPT 100/30 rev1'; break; case '0xA04A': $ve_modele_retour='BlueSolar MPPT 100/30 rev2'; break; case '0xA041': $ve_modele_retour='BlueSolar MPPT 150/35 rev1'; break; case '0xA04B': $ve_modele_retour='BlueSolar MPPT 150/35 rev2'; break; case '0xA04D': $ve_modele_retour='BlueSolar MPPT 150/45'; break; case '0xA040': $ve_modele_retour='BlueSolar MPPT 75/50'; break; case '0xA045': $ve_modele_retour='BlueSolar MPPT 100/50 rev1'; break; case '0xA049': $ve_modele_retour='BlueSolar MPPT 100/50 rev2'; break; case '0xA04E': $ve_modele_retour='BlueSolar MPPT 150/60'; break; case '0xA046': $ve_modele_retour='BlueSolar MPPT 150/70'; break; case '0xA04F': $ve_modele_retour='BlueSolar MPPT 150/85'; break; case '0xA047': $ve_modele_retour='BlueSolar MPPT 150/100'; break; case '0xA050': $ve_modele_retour='SmartSolar MPPT 250/100'; break; case '0xA051': $ve_modele_retour='SmartSolar MPPT 150/100'; break; case '0xA052': $ve_modele_retour='SmartSolar MPPT 150/85'; break; case '0xA053': $ve_modele_retour='SmartSolar MPPT 75/15'; break; case '0xA054': $ve_modele_retour='SmartSolar MPPT 75/10'; break; case '0xA055': $ve_modele_retour='SmartSolar MPPT 100/15'; break; case '0xA056': $ve_modele_retour='SmartSolar MPPT 100/30'; break; case '0xA057': $ve_modele_retour='SmartSolar MPPT 100/50'; break; case '0xA058': $ve_modele_retour='SmartSolar MPPT 150/35'; break; case '0xA059': $ve_modele_retour='SmartSolar MPPT 150/100 rev2'; break; case '0xA05A': $ve_modele_retour='SmartSolar MPPT 150/85 rev2'; break; case '0xA05B': $ve_modele_retour='SmartSolar MPPT 250/70'; break; case '0xA05C': $ve_modele_retour='SmartSolar MPPT 250/85'; break; case '0xA05D': $ve_modele_retour='SmartSolar MPPT 250/60'; break; case '0xA05E': $ve_modele_retour='SmartSolar MPPT 250/45'; break; case '0xA05F': $ve_modele_retour='SmartSolar MPPT 100/20'; break; case '0xA060': $ve_modele_retour='SmartSolar MPPT 100/20 48V'; break; case '0xA061': $ve_modele_retour='SmartSolar MPPT 150/45'; break; case '0xA062': $ve_modele_retour='SmartSolar MPPT 150/60'; break; case '0xA063': $ve_modele_retour='SmartSolar MPPT 150/70'; break; case '0xA064': $ve_modele_retour='SmartSolar MPPT 250/85 rev2'; break; case '0xA065': $ve_modele_retour='SmartSolar MPPT 250/100 rev2'; break; case '0xA201': $ve_modele_retour='Phoenix Inverter 12V 250VA 230V'; break; case '0xA202': $ve_modele_retour='Phoenix Inverter 24V 250VA 230V'; break; case '0xA204': $ve_modele_retour='Phoenix Inverter 48V 250VA 230V'; break; case '0xA211': $ve_modele_retour='Phoenix Inverter 12V 375VA 230V'; break; case '0xA212': $ve_modele_retour='Phoenix Inverter 24V 375VA 230V'; break; case '0xA214': $ve_modele_retour='Phoenix Inverter 48V 375VA 230V'; break; case '0xA221': $ve_modele_retour='Phoenix Inverter 12V 500VA 230V'; break; case '0xA222': $ve_modele_retour='Phoenix Inverter 24V 500VA 230V'; break; case '0xA224': $ve_modele_retour='Phoenix Inverter 48V 500VA 230V'; break; case '0xA231': $ve_modele_retour='Phoenix Inverter 12V 250VA 230V'; break; case '0xA232': $ve_modele_retour='Phoenix Inverter 24V 250VA 230V'; break; case '0xA234': $ve_modele_retour='Phoenix Inverter 48V 250VA 230V'; break; case '0xA239': $ve_modele_retour='Phoenix Inverter 12V 250VA 120V'; break; case '0xA23A': $ve_modele_retour='Phoenix Inverter 24V 250VA 120V'; break; case '0xA23C': $ve_modele_retour='Phoenix Inverter 48V 250VA 120V'; break; case '0xA241': $ve_modele_retour='Phoenix Inverter 12V 375VA 230V'; break; case '0xA242': $ve_modele_retour='Phoenix Inverter 24V 375VA 230V'; break; case '0xA244': $ve_modele_retour='Phoenix Inverter 48V 375VA 230V'; break; case '0xA249': $ve_modele_retour='Phoenix Inverter 12V 375VA 120V'; break; case '0xA24A': $ve_modele_retour='Phoenix Inverter 24V 375VA 120V'; break; case '0xA24C': $ve_modele_retour='Phoenix Inverter 48V 375VA 120V'; break; case '0xA251': $ve_modele_retour='Phoenix Inverter 12V 500VA 230V'; break; case '0xA252': $ve_modele_retour='Phoenix Inverter 24V 500VA 230V'; break; case '0xA254': $ve_modele_retour='Phoenix Inverter 48V 500VA 230V'; break; case '0xA259': $ve_modele_retour='Phoenix Inverter 12V 500VA 120V'; break; case '0xA25A': $ve_modele_retour='Phoenix Inverter 24V 500VA 120V'; break; case '0xA25C': $ve_modele_retour='Phoenix Inverter 48V 500VA 120V'; break; case '0xA261': $ve_modele_retour='Phoenix Inverter 12V 800VA 230V'; break; case '0xA262': $ve_modele_retour='Phoenix Inverter 24V 800VA 230V'; break; case '0xA264': $ve_modele_retour='Phoenix Inverter 48V 800VA 230V'; break; case '0xA269': $ve_modele_retour='Phoenix Inverter 12V 800VA 120V'; break; case '0xA26A': $ve_modele_retour='Phoenix Inverter 24V 800VA 120V'; break; case '0xA26C': $ve_modele_retour='Phoenix Inverter 48V 800VA 120V'; break; case '0xA271': $ve_modele_retour='Phoenix Inverter 12V 1200VA 230V'; break; case '0xA272': $ve_modele_retour='Phoenix Inverter 24V 1200VA 230V'; break; case '0xA274': $ve_modele_retour='Phoenix Inverter 48V 1200VA 230V'; break; case '0xA279': $ve_modele_retour='Phoenix Inverter 12V 1200VA 120V'; break; case '0xA27A': $ve_modele_retour='Phoenix Inverter 24V 1200VA 120V'; break; case '0xA27C': $ve_modele_retour='Phoenix Inverter 48V 1200VA 120V'; break; default; $ve_modele_retour = 'Inconnu'; break; } return $ve_modele_retour; } # Victron : détermine plein de trucs en fonction du label # Source doc Victron "VE.Direct Protocol" function ve_label2($label, $valeur) { global $config; $veData['label']=$label; $veData['desc']=$label; $veData['value']=$valeur; $veData['units']=''; $veData['screen']=0; $veData['smallScreen']=0; if (in_array($label, $config['www']['dataPrimaire'])) { $veData['screen']=1; } if (in_array($label, $config['www']['dataPrimaireSmallScreen'])) { $veData['smallScreen']=1; } switch ($label) { case 'V': $veData['value']=round($valeur*0.001, 2); $veData['desc']='Tension de la batterie'; $veData['units']='V'; break; case 'VS': $veData['desc']='Auxiliary (starter) voltage'; $veData['value']=$valeur*0.001; $veData['units']='V'; break; case 'VM': $veData['desc']='Mid-point voltage of the battery bank'; $veData['value']=$valeur*0.001; $veData['units']='V'; break; case 'DM': $veData['desc']='Mid-point deviation of the battery bank'; $veData['units']='%'; break; case 'VPV': $veData['desc']='Voltage des panneaux'; $veData['units']='mV'; break; case 'PPV': $veData['desc']='Production des panneaux'; $veData['descShort']='PV'; $veData['units']='W'; break; case 'I': $veData['value']=$valeur*0.001; $veData['desc']='Courant de la batterie'; $veData['units']='A'; break; case 'IL': $veData['value']=$valeur*0.001; $veData['desc']='Courant de charge'; $veData['units']='A'; break; case 'LOAD': $veData['value']=$valeur; $veData['desc']='Charge sortie status'; break; case 'T': $veData['desc']='Température de la batterie'; $veData['units']='°C'; break; case 'P': $veData['desc']='Puissance instantané'; $veData['units']='W'; break; case 'CE': $veData['desc']='Ampères heures consommées'; $veData['value']=$valeur*0.001; $veData['units']='Ah'; break; case 'SOC': $veData['desc']='État de charge'; $veData['value']=$valeur/10; $veData['units']='%'; break; case 'TTG': if ($veData['value'] == '-1') { $veData['value'] = '∞'; } else { $total=$veData['value']*60; $jours=floor($total/86400); $reste=$total%86400; $heures=floor($reste/3600); $reste=$reste%3600; $minutes=floor($reste/60); $secondes=$reste%60; if ($veData['value'] > 1440) { $veData['value'] = $jours . 'j '. $heures. 'h ' . $minutes .'m'; } else { $veData['value'] = '.'.$heures. 'h ' . $minutes .'m'; } } $veData['desc']='Temps restant'; break; case 'Alarm': $veData['desc']='Condition d\'alarme active'; break; case 'Relay': $veData['desc']='Etat du relai'; break; case 'AR': $veData['desc']='Raison de l\'alarme'; switch ($veData['value']) { case 0: $veData['value']= 'Aucune'; break; case 1: $veData['value']= 'Low Voltage'; break; case 2: $veData['value']= 'High Voltage'; break; case 4: $veData['value']= 'Low SOC'; break; case 8: $veData['value']= 'Low Starter Voltage'; break; case 16: $veData['value']= 'High Starter Voltage'; break; case 32: $veData['value']= 'Low Temperature'; break; case 64: $veData['value']= 'High Temperature'; break; case 128: $veData['value']= 'Mid Voltage'; break; case 256: $veData['value']= 'Overload'; break; case 512: $veData['value']= 'DC-ripple'; break; case 1024: $veData['value']= 'Low V AC out'; break; case 2048: $veData['value']= 'High V AC out'; break; } break; case 'H1': $veData['desc']='Profondeur de la décharge la plus profonde'; $veData['value']=$valeur*0.001; $veData['units']='Ah'; break; case 'H2': $veData['desc']='Profondeur de la dernière décharge'; $veData['value']=$valeur*0.001; $veData['units']='Ah'; break; case 'H3': $veData['desc']='Profondeur de la décharge moyenne'; $veData['value']=$valeur*0.001; $veData['units']='Ah'; break; case 'H4': $veData['desc']='Nombre de cycles de charge'; break; case 'H5': $veData['desc']='Nombre de cycles de décharge'; break; case 'H6': $veData['desc']='Cumulative Amp Hours drawn'; $veData['value']=$valeur*0.001; $veData['units']='Ah'; break; case 'H7': $veData['desc']='Tension minimale batterie'; $veData['value']=$valeur*0.001; $veData['units']='V'; break; case 'H8': $veData['desc']='Tension maximale de la batterie'; $veData['value']=$valeur*0.001; $veData['units']='V'; break; case 'H9': $veData['desc']='Nombre de secondes depuis la dernière charge complète'; $veData['units']='s'; break; case 'H10': $veData['desc']='Nombre de synchronisations automatiques'; break; case 'H11': $veData['desc']='Nombre d\'alarmes de tension faible'; break; case 'H12': $veData['desc']='Nombre d\'alarmes de tension élevée'; break; case 'H13': $veData['desc']='Nombre d\'alarmes de tension faible sur l`auxiliaire'; break; case 'H14': $veData['desc']='Nombre d\'alarmes de tension élevée sur l`auxiliaire'; break; case 'H15': $veData['desc']='Tension minimale batterie auxiliaire'; $veData['value']=$valeur*0.001; $veData['units']='V'; break; case 'H16': $veData['desc']='Tension maximale de la batterie auxiliaire'; $veData['value']=$valeur*0.001; $veData['units']='V'; break; case 'H17': $veData['desc']='Quantité d\'énergie déchargée'; $veData['value']=$valeur*0.01; $veData['units']='kWh'; break; case 'H18': $veData['desc']='Quantité d\'énergie chargée'; $veData['value']=$valeur*0.01; $veData['units']='kWh'; break; case 'H19': $veData['value']=$valeur*0.01; $veData['desc']='Le rendement total'; $veData['units']='kWh'; break; case 'H20': $veData['value']=$valeur*0.01; $veData['desc']='Rendement aujourd\'hui'; $veData['units']='kWh'; break; case 'H21': $veData['desc']='Puissance maximum ce jour'; $veData['units']='W'; break; case 'H22': $veData['value']=$valeur*0.01; $veData['desc']='Rendement hier'; $veData['units']='kWh'; break; case 'H23': $veData['desc']='Puissance maximum hier'; $veData['units']='W'; break; case 'ERR': $veData['desc']='Présence d\'erreur'; if ($valeur == 0) { $veData['value']='Aucune'; } else { switch ($veData['value']) { case 2: $veData['value'] = 'Battery voltage too high'; break; case 17: $veData['value'] = 'Charger temperature too high'; break; case 18: $veData['value'] = 'Charger over current'; break; case 19: $veData['value'] = 'Charger current reversed'; break; case 20: $veData['value'] = 'Bulk time limit exceeded'; break; case 21: $veData['value'] = 'Current sensor issue (sensor bias/sensor broken)'; break; case 26: $veData['value'] = 'Terminals overheated'; break; case 33: $veData['value'] = 'Input voltage too high (solar panel)'; break; case 34: $veData['value'] = 'Input current too high (solar panel)'; break; case 38: $veData['value'] = 'Input shutdown (due to excessive battery voltage)'; break; case 116: $veData['value'] = 'Factory calibration data lost'; break; case 117: $veData['value'] = 'Invalid/incompatible firmware'; break; case 119: $veData['value'] = 'User settings invalid'; break; default: $veData['value'] = $dataSplit[1]; break; } } break; case 'CS': $veData['desc']='Status de charge'; switch ($veData['value']) { case 0: $veData['value']= 'Off'; break; case 1: $veData['value']= 'Faible puissance'; break; case 2: $veData['value']= 'Fault'; break; case 3: $veData['value']= 'Bulk (en charge)'; break; case 4: $veData['value']= 'Absorption'; break; case 5: $veData['value']= 'Float (maintient la charge pleine)'; break; case 9: $veData['value']= 'On'; break; } break; case 'HSDS': $veData['desc']='Numéro de séquence du jour'; break; case 'MODE': $veData['desc']='Device mode'; switch ($veData['value']) { case 2: $veData['value']= 'Inverter'; break; case 4: $veData['value']= 'Off'; break; case 5: $veData['value']= 'Eco'; break; } break; case 'AC_OUT_V': $veData['value']=$valeur*0.01; $veData['desc']='AC output voltage'; $veData['units']='V'; break; case 'AC_OUT_I': $veData['desc']='AC output current'; $veData['value']=$valeur*0.1; $veData['units']='A'; break; case 'WARN': $veData['desc']='Warning reason'; break; } return $veData; } function ve_nom($ve_serial) { global $config; $ve_nom=$ve_serial; foreach ($config['deviceCorrespondance'] as $serialName => $nom) { if ($ve_serial == $serialName) { $ve_nom=$nom; } } return $ve_nom; } # Hack value function ve_value($label, $value) { switch ($label) { case 'Relay': if ($value == 'ON') { $retour = 1; } else { $retour = 0; } break; default; $retour = $value; break; } return $retour; } # Fonction vedirect MPTT / BMV function vedirect_scan() { global $config; trucAdir(4, 'Recherche de périphérique vedirect'); $idDevice=0; foreach (scandir('/dev') as $unDev) { if (substr($unDev, 0, 6) == 'ttyUSB') { trucAdir(4, 'Un périphérique TTY à été trouvé : '.$unDev); unset($vedirect_sortie); unset($vedirect_retour); exec($config['vedirect']['usb']['bin'].' /dev/'.$unDev, $vedirect_sortie, $vedirect_retour); if ($vedirect_retour != 0){ trucAdir(1, 'Erreur à l\'exécution du script '.VEDIRECT_BIN.' sur le '.$unDev); } else { // Pour gérer le BMV-600 $BMV600=false; $ve_nom=null; $ve_type='Inconnu'; $ve_modele='Inconnu'; foreach ($vedirect_sortie as $vedirect_ligne) { $vedirect_data = explode(':', $vedirect_ligne); switch ($vedirect_data[0]) { case 'PID': $ve_type=ve_type($vedirect_data[1]); $ve_modele=ve_modele($vedirect_data[1]); break; case 'SER#': $ve_serial=$vedirect_data[1]; $ve_nom=ve_nom($vedirect_data[1]); break; case 'BMV': $ve_type='BMV'; $ve_nom=str_replace(' ', '', $vedirect_data[1]); break; } } trucAdir(3, 'C\'est un '.$ve_type.', modèle "'.$ve_modele.'" du nom de '.$ve_nom); $vedirect_data_formate=''; foreach ($vedirect_sortie as $vedirect_ligne) { $vedirect_data = explode(':', $vedirect_ligne); switch ($ve_type) { case 'MPTT': if (in_array($vedirect_data[0], $config['vedirect']['data_ok']['mppt'])) { # éviter les doublons if (!stristr($vedirect_data_formate, $vedirect_data[0].':')) { trucAdir(5, 'Valeur trouvé : '.$vedirect_data[0].':'.$vedirect_data[1]); if ($vedirect_data_formate != '') { $vedirect_data_formate = $vedirect_data_formate.','; } $vedirect_data_formate = $vedirect_data_formate.$vedirect_data[0].':'.ve_value($vedirect_data[0], $vedirect_data[1]); } else { trucAdir(5, 'Doublon, on passe'); } } break; case 'BMV': if (in_array($vedirect_data[0], $config['vedirect']['data_ok']['bmv'])) { # éviter les doublons if (!stristr($vedirect_data_formate, $vedirect_data[0].':')) { trucAdir(5, 'Valeur trouvé : '.$vedirect_data[0].':'.$vedirect_data[1]); if ($vedirect_data_formate != '') { $vedirect_data_formate = $vedirect_data_formate.','; } $vedirect_data_formate = $vedirect_data_formate.$vedirect_data[0].':'.ve_value($vedirect_data[0], $vedirect_data[1]); } else { trucAdir(5, 'Doublon, on passe'); } } break; case 'PhoenixInverter': if (in_array($key, $config['vedirect']['data_ok']['phoenix'])) { # éviter les doublons if (!stristr($vedirect_data_formate, $vedirect_data[0].':')) { trucAdir(5, 'Valeur trouvé : '.$vedirect_data[0].':'.$vedirect_data[1]); if ($vedirect_data_formate != '') { $vedirect_data_formate = $vedirect_data_formate.','; } $vedirect_data_formate = $vedirect_data_formate.$key.':'.ve_value($vedirect_data[0], $vedirect_data[1]); } else { trucAdir(5, 'Doublon, on passe'); } } break; default: if ($vedirect_data_formate != '') { $vedirect_data_formate = $vedirect_data_formate.','; } $vedirect_data_formate = $vedirect_data_formate.$key.':'.ve_value($vedirect_data[0], $vedirect_data[1]); } } trucAdir(3, 'Les données sont formatées comme ceci : '.$vedirect_data_formate ); $vedirect_scan_return[$idDevice]['nom']=$ve_nom; $vedirect_scan_return[$idDevice]['type']=$ve_type; $vedirect_scan_return[$idDevice]['serial']=$ve_serial; $vedirect_scan_return[$idDevice]['modele']=$ve_modele; $vedirect_scan_return[$idDevice]['data']=$vedirect_data_formate; $idDevice++; } } } return $vedirect_scan_return; } function vedirect_parse_arduino($data) { global $config; // Pour gérer le BMV-600 $BMV600=false; $ve_nom=null; $ve_type='Inconnu'; $ve_modele='Inconnu'; $ve_serial='Inconnu'; foreach ($data as $key => $value) { switch ($key) { case 'PID': $ve_type=ve_type($value); $ve_modele=ve_modele($value); break; case 'SER#': $ve_serial=$value; $ve_nom=ve_nom($value); break; case 'BMV': $ve_type='BMV'; $ve_nom=$value; break; } } trucAdir(3, 'C\'est un '.$ve_type.', modèle "'.$ve_modele.'" du nom de '.$ve_nom); $vedirect_data_formate=''; krsort($data); foreach ($data as $key => $value) { switch ($ve_type) { case 'MPTT': if (in_array($key, $config['vedirect']['data_ok']['mppt'])) { # éviter les doublons if (!stristr($vedirect_data_formate, "$key:$value")) { trucAdir(5, 'Valeur trouvé : '.$key.':'.$value); if ($vedirect_data_formate != '') { $vedirect_data_formate = $vedirect_data_formate.','; } $vedirect_data_formate = $vedirect_data_formate.$key.':'.ve_value($key, $value); } else { trucAdir(5, 'Doublon, on passe'); } } break; case 'BMV': if (in_array($key, $config['vedirect']['data_ok']['bmv'])) { if ($vedirect_data_formate != '') { $vedirect_data_formate = $vedirect_data_formate.','; } $vedirect_data_formate = $vedirect_data_formate.$key.':'.ve_value($key, $value); } break; case 'PhoenixInverter': if (in_array($key, $config['vedirect']['data_ok']['phoenix'])) { if ($vedirect_data_formate != '') { $vedirect_data_formate = $vedirect_data_formate.','; } $vedirect_data_formate = $vedirect_data_formate.$key.':'.ve_value($key, $value); } break; default: if ($vedirect_data_formate != '') { $vedirect_data_formate = $vedirect_data_formate.','; } $vedirect_data_formate = $vedirect_data_formate.$key.':'.ve_value($key, $value); } } trucAdir(3, 'Les données sont formatées comme ceci : '.$vedirect_data_formate ); $vedirect_scan_return['nom']=$ve_nom; $vedirect_scan_return['type']=$ve_type; $vedirect_scan_return['serial']=$ve_serial; $vedirect_scan_return['modele']=$ve_modele; $vedirect_scan_return['data']=$vedirect_data_formate; return $vedirect_scan_return; } # Fonction de debug function trucAdir($niveau, $msg) { global $config; if ($config['printMessage'] >= $niveau) { if (isset($_SERVER['SERVER_NAME'])) { echo ''; } else { echo date('c') . ' - ' . $msg."\n"; } } if ($config['printMessageLogfile'] != false) { if (! is_file($config['printMessageLogfile'])) { touch($config['printMessageLogfile']); if (substr(sprintf('%o', fileperms($config['printMessageLogfile'])), -3) != '777') { chmod($config['printMessageLogfile'], 0777); } } file_put_contents($config['printMessageLogfile'], date('c') . ' - ' . $_SERVER['SCRIPT_NAME']. ' - ' . $msg . "\n", FILE_APPEND); } } # Récupérer les informations de la sonde de température function Temperature_USB($TEMPERV14_BIN) { global $config; # Exécussion du programme pour récupérer les inforamtions de la sonde de température exec($TEMPERV14_BIN, $temperv14_sortie, $temperv14_retour); if ($temperv14_retour != 0){ trucAdir(3, 'La sonde de température n\'est probablement pas connecté.'); trucAdir(5, 'Erreur '.$temperv14_retour.' à l\'exécussion du programme .'.$TEMPERV14_BIN); $temperature_retour='NODATA'; } else { trucAdir(4, 'La sonde de température indique '.$temperv14_sortie[0].'°C, il y aura peut être correction.'); $temperature_retour=$temperv14_sortie[0]; } return $temperature_retour; } function Amp_USB($bin) { global $config; $consommation_retour='NODATA'; for ($i = 1; $i <= 3; $i++) { trucAdir(3, 'Tentative '.$i.' de récupération de la sonde '); exec($bin.' | sed "s/A//" 2>/dev/null', $exec_consommation_sortie, $exec_consommation_retour); if ($exec_consommation_retour != 0){ trucAdir(3, 'L\'amphèrmètre n\'est probablement pas connecté.'); trucAdir(5, 'Erreur '.$exec_consommation_retour.' avec pour sortie .'.$exec_consommation_sortie); } else { if ($exec_consommation_sortie[0] != '') { trucAdir(3, 'Trouvé à la tentative '.$i.' : la La consommation trouvé est '.$exec_consommation_sortie[0].'A'); $re = '/^[0-9][0-9]+.[0-9]$/'; if (!preg_match_all($re, $exec_consommation_sortie[0])) { trucAdir(5, 'La vérification par expression régulière à échoué ('.$re.')'); } else { $conso_en_w=$exec_consommation_sortie[0]*230; trucAdir(1, 'La consommation est de '.$exec_consommation_sortie[0].'A soit '.$conso_en_w.'W'); if ($conso_en_w > $config['consoPlafond']) { trucAdir(1, 'C`est certainement une erreur, le plafond possible est atteind'); } else { $consommation_retour=$exec_consommation_sortie[0]; } } break; } else { trucAdir(5, 'Echec à la tentative '.$i.' : la La consommation trouvé est null'); sleep(1); } } } return $consommation_retour; } // Class source : http://abhinavsingh.com/how-to-use-locks-in-php-cron-jobs-to-avoid-cron-overlaps/ class cronHelper { private static $pid; function __construct() {} function __clone() {} private static function isrunning() { $pids = explode(PHP_EOL, `ps -e | awk '{print $1}'`); if(in_array(self::$pid, $pids)) return TRUE; return FALSE; } public static function lock() { global $config; global $argv; $lock_file = $config['emoncms']['lockFile']; if(file_exists($lock_file)) { //return FALSE; // Is running? self::$pid = file_get_contents($lock_file); if(self::isrunning()) { error_log("==".self::$pid."== Already in progress..."); return FALSE; } else { error_log("==".self::$pid."== Previous job died abruptly..."); } } self::$pid = getmypid(); file_put_contents($lock_file, self::$pid); //error_log("==".self::$pid."== Lock acquired, processing the job..."); return self::$pid; } public static function unlock() { global $argv; global $config; $lock_file = $config['emoncms']['lockFile']; if(file_exists($lock_file)) unlink($lock_file); //error_log("==".self::$pid."== Releasing lock..."); return TRUE; } } // Check cache expire function checkCacheTime($file) { global $config; if (!is_dir($config['cache']['dir'])) { mkdir($config['cache']['dir'], 0777); chmod($config['cache']['dir'], 0777); } if (!is_file($file)) { return false; } else if (filemtime($file)+$config['cache']['time'] < time()) { return false; } else if (isset($_GET['nocache'])) { return false; } else { return true; } } +# Domo gen json file +function genDefaultJsonFile($type) { + global $config; + if ($type == 'etat') { + $filePath = $config['domo']['jsonFile']['etatPath']; + } elseif ($type == 'mod') { + $filePath = $config['domo']['jsonFile']['modPath']; + } + $relay[] = null; + for ($i = 1; $i <= $config['domo']['relayNb']; $i++) { + if ($type == 'etat') { + $relay[$type][$i] = 0; + } else { + $relay[$type][$i] = 1; + } + } + file_put_contents($filePath, json_encode($relay[$type])); +} ?> diff --git a/getForEmoncms.php b/getForEmoncms.php index 232aac3..6ceb250 100755 --- a/getForEmoncms.php +++ b/getForEmoncms.php @@ -1,109 +1,128 @@ #!/usr/bin/php $device_data) { if (preg_match_all('/^Serial[0-9]$/m', $device_id)) { $device_vedirect_data[$idDevice]=vedirect_parse_arduino($device_data); $idDevice++; } } $vedirect_data_ready = $device_vedirect_data; } +$ppv_total=null; +$bmv_p=null; +$nb_ppv_total=0; foreach ($vedirect_data_ready as $device) { - if ($device['nom'] != '') { + if ($device['nom'] != '') { sauvegardeDesDonnes("www-browser --dump '".$config['emoncms']['urlInputJsonPost']."?json={".$device['data']."}&node=".$device['nom']."&time=".time()."&apikey=".$config['emoncms']['apiKey']."'\n"); } + foreach (explode(',', $device['data']) as $data) { + $dataSplit = explode(':', $data); + if ($dataSplit[0] == 'PPV'){ + $ppv_total=$ppv_total+$dataSplit[1]; + $nb_ppv_total++; + } + if ($device['type'] == "BMV" && $dataSplit[0] == 'P'){ + $bmv_p=$dataSplit[1]; + } + } } $dataNode1=null; // Scan du répertoire bin-enabled $bin_enabled_data = scandir($config['dir']['bin_enabled']); foreach ($bin_enabled_data as $bin_script_enabled) { $bin_script_info = pathinfo($config['dir']['bin_enabled'].'/'.$bin_script_enabled); if ($bin_script_info['extension'] == 'php') { trucAdir(3, "Le script ".$config['dir']['bin_enabled'].'/'.$bin_script_enabled." est appelé"); $filenameSplit = explode("-", $bin_script_info['filename']); $idParent=$filenameSplit[0]; $id=$filenameSplit[1]; $cache_file_script=$config['cache']['dir'].'/'.$config['cache']['file_prefix'].$bin_script_enabled; if(!checkCacheTime($cache_file_script)) { // Ménage foreach ($array_data as $i => $value) { unset($array_data[$i]); } $script_return = (include $config['dir']['bin_enabled'].'/'.$bin_script_enabled); file_put_contents($cache_file_script, json_encode($script_return)); chmod($cache_file_script, 0777); } $timerefresh=filemtime($cache_file_script); $script_return_datas = json_decode(file_get_contents($cache_file_script), true) ; #print_r($script_return_datas); foreach ($script_return_datas as $script_return_data) { if (isset($script_return_data['id'])) { $id_data=$script_return_data['id']; } else { $id_data=$id; } if (!is_null($dataNode1)) { $dataNode1=$dataNode1.','; } $dataNode1=$dataNode1.strtolower($id_data).':'.$script_return_data['value']; } } } +if ($config['data']['ppv_total'] && $config['data']['conso_calc'] && $ppv_total !== null && $bmv_p != null) { + $conso=$ppv_total-$bmv_p; + $dataNode1=$dataNode1.','.strtolower('CONSO').':'.abs($conso); +} + + if (!is_null($dataNode1)) { sauvegardeDesDonnes("www-browser --dump '".$config['emoncms']['urlInputJsonPost']."?json={".$dataNode1."}&node=1&time=".time()."&apikey=".$config['emoncms']['apiKey']."'\n"); } ?> diff --git a/www/css/style.css b/www/css/style.css index f6a6f45..3916f76 100644 --- a/www/css/style.css +++ b/www/css/style.css @@ -1,221 +1,285 @@ /* Generated by http://www.cssportal.com */ @import url("reset.css"); body { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 13px; color:#333; } p { padding: 10px; } #wrapper { margin: 0 auto; width: 1000px; } #headerwrap { width: 1000px; float: left; margin: 0 auto; } #header { color: #fff; background: #4d394b; border-radius: 10px; border: 1px solid #392537; margin: 5px; } #header a { color: #fff; } #header h1 { font-size: 130%; padding: 10px; } #header nav{ display:block; float:right; margin:10px 0 0 0; padding:20px 0; color:#ffa500; } #header nav ul{ padding:0 20px; } #header nav li{ display:inline; margin-right:25px; text-transform:uppercase; } #header nav li.last{ margin-right:0; } #header nav li a{ text-decoration:none; color:#ffa500; } #header nav li a:hover{ color:#fff; } #contentwrap { width: 1000; float: left; margin: 0 auto; } #content { background: #e5e4e0; border-radius: 10px; border: 1px solid #d1d0cc; margin: 5px; padding: 10px; } progress { background: #e5e4e0; border: none; width: 200px; } progress.jaugeRouge::-moz-progress-bar { background: #FF785B; } progress.jaugeOrange::-moz-progress-bar { background: #FFBB3E; } progress.jaugeVerte::-moz-progress-bar { background: #76D176; } progress.jaugeBleu::-moz-progress-bar { background: #8590F7; } .waitFirst { width: 100%; text-align:center; } .box { margin: 8px; width: 300px; background-color: #fff; border-radius: 10px 10px 0 0; float: left; } .box .plus { display: none; } .box .plusboutton, .box .moinsboutton { text-align: center; font-size: 80%; cursor: pointer; } .box .moinsboutton { display: none; } .box .title { border-radius: 10px 10px 0 0; background-color:#ffa500; padding: 5px; } .box .boxvaleur { padding: 5px; border-bottom: 1px solid #BCB7B7; color : #6B6B6B; } .box h3 { font-size: 80%; } .box .ppv, .box .ppvt { height: 28px; padding-left: 38px; background-image: url("../images/PPV.png"); background-position: left top; background-repeat: no-repeat; } .box .vbat { height: 28px; padding-left: 38px; background-image: url("../images/VBAT.png"); background-position: left top; background-repeat: no-repeat; } .box .cs, .box .soc { height: 28px; padding-left: 38px; background-image: url("../images/CS.png"); background-position: left top; background-repeat: no-repeat; } .box .err, .box .ar { height: 28px; padding-left: 38px; background-image: url("../images/ERR.png"); background-position: left top; background-repeat: no-repeat; } .souligner { color: red; font-weight: bold; } .box .temp { height: 28px; padding-left: 38px; background-image: url("../images/TEMP.png"); background-position: left top; background-repeat: no-repeat; } .box .conso { height: 28px; padding-left: 38px; background-image: url("../images/CONSO.png"); background-position: left top; background-repeat: no-repeat; } .box .boxvaleur { margin: 3px; } #footerwrap { width: 1000px; float: left; margin: 0 auto; clear: both; } #footer { color: #fff; background: #4d394b; border-radius: 10px; border: 1px solid #392537; margin: 5px; } #footer a { color: #fff; text-decoration:none; } .footer_right { float: right; } @media screen and (max-width: 1000px) { #headerwrap { width: 100%; } #wrapper { width: 100%; } #footerwrap { width: 100%; } } @media screen and (max-width: 600px) { .box .plusplus { display: none; } } + +#box_domo .boxvaleur{ + padding-bottom: 20px; + border-bottom: none; +} +.relayEtat { + margin: 5px; + padding: 5px; + border-radius: 20px; + border: 1px solid #AFAFAF; + width: 5%; + float: left; +} +.relayMod { + padding: 10px; + border-radius: 15px; + border: 1px solid #AFAFAF; + width: 75%; + float: right; +} +.relayModButtons { + float: right; +} +.relayChange { + padding: 5px; + border-radius: 15px; + border: 1px solid #AFAFAF; +} +.etatNull { + background-color: #C0C0C0; +} +.etat0{ + background-color: #FFFFFF; +} +.etat1{ + background-color: #FF9999; +} +.modNull{ + background-color: #C0C0C0; +} +.mod0{ + background-color: #FF9999; +} +.mod1{ + background-color: #FFC042; +} +.mod2{ + background-color: #FFC042; +} +.mod3{ + background-color: #7BDF7B; +} +.relayChange.mod0 { + color: #FF9999; + background-color: #FFFFFF; +} +.relayChange.mod1 { + color: #FFC042; + background-color: #FFFFFF; +} +.relayChange.mod3 { + color: #7BDF7B; + background-color: #FFFFFF; +} diff --git a/www/data-xml.php b/www/data-xml.php index 30f10a1..f7a0e46 100755 --- a/www/data-xml.php +++ b/www/data-xml.php @@ -1,192 +1,192 @@ $device_data) { if (preg_match_all('/^Serial[0-9]$/m', $device_id)) { $device_vedirect_data[$idDevice]=vedirect_parse_arduino($device_data); $idDevice++; } } $vedirect_data_ready = $device_vedirect_data; } } foreach ($vedirect_data_ready as $device) { if ($device['serial'] == 'Inconnu' || $device['serial'] == '') { $device['serial'] = $device['nom']; } echo "\n\t".''; echo "\n\t\t".''.$device['nom'].''; echo "\n\t\t".''.time().''; echo "\n\t\t".''.$device['type'].''; echo "\n\t\t".''.$device['modele'].''; echo "\n\t\t".''.$device['serial'].''; echo "\n\t\t".''; sort($device['data']); foreach (explode(',', $device['data']) as $data) { $dataSplit = explode(':', $data); $veData=ve_label2($dataSplit[0], $dataSplit[1]); echo "\n\t\t\t".''; echo "\n\t\t\t\t".''.$veData['desc'].''; echo "\n\t\t\t\t".''.$veData['value'].''; echo "\n\t\t\t\t".''.$veData['units'].''; echo "\n\t\t\t".''; if ($dataSplit[0] == 'PPV'){ $ppv_total=$ppv_total+$dataSplit[1]; $nb_ppv_total++; } if ($device['type'] == "BMV" && $dataSplit[0] == 'P'){ $bmv_p=$dataSplit[1]; } } echo "\n\t\t".''; echo "\n\t".''; } # Divers $bin_enabled_data = scandir($config['dir']['bin_enabled']); if(count($bin_enabled_data) > 2 || $config['data']['ppv_total'] || $config['data']['ppv_total']) { ?> Divers Production total des panneaux '.$ppv_total.' W '; } ?> Consommation du foyer - '.abs($test).' + '.abs($conso).' W '; } ?> $value) { unset($array_data[$i]); } $script_return = (include $config['dir']['bin_enabled'].'/'.$bin_script_enabled); file_put_contents($cache_file_script, json_encode($script_return)); if (substr(sprintf('%o', fileperms($cache_file)), -3) != '777') { chmod($cache_file, 0777); } } $timerefresh=filemtime($cache_file_script); $script_return_datas = json_decode(file_get_contents($cache_file_script), true) ; //trucAdir(4, print_r($script_return_datas)); echo "\n\t"; echo "\n\t\t"; echo "\n\t\t".$timerefresh.""; echo "\n\t\t"; echo "\n\t\t"; echo "\n\t\t"; echo "\n\t\t"; sort($script_return_datas); foreach ($script_return_datas as $script_return_data) { if (isset($script_return_data['id'])) { $id_data=$script_return_data['id']; } else { $id_data=$id; } echo "\n\t\t\t"; echo "\n\t\t\t\t".$script_return_data['desc'].""; echo "\n\t\t\t\t".$script_return_data['value'].""; echo "\n\t\t\t\t".$script_return_data['units'].""; echo "\n\t\t\t"; } echo "\n\t\t"; echo "\n\t"; } } } trucAdir(5, "Fin du data-xml.php"); ?> diff --git a/www/domo.php b/www/domo.php new file mode 100644 index 0000000..ac39b03 --- /dev/null +++ b/www/domo.php @@ -0,0 +1,40 @@ + + diff --git a/www/index.php b/www/index.php index 6800d0d..edb2cdd 100755 --- a/www/index.php +++ b/www/index.php @@ -1,333 +1,458 @@ Pv Monit
    +
    Patience...
    '; echo '
    Erreur
    '; echo '

    Heure du système :

    '; echo ''; echo 'incorrect, on ne collecte rien.'; echo ''; echo '
    '; echo '
    '; } ?> - - - + +