diff --git a/INSTALL.md b/INSTALL.md index 4bc63e7..dac2bcf 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,465 +1,466 @@ # Installation Deux type d'installation possible : - Une version Raspbery PI 3B, si vous avez un point wifi actif (même occasionnellement) et que votre matériel solaire est à porté de wifi. C'est une solution plutôt simple (si on touche un peu sous linux). ![Schéma de câblage PI3B et ve.direct USB officiel](https://david.mercereau.info/wp-content/uploads/2019/10/PvMonitV1_USB.png)- Une version Raspbery Pi 0 + Arduino : plus complexe à mettre en oeuvre (il faut savoir souder et avoir plus de connaissance) mais beaucoup plus souple et moins chère. Particulièrement adapté si votre installation réseau est loin (max 60m) de votre maison ![Schéma de câblage avec Pi0 et Arduino Mega (ve.direct Diy)](https://david.mercereau.info/wp-content/uploads/2019/10/PvMonitV1_Arduino.png) PvMonit support tout le matériel Victron compatible Ve Direct (via USB) : Les fonctionnalités de PvMonit sont dissociable : * Interface web en temps réel * Export vers emoncms * Affichage LCD + * Domotique (voir domo/README.md) pour déclencher des actions (allumer des appareils par exemple) suivant l'état du système #### La base / le socle Installation de PvMonit via le dépôt git et de ses dépendances : ```bash apt-get install php-cli php-yaml git python-serial sudo screen cd /opt git clone https://github.com/kepon85/PvMonit.git cd PvMonit cp config-default.yaml config.yaml ``` Vous pouvez maintenant éditer le fichier config.yaml à votre guise ! ### Ve.direct via USB Dans le fichier config.yaml mentionner : ```yaml vedirect: by: usb ``` Test du script vedirect.py : brancher un appareil Victron avec un Câble Ve.Direct USB et voici un exemple de ce que vous devriez obtenir (Ici un MPTT BlueSolare branché sur le ttyUS0) ``` $ /opt/PvMonit/bin/vedirect.py /dev/ttyUSB0 PID:0xA04A FW:119 SER#:HQ******** V:25660 I:500 VPV:53270 PPV:14 CS:3 ERR:0 LOAD:ON H19:3348 H20:1 H21:17 H22:33 H23:167 HSDS:52 ``` Pour comprendre chaque valeur, téléchargez la documentation *Victron VE Direct Protocol documentation* : https://www.victronenergy.fr/support-and-downloads/whitepapers ([disponible aussi à cet url](https://david.mercereau.info/wp-content/uploads/2019/10/VE.Direct-Protocol.pdf)) Lancer la commande : ```sh visudo ``` -Si vous utilisez l'interface web pvmonit, ajouter : - -```diff -+ www-data ALL=(ALL) NOPASSWD:/usr/bin/python /opt/PvMonit/bin/vedirect.py * -``` - -Si vous utilisez l'export vers emoncms, ajouter : +Ajouter : ```diff + pvmonit ALL=(ALL) NOPASSWD:/usr/bin/python /opt/PvMonit/bin/vedirect.py * ``` ### Ve.direct via Arduino Avec l'Arduino IDE, uploader le firmware "ArduinoMegaVeDirect.ino" contenu dans le dossier "firmware" Faites vos câble ve.direct avec les connecteur JST-PH. De la documentation à ce sujet : - https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable - http://www.svpartyoffive.com/2018/02/28/victron-monitors-technical/ - https://store.volts.ca/media/attachment/file/v/e/ve.direct-protocol-3.23.pdf - http://jeperez.com/connect-bmv-victron-computer/ - http://www.mjlorton.com/forum/index.php?topic=238.0 - BMV e 3,3V : https://github.com/winginitau/VictronVEDirectArduino - https://www.victronenergy.com/live/vedirect_protocol:faq#q4is_the_vedirect_interface_33_or_5v Conseil : utiliser des connecteur MOLEX (pratique pour que les câbles soit dé-connectable) : https://arduino103.blogspot.com/2013/07/connecteur-molex-comment-utiliser-le-kit.html Connecté l'arduino en série (utiliser 3 fils d'un câble téléphonie/RJ45) avec le raspbery pi comme sur le schéma ci-après : ![Schéma de câble Pi0-ArduinoMega](https://david.mercereau.info/wp-content/uploads/2019/10/PvMonit-1.0_bb.png) Sur le Raspbery pi, il faut que le port série soit actif ```bash raspi-config # Interfacing Option / P6 Serial / # Login shell : NO # Serial port harware enable : Yes reboot ``` Pour être sûr que cela fonctionne vous pouvez lancer la commande suivante sur le pi : ```bash screen /dev/ttyAMA0 4800 ``` Vous devriez obtenir quelque chose comme : ``` S:3_P -95 S:3_CE -39101 S:3_SOC 889 S:3_TTG 1597 S:3_Alarm OFF S:3_Relay OFF S:3_AR 0 S:3_BMV 700 S:3_FW 0308 S:3_Checksum 8 S:3_H1 -102738 S:3_H2 -45215 S:3_H3 -102738 S:3_H4 1 S:3_H5 0 S:3_H6 -21450007 S:3_H7 21238 S:3_H8 29442 S:3_H9 362593 S:3_H10 103 S:3_H11 0 S:3_H12 0 S:3_H17 53250 S:3_H18 62805 S:3_Checksum � S:3_PID 0x203 STOP ``` Dans le fichier config.yaml mentionner : ```yaml vedirect: by: arduino ``` ```bash apt-get install python3 python3-pip python3-yaml pip3 install pyserial ``` Ajouter dans le fichier /etc/rc.local :(avant le exit 0) ```bash screen -A -m -d -S arduino /opt/PvMonit/bin/getSerialArduino-launch.sh ``` Vous pouvez le lancer "à la main" avec la commande : ```bash python3 /opt/PvMonit/bin/getSerialArduino.py ``` Et vous assurez que le fichier /tmp/PvMonit_getSerialArduino.data.yaml existe bien et que les données sont justes. #### Interface web en temps réel Installation des dépendances : ```bash aptitude install lighttpd php-cgi php-xml php7.3-json lighttpd-enable-mod fastcgi lighttpd-enable-mod fastcgi-php ``` -Configuration du serveur http, avec le fichier /etc/lighttpd/lighttpd.conf : +On change la configuration de lighttpd : + +```bash +service lighttpd stop +``` + +Configuration du serveur http, avec le fichier /etc/lighttpd/lighttpd.conf car il faut modifier le document root et il faut que lighttpd se lance en tant que pvmonit (Pour le changement de user : [ici](https://alexanderhoughton.co.uk/blog/lighttpd-changing-default-user-raspberry-pi/) ou [ici](https://redmine.lighttpd.net/boards/2/topics/6247)) + ```diff - server.document-root = "/var/www/html/" +- server.username = "www-data" +- server.groupname = "www-data" + server.document-root = "/opt/PvMonit/www/" ++ server.username = "pvmonit" ++ server.groupname = "pvmonit" ``` -On applique la configuration : +Modifier le fichier /etc/init.d/lighttpd et remplacer tout "www-data" par "pvmonit" + +```diff +- owner=www-data +- group=www-data ++ owner=pvmonit ++ group=pvmonit +``` + +Changer les utilisateurs : + +``` +chown -R pvmonit:pvmonit /var/log/lighttpd +chown -R pvmonit:pvmonit /var/cache/lighttpd +chown pvmonit:pvmonit /var/run/lighttpd +``` + +On applique la configuration (si lighttpd ne démarre pas il faut creuser la question, regarder les logs...) : ```bash -service lighttpd restart +service lighttpd start ``` C'est terminé, vous pouvez vous connecter sur votre IP local pour joindre votre serveur web : Attention : dans la configuration l'appel du fichier data (urlDataXml) doit contenir un nom de domaine, quand vous joingné l'interface ce nom de domaine doit être identique à celui. Exemple vous ateignez l'interface par pv.chezmoi.fr, dans urlDataXml il doit y avoir urlDataXml: http://pv.chezmoi.fr/data-xml.php (modifier le fichier /etc/hosts au besoin...) ![Screenshot PvMonit](http://david.mercereau.info/wp-content/uploads/2016/11/PvMonit_Full.png) #### Export vers emoncms Connectez-vous à votre interface emoncms hébergée ou créez un compte sur [emoncms.org](https://emoncms.org/) et rendez-vous sur la page "Input api" https://emoncms.org/input/api : ![Screenshot input API emoncms](http://david.mercereau.info/wp-content/uploads/2016/11/Sélection_011.png) Récupérez la valeur "Accès en écriture" et ajoutez-la dans le fichier de configuration Pvmonit */opt/PvMonit/config.yaml* : ```yaml emoncms: urlInputJsonPost: https://emoncms.org/input/post.json apiKey: XXXXXXXXXXXXXXXXXXXXXXXX ``` Création d'un utilisateur dédié avec pouvoir restreint ```bash adduser --shell /bin/bash pvmonit ``` Installation des dépendances : ```bash aptitude install lynx ``` Test de collecte : ``` $ su - pvmonit -c /opt/PvMonit/getForEmoncms.php 2016-11-02T10:55:30+01:00 - C'est un MPTT, modèle "BlueSolar MPPT 100/30 rev2" du nom de MpttBleu 2016-11-02T10:55:30+01:00 - Les données sont formatées comme ceci : V:26180,I:800,VPV:56360,PPV:21,CS:3,ERR:0,H19:3352,H20:5,H21:51,H22:33,H23:167 2016-11-02T10:55:31+01:00 - C'est un MPTT, modèle "BlueSolar MPPT 100/30 rev2" du nom de MpttBlanc 2016-11-02T10:55:31+01:00 - Les données sont formatées comme ceci : V:26200,I:600,VPV:53630,PPV:18,CS:3,ERR:0,H19:1267,H20:4,H21:46,H22:17,H23:201 2016-11-02T10:55:31+01:00 - Après correction, la température est de 11.88°C 2016-11-02T10:55:31+01:00 - Tentative 1 de récupération de consommation 2016-11-02T10:55:32+01:00 - Trouvé à la tentative 1 : la La consommation trouvé est 00.1A 2016-11-02T10:55:32+01:00 - La consommation est de 00.1A soit 23W ``` Test d'envoi des données : ``` $ su - pvmonit -c /opt/PvMonit/sendToEmoncms.php 2016-11-02T10:56:44+01:00 - Données correctements envoyées : 1, données en erreurs : 0 ``` Mettre les scripts en tâche planifiée ```bash crontab -e -u pvmonit ``` Ajouter : ```diff +# Script de récupération des données, toutes les 5 minutes +*/5 * * * * /usr/bin/php /opt/PvMonit/getForEmoncms.php >> /tmp/PvMonit.getForEmoncms.log +# Script d'envoi des données, ici toutes les 1/2 heures +3,33 * * * * /usr/bin/php /opt/PvMonit/sendToEmoncms.php >> /tmp/PvMonit.sendToEmoncms.log ``` Je n'explique pas ici comment configurer emoncms, les flux pour obtenir de beaux dashboard, je vous laisse lire la documentation... ![Screenshot source config emoncms](http://david.mercereau.info/wp-content/uploads/2016/11/emoncms_source_config.png) Voici, pour exemple, mon dashboard : http://emoncms.mercereau.info/dashboard/view?id=1 Une capture : ![Screenshot emoncms dashboard](http://david.mercereau.info/wp-content/uploads/2016/11/emoncms-mon-dashboard-pvmonit.png) #### Sonde température/humidité (DHT) sur GPIO (sur raspberi pi) Installation des dépendances : ``` pip3 install Adafruit_DHT ``` Lancer la commande : ```sh visudo ``` -Si vous utilisez l'interface web pvmonit, ajouter : - -```diff -+ www-data ALL=(ALL) NOPASSWD:/usr/bin/python3 /opt/PvMonit/bin/DHT.py * -``` - -Si vous utilisez l'export vers emoncms, ajouter : +Ajouter : ```diff + pvmonit ALL=(ALL) NOPASSWD: /usr/bin/python3 /opt/PvMonit/bin/DHT.py * ``` Puis activer la sonde : ```bash ln -s /opt/PvMonit/bin-available/DhtGpio.php /opt/PvMonit/bin-enabled/OTHER-THome.php ``` #### Sonde température/humidité (DHT) récupéré sur l'arduino /!\ Uniquement si vous avez un Arduino pour récolter les donnée ```bash ln -s /opt/PvMonit/bin-available/TempHumByArduino.php /opt/PvMonit/bin-enabled/OTHER-TSol.php ``` #### Sonde de courant (type ACS712) récupéré sur l'arduino /!\ Uniquement si vous avez un Arduino pour récolter les donnée ```bash ln -s /opt/PvMonit/bin-available/CurrentByArduino.php/opt/PvMonit/bin-enabled/OTHER-CONSO.php ``` #### Sonde température USB (option) La sonde *thermomètre USB TEMPer*, cette sonde fonctionne avec le logiciel temperv14 qui est plutôt simple à installer ```bash apt-get install libusb-dev libusb-1.0-0-dev unzip cd /opt wget http://dev-random.net/wp-content/uploads/2013/08/temperv14.zip #ou un miroir #wget http://www.generation-linux.fr/public/juin14/temperv14.zip unzip temperv14.zip cd temperv14/ make ``` Test de la sonde : ```bash $ /opt/temperv14/temperv14 -c 18.50 ``` On ajoute ensuite la possibilité à des utilisateurs "restrint" d'exécutant de lancer les script avec sudo sans mot de passe : Lancer la commande : ```sh visudo ``` -Si vous utilisez l'interface web pvmonit, ajouter : - -```diff -+ www-data ALL=(ALL) NOPASSWD: /opt/temperv14/temperv14 -c -``` - -Si vous utilisez l'export vers emoncms, ajouter : +Ajouter : ```diff + pvmonit ALL=(ALL) NOPASSWD: /opt/temperv14/temperv14 -c ``` Activer le script (et l'éditer au besoin) ```bash ln -s /opt/PvMonit/bin-available/TemperatureUSB.php /opt/PvMonit/bin-enabled/other-TEMP.php ``` Autres documentations à propos de cette sonde : - http://www.generation-linux.fr/index.php?post/2014/06/21/Relever-et-grapher-la-temp%C3%A9rature-de-sa-maison-sur-Debian - http://dev-random.net/temperature-measuring-using-linux-and-raspberry-pi/ #### Pince ampèremétrique USB (option) /!\ Uniquement si vous n'avez pas d'Arduino J'utilise la pince ampèremétrique USB Aviosys 8870 pour mesurer ma consommation électrique. Le petit script perl (/opt/PvMonit/bin/ampermetre.pl) est très simple pour lire la pince ampèremétrique qui sera branchée en USB et apparaîtra dans votre système sur le port /dev/ttyACM0 Celui-ci dépend de la librairie serialport : ```bash aptitde install libdevice-serialport-perl ``` Test : : ```bash $ /opt/PvMonit/bin-available/ampermetre.pl 00.1A ``` -Si vous utilisez l'interface web pvmonit, ajouter : - -```diff -+ www-data ALL=(ALL) NOPASSWD: /opt/PvMonit/bin/* -``` +Ajouter : Si vous utilisez l'export vers emoncms, ajouter : ```diff + pvmonit ALL=(ALL) NOPASSWD: /opt/PvMonit/bin/* ``` Activer le script (et l'éditer au besoin) ```bash ln -s /opt/PvMonit/bin-available/AmpermetreUSB.php /opt/PvMonit/bin-enabled/other-CONSO.php ``` #### Co² Meter Il s'agit le ht2000 co² meter. Je ne l'utilise que pour le co² ayant des sondes ailleur mais il peut aussi donner l'humidité et la température. Si vous voulez aussi ces informations vous pouvez regarder de ce côté : https://github.com/tomvanbraeckel/slab_ht2000 Pour ma part (uniquement pour le co²) il faut compile le script : ```bash cd /opt/PvMonit/bin gcc ht2000.c -o ht2000 ``` Tester : ```bash ./ht2000 /dev/hidraw0 ``` Doit retourner une valeur numérique Ensuite (vu qu'il faut le lancer en root) vous devez le mettre dans le sudo : -Si vous utilisez l'interface web pvmonit, ajouter : - -```diff -+ www-data ALL=(ALL) NOPASSWD: /opt/PvMonit/bin/ht2000 * -``` - -Si vous utilisez l'export vers emoncms, ajouter : +Ajouter : ```diff + pvmonit ALL=(ALL) NOPASSWD: /opt/PvMonit/bin/ht2000 * ``` #### Raspberry Adafruit LCD RGB - 16x2 + Keypad (option) Uniquement pour les Raspbery Pi ![Ecran Adafruit LCD](https://david.mercereau.info/wp-content/uploads/2019/10/P1030685-e1571260899411.jpg) Permet d'afficher les informations principales sur le raspbery pi (Etat des batteries, puissance en cours...) ```bash raspi-config # Interfacing Option / P6 Serial / # Login shell : NO # Serial port harware enable : Yes reboot aptitude install i2c-tools i2cdetect 1 ``` La dernière commande (i2cdetect 1) doit afficher quelque chose comme : ``` 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: *-- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- ``` Pour tester le LCD lancer la commande : ```bash pip3 install adafruit-circuitpython-charlcd lxml python3 /opt/PvMonit/lcd/lcd.py ``` Pour que le LCD fonctionne au démarrage, ajouter avant "exit 0" dans le fichier /etc/rc.local la ligne suivant ```bash screen -A -m -d -S lcd /opt/PvMonit/lcd/lcd-launch.sh ``` diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 0000000..340b766 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,46 @@ +# Upgrade + +## Upgrade V1.0 > V2.0 + +Le principale changement réside dans le fait que lighttpd (le serveur web) est lancé avec l'utilisateur "pvmonit" et non plus "www-data" par défaut. : + +Pour cela : + +```bash +service lighttpd stop +``` + +Configuration du serveur http, avec le fichier /etc/lighttpd/lighttpd.conf + +```diff +- server.username = "www-data" +- server.groupname = "www-data" ++ server.username = "pvmonit" ++ server.groupname = "pvmonit" +``` + +Modifier le fichier /etc/init.d/lighttpd et remplacer tout "www-data" par "pvmonit" + +```diff +- owner=www-data +- group=www-data ++ owner=pvmonit ++ group=pvmonit +``` + +Changer les utilisateurs : + +``` +chown -R pvmonit:pvmonit /var/log/lighttpd +chown -R pvmonit:pvmonit /var/cache/lighttpd +chown pvmonit:pvmonit /var/run/lighttpd +``` + +On applique la configuration (si lighttpd ne démarre pas il faut creuser la question, regarder les logs...) : + +```bash +service lighttpd start +``` + +Plus de doc sur le changement de user : [ici](https://alexanderhoughton.co.uk/blog/lighttpd-changing-default-user-raspberry-pi/) ou [ici](https://redmine.lighttpd.net/boards/2/topics/6247) + diff --git a/domo/README.md b/domo/README.md index ab27aff..965837a 100644 --- a/domo/README.md +++ b/domo/README.md @@ -1,93 +1,118 @@ # 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 - -​ https://alexanderhoughton.co.uk/blog/lighttpd-changing-default-user-raspberry-pi/ domo : relay-action dépendance wiringpi + sudo ? aptitude install php-pdo-sqlite - Documenté script relay/id.php avec ce qu'il est possible de récupérer comme étét -Décrire état et mod (qui correspond à quoi) -Phylosophie : seul les mod son modifié, les état en son déduit... + ++ documenté mes scripts exemple... + + + + 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 + +**Prés-requis: programmation PHP sommaire**. En effet pour le moment il n'y a pas d'interface graphique pour programmer des évènement sur les relais, il faut donc coder un peu en PHP pour s'en sortir... ## 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€) +- (option) [Un afficheur 8 chiffres + 8 leds + 8 boutons (tm1638)](https://os.mbed.com/components/TM1638-LED-controller-80-LEDs-max-Keyboa/) nous permet d’interagir avec le système sans allumer un ordinateur (ou sans smartphone) : forcé l’allumage, interdire l'allumage… (~6€). -https://david.mercereau.info/wp-content/uploads/2019/11/PvMonit-Domo_bb.png +https://david.mercereau.info/wp-content/uploads/2020/01/PvMonit-Domo-v2_bb.png -## Arduino +## Daemon "domo" -Avec l'Arduino IDE, uploader le firmware "arduinoDomoRelayi2c.ino" contenu dans le dossier "firmware" +C'est le daemon qui regard l'état du système (batterie, régulateur) et qui déclenche des actions selon des scripts -## Installation +Pour la configuration, regarder le fichier config.yaml, dans la partie "domo:" Dépendance du script : ```bash -aptitude install python3 python3-yaml pip3 -pip3 install lxml pysqlite3 json smbus2 wget +aptitude install php-pdo-sqlite ``` Lancement du script à la main ```bash -cd /opt/PvMonit/domo/ -python3 domo.py +/opt/PvMonit/domo/domo-launch.sh ``` 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 +Il vous faut ensuite configurer les scripts pour l'automatisme, il se trouve dans /opt/PvMonit/domo/relay.script.d/ et il faut les nommer X.php (X étant le numéro du relai) -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 +Voir /opt/PvMonit/domo/relay.script.d/ID.php.exemple pour connaître le champs des possibles / des fonctions. + +## Activation dans l'interface web PvMonit -## Explication +Dans le ficheir config.yaml activer "domo" : + +```yaml +www: + domo: true +``` -Les interupteur on 3 modes : +Ceci vous permet d’interagir avec les relais via l'interface web : -- Off forcé -- Mode Automatique (uniquement possible si le PI est présent) -- On forcé +[https://david.mercereau.info/wp-content/uploads/2020/01/PvMonit-Domo-v2_bb.png](https://david.mercereau.info/wp-content/uploads/2020/01/Screenshot_2020-01-07-Pv-Monit1.png) + +## (Option) Afficheur 8 chiffres + 8 leds + 8 boutons (tm1638) + +Nous permet d’interagir avec le système sans allumer un ordinateur (ou sans smartphone). + +Dépendance du script : + +```bash +aptitude install python3 python3-yaml python3-json pip3 +pip3 install rpi-TM1638 +``` + +Lancement du script à la main + +```bash +/opt/PvMonit/domo/tm1638-launch.sh +``` + +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 tm1638 /opt/PvMonit/domo/tm1638-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 -L'affichange des 8 segments change quand le PI est présent et que le script domo.py est lancé. +## Pour interagir avec d'autres scripts maisons -Les leds au dessus des 8 segments sont synchronisé avec l'état des relaisre +Seul les mod son modifiés, les état en son déduit par le script domo/relay-actions si vous voulez interagir avec d'autres applications il vous suffit de modifier le fichier json "/tmp/PvMonit_domo_mod" (par défaut, modifiable dans le config.yaml : domo / jsonFile / modPath: /tmp/PvMonit_domo_mod) ------------------- -Max 16 relay (contrainte 2x tm1638 diff --git a/domo/domo-launch-log.sh b/domo/domo-launch-log.sh deleted file mode 100755 index b55cc18..0000000 --- a/domo/domo-launch-log.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -echo > /tmp/domo.log -cd /opt/PvMonit/domo -while (true); do - python3 domo.py >> /tmp/domo.log - sleep 0.5 -done diff --git a/domo/domo.py b/domo/domo.py deleted file mode 100644 index 1a57282..0000000 --- a/domo/domo.py +++ /dev/null @@ -1,378 +0,0 @@ -import yaml -import time -from smbus2 import SMBus -import os -from lxml import etree -from urllib.request import urlopen -import wget -from past.builtins import execfile -import re -import time -import sqlite3 -import sys - - -## for debug : -import pprint - -with open('../config-default.yaml') as f1: - config = yaml.load(f1, Loader=yaml.FullLoader) -with open('../config.yaml') as f2: - config_perso = yaml.load(f2, Loader=yaml.FullLoader) - -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] - -# Cherche a savoir si le MPPT est en Absorption ou en Float (en fin de charge) -def MpptAbsOrFlo(cs): - patternAbsFlo = re.compile(r"Absorption|Float") - if patternAbsFlo.match(cs): - return True; - else : - return False; -def MpptFlo(cs): - patternAbsFlo = re.compile(r"Float") - if patternAbsFlo.match(cs): - return True; - else : - return False; - -# Function for log -def logMsg(level, msg): - if level <= configGet('printMessage') : - print(time.strftime ('%m/%d/%Y %H:%M') ," - ",msg) - return -1 - -# i2c write -def writeNumber(value): - bus.write_byte(configGet('domo', 'i2c', 'adress'), value) - return -1 - -def download_data(): - # téléchargement des données - logMsg(3, 'Download data') - with open(configGet('tmpFileDataXml'), 'wb') as tmpxml: - tmpxml.write(urlopen(configGet('urlDataXml')).read()) - return time.time() - -logMsg(1, 'Lancement du script domo.py') - -heartGo=False -heartLastCheck=0 -relayDataLastCheck=0 -scriptExecLast=0 -xmlLastCheck=0 -xmlfileCheckError=0 -xmlData = {} -xmlDataLastControleOk=0 -xmlDataControle=False -dernierScriptJoue=-1 -sortie=False -firstDataRece=True -bus=SMBus(configGet('domo', 'i2c', 'device')); - -# BD -if not os.path.isfile(configGet('domo', 'dbFile')): - con = sqlite3.connect(configGet('domo', 'dbFile')) - db = con.cursor() - # Création de la base si elle n'existe pas - db.execute('''CREATE TABLE relay (id INTEGER PRIMARY KEY, relay_number INTEGER, info TEXT, valeur INTEFER, date INTEGER, event TEXT)''') - con.commit() -else: - con = sqlite3.connect(configGet('domo', 'dbFile')) - db = con.cursor() - -def logInDb(relay, info, valeur, event): - db.execute("INSERT INTO relay VALUES (null,"+str(relay)+",'"+str(info)+"',"+str(valeur)+"," + str(int(time.time())) + ",'"+str(event)+"')") - con.commit() - -def relayLastUp(relay): - req=db.execute('SELECT date FROM relay WHERE relay_number = '+str(relay)+' AND info = "E" AND (valeur = 2 OR valeur = 3) ORDER BY date DESC LIMIT 1') - try: - return req.fetchone()[0] - except: - return 0 - -def relayLastUpAuto(relay): - req=db.execute('SELECT date FROM relay WHERE relay_number = '+str(relay)+' AND info = "E" AND valeur = 2 ORDER BY date DESC LIMIT 1') - try: - return req.fetchone()[0] - except: - return 0 - -def relayLastDown(relay): - req=db.execute('SELECT date FROM relay WHERE relay_number = '+str(relay)+' AND info = "E" AND (valeur = 0 OR valeur = 1) ORDER BY date DESC LIMIT 1') - try: - return req.fetchone()[0] - except: - return 0 - -# Est-ce que le relay c'est allumé puis est maintenant éteind aujourd'hui ? (dans les 12 heures) -def relayUpDownToday(relay): - req=db.execute('SELECT count(date) FROM relay WHERE relay_number = '+str(relay)+' AND info = "E" AND (valeur = 1 OR valeur = 2) AND date > ' + str(t-720) + ' ORDER BY date DESC LIMIT 2') - try: - if req.fetchone()[0] == 2: - return True - else: - return False - except: - return False - -# Est-ce que le relay c'est allumé aujourd'hui ? (dans les 12 heures) -def relayUpToday(relay): - #print('SELECT count(date) FROM relay WHERE relay_number = '+str(relay)+' AND info = "E" AND (valeur = 2 OR valeur = 3) AND date > ' + str(t-43200) + ' ORDER BY date DESC LIMIT 1') - db.execute('SELECT count(date) FROM relay WHERE relay_number = '+str(relay)+' AND info = "E" AND (valeur = 2 OR valeur = 3) AND date > ' + str(t-43200) + ' ORDER BY date DESC LIMIT 1') - resulta=db.fetchone()[0] - try: - if resulta >= 1: - return True - else: - return False - except: - return False - - -def timeUpMax(timeUp): - # Si le temps - if relayLastUpAuto(relayId)+timeUp < t: - return True - else: - return False - -def timeUpMin(timeUp): - # Si le temps - if relayLastUpAuto(relayId)+timeUp > t: - return True - else: - return False - -# ~ @todo : Mode debug in write -# ~ ######################### -# ~ # Paramètre lancement script -# ~ ######################### -# ~ # HELP @todo à faire !!! -# ~ patternHelp = re.compile("help$") -# ~ if patternHelp.match(sys.argv[1]): - # ~ print("") - # ~ print("Usage: python3 "+sys.argv[0]+" debugScript RelayNumber ObjectDataFile") - # ~ print(" - RelayNumber : 0-9") - # ~ print(" - jsonDataFile : /tmp/Pvmonit_domo_debug.data (option, sinon c'est téléchargé en direct)") - # ~ print("") - # ~ print("Le fichier jsonDataFile contient par exemple : ") - # ~ print("{'CS': 'Bulk (en charge)', 'P': '-58', 'PPVT': '6', 'SOC': '95.6'}") - # ~ sys.exit(1) - -# ~ # DEBUG -# ~ patternDebug = re.compile("debugScript") -# ~ if patternDebug.match(sys.argv[1]): - # ~ if len (sys.argv) != 3 : - # ~ print("Erreur, il manque des arguments pour le debug : ") - # ~ print("Usage: python3 "+sys.argv[0]+" debugScript RelayNumber ObjectDataFile") - # ~ print(" - RelayNumber : 0-9 (numéro du script contenu dans relay.script.d)") - # ~ print(" - jsonDataFile : /tmp/Pvmonit_domo_debug.data (option, sinon c'est téléchargé en direct)") - # ~ print("") - # ~ print("Le fichier jsonDataFile contient par exemple : ") - # ~ print("{'CS': 'Bulk (en charge)', 'P': '-58', 'PPVT': '6', 'SOC': '95.6'}") - # ~ sys.exit (1) - - -logMsg(3, "Début de la boucle") -while 1: - # XML data recup - t=int(time.time()) - # S'il y a trop d'erreur : - if xmlfileCheckError >= configGet('domo', 'fileCheckError'): - logMsg(1, 'Trop d\'erreur, on patiente 10 secondes') - time.sleep(10) - xmlfileCheckError=0 - if xmlLastCheck+configGet('domo', 'dataCheckTime') < t: - download_data() - if not os.path.isfile(configGet('tmpFileDataXml')): - logMsg(2, "Le fichier XML de donnée " + configGet('tmpFileDataXml') + " n'existe pas.") - xmlfileCheckError=xmlfileCheckError+1 - elif os.path.getmtime(configGet('tmpFileDataXml'))+configGet('domo', 'fileExpir') < t : - logMsg(2, "Le fichier data est périmé !") - xmlfileCheckError=xmlfileCheckError+1 - else: - logMsg(3, "Récupération des données XML (état de l'installation solaire)") - try: - # Tentative de lecture - tree = etree.parse(configGet('tmpFileDataXml')) - datacount=0 - for datas in tree.xpath("/devices/device/datas/data"): - if datas.get("id") in configGet('domo', 'valueUse'): - datacount = datacount + 1 - for data in datas.getchildren(): - if data.tag == "value": - xmlData[datas.get("id")]=data.text - logMsg(5, pprint.pprint(xmlData)) - # Test intégrité des données - controle=configGet('domo', 'valueUse') - xmlDataControle=True - for xmlDataVerif in xmlData: - pattern = re.compile(controle[xmlDataVerif]) - if not pattern.match(xmlData[xmlDataVerif]): - logMsg(2, "Contrôle données erreur : " + xmlDataVerif + " = " + xmlData[xmlDataVerif]) - xmlDataControle=False - except: - # Si ce n'est pas bon, c'est que les données ne sont pas bonnes ou incomplètes, on télécharge donc de nouveau le fichier XML - logMsg(1, "Erreur dans la lecture du XML la syntax n'est pas bonne ?") - xmlDataControle=False - - if xmlDataControle == True: - xmlLastCheck=t - xmlDataLastControleOk=t - xmlfileCheckError=0 - heartGo=True - else: - # Directement on ce met en mode "trop d'erreur" on patiente - xmlfileCheckError=configGet('domo', 'fileCheckError') - - if xmlDataLastControleOk+configGet('domo', 'xmlDataExpir') < t: - logMsg(1, "Les données XML ont expirés, on stop le hearbeat") - heartGo=False - - ######################### - # Le heartbeat - ######################### - if heartGo == True and heartLastCheck+configGet('domo', 'heartbeatTime') < t: - writeNumber(int(ord("H"))) - logMsg(5, 'Heardbeat envoyé') - heartLastCheck=t - - # Si les données sont bonnes : - if (xmlDataControle == True): - ######################### - # Data Relay - ######################### - if relayDataLastCheck+configGet('domo', 'relay', 'dataFreq') < t: - logMsg(4, 'On récupère les données des relay (via i2c arduino)') - # A FAIRE - # Simulation monsieur l'arbitre - #// Etat : - #// - 0 : off - #// - 1 : on - #// Mode - #// - 0 : Null - #// - 1 : Off forcé - #// - 2 : Off Auto - #// - 3 : On Auto - #// - 4 : On forcé - - time.sleep(0.3) - # Requête i2c pour demande de data (état et mode des relay) - i2cResults = bus.read_i2c_block_data(configGet('domo', 'i2c', 'adress'), int(ord('D')), configGet('domo', 'relay', 'nb')*2+1) - # On conserve pour comparaison - try: - relayEtatOld=relayEtat - firstDataRece=False - except: - firstDataRece=True - # Remise à 0 - relayEtat=[] - relayMod=[] - x=0 - dataOrdre=1 - logEtat="" - logMod="" - for i2cDatas in i2cResults: - # Si les données sont présentes - if i2cDatas != 255: - if i2cDatas == 29: # C'est le sépartateur : https://fr.wikibooks.org/wiki/Les_ASCII_de_0_%C3%A0_127/La_table_ASCII - dataOrdre=2 - x=0 - elif dataOrdre == 1: - relayEtat.insert(x,i2cDatas) - logEtat=logEtat+","+str(i2cDatas) - # Détection des changement pour les mettres en BD - if firstDataRece == False: - if i2cDatas != relayEtatOld[x]: - logMsg(2, 'Un changement est détecté sur le relay ' + str(x) + ', il est passé à '+str(i2cDatas)+' on l\'enregistre en BD !') - testDoublon=db.execute('SELECT count(id) FROM relay WHERE relay_number = ' + str(x) + ' AND info = "E" AND valeur = '+str(i2cDatas)+' AND date > ' + str(t-configGet('domo', 'relay', 'dataFreq')) ) - if testDoublon.fetchone()[0] == 0: - logInDb(x, 'E', i2cDatas, '') - else: - logMsg(2, 'Finalement non, c\'est un doublon') - # Premier lancement, on enregistre les init en base - if firstDataRece == True: - testDoublon=db.execute('SELECT count(id) FROM relay WHERE relay_number = ' + str(x) + ' AND info = "E" AND valeur = '+str(i2cDatas)+' ORDER BY date ASC') - if testDoublon.fetchone()[0] == 0: - logInDb(x, 'E', i2cDatas, 'Init') - else: - logMsg(2, 'Finalement non, on log pas d\'INIT c\'est un doublon') - else: - relayMod.insert(x,i2cDatas) - logMod=logMod+","+str(i2cDatas) - - x=x+1 - logMsg(3, "DATA reçu : Etat " + logEtat) - logMsg(3, "DATA reçu : Mod " + logMod) - relayDataLastCheck=t - ######################### - # On joue les scripts - ######################### - if scriptExecLast+configGet('domo', 'relay', 'scriptExecInterval') < t: - logMsg(4, 'On joue les script des relay en mode auto') - relayId=0 - for mod in relayMod: - scriptFile=configGet('domo','relay', 'scriptDir') + "/" + str(relayId) + ".py" - if (relayMod[relayId] != 2): - logMsg(4, 'Le relay ' + str(relayId) + ' n\'est pas en mode automatique, mais en mode ' + str(relayMod[relayId])) - elif (relayEtat[relayId] == 0): - logMsg(4, 'Les relay sont encore en état 0, les mods sont donc juste changé et les relay n\'on pas eu le temps de changé d\'état, on patiente') - elif not os.path.isfile(scriptFile): - logMsg(4, 'Pas de script ' + scriptFile) - else: - # On joue les script 1 par 1, on attend pour jouer le suivant - if (relayId > dernierScriptJoue): - dernierScriptJoue=relayId - logMsg(3, 'Lecture du script ' + scriptFile) - returnEtat=None - returnLog=None - execfile(scriptFile) - if returnLog != None: - logMsg(1, '['+str(relayId)+'] ' + returnLog) - if returnEtat != None and returnEtat != relayEtat[relayId]: - logMsg(1, 'Un changement d\'état vers ' + str(returnEtat) + ' de est demandé pour le relay ' + str(relayId)) - data=[relayId,returnEtat] - bus.write_i2c_block_data(configGet('domo', 'i2c', 'adress'), int(ord('O')), data) - time.sleep(0.2) - sortie=True - logInDb(relayId, 'E', returnEtat, returnLog) - else: - logMsg(4, 'Pas de changement d\'état demandé pour le relay ' + str(relayId)) - # Remise à 0 de la position de lecture des scripts - if (relayId >= len(relayMod)-1): - dernierScriptJoue=-1 - relayId=relayId+1 - # Sortie de la boucle demandé - if (sortie == True): - sortie=False - break - scriptExecLast=t - - - # Pour être gentil avec le système - time.sleep(0.05) - -con.close() - diff --git a/domo/relay.script.d/0.py b/domo/relay.script.d/0.py deleted file mode 100644 index 1cdf9ea..0000000 --- a/domo/relay.script.d/0.py +++ /dev/null @@ -1,56 +0,0 @@ - -#################################### -# Ce script est un exemple pour vous -# il est a adapté a vos besoin -#################################### - -# Script pour ma box internet - -# config -pingHost=["192.168.1.10","192.168.1.12"] - -def checkIfComputerIsUp(pingHost) : - onlineHost=0 - pingBin="ping -i 0.2 -W 1 -c1 " - if os.path.isfile('/usr/bin/fping'): - pingBin='/usr/bin/fping -c1 -t500 ' - logMsg(5, "Ping bin utilisé : " + pingBin) - for host in pingHost: - logMsg(5, pingBin + host + '>/dev/null 2>/dev/null') - response = os.system(pingBin + host + '>/dev/null 2>/dev/null') - #and then check the response... - if response == 0: - onlineHost=onlineHost+1 - logMsg(5, 'onlineHost ' + str(onlineHost)) - return onlineHost - -timeUp=600 - -if os.path.isfile('/tmp/domo0up'): - returnLog='UP Présence du fichier /tmp/domo0up' - returnEtat=2 -else: - # Si il est éteind, faut-il l'allumer ? - if relayEtat[relayId] == 1: - # Si le régulateur dit que c'est bientôt la fin de charge et qu'il est plus de 11h c'est qu'il va faire beau ! - if MpptAbsOrFlo(xmlData['CS']): - returnLog='UP Le régulateur est en mode abs ou float' - returnEtat=2 - if float(xmlData['SOC']) > 93 and int(time.strftime ('%H')) > 11 and int(time.strftime ('%H')) < 17: - returnLog='UP La batterie est chargé à plus de 93% et qu\'il est entre 11 et 17h' - returnEtat=2 - # Si il est allumé, faut-il l'éteindre ? - elif relayEtat[relayId] == 2: - nbComputerUp=checkIfComputerIsUp(pingHost) - # S'il n'y a plus d'ordinateur d'allumé et que les batterie sont sous les 95% ou qu'il est après 17h on éteind - if (nbComputerUp == 0 and float(xmlData['SOC']) <= 93): - returnLog='DOWN pas d`ordinateur connecté et les batterie sous 93%' - returnEtat=1 - if (nbComputerUp == 0 and int(time.strftime ('%H')) >= 17) : - returnLog='DOWN pas d`ordinateur connecté et il est plus de 17h' - returnEtat=1 - if timeUpMin(timeUp): - returnLog='On maintient allumé, ' - returnEtat=2 - - diff --git a/domo/relay.script.d/1.php b/domo/relay.script.d/1.php index 10d7da6..626398e 100644 --- a/domo/relay.script.d/1.php +++ b/domo/relay.script.d/1.php @@ -1,82 +1,78 @@ 11 && date('G') < 17 && $data['SOC'] > 95) { + $return['log'] = 'Il est plus de 11 heure et que les batterie sont suppérieur à 95 c\'est qu\'il fait beau...'; $return['mod'] = 2; - } + } if (MpptAbsOrFlo($data['CS'])) { $return['log'] = 'Régulateur en Abs ou Float'; $return['mod'] = 2; } - if (date('G') > 11 && $data['SOC'] > 95) { - $return['log'] = 'Il est plus de 11 heure et que les batterie sont suppérieur à 95 c\'est qu\'il fait beau...'; - $return['mod'] = 2; - } // Si c'est allumé, faut-il l'éteindre ? } else if ($thisEtat == 1) { $checkIfComputerIsUp = checkIfComputerIsUp($pingHost); if (date('G') >= 17 && $checkIfComputerIsUp == 0) { $return['log'] = 'S\'il est plus de 17h et qu\'il n\'y a pas d\'ordinateur d\'allumé'; $return['mod'] = 1; } if ($data['SOC'] <= 95 && $checkIfComputerIsUp == 0) { $return['log'] = 'Les batteries pass sous les 95% et qu\'il n\'y a pas d\'ordinateur d\'allumé'; $return['mod'] = 1; - } - if (timeUpMin($thisId, $timeUp)) { + } + // Si un passage à 1 est décidé mais que le temps minimum n'est pas dépassé : + if ($return['mod'] == 1 && timeUpMin($thisId, $timeUp)) { $return['log'] = 'Temps minimum non dépassé, on maintient allumé'; - echo "ici"; $return['mod'] = 2; } } } return $return; ?> diff --git a/domo/relay.script.d/1.py b/domo/relay.script.d/1.py deleted file mode 100644 index 4e9420e..0000000 --- a/domo/relay.script.d/1.py +++ /dev/null @@ -1,18 +0,0 @@ -# Script pour mon téléphone fixe - -# Par défaut on le laisse éteind -returnEtat=1 -timeUp=1800 - -# Si la box est allumé automatiquement ou de force -if relayEtat[0] >= 2: - # Si le régulateur dit que c'est bientôt la fin de charge et qu'il est plus de 11h c'est qu'il va faire beau ! - if MpptFlo(xmlData['CS']): - returnLog='UP Le régulateur est en mode float' - returnEtat=2 - if float(xmlData['SOC']) > 95 and int(time.strftime ('%H')) > 11 and int(time.strftime ('%H')) < 19: - returnLog='UP La batterie est chargé à plus de 95% et il est enre 11h et 19h' - returnEtat=2 - if timeUpMin(timeUp): - returnLog='On maintient allumé, ' - returnEtat=2 diff --git a/domo/relay.script.d/2.php b/domo/relay.script.d/2.php index 39ba399..2f81504 100644 --- a/domo/relay.script.d/2.php +++ b/domo/relay.script.d/2.php @@ -1,24 +1,31 @@ 11 && date('G') < 19 && $data['SOC'] > 95) { + if (date('G') > 11 && date('G') < 20 && $data['SOC'] > 95) { $return['log'] = 'Il est plus de 11 heure et que les batterie sont suppérieur à 95 c\'est qu\'il fait beau...'; $return['mod'] = 2; } } +if ($thisEtat == 1) { + if (timeUpMin($thisId, $timeUp)) { + $return['log'] = 'Temps minimum non dépassé, on maintient allumé'; + $return['mod'] = 2; + } +} + return $return; ?> diff --git a/domo/relay.script.d/2.py b/domo/relay.script.d/2.py deleted file mode 100644 index 58893a3..0000000 --- a/domo/relay.script.d/2.py +++ /dev/null @@ -1,19 +0,0 @@ -# Pompe de rellevage - -# Par défaut on laisse éteind -returnEtat=1 - -# Temps d'allumage -timeUp=300 - -if relayEtat[relayId] == 2 and not timeUpMax(timeUp): - returnLog='UP, maintient allume, le temps n est pas passé' - returnEtat=2 -# Si elle a démarré aujourd'hui et que le temps d'allumage maxium est passé alors on le laisse à down -elif relayEtat[relayId] == 2 and timeUpMax(timeUp): - returnLog='DOWN, le temps d allumage est passé' - returnEtat=1 -# Sinon on le lance si la batterie est à 100% -elif float(xmlData['SOC']) > 99 and not relayUpToday(relayId) : - returnLog='UP La batterie est chargé à 100% et pas lancé aujourd hui' - returnEtat=2 diff --git a/domo/relay.script.d/3.php b/domo/relay.script.d/3.php index 527fcd1..a4cf5a1 100644 --- a/domo/relay.script.d/3.php +++ b/domo/relay.script.d/3.php @@ -1,23 +1,23 @@ = 98 && !relayUpToday($thisId)) { +if (MpptAbsOrFlo($data['CS']) && $data['SOC'] > 94 && !relayUpToday($thisId)) { $return['log'] = 'UP La batterie est chargé à 98% et pas lancé aujourd hui'; $return['mod'] = 2; } if ($thisEtat == 1 && timeUpMin($thisId, $timeUp)) { $return['log'] = 'UP, maintient allume, le temps n est pas passé'; $return['mod'] = 2; } return $return; ?> diff --git a/domo/relay.script.d/3.py b/domo/relay.script.d/3.py deleted file mode 100644 index 6e23157..0000000 --- a/domo/relay.script.d/3.py +++ /dev/null @@ -1,17 +0,0 @@ -# Script pour la rechage makita - -# Par défaut on laisse éteind -returnEtat=1 - -# Temps d'allumage -timeUp=3600 - -# Si démarré aujourd'hui et que le temps d'allumage maxium est passé alors on le laisse à down -if relayUpToday(relayId) and timeUpMax(timeUp): - returnLog='DOWN, le temps d allumage est passé' - returnEtat=1 -# Sinon on le lance si la batterie est à 100% & que la pome de relevage c'est lancé aujourd'hui -elif float(xmlData['SOC']) > 100 and relayUpDownToday(2): - returnLog='UP La batterie est chargé à 100%' - returnEtat=2 - diff --git a/domo/relay.script.d/7.py b/domo/relay.script.d/7.py deleted file mode 100644 index 6927be8..0000000 --- a/domo/relay.script.d/7.py +++ /dev/null @@ -1,13 +0,0 @@ -# Test DD externe par exemple - -# Si il est éteind, faut-il l'allumer ? -if relayEtat[relayId] == 1: - if os.path.isfile('/tmp/domo7up'): - returnLog='UP Présence du fichier /tmp/domo7up' - returnEtat=2 -# Si il est allumé, faut-il l'éteindre ? -elif relayEtat[relayId] == 2: - if not os.path.isfile('/tmp/domo7up'): - returnLog='DOWN Le fichier n est /tmp/domo7up n existe plus' - returnEtat=1 - diff --git a/domo/relay.script.d/ID.php.exemple b/domo/relay.script.d/ID.php.exemple new file mode 100644 index 0000000..0003c66 --- /dev/null +++ b/domo/relay.script.d/ID.php.exemple @@ -0,0 +1,39 @@ + diff --git a/domo/relay.script.d/ID.py.exemple b/domo/relay.script.d/ID.py.exemple deleted file mode 100644 index faef004..0000000 --- a/domo/relay.script.d/ID.py.exemple +++ /dev/null @@ -1,13 +0,0 @@ -# Test DD externe par exemple - -# Si il est éteind, faut-il l'allumer ? -if relayEtat[relayId] == 1: - # if ... - # On l'allume - returnEtat=2 -# Si il est allumé, faut-il l'éteindre ? -elif relayEtat[relayId] == 2: - # if ... - # on l'éteind ! - returnEtat=1 - diff --git a/function.php b/function.php index e4c3be5..ea2c9fb 100644 --- a/function.php +++ b/function.php @@ -1,802 +1,802 @@ $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_perso[$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/www/index.php b/www/index.php index edb2cdd..24fed92 100755 --- a/www/index.php +++ b/www/index.php @@ -1,458 +1,461 @@ Pv Monit
Patience...
'; echo '
Erreur
'; echo '

Heure du système :

'; echo ''; echo 'incorrect, on ne collecte rien.'; echo ''; echo '
'; echo '
'; } ?>
Domo
'; if (!is_file($config['domo']['jsonFile']['etatPath'])) { genDefaultJsonFile('etat'); } if (!is_file($config['domo']['jsonFile']['modPath'])) { genDefaultJsonFile('mod'); } for ($i = 1; $i <= $config['domo']['relayNb']; $i++) { echo '
'; echo '
 
'.$config['domo']['relayName'][$i].' Null Off Auto On
'; echo '
'; } echo '


'; echo '
'; } ?>
+