La solution de repli un script perl
Suite à mon article sur l’exploitation des informations de l’appareil BaroWatt « baromètre énergétique » de la société Watteco et à ma tentative infructueuse pour faire fonctionner le logiciel CurrentCost GUI écrit en python, j’ai finalement opté pour la réalisation d’un script perl.
Mon but étant de pouvoir ajouter un programme lancé au démarrage de mon serveur domotique cette solution me semble être la plus pertinante.
Je vais donc ici noté pas à pas mes investigations pour réaliser cette mission.
Objectif : Lire et sauvegarder dans une base de données les informations envoyés par le Baromètre énergétique.
Prérequis : avoir installer le driver pour le cable USB téléchargeable ici
Perl et SqLite3 sont tous deux présent de base sur un système mac OSX 10.6.
SQLite est présent dans le dossier /usr/bin/sqlite3 et il suffit de pater la commande ci-dessous dans une fenêtre de terminal pour s’en assurer.
perl --version
En suite il faut ajouter un module perl indispensable pour accéder aux port séries (port com) mais ATTENTION il est impératif que le logiciel Xcode soit installé sur votre ordinateur ou un compilateur C.
Pour ajouter ce module il suffit depuis une fenêtre de terminal (Application->Utilitaires->Terminal) de taper les commandes :
perl -MCPAN -e shell Device::SerialPort
Utilisation de SQLite3 ou MySql
Mac OS X 10.6 fournit nativement le support de SQLite3 il est donc simple et facile de sauvegardé les données remontées par l’appareil BaroWatt ou CiurrentCost dans une DB de ce type.
Il faut ajouter les modules permettant d’accéder à SQLite depuis perl (modules DBI et DBD pour SQLite).
Donc toujours depuis le terminal
perl -MCPAN -e shell # (si vous n'êtes plus dans la ligne de commande perl) install DBI install DBD::Mysql install DBD::SQLite
Impossible d’installer le driver MySql avec la version de perl installé sur Mac OSX 10.6.
Le but étant d’obtenir une procédure simple permettant à un non informaticien de procéder à l’installation du script la piste de résolution de ce problème sera laissée de côté au profits de l’utilisation de SQLite.
Script utilisant SQLite3 pour enregistrer les données XML remontées par le baromètre énergétique
#!/usr/bin/perl -w # Lecture d'information envoyé par l'appareil "Current Cost" ou "BaroWatt" via cable USB et émulation de port série et enregistrement dans Database SQLITE; use DBI; use strict; use Device::SerialPort qw(ARAM :STAT 0.07 ); my $PORT = "/dev/tty.usbserial"; my $dbfile = 'BaroWattV3.sqlite'; # your database file #my $dbfile = '/WWW/SQLIteBaroWatt.db'; # your database file my $dbh = DBI->connect( # connect to your database, create if needed "dbi:SQLite:dbname=$dbfile", # DSN: dbi, driver, database file "", # no user "", # no password { RaiseError => 1 }, # complain if something goes wrong ) or die $DBI::errstr; $dbh->do("CREATE TABLE IF NOT EXISTS xmldata (time DATE, data TEXT)") or die $DBI::errstr; $dbh->do("CREATE TABLE IF NOT EXISTS xmldataRT (time DATE, temp TEXT, watts TEXT)") or die $DBI::errstr; $dbh->do("CREATE TABLE IF NOT EXISTS xmldataHH (time DATE, unit TEXT, val TEXT)") or die $DBI::errstr; $dbh->do("CREATE TABLE IF NOT EXISTS xmldataHD (time DATE, unit TEXT, val TEXT)") or die $DBI::errstr; $dbh->do("CREATE TABLE IF NOT EXISTS xmldataHM (time DATE, unit TEXT, val TEXT)") or die $DBI::errstr; my $ob = Device::SerialPort->new($PORT); $ob->baudrate(57600); $ob->write_settings; # déclaration et initialisation des valeurs de références pour savoir si on doit traiter les infos remontées et faire un insert en DB my $dateAnter = time - (365 * 24 * 60 * 60) - 36000; # un an et 10 heures au paravant my ($lastHM, $lastHD, $lastHH) = (localtime($dateAnter))[4,3,2]; #print "Val de ref : lastHM = ".$lastHM." lastHD = ".$lastHD." lastHH = ".$lastHH.".\n"; #my ($month, $day, $hour) = (localtime(time))[4,3,2]; open(SERIAL, "+>$PORT"); while (my $line = <SERIAL>) { # [4,3,2] récupération du mois, jour et heure depuis le tableau retourné par localtime my ($month, $day, $hour) = (localtime(time))[4,3,2]; #print "Val de ref : lastHM = ".$lastHM." - ".$month." lastHD = ".$lastHD." - ".$day." lastHH = ".$lastHH." - ".$hour.".\n"; if ($line =~ m!<tmpr> *([\-\d.]+)</tmpr>.*<ch1><watts>0*(\d+)</watts></ch1>!) { print "Temps réel \n"; #Information temps réel (toutes les 6 secondes avec mon BaroWatt) #print "insert into xmldataRT (time, temp, watts) values (datetime('now' , 'localtime'),'".$1."','".$2."')"; $dbh->do("insert into xmldataRT (time, temp, watts) values (datetime('now' , 'localtime'),'".$1."','".$2."')") or die $DBI::errstr; } elsif ($line =~ m!<units> *(\w+)</units>.*<h004>(\d+)</h004>!) { print "H-4 \n"; #Information historique h-4 (toutes les 2 heures avec mon BaroWatt) je sais pas pourquoi il y a pas de h002 envoyé par mon cc128 if (!($hour == $lastHH)) { $dbh->do("insert into xmldataHH (time, unit, val) values (datetime('now' , 'localtime'),'".$1."','".$2."')") or die $DBI::errstr; $lastHH = $hour; } } elsif ($line =~ m!<units> *(\w+)</units>.*<d001>(\d+)</d001>!) { print "D-1 \n"; #Information historique j-1 (toutes les 2 heures avec mon BaroWatt) if (!($day == $lastHD)) { $dbh->do("insert into xmldataHD (time, unit, val) values (datetime('now' , 'localtime'),'".$1."','".$2."')") or die $DBI::errstr; $lastHD = $day; } } elsif ($line =~ m!<units> *(\w+)</units>.*<m001>(\d+)</m001>!) { print "M-1 \n"; #Information historique m-1 (toutes les 2 heures avec mon BaroWatt) if (!($month == $lastHM)) { $dbh->do("insert into xmldataHM (time, unit, val) values (datetime('now' , 'localtime'),'".$1."','".$2."')") or die $DBI::errstr; $lastHM = $month; } } # LES AUTRES MESSAGES XML NE SONT PAS UTILES SI TOUTE FOIS ON SOUHAITE LES ENREGISTRER ALORS IL SUFFIT DE SUPPRIMER LES # EN DEBUT DES 5 LIGNES CI-DESSOUS # elsif (!($line eq '') && (length($line)>21)) { # print "Stange \n"; # $dbh->do("insert into xmldata (time, data) values (datetime('now' , 'localtime'),'".$line."')") or die $DBI::errstr; # #$dbh->do("select ROWID,* from xmldata;"); requète permettant de récuperer tous les champs plus le champs autoincrement automatiquement àjouté à la table par SQLITE #} print $line."\n"; } $dbh->disconnect();
La encore pur des raisons de simplicité de mise en oeuvre par les éventuels utilisateur de ce script nous ne suivrons pas la piste de résolution décrite ici.
Finalement on utilisera MySql son installation devrait être « aisée » (MySql n’est pas installé d’origine sur les version non Server de Snow Léopard) et la quantité d’information enregistré s’avérant importante (un message toutes les 6 secondes avec mon BaroWatt) l’exploitation des informations enregistrées dans la data-base devrait être plus rapide qu’avec SQLite.
Si vous avez lu cet article en détail vous savez déjà que l’installation du driver DBD pour MySql à échoué, aussi nous allons devoir ruser.
Les requêtes seront réalisées par le biais de l’appel de la fonction perl « system(‘echo ».$sqlins. » | mysql -u SQLUTILISATEUR –password=VOTREMDP conso_watt_et_temp’); » en remplaçant SQLUTILISATEUR et VOTREMDP par les infos qui vont bien
Pour commencer voici la structure de la DB nommée « conso_watt_et_temp » (requète à réaliser depuis PhpMyAdmin ou MysQlWorkBench)
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO" -- Base de données: `conso_watt_et_temp` -- -------------------------------------------------------- -- -- Structure de la table `test` -- CREATE TABLE IF NOT EXISTS `test` (`id` int(11) NOT NULL auto_increment,`val` text NOT NULL,PRIMARY KEY (`id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; -- -------------------------------------------------------- -- -- Structure de la table `xmldata` -- CREATE TABLE IF NOT EXISTS `xmldata` (`date` datetime NOT NULL default '0000-00-00 00:00:00',`data` text,PRIMARY KEY (`date`)) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- -------------------------------------------------------- -- -- Structure de la table `xmldataHH` -- CREATE TABLE IF NOT EXISTS `xmldataHH` (`date` datetime NOT NULL default '0000-00-00 00:00:00',`unit` char(64) default NULL,`val` float default NULL,PRIMARY KEY (`date`)) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- -------------------------------------------------------- -- -- Structure de la table `xmldataHJ` -- CREATE TABLE IF NOT EXISTS `xmldataHJ` (`date` datetime NOT NULL default '0000-00-00 00:00:00',`unit` char(64) default NULL,`val` float default NULL,PRIMARY KEY (`date`)) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- -------------------------------------------------------- -- -- Structure de la table `xmldataHM` -- CREATE TABLE IF NOT EXISTS `xmldataHM` (`date` datetime NOT NULL default '0000-00-00 00:00:00',`unit` char(64) default NULL,`val` float default NULL,PRIMARY KEY (`date`)) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- -------------------------------------------------------- -- -- Structure de la table `xmldataRT` -- CREATE TABLE IF NOT EXISTS `xmldataRT` (`date` datetime NOT NULL default '0000-00-00 00:00:00',`temp` float default NULL,`watts` float default NULL,PRIMARY KEY (`date`)) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Script final ci-dessous utilisant MySql via la fonction perl « system() »
#!/usr/bin/perl -w # Lecture d'information envoyé par l'appareil "Current Cost" ou "BaroWatt" via cable USB et émulation de port série; use strict; use Device::SerialPort qw(ARAM :STAT 0.07 ); use Date::Calc qw(Add_Delta_YMDHMS Today_and_Now); my $PORT = "/dev/tty.usbserial"; my $ob = Device::SerialPort->new($PORT); $ob->baudrate(57600); $ob->write_settings; # déclaration et initialisation des valeurs de références pour savoir si on doit traiter les infos remontées et faire un insert en DB my ($year,$lastHM,$lastHD, $lastHH,$min,$sec) = Add_Delta_YMDHMS(Today_and_Now(), 0,0,0,-3,0,0); # 3 heures auparavant car il faut avoir lancer le script de récup d'historique au préalable si on veut le conserver open(SERIAL, "+>$PORT"); while (my $line = <SERIAL>) { my $sqlins = ""; # [4,3,2] récupération du mois, jour et heure depuis le tableau retourné par localtime my ($refMonth, $refDay, $refHour) = (localtime(time))[4,3,2]; #print "Val de ref : lastHM = ".$lastHM." - ".$month." lastHD = ".$lastHD." - ".$day." lastHH = ".$lastHH." - ".$hour.".\n"; if ($line =~ m!<tmpr> *([\-\d.]+)</tmpr>.*<ch1><watts>0*(\d+)</watts></ch1>!) { print "Temps réel \n"; #Information temps réel (toutes les 6 secondes avec mon BaroWatt) $sqlins = 'insert into xmldataRT (date, temp, watts) values (now(),'.$1.','.$2.')'; } elsif ($line =~ m!<units> *(\w+)</units>.*<h004>(\d+)</h004>!) { print "H-4 \n"; #Information historique h-4 (toutes les 2 heures avec mon BaroWatt) je sais pas pourquoi il y a pas de h002 envoyé par mon cc128 if (!($refHour == $lastHH)) { $lastHH = $refHour; my ($year,$month,$day, $hour,$min,$sec) = Add_Delta_YMDHMS(Today_and_Now(), 0,0,0,-4,0,0);# on retranche les 4 heures à la date actuelle my $strHeureMesure = sprintf("%4d-%02d-%02d %02d:%02d:%02d",$year,$month,$day,$hour,$min,$sec); $sqlins = 'insert into xmldataHH (date, unit, val) values ("'.$strHeureMesure.'","'.$1.'",'.$2.')'; } } elsif ($line =~ m!<units> *(\w+)</units>.*<d001>(\d+)</d001>!) { print "D-1 \n"; #Information historique j-1 (toutes les 2 heures avec mon BaroWatt) if (!($refDay == $lastHD)) { $lastHD = $refDay; my ($year,$month,$day, $hour,$min,$sec) = Add_Delta_YMDHMS(Today_and_Now(), 0,0,-1,0,0,0);# on retranche 1 jour à la date actuelle my $strHeureMesure = sprintf("%4d-%02d-%02d %02d:%02d:%02d",$year,$month,$day,$hour,$min,$sec); $sqlins = 'insert into xmldataHD (date, unit, val) values ("'.$strHeureMesure.'","'.$1.'",'.$2.')'; } } elsif ($line =~ m!<units> *(\w+)</units>.*<m001>(\d+)</m001>!) { print "M-1 \n"; #Information historique m-1 (toutes les 2 heures avec mon BaroWatt) if (!($refMonth == $lastHM)) { $lastHM = $refMonth; my ($year,$month,$day, $hour,$min,$sec) = Add_Delta_YMDHMS(Today_and_Now(), 0,-1,0,0,0,0);# on retranche 1 mois à la date actuelle my $strHeureMesure = sprintf("%4d-%02d-%02d %02d:%02d:%02d",$year,$month,$day,$hour,$min,$sec); $sqlins = 'insert into xmldataHM (date, unit, val) values ("'.$strHeureMesure.'","'.$1.'",'.$2.')'; } } if (!($sqlins eq '')) { system('echo \''.$sqlins.'\' | mysql -u root --password=asnow06 conso_watt_et_temp'); } print $sqlins."\n".$line."\n"; }
Démarrage automatique du script à l’allumage
Il suffit d’ajouter le fichier com.eticweb.barowatt.currentcost.plist qui sera utilisé par launchctl dans le dossier /Library/LaunchDaemons/ (Library = Bibliothèque depuis le finder).
Le contenu du fichier :
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>KeepAlive</key> <true/> <key>Label</key> <string>com.eticweb.barowatt.currentcost</string> <key>OnDemand</key> <false/> <key>RunAtLoad</key> <true /> <key>ProgramArguments</key> <array> <string>/Groups/EticWeb/SnowLeoServ/ReadSerialPort-barowatt-PerlOK-Mysql.sh</string> </array> <key>StandardErrorPath</key> <string>/dev/null</string> <key>StandardOutPath</key> <string>/dev/null</string> </dict> </plist>
Next step
Super on à les informations enregistrées dans une base de données mais on fait comment pour les afficher graphiquement?
Et bien on réalise une application en PHP par exemple qui utilise du javascript (jqPlot) pour faire de beaux graphique.






Commentaires récents