9. Dispositifs Zigbee

Avec zigbee2mqtt

9.1 Démarrage automatique

Important

Si Z2m n’envoie plus de messages à Domoticz ou Home assistant:

le souci vient de Z2M et du driver ember : à la moindre interruption du courter mqtt, il faut redémarrer Z2m

  • Pour une installation classique node.js

Démarrage auto : avec PM2 , Voir la page domo-site : http://domo-site.fr/accueil/dossiers/74

image658

image659

  • Pour une installation sous Docker, le démarrage sera automatique.

  • Pour un démarrage avec systemg zigbee2mqtt.service

[Unit]
Description=zigbee2mqtt
After=network.target
[Service]
Environment=NODE_ENV=production
ExecStart=/usr/bin/pnpm start
WorkingDirectory=/opt/zigbee2mqtt
StandardOutput=inherit
StandardError=inherit
Restart=always
User=root
[Install]
WantedBy=multi-user.target

9.2 Le fronted

image1143

Affichage

image653

ajouter une fonction utile: vu pour la derière fois

image1142

A parir du frontend:

image1141

Note

ISO 8601 c’est la représentation de la date, en format internationalement, exemple : 2023-10-06T17:13:26Z

le payload MQTT:

MQTT publish: topic 'zigbee2mqtt/lampe_jardin', payload '{"last_seen":"2023-10-05T20:52:34.706Z","linkquality":156,"state_l1":"OFF","state_l2":"OFF"}'

La donnée « last_seen » peut être utilisé avec Node , HA (mais pas avec Dz) pour connaitre les dispositifs offline

Note

Voir la page du site consacrée à frontend : http://domo-site.fr/accueil/dossiers/48

9.3 Ajouter le Fronted dans monitor

  • la page zigbee.php

image654

  • Le fichier admin/config.php

// Page zigbee2mqtt
define('ON_ZIGBEE',true);// mise en service Zigbee
define('IPZIGBEE', 'http://192.168.1.92:8084');//ip:port
define('URLZIGBEE', 'https://zigbee.<DOMAINE>');//url
  • Le fichier index_loc.php : pour info, ne pas modifier

if (ON_ZIGBEE==true) include ("include/zigbee.php");// fronted zigbee2mqtt
  • Les styles CSS

En plus des css pour la page:

/*zigbee2mqtt zwavejs2mqtt & ngiosmobile   (----------------*/
#zbmqtt,#zwmqtt {margin-top:-40px;width: 100%;height: 800px;}
  • zigbee.php

on ajoute une iframe (permet d’obtenir une page HTML intégrée dans la page courante)

<iframe id="zbmqtt" src="<?php echo $lien_zigbee;?>" frameborder="0" ></iframe>

image657

9.4 accès distant HTTPS

Il faut configurer NGINX : - 1.8 Accès distant HTTPS

Exemple de fichier .conf avant de demander un certificat cerbot

server {
 listen       80;
 server_name  zigbee.<DOMAINE>;
 #return 301   https://zigbee<DOMAINE>$request_uri;
}
 location / {
  proxy_pass http://<IP>:<PORT>/;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 }
 location /api {
  proxy_pass         http://<IP>:<PORT>/api;
  proxy_set_header Host $host;

  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  }
 }
  • Demande de certificat Let’s Encrypt :

sudo cerbot --nginx

Le fichier modifié par cerbot lors de la demande de certificat

image655

Attention

** Pour utiliser auth basic** comme c’est le cas ici

Il faut créer un fichier de mot de passe et ajouter des utilisateurs

https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/

9.5 utilisation directe avec monitor

sans l’intermédiaire de Domoticz, Home Assistant ou Ipbroker

9.5.1 Créer un lien symbolique de state.json

Ce fichier json contient les dernières valeurs de tous les dispositifs. il est mis à jour toutes les 5 minutes.

Note

la mise à jour en temps réel n’est effective que pour les dispositifs affichant un état ON/OFF; en règle général la mise à jour des autres dispositifs s’effectue dans le delai indiqué dans include/admin/config.php TEMPO_DEVICES”, 180000) pour DZ, HA, IOB ; il peut être judicieux , lors de l’utilisation de z2m directement par monitor d’armoniser cette mise à jour: 150 000 , 300 000, 450 000 milli secondes; pour une utilisation de seulement monitor + z2m , régler TEMPO_DEVICES sur 3000000.

ln -s /opt/zigbee2mqtt/data/state.json /opt/zigbee2mqtt/node_modules/zigbee2mqtt-windfront/dist/state.json

Note

Ce lien permer de récutérer en http le contenu du fichier : http://IP_Z2M:8084:state.json

image1955

9.5.2 Installation de php-mqtt/client coté serveur

https://github.com/php-mqtt/client

Note

Un autre choix existe : mosquitto client; le paquet pour Debian, libmosquitto-dev mais l’installation n’est pas aussi simple qu’avec php-mqtt/client

installé depuis composer; composer ne peut pas être installé en root

cd /www/monitor
sudo apt install composer
composer require php-mqtt/client

image1956

9.5.2.1 envoyer et recevoir les messages

Le script est est exécuter dans le répertoire ws_z2m

<?php // PHP-MQTT
require('/www/monitor/vendor/autoload.php');
require('/www/monitor/admin/config.php');

use \PhpMqtt\Client\MqttClient;
use \PhpMqtt\Client\ConnectionSettings;

function id_name($nom_objet) {$rq=[];
$zb_donnees=array();
$zb_donnees = [
     'state' => "Data",
 'state_l2' => "Data",
 'state_l1' => "Data",
     'temperature' => "temperature",
 'soil_moisture' => "Data",
 'humidity' => "humidity",
     "contact" => "Data"
 ];
 if ($nom_objet!="") {
 $conn = new mysqli(SERVEUR,UTILISATEUR,MOTDEPASSE,DBASE);
     $sql="SELECT * FROM ".DISPOSITIFS." WHERE ( nom_objet = '".$nom_objet."' AND Actif = '6' AND maj_js <> 'variable');";
     $result = $conn->query($sql);$nb_rows=$result->num_rows;
     if ($nb_rows>0) {$i=0;//$row = $result->fetch_assoc();echo $row['ID'];
         while($row = $result->fetch_array(MYSQLI_ASSOC)){
     $ro=explode(":",$row['param']) ;
         $rq[$i]=['ID' => $row['ID'],
              'idm' => $row['idm'],
              'champ' => $zb_donnees[$ro[1]],
              'nb' => $nb_rows,
                              'json' => $ro[1]
      ];}
      //$rx=json_encode($rq);echo $rx;
      }
     else { $rq[0]=['ID' => '0'];}}
 else { $rq[0]=['ID' => '0'];}
 return $rq;}

$server   = MQTT_IP;
$port     = 1883;
$clientId = rand(5, 15);
$username = MQTT_USER;
$password = MQTT_PASS;
$clean_session = false;
$mqtt_version = MqttClient::MQTT_3_1_1;
$connectionSettings = (new ConnectionSettings)
  ->setUsername($username)
  ->setPassword($password)
  ->setKeepAliveInterval(60)
  ->setLastWillTopic('monitor/last-will')
  ->setLastWillMessage('client mqtt déconnecté')
  ->setLastWillQualityOfService(1)
      ->setReconnectAutomatically(true)
  ->setMaxReconnectAttempts(3)
  ->setDelayBetweenReconnectAttempts(0);
try {
debut:
// Boucle d’attente de connexion
 $maxWait = 30; // secondes max d’attente
 $start = time();
 $mqtt = new MqttClient($server, $port, $clientId, $mqtt_version);
 $mqtt->connect($connectionSettings, $clean_session);
 printf("client connecté\n");
 $mqtt->subscribe('zigbee2mqtt/#', function ($topic, $message) use ($mqtt) {
//if ($topic == "monitor") {sms($message);}
$str=explode("/",$topic);$name=$str[1];$search_id=[];
$search_id=id_name($name);$id=$search_id[0]['ID'];
if ($id!="0") {$n=$search_id[0]['nb'];$i=0;while($i<$n){$search=$search_id[$i];echo "----->".$n."  ".$i;
 $id=$search['ID'];$idm=$search['idm'];$json=$search['json'];$champ=$search['champ'];$obj = json_decode($message);
 if (isset($obj->state) && $obj->state=="offline"){$ob=$obj->state;$msg='{ "id" : "'.$id.'", "objet" : "'.$name.'", "state" : "'.$ob.'" }';maj($id,$ob);}
 if (isset($obj->$json)) {$ob=$obj->$json;$msg='{ "id" : "'.$id.'", "objet" : "'.$name.'","state" : "'.$ob.'", "champ1" : "'.$champ.'", "champ2" : "'.$json.'", "idm" : "'.$idm.'"}';
  // echo '------------'.$msg;
  $mqtt->publish('z1m', $msg, 0,false);$id="0";$str=[];
 echo "envoi msg:".$msg;
} $i++;}}} );
 $mqtt->loop();
 $mqtt->disconnect();
}
catch (\Throwable $e) {
 echo 'An exception occured: ' . $e->getMessage() . PHP_EOL;
 while (time() - $start <= $maxWait) {
     usleep(1000000); // pause 1000ms pour éviter de saturer le CPU
     echo ".";}
     goto debut;
 //$mqtt->disconnect();
 }

image1957

image1967

image1225

Note

Ne pas utiliser « z2m » pour le topic à publier car il est utilisé pour publier des erreurs de zigbee2mqtt; j’ai utilisé « z1m »

9.5.2.2 démarrage automatique systemd

le fichier php-mqtt.service

[Unit]
Description=cclient mqtt php
After=multi-user.target
[Service]
Type=idle
ExecStart=php /var/www/monitor/ws_z2m/sub_messages_mqtt.php > /home/michel/sub_mqtt.log 2>&1
[Install]
WantedBy=multi-user.target

image1965

Pour activer et démarrer le service :

Systemctl enable php-mqtt
systemctl start php-mqtt.service

image1966

9.5.3 Scripts concernés dans monitor

fonctions.php & include/fonctions_1.php

fonctions.php

image1958

image1979

include/fionctions_1.php

image1964

image1980

9.5.4 Installation de mqtt.JS coté client

image1959

https://github.com/mqttjs/MQTT.js

if (MQTT==true) {echo '<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>';}

image1960

  • Ajout de MQTT.js à JS dans include/footer.php

    image1968

9.5.4.1 Le javascript sur la page html

fichier include/mqtt-js.php

<?php require_once('admin/config.php');
$domaine=$_SESSION["domaine"];
if ($domaine==URLMONITOR) {$lien_mqtt=MQTT_URL;$w='wss://';}
if ($domaine==IPMONITOR) {$lien_mqtt=MQTT_IP;$w='ws://';}
?>
<script>
    const clientId = 'mqttjs_' + Math.random().toString(16).substring(2, 8)
    const connectUrl = '<?php echo $w.$lien_mqtt.":".MQTT_PORT;?>'
 const options = {
   keepalive: 60,
   clientId: clientId,
   clean: true,
   connectTimeout: 30 * 1000,
   username: '<?php echo MQTT_USER;?>',
   password: '<?php echo MQTT_PASS;?>',
   reconnectPeriod: 1000,
 }
 const topic = 'z1m/#'
 const payload = ""
 const qos = 0
 var topic1="z1m"
 var state=""
 console.log('connecting mqtt client')
 const client = mqtt.connect(connectUrl, options)
 client.on('error', (err) => {
   console.log('Connection error: ', err)
   client.end()
 })
 client.on('reconnect', () => {
   console.log('Reconnecting...')
 })
 client.on('connect', () => {
   console.log('Client connected:' + clientId)
 client.subscribe(topic, { qos }, (error) => {
     if (error) {S
       console.log('Subscribe error:', error)
       return
     }
     console.log(`Subscribe to topic ${topic}`);
          })
   // publish message
   client.publish(topic1, payload, { qos }, (error) => {
     if (error) {
       console.error(error)
     }
   })
 })
 client.on('message', (topic, payload) => {if (payload!=""){
   console.log('Received Message: ' + payload.toString() + '\nOn topic: ' + topic);
   var emsg=document.getElementById('msg_zb').innerText;
   document.getElementById('msg_zb1').innerText = emsg;
   document.getElementById('msg_zb').innerText=payload;
  msg=JSON.parse(payload);var idm=msg.idm;var state=msg.state;var champ=msg.champ1;
  var ind=4;if (champ=="Data") {ind=2;}
  if (champ=="temp") {ind=3;}
   maj_mqtt(idm,state,ind,0,champ) ;// fonction ds footer.php
 }
})
</script>

image1961

9.5.4.2 envoyer et recevoir les messages

  • publier

    client.publish(topic, msg);} // topic , payload
    

Exemples:

image1962

Scripts concernés dans include/footer.php

image1963

  • recevoir et mettre à jour les données

image1969

image1976

Voir aussi le § 8.1.2.2 Commandes de changement de couleur des lampes

9.5.5 Automatisations

9.5.5.1 Le plugin Zigbee2MQTT-automations

image1228 https://github.com/Luligu/zigbee2mqtt-automations

Installation

cd /opt/zigbee2mqtt/data
mkdir external_extensions
cd external_extensions
wget https://github.com/Luligu/zigbee2mqtt-automations/blob/master/dist/automations.js
systemctl stop zigbee2mqtt
systemctl start zigbee2mqtt

image1229

image1994

Après le redémarrage, automations.js est pris en compte par le fronted

image1995

Exemple d’une première automation : dans /opt/zigbee2mqtt/data/automations.yaml

Allumage lampes jardin 2mn  depuis contact lampe_ porche:
  active: true
  trigger:
    entity: lampe_terrasse_nord
    state: ON
  action:
    - entity: lampe_jardin
      payload:
        state_l2: ON
      turn_off_after: 120
      payload_off:
      logger: info

Dans le frontend -> Extensions: Sélectionner automations.js et sauvegarder; automations.yaml et scenes.yaml (si il existe) sont rechargés.

image1996

9.5.5.2 Script d’aide pour automations.yaml de monitor

image1997

  • le SCRIPT

    image2009

  • UTILISATION:

    . pour obtenir de l’aide

    image1999

    . indiquer le nom de l’automation, . choisir de l’activer ou non . choisir un déclenchement

    image1998

    image2000

    . choisir une condition si il y a lieu, choisir une action

    image2001

    image2002

    image2003

    image2004

    image2005

    . POUR TERMINER APPUYER sur Envoyer

    image2006

    . et sur COPY pour récupérer le script et l’ajouter à :green:`automations.yaml”

    image2007

Note

la console est affichée par un textarea html et l’indentation peut être quelquefois modifiée mais la copie crée dans le presse papier est correcte et peut être ajoutée dans automations.yaml.

La copie dans le presse papier est réalisée par clipboard.js : https://clipboardjs.com/

image2008