Notre extension a été démarrée dans les premiers articles de cette série (Un plugin WordPress avec page de réglage). A ce stade, elle affiche uniquement des champs de type « texte » et n’affiche pas le contenu des arguments supplémentaires que l’on peut ajouter au champs.
Nous allons donc voir maintenant comment afficher des champs de type textarea, select, checkbox, radio button, wysiwig.
Modifier la fonction de restitution des champs
Dans le fichier « /admin/clea-add-button-settings-page.php « , il faut modifier la fonction clea_add_button_settings_field_callback( $arguments ) pour qu’elle s’adapte à différents types de champs.
Nous utilisons la commande « switch » qui choisira l’action à réaliser selon le ‘type’ de champs à restituer :
switch( $arguments['type'] ){
case 'text' :
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
break ;
default :
printf( esc_html__( 'This field is type <em>%s</em> and could not be rendered.', 'clea-add-button' ), $arguments['type'] );
}
Ca fonctionne : le champs 1-1 est restitué comme un texte mais pas les autres puisque nous n’avons pas défini leur type pour l’instant.
Il va donc falloir que je modifie l’array qui définit les champs, dans la fonction clea_add_button_settings_fields_val() . Je veux que tous les champs soient définis par un array avec la forme suivante :
Noter que l’on utilise la fonction esc_textarea() pour interdire qu’un caractère présent dans la variable $description puisse être interprété comme du HTML.
autres types de champs, probablement avec esc_attr() aussi.
Affichage du « textarea » plus précis
Dans la référence HTML5 du W3C, la page dédiée à <textarea> indique une multitude d’arguments pour définir sa forme. Et les attributs globaux, applicables à tous les éléments HTML5 contiennent entre autre « id » et « value ». Nous allons au minimum vouloir définir « rows » et « cols », respectivement le nombre de lignes et de colonnes de la zone de texte à définir. Au final, l’expression de notre textarea sera sous la forme :
Pour tous nos champs, les attributs devront prendre une valeur liée à la définition du champs. J’utilise printf pour rendre plus simple la définition de ce qui doit être affiché.
Et voici notre fonction modifiée pour utiliser esc_attr pour le cas ‘text’ et pour afficher correctement notre zone textarea :
function clea_add_button_settings_field_callback( $arguments ) {
$settings = (array) get_option( 'my-plugin-settings' );
$field = $arguments['field_id'] ;
$value = $settings[$field] ;
// for development only
if ( ENABLE_DEBUG ) {
echo "<hr /><pre>";
print_r( $arguments ) ;
echo "</pre><hr />";
}
$name = 'my-plugin-settings['. $field . ']' ;
switch( $arguments['type'] ){
case 'text' :
printf( '<input type="text" id="%3$s" name="%2$s" value="%1$s" />', esc_attr( $value ), $name, $field );
break ;
case 'textarea' :
printf( '<textarea name="%2$s" id="%3$s" rows="4" cols="80" value="%1$s">%1$s</textarea>', esc_textarea( $value ), $name, $field );
break ;
default :
printf( esc_html__( 'This field is type <em>%s</em> and could not be rendered.', 'clea-add-button' ), $arguments['type'] );
}
}
A ce stade, notre page de réglages affiche :
Affichage correct d’un champs text et d’un champs textarea
Correction d’une notice PHP
Query Monitor (cf l’article précédent de cette série « Un plugin WordPress avec page de réglage » indique 3 notices PHP. Ce sont des erreurs bénignes qu’il faut corriger.
Les trois notices portent sur des index indéfinis pour field-2-1, field-2-2 et field-2-3, sous la forme « Undefined index: field-2-1 ». Ces trois champs n’ont jamais été définis puisqu’on n’a pas encore pu leur attribuer une valeur. Cependant, il n’est pas normal que notre code traite des valeurs indéfinies sans « s’en rendre compte ».
Dans cet article de code.tutsplus, je vois qu’ils font deux vérifications avant de lire la valeur de l’option. La première consiste à vérifier qu’il existe bien des options pour notre réglage (‘my-plugin-settings’ pour moi, ‘sandbox_theme_display_options’ pour eux :
La seconde vérification porte sur l’option du champs en question :
// First, we read the social options collection
$options = get_option( 'sandbox_theme_social_options' );
// Next, we need to make sure the element is defined in the options. If not, we'll set an empty string.
$url = '';
if( isset( $options['twitter'] ) ) {
$url = $options['twitter'];
} // end if
Cette partie là est réalisée dans la fonction « callback » du champs ‘twitter’.
Là dessus, j’ai longtemps bloqué car je dois vérifier si $settings[ $field ] est « isset » et pour une raison que j’ignorais ce n’était jamais le cas, même lorsqu’il y avait bien une valeur… Et puis je suis tombée sur cette question dans wordpress.stackexchange et j’ai enfin compris comment faire (mais pas franchement pourquoi !).
Ma fonction clea_add_button_settings_field_callback( $arguments ) contient maintenant une vérification au début. Si $options[ « $field » ] est « set », $value prend la valeur de $settings[ $field ] . Dans le cas contraire, elle prend la valeur par défaut du champs.
function clea_add_button_settings_field_callback( $arguments ) {
$settings = (array) get_option( "my-plugin-settings" );
$field = $arguments[ 'field_id' ] ;
// for development only
if ( ENABLE_DEBUG ) {
echo "<hr /><p>Arguments</p><pre>";
print_r( $arguments ) ;
echo "</pre><hr />";
echo "<hr /><p>Options</p><pre>";
print_r( $settings ) ;
echo "</pre><hr />";
}
// set a $options array with the field id as it's key
if ( !empty( $settings ) ) {
foreach ( $settings as $key => $option ) {
$options[$key] = $option;
}
}
// now check if $options[ $field ] is set
if( isset( $options[ "$field" ] ) ) {
$value = $settings[ $field ] ;
} else {
// set the value to the default value
$value = $arguments[ 'default' ] ;
}
$name = 'my-plugin-settings['. $field . ']' ;
switch( $arguments['type'] ){
case 'text' :
printf( '<input type="text" id="%3$s" name="%2$s" value="%1$s" />', esc_attr( $value ), $name, $field );
break ;
case 'textarea' :
printf( '<textarea name="%2$s" id="%3$s" rows="4" cols="80" value="%1$s">%1$s</textarea>', esc_textarea( $value ), $name, $field );
break ;
default :
printf( esc_html__( 'This field is type <em>%s</em> and could not be rendered.', 'clea-add-button' ), $arguments['type'] );
}
}
Les notices PHP pour les champs qui n’ont pas encore pu être saisis ont maintenant disparu.
Si les options ne sont pas fixes (cas où il y a plusieurs champs select), il faut pouvoir les ajouter dans l’array de définition du champs. Je crée un troisième champs dans la section 1 avec les caractéristiques suivantes :
Si les options ne sont pas fixes, il faut pouvoir les ajouter dans l’array de définition du champs. Je modifie le premier champ de la section 2 comme pour le champs de type radio, en y ajoutant des options.
Pour le visualiser, j’ajoute le cas ‘radio’ comme suit :
Et voilà maintenant la section 2 de ma page de réglage contient un premier champs radio correctement présenté :
Affichage d’un champs « radio »
Afficher des champs « wysiwig «
Notre champs wisiwig correspond à un éditeur, un peu comme lorsqu’on crée un article dans WordPress. Il est restitué par la fonction wp_editor de WordPress. Ici j’ai choisi de ne pas ajouter d’arguments spécifiques.
Pour le visualiser, j’ajoute le cas ‘wysiwig’ comme suit :
case 'wysiwig' :
// sanitize data for the wp_editor
$content = wp_kses_post( $value ) ;
$args = array(
'textarea_name' => $name
) ;
wp_editor( $content, $field, $args );
break ;
Le Codex pour wp_editor() indique que l’ID de l’éditeur ne peut contenir que des minuscules et des tirets bas (underscores). J’ai donc modifié le ‘field_id’ pour qu’il devienne field_2_2.
On notera que $value est nettoyé avec la fonction wp_kses_post(), qui permet entre autres d’avoir des images dans le texte.
Et voilà maintenant la section 2 contient un éditeur de texte :
Affichage d’un champs « wp-editor »
Et maintenant
Nous verrons dans le prochain article de cette série (Un plugin WordPress avec page de réglage) comment restituer le dernier champs comme un color picker, et aussi comment afficher les arguments supplémentaires.
Lorsqu’on crée une extension (ou un thème) WordPress, il est indispensable de prévoir des moyens de déboguer efficacement. Nous allons voir ici quelques méthodes applicables, telles que l’affichage du contenu de variables dans une page du tableau de bord WordPress, l’utilisation de debug.log de WordPress ou l’utilisation d’extensions WordPress telles que Debug Bar et Query Monitor.
Sur un site de développement (mais jamais sur un site de « production » utilisable par nos internautes cibles), on modifie wp-config.php pour que le debogage soit réalisé et que les erreurs s’affichent lors de l’affichage des pages du site (front ou back). Par défaut, les paramètres suivants sont réglés sur « false » :
Dans ce cas, s’il y a une erreur importante, notre page web affiche un contenu blanc, sans aucune indication du type d’erreur rencontré. Si je met ces paramètres à ‘true’, j’aurai un affichage de l’erreur à l’écran et je pourrai y remédier :
Exemple : je crée volontairement une erreur (par exemple en enlevant un « ; » à la ligne 45 de clea-add-button-settings-page.php. Je recharge la page d’options ou je vais sur le site et le site a disparu. A la place, il y a une page blanche, avec le message « Fatal error: syntax error, unexpected ‘$set_sections’ (T_VARIABLE) in /home/cecilebo/test1/wp-content/plugins/clea-add-button/admin/clea-add-button-settings-page.php on line 47″. Je sais immédiatement dans quel fichier aller corriger l’erreur. Je regarde les lignes qui précèdent la ligne 47 : la 46 est vide et c’est plutôt simple d’ajouter le « ; » manquant en ligne 45.
Donc, dès qu’on est sur un site de développement, on régle les paramètres de wp-config.php pour afficher les erreurs :
On peut aussi régler la ligne suivante à « true » :
define('WP_DEBUG_LOG', true);
Dans ce cas, les erreurs sont stockées dans un fichier debug.log situé dans le répertoire /wp-content. C’est pratique si on doit absolument intervenir sur un site de production et qu’on ne peut pas avoir WP_DEBUG_DISPLAY réglé à « true ». Mais ça oblige à aller sans arrêt éditer le contenu de debug.log pour voir ce qui se passe. Je n’aime pas beaucoup, sauf cas particuliers comme lorsque je veux afficher des contenus de variables dans un fichier (voir plus loin).
Utilisation d’extensions spécialisées
l’extension « Debug Bar »
j’utilise depuis longtemps l’extension ‘Debug Bar‘. Elle ajoute un élément « debug » à gauche du nom Jde l’utilisateur dans le tableau de bord WordPress. Si on clique dessus, et que WP_DEBUG est réglé à ‘true’, on voit s’afficher la liste des notices et warnings auxquels il faut qu’on fasse attention. Lorsqu’il y a des warnings, l’élément « debug » devient rouge pour nous alerter.
Avantages :
de nombreuses autres extensions d’aide au développement s’appuient sur Debug Bar. J’utilise par exemple Debug Bar Post Types lorsque je crée des custom post types. Le « Plugin Handbook » de WordPress contient une page dédiée à ces extensions complémentaires. Je remarque en particulier Debug Bar Shortcodes, que je n’ai jamais utilisé mais qui pourrait être utile.
Inconvénients :
on doit jongler entre différents écrans pour visualiser ce qui se passe dans notre site.
Debug Bar est très bien, mais il y a encore mieux !
La solution idéale : l’extension ‘Query Monitor’
Depuis que j’ai découvert cette extension, je ne lui ai pas trouvé un seul défaut. Elle permet de recueillir toutes les informations nécessaires au développement tout en étant pratique. C’est surtout l’interface utilisateur qui fait la différence.
Pour plus d’informations, voir sur WordPress.org, ici. Je note que les FAQ indiquent que les compléments de Debug Bar puvent être utilisés avec Query Monitor, sans que Debug Bar soit présent. Et il existe aussi des compléments pour Query Monitor. Je note en particulier les compléments suivants, que je n’ai jamais essayés :
Query Monitor Extend qui permet de faire des « var_dump » visible dans l’écran Query Monitor et de suivre les conditions de WooCommerce.
Il arrive que j’utilise « User Switching » lorsque j’ai besoin de tester avec différents rôles d’utilisateur.
Afficher le contenu d’une variable pour aider au développement / débogage
Ce n’est pas toujours simple de savoir ce que contient une variable définie par notre programme ou par WordPress. C’est en particulier difficile lorsque la variable est un array, dont on ne connait pas la structure, ou pire une chaîne de caractères « sérialisée » (« serialized data » en anglais) comme le contenu de get_option .
Pour afficher des variables, on a plusieurs options :
Afficher la variable directement dans la page que l’on crée ;
Afficher la variable directement dans la page que l’on crée
C’est une solution simple. Par exemple, dans clea-add-button-settings-page.php, pour définir le contenu de la fonction clea_add_button_settings_field_callback( $arguments ), qui affiche les différents champs, j’ai affiché le contenu de l’array $arguments :
l’ajout de <pre> et </pre> permet un affichage propre de l’array.
Mais l’inconvénient d’un tel choix est qu’on doit ensuite commenter toutes les lignes de débogage. J’ai donc fait le choix de définir une constante en début de fichier :
/**********************************************************************
* DEBUG ?
***********************************************************************/
define('ENABLE_DEBUG', true); // if true, the script will echo debug data
et ma fonction function clea_add_button_settings_field_callback( $arguments ) contient :
function clea_add_button_settings_field_callback( $arguments ) {
$settings = (array) get_option( 'my-plugin-settings' );
$field = $arguments['field_id'] ;
$value = esc_attr( $settings[$field] );
// for development only
if ( ENABLE_DEBUG ) {
echo "<hr /><pre>";
print_r( $arguments ) ;
echo "</pre><hr />";
}
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
Comme ça, si j’ai réglé ENABLE_DEBUG à true, je voies le contenu de l’array et sinon je suis en mode utilisation normale de l’extension :
plugin page avec debogage false
plugin page avec debogage true
C’est une bonne solution, que je n’ai trouvée que récemment. Mais elle ne fonctionne pas si je n’ai pas une page dédiée à l’extension dans le tableau de bord WordPress. Il peut donc être utile d’afficher ailleurs le contenu des variables. Dans ce cas, on utilisera une extension spécialisée.
Une page de réglage « check variables » apparaît dans « Réglages ».
Pour les deux exemples de réglage qui suivent, j’ai ajouté une ligne à la fonction clea_add_button_settings_section_callback( $args ) :
console( $args );
On peut faire apparaître les variables dans le pied de page comme dans le réglage qui suit :
Réglage de Check Variable pour affichage dans le pied de page
Affichage de Check Variable dans le pied de page
On peut aussi préférer afficher les variables dans Query Monitor comme dans le réglage qui suit :
Affichage de Check Variable dans la page Query Monitor
Le menu de Query Monitor
Réglage de Check Variable pour affichage dans l’écran Query Monitor
C’est pratique comme système.
Et maintenant ?
Une fois qu’on sait ce que contiennent nos variables, tout devient plus simple !
Nous allons poursuivre le développement de l’extension « Add Button » et améliorer la page de réglage pour qu’elle puisse afficher des formats de champs autres que texte. Ce sera l’objet du prochain article de cette série Un plugin WordPress avec page de réglage.
Je poursuis le travail sur la création d’une extension « best practice » généré dans l’article précédent (Créer une extension WordPress ; Partie 1) de cette série. Une extension sur laquelle le webmaster ne peut pas intervenir n’est pas très intéressante puisqu’elle oblige à modifier le code à chaque fois qu’un paramètre change. J’explore donc ici la manière de créer une page de réglage pour l’extension. J’utilise l’API « settings » de WordPress car c’est une méthode plus sûre (la « sanitation » des données est assurée par WordPress).
L’extension est au stade où je l’ai laissée dans le premier article de cette série :
Premier essai
J’ai fait plusieurs essais avant celui-ci mais à chaque fois j’avais un « petit » problème dont je n’arrivais pas trouver la solution. Je préfère donc repartir de zéro pour comprendre. J’ai tout simplement copié le contenu du « gist » de Anna et je l’ai placé dans un fichier « clea-add-button-settings-page.php » du répertoire /admin. Dans le fichier principal de l’extension (clea-add-button.php), j’ai ajouté la ligne require_once CLEA_ADD_BUTTON_DIR_PATH . ‘admin/clea-add-button-settings-page.php’; et j’ai rechargé la fenêtre de mon navigateur.
Une page de réglage « My Plugin Options » apparaît sous « réglages » (la copie d’écran correspond à ce qui est affiché une fois que j’ai enregistré une première fois des données) :
Essai 1 de page de réglage d’une extension
Et dans la base de données, la table « prefix-options » contient une nouvelle ligne (option_name = my-plugin-settings), qui contient :
C’est donc exactement ce que je cherche à faire. Reste maintenant à le faire mieux !
Qu’est-ce que je veux faire ?
Je veux obtenir une page de réglage avec plusieurs sections et des champs qui pourraient être présentés sous forme de texte court, de texte long (textarea), de cases à cocher (radio ou checkbox), de liste déroulante, d’un éditeur wysiwig pour des textes sophistiqués et d’un sélecteur de couleur (color picker).
Je voudrais aussi pouvoir définir les sections et les champs dans des « arrays » (tableaux) et générer automatiquement la page de réglages.
Enfin, je voudrais qu’une case à cocher permette d’afficher des éléments de débogage à la fin de la page de réglages si je le souhaite.
Etape 1 : générer automatiquement l’exemple ci-dessus
Je vais modifier le gist de Anna pour générer automatiquement sa page de réglages avec moins de code.
Respecter les bonnes pratiques :
La première chose que je fais, c’est de remplacer ‘textdomain’ par ‘clea-add-button’ et de mettre un préfixe clea_add_button_ devant toutes les fonctions.
regrouper la définition des sections et champs, automatiser la génération
J’ai regroupé la définition des sections à la fin, dans la fonction clea_add_button_settings_sections_val() . Idem pour la définition des champs, dans la fonction clea_add_button_settings_sections_val() .
Ainsi la fonction clea_add_button_admin_init() ne contient plus qu’une boucle « foreach », qui lit les arrays de sections ou champs à générer et les génère.
<?php
/**
*
* Créer une page de settings pour l'extension
*
* @link
* @since 0.2.0
*
* @package clea-add-button
* @subpackage clea-add-button/includes
* Text Domain: clea-add-button
*/
// Based on Anna's gist https://gist.github.com/annalinneajohansson/5290405
// http://codex.wordpress.org/Settings_API
/**********************************************************************
* to set the title of the setting page see -- clea_add_button_options_page()
* to set the sections see -- clea_add_button_settings_sections_val()
* to set the fields see -- clea_add_button_settings_fields_val()
**********************************************************************/
// create the settings page and it's menu
add_action( 'admin_menu', 'clea_add_button_admin_menu' );
// set the content of the admin page
add_action( 'admin_init', 'clea_add_button_admin_init' );
function clea_add_button_admin_menu() {
add_options_page(
__('Options de Clea Add Button', 'clea-add-button' ), // page title (H1)
__('Options', 'clea-add-button' ), // menu title
'manage_options', // required capability
'my-plugin', // menu slug (unique ID)
'clea_add_button_options_page' ); // callback function
}
function clea_add_button_admin_init() {
register_setting( 'my-settings-group', 'my-plugin-settings' );
$set_sections = clea_add_button_settings_sections_val() ;
// add_settings_section
foreach( $set_sections as $section ) {
add_settings_section(
$section[ 'section_name' ],
$section[ 'section_title' ] ,
$section[ 'section_callbk' ],
$section[ 'menu_slug' ]
);
}
$set_fields = clea_add_button_settings_fields_val() ;
// add the fields
foreach ( $set_fields as $section_field ) {
foreach( $section_field as $field ){
add_settings_field(
$field['field_id'],
$field['label'],
$field['field_callbk'],
$field['menu_slug'],
$field['section_name']
);
}
}
}
/**********************************************************************
* The actual page
**********************************************************************/
function clea_add_button_options_page() {
?>
<div class="wrap">
<h2><?php _e('My Plugin Options', 'clea-add-button'); ?></h2>
<form action="options.php" method="POST">
<?php settings_fields('my-settings-group'); ?>
<?php do_settings_sections('my-plugin'); ?>
<?php submit_button(); ?>
</form>
</div>
<?php }
function section_1_callback() {
_e( 'Some help text regarding Section One goes here.', 'clea-add-button' );
}
function section_2_callback() {
_e( 'Some help text regarding Section Two goes here.', 'clea-add-button' );
}
function field_1_1_callback() {
$settings = (array) get_option( 'my-plugin-settings' );
$field = "field_1_1";
$value = esc_attr( $settings[$field] );
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
function field_1_2_callback() {
$settings = (array) get_option( 'my-plugin-settings' );
$field = "field_1_2";
$value = esc_attr( $settings[$field] );
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
function field_2_1_callback() {
$settings = (array) get_option( 'my-plugin-settings' );
$field = "field_2_1";
$value = esc_attr( $settings[$field] );
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
function field_2_2_callback() {
$settings = (array) get_option( 'my-plugin-settings' );
$field = "field_2_2";
$value = esc_attr( $settings[$field] );
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
/*
* INPUT VALIDATION:
* */
function clea_add_button_settings_validate_and_sanitize( $input ) {
$settings = (array) get_option( 'my-plugin-settings' );
if ( $some_condition == $input['field_1_1'] ) {
$output['field_1_1'] = $input['field_1_1'];
} else {
add_settings_error( 'my-plugin-settings', 'invalid-field_1_1', 'You have entered an invalid value into Field One.' );
}
if ( $some_condition == $input['field_1_2'] ) {
$output['field_1_2'] = $input['field_1_2'];
} else {
add_settings_error( 'my-plugin-settings', 'invalid-field_1_2', 'You have entered an invalid value into Field One.' );
}
// and so on for each field
return $output;
}
/**********************************************************************
* THE SECTIONS
**********************************************************************/
function clea_add_button_settings_sections_val() {
$sections = array(
array(
'section_name' => 'section-1',
'section_title' => __( 'Section One', 'clea-add-button' ),
'section_callbk'=> 'section_1_callback',
'menu_slug' => 'my-plugin' ,
),
array(
'section_name' => 'section-2',
'section_title' => __( 'Section Two', 'clea-add-button' ),
'section_callbk'=> 'section_2_callback' ,
'menu_slug' => 'my-plugin'
),
);
return $sections ;
}
/**********************************************************************
* THE FIELDS
**********************************************************************/
function clea_add_button_settings_fields_val() {
$section_1_fields = array (
array(
'field_id' => 'field-1-1',
'label' => __( 'Field One', 'clea-add-button' ),
'field_callbk' => 'field_1_1_callback',
'menu_slug' => 'my-plugin',
'section_name' => 'section-1',
),
array(
'field_id' => 'field-1-2',
'label' => __( 'Field Two', 'clea-add-button' ),
'field_callbk' => 'field_1_2_callback',
'menu_slug' => 'my-plugin',
'section_name' => 'section-1'
),
);
$section_2_fields = array (
array(
'field_id' => 'field-2-1',
'label' => __( 'Field One', 'clea-add-button' ),
'field_callbk' => 'field_2_1_callback',
'menu_slug' => 'my-plugin',
'section_name' => 'section-2'
),
array(
'field_id' => 'field-2-2',
'label' => __( 'Field Two', 'clea-add-button' ),
'field_callbk' => 'field_2_2_callback',
'menu_slug' => 'my-plugin',
'section_name' => 'section-2'
),
);
$section_fields = array(
'section-1' => $section_1_fields,
'section-2' => $section_2_fields
) ;
return $section_fields ;
}
Ajouter d’autres arguments pour les champs
Il faut que j’ajoute des arguments à chaque champs :
‘type’ : dans l’exemple dont je pars, tous les champs sont des textes courts. Comme je ne veux pas avoir autant de fonctions que de champs pour les afficher, il faut que le programme sache quel est le type du champs pour savoir comment le restituer.
‘helper’ : un texte qui aidera l’utilisateur à comprendre comment faire son réglage.
‘default’ : qui permettra de définir une valeur si l’utilisateur n’en définit pas.
Chaque champs doit pouvoir être défini par un array contenant les 5 éléments obligatoires et les trois nouveaux.
Dans la fonction qui génère les champs, j’ajoute une dernière valeur qui correspondra à $args, le 6ème élément de la fonction add_settings_field.
// add the fields
foreach ( $set_fields as $section_field ) {
foreach( $section_field as $field ){
add_settings_field(
$field['field_id'],
$field['label'],
$field['field_callbk'],
$field['menu_slug'],
$field['section_name'],
$field
);
}
}
Et dans la fonction field_1_1_callback() , j’ajoute $argument comme élément reçu, ainsi que l’affichage du contenu de cette variable (c’est un array, j’utilise print_r) :
Page de réglage d’une extension WordPress : affichage des arguments de add_settings_field
Regrouper les fonctions « callback » pour les sections
Il n’y a pas beaucoup de sections en général mais ce n’est pas la peine de créer autant de fonctions que de sections.
Je crée donc une fonction clea_add_button_settings_section_callback( $args ) (que je n’oublie pas de déclarer comme ‘section_callbk’ dans la définition des sections) :
Cette ligne va chercher la valeur enregistrée pour toutes les options d’un nom défini lorsque nous avons exécuté la commande ‘register_setting’, au début de la fonction clea_add_button_admin_init() . C’est une valeur fixe que je laisse telle quelle pour l’instant (ce n’est certainement pas une bonne idée de laisser un nom si générique, sans préfixe spécifique).
$field = "field_1_1";
Là on voit que « field_1_1 » correspond au contenu de $args[field_id].
$value = esc_attr( $settings[$field] );
Cette ligne lit la valeur attribuée à l’option et la met dans la variable « value ». On ne la change pas puisque $field n’est plus fixe.
Ne change pas non plus. Ma fonction clea_add_button_settings_field_callback( $args ) va donc contenir quasiment la même chose qu’avant mais elle sera utilisable par tous les champs :
Maintenant je supprime les 4 fonctions callback des 4 champs définis et dans la définition des champs, je remplace le contenu de ‘field_callbk’ par le nom de la fonction collective.
J’obtiens bien l’affichage de mon formulaire, mais pas des valeurs déjà enregistrées. Et il y a 4 PHP notices. La première notification signale Undefined index: field-1-1 en ligne 128 de clea-add-button-settings-page.php. Les trois autres sont identiques et portent sur les 3 autres champs.
C’est en fait « normal » : je me suis trompée en définissant l’array des champs. J’ai mis des id de type ‘field-1-1’ alors que Anna, l’auteure du Gist dont je m’inspire utilisais field_1_1… Il m’a suffi d’enregistrer une valeur pour que tout rentre dans l’ordre.
Et voilà, j’obtiens exactement le même résultat qu’Anna mais même si j’ajoute des dizaines de paramètres, les fonctions qui les restituent resteront uniques. C’est plus « élégant », mais surtout ça réduit fortement le risque d’erreur si j’ajoute un nouveau paramètre car le code est beaucoup plus lisible.
Dans des articles à venir, il faudra voir comment ajouter d’autres types de champs, comment charger des feuilles de style, comment faire utiliser un template de l’extension…
Mais d’abord, il faut voir comment on peut plus facilement déboguer. C’est l’objet du prochain article de cette série [the-series).
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 :
Debug Bar
Query Monitor
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.
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.
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 :
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).
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 :
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 :
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).
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 !
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.
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.
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) :
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 :
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.
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).
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.
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.
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 :
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.
Les 5 précédents articles de cette série Caméra de surveillance et Raspberry Pi explorent différents aspects de la gestion de caméras de surveillance sur Rasberry Pi et la diffusion des flux vidéos sur une page web. Cet article en fait la synthèse.
L’objectif :
A partir d’une Raspberry Pi B+, installer motion réglé correctement pour :
gérer deux caméras de surveillance IP ;
collecter les vidéos et images générées sur détection de mouvement avec aussi peu de faux positifs que possible ;
afficher les flux vidéos en temps réel sur une page web accessible sur le réseau local.
Régler le Pi pour fonctionner correctement en wifi sur une adresse fixe 192.168.1.104 (nom nautilus) – voir l’article Raspberry Pi : connexion internet en wifi et/ou IP fixe
Pour moi toutes ces étapes réalisées correspondent à l’image de carte sd « jessie-lite-wifi fixe-103-2016-07-16.img « , dans le répertoire « Downloads\2016-07 Pi Images « . Il me suffit de régler l’adresse wifi et le nom du Pi comme suit :
Connecter le Pi en ethernet, sans dongle wifi ;
Editer /etc/dhcpcd.conf et modifier l’adresse IP attribuée ;
Taper raspi-config puis modifier Hostname (advanced options) et changer le nom vers « nautilus ».
Mettre le dongle wifi sur le Pi puis taper « reboot » pour redémarrer.
Ensuite se reconnecter en wifi et mettre le Pi à jour :
apt-get update
apt-get upgrade
rpi-update
Enlever la connexion ethernet et taper « reboot » pour redémarrer.
uname -a nous indique la version du système d’exploitation en fonctionnement.
Le résultat, le 14/08/2016 est 4.4.17+ #901 Fri Aug 12 17:48:40 BST 2016 armv6l GNU/Linux .
df -h montre qu’on utilise 17% de la carte SD de 8 Go.
Attention, les fichiers créés avec touch via Cygwin sur mon PC sont en mode sauts de ligne Windows. Il faut les passer en saut de ligne UNIX (/home/jf/motion/log/motion-log.log et /var/run/motion/motion.pid).
éditer les fichiers de configuration
Dans etc/motion/motion.conf les lignes suivantes sont modifiées ou décommentées :
process_id_file /var/run/motion/motion.pid
logfile /home/jf/motion/log/motion-log.log
v4l2_palette 8
width 640
height 480
netcam_keepalive on
# Number of motion thread to show in SDL Window (default: 0 = disabled)
sdl_threadnr 0
text_double on
target_dir /home/jf/log/
# Restrict stream connections to localhost only (default: on)
stream_localhost off
webcontrol_port 8080 #must be in motion.conf, not in a thread.
# Restrict control connections to localhost only (default: on)
webcontrol_localhost off
# Output for http server, select off to choose raw text plain (default: on)
webcontrol_html_output on
thread /etc/motion/thread0.conf
thread /etc/motion/thread1.conf
Attention : il faut bien vérifier que thread0.conf et thread1.conf ont des sauts de ligne UNIX.
etc/thread0.conf règle la configuration pour la caméra devant la maison (évidemment il faut modifier USER et PWD ainsi que l’adresse IP pour que ça corresponde à la caméra) :
# caméra FOSCAM devant la maison
# thread0.conf
videodevice /dev/video0
netcam_url http://USER:PWD@192.168.X.X:80/videostream.cgi?user=USER&pwd=PWD
############################################################
# Régler sensibilité aux mouvements pour cette caméra extérieure
threshold 3000
threshold_tune off
noise_level 32
noise_tune off
despeckle_filter EedDl
smart_mask_speed 10
lightswitch 25
minimum_motion_frames 3
############################################################
# Target base directory for pictures and films
target_dir /home/jf/motion/log/foscam
snapshot_filename f-%v-%Y%m%d%H%M%S-snapshot
picture_filename f-%v-%Y%m%d%H%M%S-%q
movie_filename f-%v-%Y%m%d%H%M%S
timelapse_filename f-%Y%m%d-timelapse
############################################################
stream_port 8081
A ce stade, la page php s’affiche lorsqu’on tape « nautilus » dans un navigateur local (192.168.1.104 dans une tablette)et les deux flux vidéos s’affichent en streaming.
df -h montre qu’on utilise maintenant 19% de la carte SD de 8 Go.
Je crée un cours (une formation) en ligne pour mon site professionnel. Je me rends compte que ce n’est pas si simple que ça de créer des vidéos à partir de powerpoint (2010) et d’un enregistrement de ma voix. J’ai déjà exposé les grands principes dans un article précédent (créer une vidéo à partir d’un Powerpoint (2010)). Ici je met en ligne mon mode opératoire précis. Ca servira au moins à m’en souvenir la prochaine fois !
Etape 1. Créer le Powerpoint.
Rien à dire de spécial là dessus.
Etape 2. Ecrire le texte qui sera ensuite dit
Je le met dans les commentaires, en caractères de taille 14.
Au fur et à mesure, je crée le résumé que je met sur la diapo et chaque fois que je résume une idée je passe à la ligne dans les commentaires.
(Cliquer sur l’image pour la voir en plus grand).
Etape 3. Préparer les changements de vue
Lorsque je dirai mon texte il faudra que je cliques sur le diaporama pour changer de page ou faire afficher les animations correspondants à ce que je dis.
Je crée donc les animations et j’indique « CLIC » dans le texte pour me souvenir que c’est à faire à ce stade.
(Cliquer sur l’image pour la voir en plus grand).
Etape 4. Créer l’aide visuelle pour dire le texte
J’imprime en pdf le diaporama en mode pages de commentaire.
C’est ce document qui me servira de script pour dire le texte.
(Cliquer sur l’image pour la voir en plus grand).
Etape 5. s’entraîner et vérifier
J’ai connecté un deuxième écran à mon PC. Je pourrais aussi utiliser un vidéoprojecteur en le réglant pour qu’il ne duplique pas l’écran de l’ordinateur.
J’ouvre le pdf du texte à dire dans le deuxième écran.
Je démarre le diaporama dans le premier écran.
Je dis le texte une ou deux fois pour vérifier que les clics vont bien, que mon texte va bien. Si nécessaire je modifie le diaporama et je crée un nouveau pdf du texte à dire.
Etape 6. Dire le texte dans le powerpoint
J’utilise un micro Yeti (ici sur amazon). Il est cher mais permet de régler la direction du son. C’est très utile dans certaines circonstances. Je le connecte en USB à mon ordinateur et je vérifie que le bouton mute est désactivé (la LED rouge est fixe lorsqu’il peut enregistrer, clignotante lorsqu’il est sourd / mute).
Je fais un premier test vite fait pour m’assurer que le micro est bien réglé :
j’enregistre quelques phrases puis j’arrête l’enregistrement ;
Je lis la diapo en mode diaporama. Si le son est bon, j’efface l’enregistrement et je procède maintenant à la version définitive.
je commence à la première diapo, en enregistrant à la fois le son et le minutage.
Lorsque j’ai fini la première diapo, j’arrête l’enregistrement.
Je me place sur la deuxième diapo et je choisis « démarrer l’enregistrement à partir de la diapositive actuelle ».
Et ainsi de suite jusqu’à la fin du diaporama. Evidemment, il est prudent de faire des sauvegardes intermédiaires.
Etape 7 : vérifier puis transformer en vidéo
J’enregistre en qualité ‘Qualité HD & ordinateur’ puisque je vais ensuite la mettre en ligne via Viméo qui gérera la qualité en fonction de la bande passante de l’internaute.
J’obtiens un fichier WMV d’environ 8 Mo pour 58 » de vidéo (en qualité internet et DVD, la même vidéo ferait 3 Mo.
Etape 8 : mettre la vidéo en ligne (sur VIMEO)
J’ai acheté un compte « plus » sur Vimeo, qui permet de gérer la confidentialité des vidéos mises en ligne. C’est très simple.
On peut mettre en ligne soit en faisant glisser le fichier dans la zone correspondante, soit, encore mieux, via dropbox (c’est beaucoup plus rapide puisque je ne monte qu’une seule fois le fichier sur internet).
Etape 9 : régler les paramètres de la vidéo (sur VIMEO)
Paramètres basic :
choisir la vignette
Paramètres Confidentialité :
Qui peut regarder cette vidéo : masquer cette vidéo sur vimeo.com (Cette vidéo peut être intégrée à d’autres sites mais ne peut pas être visionnée sur Vimeo.com)
Où est-ce que cette vidéo peut être intégrée ? : seulement sur les sites de mon choix (et saisir les noms de domaine)
Qui peut commenter cette vidéo : tout le monde
Que peuvent faire les gens avec cette vidéo : ne rien cocher (pas de téléchargement, pas d’ajout à une collection)
Paramètres collections
Album : cocher le bon album
Paramètres Intégration
Pré-réglage « cours – par défaut »
Paramètres Avancé :
Licence Creative Commons : aucune
Intégrer la vidéo sur une page WordPress
Le domaine d’intégration doit avoir été défini dans les paramètres de confidentialité.
Dans Viméo, cliquer sur « share » en haut à droite de la vidéo. Copier le lien et le coller directement dans l’éditeur visuel du contenu WordPress de destination
Commentaires récents