Dans un précédent article, j’ai décris comment écouter le bus KNX pour allumer une rampe de LED via Open DMX.
En fin d’article, j’ai abordé l’aspect possible d’un frontend en PHP pour avoir une base de données MySQL à jour, en fonction des évènements du KNX.
L’intérêt est quasi le même que linknx, un changement sur le bus est égal à un changement en BDD sur les Groupes d’Adresses qui m’intéressent.
Le but est de pouvoir exploiter ces données sur une page web et d’autres scripts maisons sans faire de requête sur le bus, mais en interrogeant ma BDD.
Une espèce de travail en couche d’applications.
Pour rappel, je n’ai rien contre linknx, j’aurais juste du mal à adapter mon cahier des charges à cet outil, voir mon article :
Se passer de linknx et webknx2, une fausse bonne idée ?
Pour ce front-end, je me suis inspiré de mon premier script avec logtail.
En cherchant des solutions, je suis tombé sur l’extention php inotify et son tail (en commentaire).
La base
Pour commencer, toujours depuis Debian Wheezy, il faut posséder php5, php-pear ainsi qu’une base MySQL évidemment.
inotify s’installe comme ceci au plus simple :
L’extention activée pour php5 pour le cli :
Selon mon premier script pour allumer ma rampe LED, re-modification de mon script d’init.d eibnetmux.
A la différence, que je log tout ce qu’il se passe sur le bus.
# Function that starts the daemon/service # do_start() { echo -n "Starting eibdnetmux" /usr/local/bin/eibnetmux $DAEMON_ARGS echo " done" sleep 2 echo -n "Starting group listen" /usr/local/bin/groupsocketlisten ip:127.0.0.1 > /tmp/knx_groupsocketlisten & echo " done" }
Mon fichier « log » du bus est /tmp/knx_groupsocketlisten
J’ai crée, ce qui est pour l’instant une simple table avec les Groupe Addresses qui m’intéressent, à l’éfigie de linknx :
-- -- Structure de la table `knx_ga` -- CREATE TABLE `knx_ga` ( `knx_ga_id` int(3) NOT NULL AUTO_INCREMENT, `knx_ga_groupaddress` tinytext NOT NULL, `knx_ga_value` text NOT NULL, `knx_ga_comment` text NOT NULL, PRIMARY KEY (`knx_ga_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- -- Contenu de la table `knx_ga` -- INSERT INTO `knx_ga` (`knx_ga_id`, `knx_ga_groupaddress`, `knx_ga_value`, `knx_ga_comment`) VALUES (1, '0/0/1', '1', 'Etat de la lumiere'), (2, '0/0/2', '10', 'Valeur d''éclairement'), (3, '0/0/10', '0', 'DMX');
J’ai 3 groupes d’adresses, ça n’a pas de sens sans une mise en situation.
Mise en situation
J’ai un Hager TXA213 (3 sorties variation 300W), un Hager WKT302 (poussoir 2 boutons), une lampe à la sortie 1 du TXA213.
Une simple pression sur le bouton 1 allume ma lampe, une pression continue augmente la luminosité ; une simple pression sur le bouton 2 éteind la lampe, une pression continue diminue la luminosité.
Pour le détail, voici un export de mes Groupes d’Adresses :
Groupes d’adresses pour tests (pdf)
J’ai trois groupes parmis les sept qui m’intéressent :
0/0/1 → Indication d’état.
0/0/2 → Valeur d’éclairement & Indication valeur d’éclairement.
0/0/10 → Allumage DMX.
Le script
Voici la formule magique, à dérouler, parce que c’est vraiment long.
<!--?php $file = "/tmp/knx_groupsocketlisten"; /** * Tail a file (UNIX only!) * Watch a file for changes using inotify and return the changed data * * @param string $file - filename of the file to be watched * @param integer $pos - actual position in the file * @return string */ function tail($file,&$pos) { // get the size of the file if(!$pos) $pos = filesize($file); // Open an inotify instance $fd = inotify_init(); // Watch $file for changes. $watch_descriptor = inotify_add_watch($fd, $file, IN_ALL_EVENTS); // Loop forever (breaks are below) while (true) { // Read events (inotify_read is blocking!) $events = inotify_read($fd); // Loop though the events which occured foreach ($events as $event=-->$evdetails) { // React on the event type switch (true) { // File was modified case ($evdetails['mask'] & IN_MODIFY): // Stop watching $file for changes inotify_rm_watch($fd, $watch_descriptor); // Close the inotify instance fclose($fd); // open the file $fp = fopen($file,'r'); if (!$fp) return false; // seek to the last EOF position fseek($fp,$pos); // read until EOF while (!feof($fp)) { $buf .= fread($fp,8192); } // save the new EOF to $pos $pos = ftell($fp); // (remember: $pos is called by reference) // close the file pointer fclose($fp); // return the new data and leave the function return $buf; // be a nice guy and program good code ;-) break; // File was moved or deleted case ($evdetails['mask'] & IN_MOVE): case ($evdetails['mask'] & IN_MOVE_SELF): case ($evdetails['mask'] & IN_DELETE): case ($evdetails['mask'] & IN_DELETE_SELF): // Stop watching $file for changes inotify_rm_watch($fd, $watch_descriptor); // Close the inotify instance fclose($fd); // Return a failure return false; break; } } } } // Conection avec la base de données mysql $db = mysql_connect("127.0.0.1", "root", "votremotdepassemysql"); $mysql=mysql_select_db("votrebase",$db); $req_sel_ga = "SELECT knx_ga_id,knx_ga_groupaddress FROM knx_ga"; $qur_sel_ga = mysql_query($req_sel_ga) or die (mysql_error()); $ga_bdd = array(); while($dat_sel_ga = mysql_fetch_array($qur_sel_ga)) { $knx_ga_id = $dat_sel_ga['knx_ga_id']; $knx_ga_groupaddress = $dat_sel_ga['knx_ga_groupaddress']; $ga_bdd[$knx_ga_groupaddress] = $knx_ga_id; } // Use it like that: $lastpos = 0; while (true) { // On tail le fichier de log $knxlisten = tail($file,$lastpos); //echo $knxlisten; // On réagit dès qu'on a un Write if (preg_match('#^Write#',$knxlisten)) { // Recup du Groupe d'Addresse et de la valeur qu'on converti preg_match('#[0-9]*\/[0-9]*\/[0-9]*#',$knxlisten,$groupaddr); preg_match("#[A-F0-9]*(.)$#",$knxlisten,$value); $value = hexdec($value[0]); $groupaddr = $groupaddr[0]; // on regarde si ca fait parti des elements à surveiller if (array_key_exists($groupaddr, $ga_bdd)) { // la valeur associé = l'id dans la BDD $knx_ga_id_update = $ga_bdd[$groupaddr]; $req_sel_ga_update = "UPDATE knx_ga SET knx_ga_value = '$value' WHERE knx_ga_id = $knx_ga_id_update"; // Et on update la BDD ! mysql_query($req_sel_ga_update); if ($knx_ga_id_update == "0/0/10" && $value == "01") exec("dmxchangecolorfader.py 255 0 255"); if ($knx_ga_id_update == "0/0/10" && $value == "00") exec("dmxchangecolor.py 0 0 0"); } } }
Abracadabra !
C’est posté sans mise au propre, mon dernier partage de script PHP était pour le 1-wire, il a fallu faire une archive pour un script plus simple, le voici brut, à personnaliser, poser des includes, variables de configuration, etc…
En gros, il fait quoi ce script ?
Il récupère les groupes d’adresses à surveiller dans la BDD.
Il fait un tail sur notre fichier de sortie groupsocketlisten.
Pour chaque nouvel évènement « Write », il regarde si le groupe d’adresse fait parti de ceux dans la BDD.
Si c’est le cas, il y écrit la valeur.
Cas pratique :
Si j’allume ma lumière, 0/0/1 passe à 1 sur mon bus.
groupsocketlisten l’écris dans le fichier /tmp/knx_groupsocketlisten
Ce script voit passer 0/0/1, il sait que sa valeur m’intéresse, il y écrit immédiatement la valeur dans la base de donnée.
Il suffit de lancer ce script par le cron, à la main (en ligne de commande uniquement), par un init.d ou autre.
La mise à jour de la base est quasi instantanée !
Le fonctionnement du script ci-dessous charge les GA de la base MySQL et les place dans une variable puis les compare à GA qui circule sur le bus.
A la fin du script, la partie liée à 0/0/10 est pour reprendre le fonctionnement de l’article de base et l’allumage du DMX.
Je songe à l’intégrer différemment, très certainement par l’ajout d’un champ contenant la spécificité et le script à exécuter.
Et voila ! Ou comment re-inventer la roue : linknx !
Il me reste à personnaliser ce script via des includes, des paramètres, rajouter un champs dans la table pour appeler un GA par un nom, un autre champs IES pour le write, et faire des essais sur une page web.
Pingback: Interagir en jQuery/Ajax avec le bus KNX | Domolio, la domotique et pas que…