par Anne-Laure DELPECH | 27 Août 2016 | Plugin (extension) WordPress
A force de travailler sur WordPress, j’ai appris pas mal de choses. Pour continuer à m’améliorer, il est essentiel (pour moi au moins) de formaliser l’état de mes connaissances. J’entame donc une nouvelle série d’articles sur la création d’extensions (plugin) pour WordPress.
Dans ce premier article de la série Un plugin WordPress avec page de réglage, j’explique comment créer l’extension de base, la version « 0.1 » que j’améliorerai ensuite. Cette extension de base doit contenir les fichiers de base, les répertoires nécessaires, les éléments d’internationalisation, …
Mon « environnement de développement »
Je travaille sur un PC Windows 10 équipé avec notepad++ pour l’édition des fichiers et filezilla pour l’échange FTP des fichiers. Filezilla est connecté à un hébergement web mutualisé sur OVH.
J’utilise aussi GitHub pour Windows, qui me permet de gérer mes versions dans un dépôt GitHub.
Lorsque je développe, j’interviens sur un site de test, qui n’est pas indexé par les robots des moteurs de recherche. Ce site de test contient au minimum les extensions activées suivantes, pour le débogage :
Selon les utilisations j’y ajouter d’autres extensions liées à debug bar, telles que : Debug Bar Actions and Filters Addon, Debug Bar Console, Debug Bar Custom Info, Debug Bar Post Types et Debug Bar Post Types. Il arrive aussi que j’ajoute « User Switching » lorsque j’ai besoin de tester avec différents rôles d’utilisateur.
L’extension de base (V 0.1)
Créer la structure
Avec Filezilla, je crée le répertoire clea-add-button dans /wp-content/plugins/.
Puis je crée les sous-répertoires suivants :
- admin pour tous les scripts relatifs au « back office » de WordPress
- css pour les feuilles de style (côté front ou back office)
- images
- includes pour les scripts qui assurent la fonction principale de l’extension
- js pour les scripts en JavaScript
- languages pour les traductions de l’extension.
Créer les fichiers de base
Une extension doit au minimum contenir un fichier PHP dans le répertoire principal. Dans mon cas c’est clea-add-button.php . J’y ajoute un README.md car c’est une bonne habitude de noter les changements au fur et à mesure de leur réalisation. Pour son contenu, je m’inspire de la mise en forme de cet exemple sur wordpress.org.
clea-add-button.php contient le code suivant :
<?php
/**
* Plugin Name: Clea add button
* Plugin URI: https://knowledge.parcours-performance.com
* Description: Add a custom button at the end of each post
* Author: Anne-Laure Delpech
* Author URI: https://knowledge.parcours-performance.com
* License: GPL2
* Domain Path: /languages
* Text Domain: clea-add-button
*
* @package clea-presentation
* @version 0.1.0
* @author Anne-Laure Delpech
* @copyright Copyright (c) 2016 Anne-Laure Delpech
* @link https://github.com/aldelpech/CLEA-presentation
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @since 0.1.0
*/
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
die;
}
/*----------------------------------------------------------------------------*
* Path to files
* @since 0.7.0
*----------------------------------------------------------------------------*/
define( 'CLEA_ADD_BUTTON_FILE', __FILE__ );
define( 'CLEA_ADD_BUTTON_BASENAME', plugin_basename( CLEA_ADD_BUTTON_FILE ));
define( 'CLEA_ADD_BUTTON_DIR_PATH', plugin_dir_path( CLEA_ADD_BUTTON_FILE ));
define( 'CLEA_ADD_BUTTON_DIR_URL', plugin_dir_url( CLEA_ADD_BUTTON_FILE ));
/********************************************************************************
* appeler d'autres fichiers php et les exécuter
* @since 0.1
********************************************************************************/
// charger des styles, fonts ou scripts correctement
require_once CLEA_ADD_BUTTON_DIR_PATH . 'includes/clea-add-button-enqueues.php';
// internationalisation de l'extension
require_once CLEA_ADD_BUTTON_DIR_PATH . 'includes/clea-add-button-i18n.php';
/******************************************************************************
* Actions à réaliser à l'initialisation et l'activation du plugin
* @since 0.1
******************************************************************************/
function clea_add_button_activation() {
}
register_activation_hook(__FILE__, 'clea_add_button_activation');
/*----------------------------------------------------------------------------*
* deactivation and uninstall
*----------------------------------------------------------------------------*/
/* upon deactivation, wordpress also needs to rewrite the rules */
register_deactivation_hook(__FILE__, 'clea_add_button_deactivation');
function clea_add_button_deactivation() {
}
// register uninstaller
register_uninstall_hook(__FILE__, 'clea_add_button_uninstall');
function clea_add_button_uninstall() {
// actions to perform once on plugin uninstall go here
// remove all options and custom tables
$option_name = 'clea_add_button';
delete_option( $option_name );
// For site options in Multisite
delete_site_option( $option_name );
}
On notera que ce fichier va ouvrir deux autres scripts php, qui sont dans le sous-répertoire ‘/includes.
On notera également qu’il n’y a rien dans les fonctions à utiliser à l’activation de l’extension ou à sa désactivation. Par contre, dans la fonction clea_add_button_uninstall , qui sera lancée si je désinstalle l’extension, il y a ce qu’il faut pour supprimer les réglages que je prévois de stocker dans la base de données.
Les fichiers pour charger des scripts ou styles
Comme je sais qu’il faudra certainement que je charge des scripts (fichiers javascript) ou des styles (fichiers css), j’ai déjà prévu un fichier clea-add-button-enqueues.php dans le répertoire /includes . Ce fichier ne contient rien à ce stade, juste son en-tête :
<?php
/**
*
* Charger styles et scripts du plugin
*
*
* @link https://github.com/aldelpech/clea-add-button
* @since 0.1.0
*
* @package clea-add-button
* @subpackage clea-presentation/includes
* Text Domain: clea-add-button
*/
Le fichier pour l’internationalisation
Tous les fichiers PHP de l’extension démarrent par un en-tête qui contient au minimum les deux lignes suivantes :
* Domain Path: /languages
* Text Domain: clea-add-button
Ces deux lignes indiquent respectivement le répertoire dans lequel seront rangés les fichiers de traduction et le nom du « text domain ». Pour plus de précision, voir un ancien article : Créer un plugin de fonctionnalités (6) ; internationalisation.
Le script clea-add-button-i18n.php contient la fonction qui chargera la traduction adaptée à la « localisation » du WordPress utilisant l’extension.
<?php
/**
*
* charger le bon text domain (internationalisation)
*
*
* @link https://github.com/aldelpech/clea-add-button
* @since 0.1.0
*
* @package clea-add-button
* @subpackage clea-presentation/includes
* Text Domain: clea-add-button
*/
add_action( 'plugins_loaded', 'clea_add_button_load_plugin_textdomain' );
function clea_add_button_load_plugin_textdomain() {
load_plugin_textdomain( 'clea-add-button', FALSE, CLEA_ADD_BUTTON_BASENAME . '/languages/' );
}
Activer l’extension sur un site WordPress
J’active l’extension comme n’importe quelle autre extension, dans « extensions » / « extensions installées » du tableau de bord WordPress.
Tout va bien : pas d’erreur (Query Monitor n’affiche qu’une « notice », qui n’a rien à voir avec notre extension).
Stocker cette version sur GitHub
Sur mon compte GitHub, je crée un nouveau dépôt.
Je le clone dans l’application GitHub pour Windows. Ensuite, avec Filezilla, je copie le répertoire de l’extension dans le répertoire affecté à ce dépôt.

Environnement de développement d’extension sous Filezilla
Je fais un « commit » puis je synchronise.
On peut voir l’ensemble des fichiers de l’extension de ce « commit » sur GitHub, ici.
Et maintenant
Dans le prochain article de cette série Un plugin WordPress avec page de réglage, je vais explorer la création d’une page de réglage pour l’extension.
par Anne-Laure DELPECH | 17 Août 2016 | Eedomus, Raspberry Pi
J’ai un Raspberry Pi qui collecte des données issues de capteurs reliés à un arduino. Actuellement les données sont archivées dans des fichiers textes sur le Pi. Je veux envoyer les données à ma box Eedomus pour qu’elle en assure l’archivage et l’affichage.
Il faut donc que je réalise les actions suivantes :
- sur l’Eedomus : créer des « éléments de programmation » de type « état » pour chaque donnée à collecter.
- Sur le Raspberry Pi : lire les fichiers texte des données collectées et les envoyer à l’Eedomus tous les quarts d’heure.
Eedomus : créer des éléments de programmation de type « état »
Sur un navigateur internet, accéder à la console eedomus (https://secure.eedomus.com/).
- Cliquer sur l’onglet « configuration » puis sur le bouton « ajouter ou supprimer un périphérique ».
- Dans la nouvelle fenêtre, cliquer sur le bouton « ajouter un autre type de périphérique ».
Régler le nouveau capteur d’état et noter son code API (450118) pour le capteur ci-dessous :

Une fois qu’on a cliqué sur le bouton « Sauver », le capteur est créé. Il est classé dans les périphériques virtuels.
Le capteur ne contient aucune valeur. Il faut lui en donner par des commandes HTTP via l’API d’Eedomus.
Raspberry Pi : envoyer une valeur au capteur créé
Voir la documentation Eedomus sur la manière de définir la valeur d’un capteur ici.
En manuel, pour envoyer une donnée à ce capteur, il me suffit d’envoyer une commande HTTP via un navigateur :
https://api.eedomus.com/set?action=periph.value&api_user=xxx&api_secret=xxx&periph_id=450118&value=24.2&value_date=2016-08-17 08:47:00
Et le navigateur affiche un message de succès :
{ "success": 1, "body": { "result": "[OK]" } }
Et dans la box Eedomus, la dernière valeur du capteur est maintenant 24.2°C à 8h47 le 17 août.
Raspberry Pi : lire un fichier texte et envoyer à l’Eedomus
J’explore les scripts PHP à faire dans l’article « Manipuler des fichiers en PHP« . Ici, je vais lire un fichier texte qui contient les températures mesurées dans mon bureau.
Localisation du fichier : /home/jf/temp/temperatures.log
Les données sont archivées sous la forme (11 dernières lignes) :
28-00043d87b4ff 24.25 Wed, 17 Aug 2016 06:58:25 GMT
28-00043d87b4ff 24.25 Wed, 17 Aug 2016 06:58:35 GMT
28-00043d87b4ff 24.312 Wed, 17 Aug 2016 07:05:52 GMT
28-00043d87b4ff 24.312 Wed, 17 Aug 2016 07:06:05 GMT
28-00043d87b4ff 24.25 Wed, 17 Aug 2016 07:06:16 GMT
Il va falloir que mon script php aille chercher la dernière ligne non vide puis en sorte la température (24.25) et la date au format compréhensible par l’Eedomus (AAAA-MM-JJ HH:MM:SS).
Pour lire le fichier à partir de la fin : voir cette question sur stackoverflow.
J’ai complété le fichier eedomus.php , créé dans l’article précédent de cette série « Un tableau de bord domotique« , avec la fonction suivante :
/******************************************************************
* appel de l'API eedomus en PHP pour donner une valeur une donnée
*
* $id = 450118
* $val = 24.2
* $date = '2016-08-17 08:47:00'
* mettra la valeur 24.2, à la date 2016-08-17 08:47:00 dans le capteur "état" d'ID 450118
*
*****************************************************************/
//&value=24.2&value_date=2016-08-17 08:47:00
function al_set_eedomus_sensor( $id, $val, $date ) {
global $api_user ;
global $api_secret ;
// eedomus date must be url encoded before it is sent
$date = urlencode( $date );
// construction de l'URL de l'API
$url = "https://api.eedomus.com/set?action=periph.value";
$url .= "&api_user=$api_user";
$url .= "&api_secret=$api_secret";
$url .= "&periph_id=$id";
$url .= "&value=$val";
$url .= "&value_date=$date";
echo "http command --------------- \n\r" ;
echo $url ;
echo "\n --------------------------- \n\r" ;
// appel de l'API
$result = file_get_contents( $url );
// on controle le résultat
if (strpos($result, '"success": 1') == false) {
echo "Une erreur est survenue : [".$result."]\n";
}
else {
// Tout s'est bien passé
echo "OK, la valeur a été transmise [".$result."] \n";
}
}
et j’ai créé un fichier send-temp-to-eedomus.php (droits 740), placé comme eedomus.php dans le répertoire ‘/home/jf/eedomus ‘ du Pi « alradio ». Ce script contient :
#!/usr/bin/php
<?php
/****************************************************
* read last non blank line from file
****************************************************/
$directory = '/home/jf/temp/';
$log_file = 'temperatures.log' ;
$pos = -10; // number of lines to read at end of file
$t = "";
$handle = fopen( $directory . DIRECTORY_SEPARATOR . $log_file, 'r' );
// source : http://stackoverflow.com/questions/6451232/reading-large-files-from-end
while ( $t != "\n" ) {
fseek( $handle, $pos, SEEK_END );
$t = fgetc( $handle );
$pos = $pos - 1;
}
$lastline = fgets( $handle );
echo " last line : " . $lastline ;
fclose( $handle );
/****************************************************
* now get each individual values
****************************************************/
date_default_timezone_set ( 'Europe/Paris' ) ;
$val = explode( "\t", $lastline );
// echo "---------------------------- \n\r" ;
// var_dump( $val ) ;
$date_var = explode( " ", $val[2] ) ;
$day = $date_var[1] ;
// for month conversion from text to number, see http://stackoverflow.com/questions/3283550/convert-month-from-name-to-number
$date = date_parse( $date_var[2] );
// echo "month --------------------- \n\r" ;
// var_dump( $date["month"] );
$month = $date["month"] ;
$year = $date_var[3] ;
$time = explode( ":", $date_var[4] ) ;
$heure = $time[0] ;
$minute = $time[1] ;
$seconde = $time[2] ;
$fuseau = $date_var[5] ;
// convertir en timestamp GMT (utiliser mktime si l'heure n'est pas GMT)
$gmt_timestamp = gmmktime( $heure, $minute, $seconde, $month, $day, $year ) ;
// convertir timestamp GMT en heure locale lisible par Eedomus 2016-08-17 08:47:00
$date_humain = date ( "d/m/Y H:i:s" , $gmt_timestamp ) ;
$date_eedomus = date ( "Y-m-d H:i:s" , $gmt_timestamp ) ;
// echo "time --------------------- \n\r" ;
// var_dump( $time ) ;
// echo "date_var --------------------- \n\r" ;
// var_dump( $date_var ) ;
echo "---------------------------- \n\r" ;
echo "°C : " . $val[1] . " date : " . $date_humain . "\n\r" ;
echo "---------------------------- \n\r" ;
/****************************************************
* now send the value to the Eedomus
* with al_set_eedomus_sensor( $id, $val, $date )
****************************************************/
include 'eedomus.php';
$id = 450118 ; // capteur eedomus 'température bureau'
al_set_eedomus_sensor( $id, $val[1], $date_eedomus ) ;
?>
Dans le Pi alradio, si je tape les commandes :
cd /home/jf/eedomus
./send-temp-to-eedomus.php
La console affiche les informations suivantes :

Et dans le tableau de bord Eedomus, la valeur a effectivement été modifiée.
Exécuter automatiquement le script d’envoi de données
Je vais utiliser le cron, comme dans l’article Supprimer les fichiers vidéo et image de plus de deux jours , à ceci près que c’est un fichier PHP que je vais exécuter.
J’utilise le générateur http://crontab-generator.org/ pour simplifier les choses.
J’ajoute donc la ligne suivante (SHIFT + INSER pour coller) au cron du Pi via crontab -e :
*/15 * * * * /usr/bin/php /home/jf/eedomus/send-temp-to-eedomus.php
Puis « reboot » à 14h17. Logiquement je devrais avoir une nouvelle valeur vers 14h30.
Ca fonctionne. Le capteur est effectivement mis à jour toutes les 15 minutes sur l’Eedomus.
par Anne-Laure DELPECH | 17 Août 2016 | php
J’ai besoin de lire le contenu de fichiers créés par une application tierce. Il faut :
- lister les fichiers avec extension ‘log’ dans un répertoire ;
- pour chaque fichier, lire chaque ligne ;
- pour chaque ligne, transformer son contenu texte en un array de données ;
- selon la valeur de certains éléments de la ligne, réaliser une action spécifique ;
- une fois le fichier traité, en modifier l’extension (devient ‘fait’).
Pour cela, j’utilise un script PHP installé dans le répertoire /var/www/html/tests d’un Raspberry Pi sur lequel j’ai installé apache et PHP (cf article Un serveur Web sur mon Raspberry Pi).
De quoi je pars ?
/var/www/html/tests contient le fichier test-explode.php et un sous répertoire data .
/var/www/html/tests/data contient 3 fichiers dont le nom est sous la forme domoAL_20150910.log . Chacun de ces fichiers contient 1000 à 2000 lignes de type :
1441868688 ! Thu, 10 Sep 2015 07:04:48 GMT ! dev_0x05 ! 2756 2 1462 1831 1931 0
1441868744 ! Thu, 10 Sep 2015 07:05:44 GMT ! dev_0x05 ! 2761 2 1462 1825 1918 0
1441868799 ! Thu, 10 Sep 2015 07:06:39 GMT ! dev_0x05 ! 2756 2 1462 1825 1950 0
1441868854 ! Thu, 10 Sep 2015 07:07:34 GMT ! dev_0x05 ! 2760 2 1468 1831 1993 868
1441868910 ! Thu, 10 Sep 2015 07:08:30 GMT ! dev_0x05 ! 2754 2 1468 1843 2037 0
1441868965 ! Thu, 10 Sep 2015 07:09:25 GMT ! dev_0x05 ! 2762 2 1468 1856 2075 0
Lister les fichiers d’un répertoire
Objectif : lister les fichiers avec extension ‘log’ dans un répertoire.
Ma solution est fortement inspirée de cette page de php.net.
<section class="data">
<H3>Test list each file in a directory</h3>
<?php
// http://php.net/manual/fr/function.scandir.php
echo "<br /><hr />" ;
$directory = '../tests/data';
$file_read = array( 'log' );
$scanned_directory = array_diff( scandir( $directory ), array( '..', '.' ) );
foreach ( $scanned_directory as $key => $value ) {
$type = explode( '.', $value );
$type = array_reverse( $type );
if( !in_array( $type[0], $file_read ) ) { // continue if not ".log"
continue;
}
$lines = 0;
$handle = fopen( $directory . DIRECTORY_SEPARATOR . $value, 'r' );
while ( !feof( $handle ) ) {
if ( is_bool( $handle ) ) {
break;
}
$line = fgets( $handle );
$lines++;
}
fclose( $handle );
$result['lines_html'][] = array ( $directory . '/' . $value , $lines ) ;
$result['lines_count'] = $result['lines_count'] + $lines;
$result['files_count'] = $result['files_count'] + 1;
}
var_dump( $result ) ;
echo "<br /><hr />" ;
?>
</section>
ce script affiche un array sur ma page (que j’ai mis en forme à la main) :
array(3) {
["lines_html"]=> array(3) {
[0]=> array(2) {
[0]=> string(33) "../tests/data/domoAL_20150910.log"
[1]=> int(1561)
}
[1]=> array(2) {
[0]=> string(33) "../tests/data/domoAL_20150913.log"
[1]=> int(1560)
}
[2]=> array(2) {
[0]=> string(33) "../tests/data/domoAL_20150914.log"
[1]=> int(1560)
}
}
["lines_count"]=> int(4681)
["files_count"]=> int(3)
}
Le script trouve donc bien les 3 fichiers et le nombre de ligne de chaque fichier.
Lire les lignes d’un fichier
Objectif : pour chaque fichier, lire chaque ligne.
Créer un array avec la liste des fichiers
Je crée une fonction qui génère un array avec le nom de chaque fichier du répertoire.
<?php
// http://php.net/manual/fr/function.scandir.php
// list all log files in the ../tests/data directory in an array
$list_files = al_list_files_in_dir( '../tests/data', array( 'log' ) ) ;
echo "<br />liste des fichiers<hr />" ;
var_dump( $list_files ) ;
echo "<br /><hr />" ;
function al_list_files_in_dir( $directory, $file_read ) {
$scanned_directory = array_diff( scandir( $directory ), array( '..', '.' ) );
foreach ( $scanned_directory as $key => $value ) {
$type = explode( '.', $value );
$type = array_reverse( $type );
if( !in_array( $type[0], $file_read ) ) { // continue if not ".log"
continue;
}
$result[] = $directory . '/' . $value ;
}
return $result ;
}
?>
J’obtiens un array comme celui ci (mis en forme à la main) :
array(3) {
[0]=> string(33) "../tests/data/domoAL_20150910.log"
[1]=> string(33) "../tests/data/domoAL_20150913.log"
[2]=> string(33) "../tests/data/domoAL_20150914.log"
}
Dans chaque fichier, lire les lignes une à une
<H4>read each line of each log file</h4>
<?php
foreach ( $list_files as $key => $value ) {
$file_content = al_read_lines_in_file( $value ) ;
echo "<br />contenu des fichiers<hr />" ;
var_dump( $file_content ) ;
echo "<br /><hr />" ;
}
function al_read_lines_in_file( $file ) {
$handle = fopen( $file, "r" );
$count = 0 ;
if ($handle) {
while (($line = fgets($handle)) !== false) {
$content[] = $line ;
}
fclose($handle);
return $content ;
} else {
echo "can't open file \n" ;
}
}
?>
Affiche 3 gigantesques arrays de 1500 lignes chacun environ.
Mettre le contenu d’une ligne dans un array
Objectif : pour chaque ligne, transformer son contenu texte en un array de données.
Chaque ligne contient des données séparées soit par « ! », soit par une tabulation. Je veux la transformer en une série de données.
<H3>Store the content of each line in an array</h3>
<?php
foreach ( $list_files as $key => $value ) {
$file_content = al_read_lines_in_file( $value ) ;
$line_count = 0 ;
foreach ( $file_content as $contents => $content ) {
$file_data[ $line_count ] = al_store_lines_in_array( $content ) ;
$line_count++ ;
}
echo "<br />données" . $value . "<hr />" ;
var_dump( $file_data ) ;
echo "<br /><hr />" ;
}
function al_store_lines_in_array( $text ) {
$val = explode("!", $text);
// split on tabulation
$temp = preg_split( "/[\t]/", trim( $val[3] ) );
// strip whitespace from the beginning and end of string.
$val[0] = trim( $val[0] );
$val[1] = trim( $val[1] );
$val[2] = trim( $val[2] );
$val[3] = trim( $val[3] );
$val[4] = trim( $temp[0] );
$val[5] = trim( $temp[1] );
$val[6] = trim( $temp[2] );
$val[7] = trim( $temp[3] );
$val[8] = trim( $temp[4] );
$val[9] = trim( $temp[5] );
return $val ;
}
?>
Pour le fichier ../tests/data/domoAL_20150910.log , ça me donne un array de 1560 arrays sous forme :
array(1560) {
[0]=> array(10) {
[0]=> string(10) "1441843202"
[1]=> string(29) "Thu, 10 Sep 2015 00:00:02 GMT"
[2]=> string(8) "dev_0x05"
[3]=> string(23) "2755 2 1531 1906 2168 0"
[4]=> string(4) "2755"
[5]=> string(1) "2"
[6]=> string(4) "1531"
[7]=> string(4) "1906"
[8]=> string(4) "2168"
[9]=> string(1) "0"
}
...
}
tout est sous la forme de texte (string). Il va falloir maintenant que je transforme la date « 1441843202 » et la dernière valeur (0 pour la ligne affiché ci-dessus) en données sur lesquelles je pourrai valider ou non une condition.
Mettre des conditions sur le contenu de l’array
Objectif : selon la valeur de certains éléments de la ligne, réaliser une action spécifique. Si le dernier élément de l’array n’est pas nul, l’afficher avec sa date. Si le temps écoulé depuis le dernier élément affiché est supérieur à 15 minutes, afficher la ligne même si sa dernière valeur est nulle.
conditions sur deux données
Je crée un array contenant des données comme celles qui doivent être produites par la lecture d’un des fichiers log. Je transforme les deux données sur lesquelles je veux pouvoir faire une condition en entiers avec la fonction intval() .
Pour le timestamp, je veux qu’il puisse être affiché de manière lisible : $date_humain = date ( « d/m/Y H:i:s » , $date_stamp ) ; va transformer 1441843313 en 10/09/2015 02:01:53 .
Le script suivant lit le contenu de l’array de données puis :
- si la dernière valeur ($value[9] ) est nulle, met $save à faux, sauf si ça fait plus de 5 minutes que $save est faux.
- si $save est true, imprime les données
Vu l’array que j’ai construit pour cet essai, je devrais avoir 4 lignes imprimées et une non (celle qui correspond à la deuxième valeur de l’array, qui a un timestamp très proche du premier).
<H3>Test conditions sur éléments de l'array de données</h3>
<?php
$data = array (
0 => array(
0 => "1441843202" ,
1 => "Thu, 10 Sep 2015 00:00:02 GMT" ,
2 => "dev_0x05" ,
3 => "2755 2 1531 1906 2168 0" ,
4 => "2755" ,
5 => "2" ,
6 => "1531" ,
7 => "1906" ,
8 => "2168" ,
9 => "0"
),
1 => array(
0 => "1441843258" ,
1 => "Thu, 10 Sep 2015 00:00:58 GMT" ,
2 => "dev_0x05" ,
3 => "2761 2 1537 1906 2137 0" ,
4 => "2761" ,
5 => "2" ,
6 => "1537" ,
7 => "1906" ,
8 => "2137" ,
9 => "0"
),
2 => array(
0 => "1441843313" ,
1 => "Thu, 10 Sep 2015 00:01:53 GMT" ,
2 => "dev_0x05" ,
3 => "2761 2 1537 1906 2137 0" ,
4 => "2761" ,
5 => "2" ,
6 => "1537" ,
7 => "1906" ,
8 => "2137" ,
9 => "216"
),
3 => array(
0 => "1441849352" ,
1 => "Thu, 10 Sep 2015 01:42:32 GMT" ,
2 => "dev_0x05" ,
3 => "2761 2 1537 1906 2137 0" ,
4 => "2761" ,
5 => "2" ,
6 => "1537" ,
7 => "1906" ,
8 => "2137" ,
9 => "0"
),
4 => array(
0 => "1441851457" ,
1 => "Thu, 10 Sep 2015 02:17:37 GMT" ,
2 => "dev_0x05" ,
3 => "2761 2 1537 1906 2137 0" ,
4 => "2761" ,
5 => "2" ,
6 => "1537" ,
7 => "1906" ,
8 => "2137" ,
9 => "51"
)
) ;
// var_dump( $data ) ;
// echo "<hr /><br />" ;
echo "<hr />" ;
$ref_time = 0000000 ;
foreach ( $data as $key => $value ) {
$date_stamp = intval( $value[0] ) ;
$date_humain = date ( "d/m/Y H:i:s" , $date_stamp ) ;
$time_lapsed = $date_stamp - $ref_time ; // temps écoulé depuis dernière ligne à imprimer
$chaudiere = intval( $value[9] ) ;
if ( $chaudiere == 0 ) {
$save = false ;
if ( $time_lapsed > 300 ) { // plus de 5 minutes
$save = true ;
$ref_time = $date_stamp ;
}
echo "<hr />" ;
} else {
$save = true ;
}
echo $key . " | \t" . $time_lapsed . " | \t" ;
if ( $save == true ) {
echo $chaudiere . " | \t" . $save . " | \t" . $date_stamp . " | \t" . $date_humain ;
} else {
echo "do not print" ;
}
echo "<hr />" ;
}
echo "<br />" ;
?>
et le résultat est conforme à mes attentes :

Modifier l’extension d’un fichier
Objectif : une fois le fichier traité, en modifier l’extension (devient ‘fait’).
ici j’ai fait beaucoup usage de /var/log/apache2/error.log !!! J’ai ainsi compris que j’obtenais une erreur « Permission denied » lorsque je voulais renommer.
Je n’ai jamais réussi à réaliser l’opération, même en la remplaçant par une copie puis une suppression du fichier initial… Puis, j’ai eu l’idée que c’est normal ! Heureusement que l’on ne peut pas supprimer ou renommer des fichiers au travers d’un navigateur internet…
j’ai donc créé un fichier test-rename.php (droits 740) dans le répertoire /home/jf/exec du Raspberry Pi. Dans le répertoire /home/jf/snd/data, j’ai placé les 3 fichiers à renommer. Et ça fonctionne !
#!/usr/bin/php
<?php
$directory = '/home/jf/snd/data';
$file_read = array( 'log' );
$scanned_directory = array_diff( scandir( $directory ), array( '..', '.' ) );
foreach ( $scanned_directory as $key => $value ) {
$type = explode( '.', $value );
// var_dump( $type ) ;
$type = array_reverse( $type );
if( !in_array( $type[0], $file_read ) ) { // continue if not ".log"
continue;
}
$old_name = $directory . DIRECTORY_SEPARATOR . $value ;
$new_name = $directory . DIRECTORY_SEPARATOR . $type[1] . ".fait" ;
$return_val = rename( $old_name, $new_name );
if ( $return_val == 1 ) {
echo "success : " . $old_name . " renamed " . $new_name . "\r\n";
} else {
echo "failed to rename " . $old_name . " to : " . $new_name . "\r\n" ;
}
}
?>
Ensuite dans la console de commande du Raspberry Pi :
cd /home/jf/exec
./test-rename.php
Et ça y est, la console affiche
success : /home/jf/snd/data/domoAL_20150910.log renamed /home/jf/snd/data/domoAL_20150910.fait
success : /home/jf/snd/data/domoAL_20150913.log renamed /home/jf/snd/data/domoAL_20150913.fait
success : /home/jf/snd/data/domoAL_20150914.log renamed /home/jf/snd/data/domoAL_20150914.fait
Et si je vérifie, les 3 fichiers ont bien été renommés.
Et maintenant ?
J’ai fait fonctionner les prototypes de code dont je vais avoir besoin. Je vais les utiliser pour faire échanger des données entre un Raspberry Pi (qui contient les fichiers log) et une box domotique qui va archiver et afficher les données.
par Anne-Laure DELPECH | 15 Août 2016 | Eedomus, Raspberry Pi
Dans ce second article de la série Un tableau de bord domotique, je veux afficher une température stockée dans ma box domotique Eedomus + à mon tableau de bord (en PHP, sur Raspberry Pi).
Cet article a été complété le 8 mai 2017.
De quoi je pars ?
d’une page index.php servie par un Raspberry Pi, comme indiqué dans le premier article de cette série.
l’API de la box Eedomus
Elle est bien documentée ici : API eedomus.
Obtenir une clé d’API Eedomus
Dans https://secure.eedomus.com/, cliquer sur configurer puis « mon compte ».
- Cocher la case « autoriser l’API via http » et « sauver ».
- Cliquer sur « consulter vos identifiants »

On obtient ainsi les valeurs de api_user et api_secret qui nous permettront de nous connecter. On accède aussi à un formulaire qui va nous permettre de construire la requête.
Faire une requête pour un capteur spécifique
J’utilise le formulaire précédent puis je copie la requête HTTP obtenue :

La requête est de type (remplacer api_user et api_secret) :
http://api.eedomus.com/get?api_user=xxx&api_secret=yyy&action=periph.caract&periph_id=166280
Lorsque je la tape dans un navigateur internet, j’obtiens la chaîne JSON suivante :
{ "success": 1, "body":{"periph_id": "166280", "name": "Nest - Température Couloir RdC", "last_value": "24.5", "last_value_text": "", "last_value_change": "2016-08-15 15:50:55"}}
Pour lire cette donnée dans mon fichier PHP, je me suis inspirée de l’exemple donné à la fin du document API eedomus.
J’ai intégré le code suivant à ma page index.php :
<h3>Autres informations</h3>
<?php include 'eedomus.php';?>
<!-- nest temperature ID 166280 -->
<p>couloir : <?php echo al_get_eedomus_value( 166280, 'last_value' ) ; ?> °C</p>
Et dans le fichier eedomus.php , j’ai intégré une fonction al_get_eedomus_value :
<?php
// définition des variables
$api_user = 'xxx'; // a récupérer sur votre compte eedomus
$api_secret = 'yyy'; // a récupérer sur votre compte eedomus
/******************************************************************
* appel de l'API eedomus en PHP pour récupérer une donnée
*
* $id = 166280
* $val = 'last_value'
* retournera la température actuellement mesurée par le thermostat Nest
*
*****************************************************************/
function al_get_eedomus_value( $id, $val ) {
global $api_user ;
global $api_secret ;
// construction de l'URL de l'API
$url = "http://api.eedomus.com/get?action=periph.caract";
$url .= "&api_user=" . $api_user;
$url .= "&api_secret=" . $api_secret;
$url .= "&periph_id=" . $id;
// appel de l'API
$result = file_get_contents($url);
// on controle le résultat
if (strpos($result, '"success": 1') == false)
{
echo "Une erreur est survenue : [".$result."]";
}
else
{
$result = json_decode( $result, true ) ; // true transforme $result en un array
$retour = $result['body'][$val];
return $retour ;
}
}
Le résultat est maintenant affiché sur mon tableau de bord :

Mais les données ne sont mises à jour que si je rafraichis le tableau de bord manuellement.
Mettre à jour automatiquement les données de la page PHP
Cette question sur StackOverflow explique comment recharger une page toutes les 30 secondes. Il suffit d’ajouter <meta http-equiv= »refresh » content= »30″ /> en haut de la page PHP.
Comme 15 minutes = 900 secondes, j’ai intégré la ligne <meta http-equiv= »refresh » content= »900″ /> en haut de ma page PHP. Le haut contient donc maintenant :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Nautilus</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="A-style.css" rel="stylesheet" media="all" type="text/css">
<meta http-equiv="refresh" content="900" />
</head>
Complément (8 mai 2017)
J’ai affiné le contenu de ma page php pour avoir automatiquement la liste des périphériques de l’eedomus. C’est un préalable à un tableau de bord qui lirai automatiquement toutes les données de l’eedomus.
Obtenir la liste des périphériques
Dans le code ci-dessous, la fonction eedomus_url génère les url correctes pour :
- vérifier qu’on se connecte correctement à la box Eedomus ;
- Obtenir la liste des périphériques de la box Eedomus ;
- Lire la valeur d’un périphérique donné de la box Eedomus
la fonction ald_decode_french permet de lire correctement des textes encodés en français, avec des caractères spéciaux.
<?php
function eedomus_url( $action, $user_id, $pw ) {
/*
* eedomus_url( "&action=auth.test", $ee_apiuser, $ee_passwd ) construit
* https://api.eedomus.com/get?api_user=USER&api_secret=SECRET&action=auth.test
* eedomus_url( "&action=periph.list", $ee_apiuser, $ee_passwd ) construit
* https://api.eedomus.com/get?api_user=USER&api_secret=SECRET&action=periph.list
* eedomus_url( "&action=periph.caract&periph_id=156595", $ee_apiuser, $ee_passwd ) construit
* https://api.eedomus.com/get?api_user=USER&api_secret=SECRET&action=periph.caract&periph_id=156595
*
* ces 3 url renvoient bien une info en Json, en provoenance de l'eedomus dans un navigateur
*/
$eedomus_get = "https://api.eedomus.com/get?api_user=" ;
$eedomus_get .= $user_id ;
$eedomus_get .= "&api_secret=" ;
$eedomus_get .= $pw ;
$eedomus_get .= $action ;
return $eedomus_get ;
}
$ee_test = eedomus_url( "&action=auth.test", $ee_apiuser, $ee_passwd ) ;
$ee_list = eedomus_url( "&action=periph.list", $ee_apiuser, $ee_passwd ) ;
$ee_156595 = eedomus_url( "&action=periph.caract&periph_id=156595", $ee_apiuser, $ee_passwd ) ;
function ald_decode_french( $string ) {
$contents = utf8_encode( $string );
$results = json_decode( $contents );
return $results ;
}
Si j’utilise la fonction ald_decode_french sans la partie ‘utf8_encode’, le résultat est vide car les accents provoquent des erreurs. Ci-dessous, dans l’image, à gauche on a le résultat de <pre><?php print_r ( file_get_contents( $ee_list ) ) ; ?> </pre> et à droite le résultat de <pre><?php print_r ( ald_decode_french( file_get_contents( $ee_list ) ) ); ?> </pre> . Les accents sont correctement restitués dans la version de droite.

Et maintenant ?
Je vais maintenant voir comment un Raspberry Pi peut transmettre une valeur mesurée à l’Eedomus. Ce sera l’objet du prochain article de cette série Un tableau de bord domotique. Ensuite, je pourrai utiliser le présent tutoriel pour l’afficher sur mon tableau de bord.
par Anne-Laure DELPECH | 15 Août 2016 | php, Raspberry Pi
Aujourd’hui, je veux lire des données issues d’une station météo proche de chez moi via l’API wunderground. Je souhaite qu’elles s’affichent automatiquement dans mon tableau de bord domotique (une page PHP servie par un Raspberry Pi).
De quoi je pars ?
J’ai réglé un Raspberry Pi pour servir une page PHP qui affiche le flux de deux caméras de surveillance (voir l’article Récapitulatif : Raspberry Pi, motion et deux caméras).
Maintenant, je veux ajouter des données issues de capteurs externes ou non. Je commence par insérer des données issues d’une station météo externe, via l’API de wunderground.
Une API ?
J’ai suivi le très bon cours en ligne sur les API de Emily REESE, sur OpenClassRoom ici.
Une API, ou « Application Programming Interface », est une interface entre un utilisateur et une application. Un menu constitue l’API d’un restaurant par exemple. En informatique, lorsqu’on parle d’API, on parle en général d’API REST (cf sur wikipedia). Elles utilisent des instructions HTTP.
En général les requêtes HTTP à des API REST obtiennent une réponse en JSON (cf sur wikipedia).
L’API de wunderground
Wunderground est un site qui met à disposition des informations météos. Les personnes qui possèdent une station météo peuvent lui communiquer des données. On a ainsi accès à des données très locales.
J’ai identifié une station locale à Plonéour-Lanvern. Je veux en extraire des données.
Je me suis créé un compte sur Wunderground et j’ai généré une clé API : voir cet article en français pour savoir comment faire.
Avec la console apigee intégrée à wunderground ici, j’ai pu déterminer que la requête est sous la forme :
http://api.wunderground.com/api/APIkey/conditions/q/pws:Station-ID.json
avec APIkey qui est ma clé API (elle ressemble à « b8e924a8f008b81e
« ) et Station-ID est égale à « IPLONOUR3 » pour la station qui m’intéresse. On obtient l’ID de la station en cliquant sur le nom de la station (flèche rouge dans la copie d’écran ci-dessous). j’accède alors à une page dont l’adresse est https://www.wunderground.com/personal-weather-station/dashboard?ID=IPLONOUR3#history. J’ai donc l’ID.

Faire une requête en PHP
Je me suis inspirée de cette question sur StackOverflow pour créer ma requête.
Si je met http://api.wunderground.com/api/APIkey/conditions/q/pws:IPLONOUR3.json (remplacer APIkey par votre clé API) dans un navigateur web, j’obtiens en retour une chaîne JSON :
{
"response": {
"version":"0.1",
"termsofService":"http://www.wunderground.com/weather/api/d/terms.html",
"features": {
"conditions": 1
}
}
, "current_observation": {
"image": {
"url":"http://icons.wxug.com/graphics/wu2/logo_130x80.png",
"title":"Weather Underground",
"link":"http://www.wunderground.com"
},
"display_location": {
"full":"Ploneour-Lanvern, France",
"city":"Ploneour-Lanvern",
"state":"",
"state_name":"France",
"country":"FR",
"country_iso3166":"FR",
"zip":"00000",
"magic":"34",
"wmo":"07201",
"latitude":"47.900333",
"longitude":"-4.287129",
"elevation":"60.00000000"
},
"observation_location": {
"full":"Rue François de Châteaubriand, Plonéour-Lanvern, ",
"city":"Rue François de Châteaubriand, Plonéour-Lanvern",
"state":"",
"country":"FR",
"country_iso3166":"FR",
"latitude":"47.900333",
"longitude":"-4.287129",
"elevation":"206 ft"
},
"estimated": {
},
"station_id":"IPLONOUR3",
"observation_time":"Last Updated on August 15, 3:01 PM CEST",
"observation_time_rfc822":"Mon, 15 Aug 2016 15:01:45 +0200",
"observation_epoch":"1471266105",
"local_time_rfc822":"Mon, 15 Aug 2016 15:04:53 +0200",
"local_epoch":"1471266293",
"local_tz_short":"CEST",
"local_tz_long":"Europe/Paris",
"local_tz_offset":"+0200",
"weather":"Clear",
"temperature_string":"79.9 F (26.6 C)",
"temp_f":79.9,
"temp_c":26.6,
"relative_humidity":"48%",
"wind_string":"Calm",
"wind_dir":"NE",
"wind_degrees":50,
"wind_mph":0.6,
"wind_gust_mph":"5.0",
"wind_kph":1.0,
"wind_gust_kph":"8.0",
"pressure_mb":"1018",
"pressure_in":"30.06",
"pressure_trend":"0",
"dewpoint_string":"59 F (15 C)",
"dewpoint_f":59,
"dewpoint_c":15,
"heat_index_string":"81 F (27 C)",
"heat_index_f":81,
"heat_index_c":27,
"windchill_string":"NA",
"windchill_f":"NA",
"windchill_c":"NA",
"feelslike_string":"81 F (27 C)",
"feelslike_f":"81",
"feelslike_c":"27",
"visibility_mi":"N/A",
"visibility_km":"N/A",
"solarradiation":"--",
"UV":"-1","precip_1hr_string":"-999.00 in ( 0 mm)",
"precip_1hr_in":"-999.00",
"precip_1hr_metric":" 0",
"precip_today_string":"0.00 in (0 mm)",
"precip_today_in":"0.00",
"precip_today_metric":"0",
"icon":"clear",
"icon_url":"http://icons.wxug.com/i/c/k/clear.gif",
"forecast_url":"http://www.wunderground.com/global/stations/07201.html",
"history_url":"http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=IPLONOUR3",
"ob_url":"http://www.wunderground.com/cgi-bin/findweather/getForecast?query=47.900333,-4.287129",
"nowcast":""
}
}
Les données qui m’intéressent sont :
- « current_observation » -> « display_location » -> « city »
- « current_observation » -> « temp_c »
- « current_observation » -> « relative_humidity »
- « current_observation » -> « wind_gust_kph »
- « current_observation » -> « forecast_url »
En PHP, j’ajoute le code suivant à mon fichier index.php :
<section class="capteurs">
<h3>wunderground</h3>
<?php
// http://stackoverflow.com/questions/20044579/how-to-get-a-value-from-wunderground-json
$json_string = file_get_contents("http://api.wunderground.com/api/APIkey/conditions/q/pws:IPLONOUR3.json");
$parsed_json = json_decode($json_string);
$location = $parsed_json->{'current_observation'}->{'display_location'}->{'city'};
$temp_c = $parsed_json->{'current_observation'}->{'temp_c'};
$humidity = $parsed_json->{'current_observation'}->{'relative_humidity'};
$wind_gust_km = $parsed_json->{'current_observation'}->{'wind_gust_kph'};
$forecast_url = $parsed_json->{'current_observation'}->{'forecast_url'};
echo "A ${location} : ${temp_c} °C, ${humidity} HR, rafales à ${wind_gust_km} km/h - <a target='_blank' href='${forecast_url}'>prévisions</a>. \n";
?>
</section>
Et maintenant mon tableau de bord affiche la température, l’humidité, la vitesse des rafales de vent et un lien vers les prévisions météo locales :

Et maintenant ?
Je vais maintenant intégrer une donnée issue de ma box domotique Eedomus. Ce sera l’objet du second article de cette série Un tableau de bord domotique.
Commentaires récents