6. GRAHIQUES & BASE DE DONNEES ------------------------------ |image523| Voir ces pages pour installer les scripts : - http://domo-site.fr/accueil/dossiers/40 - http://domo-site.fr/accueil/dossiers/42 |image524| |image525| .. admonition:: **Prérequis** - Jpgraph est installé avec le cache |image526| - php-gd est installé |image527| - la bibliothèque python fabric est importé - le module python mysql.connector est importé 6.1 Les table SQL ^^^^^^^^^^^^^^^^^ .. warning:: Pour le nom des tables concernant les graphiques, NE PAS UTILISER le CARACTERE –(moins) Ce caractère est utilisé comme séparateur pour l’indication de l’ensemble table-champ pour les graphiques |image528| **En absence de champ c’est le champ « valeur » qui est utilisé sinon** : Value= « - » |image529| *Avec 2 champs ou 3 champs* |image530| |image531| **Création de la table avec phpMyAdmin** :*exemple* .. code-block:: CREATE TABLE `pression_chaudiere` ( `num` int(5) NOT NULL, `date` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), `valeur` varchar(4) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `pression_chaudiere` CHANGE `num` `num` INT(4) NOT NULL AUTO_INCREMENT, add PRIMARY KEY (`num`); 6.2 Dans Domoticz ^^^^^^^^^^^^^^^^^ Les données à enregistrer peuvent provenir de capteurs réels ou virtuels. Pour éviter un trop grand nombre de valeurs, il est utile pour certains dispositifs, de créer des variables pour comparer les valeurs et les limiter aux valeurs entières (c’est le cas de la météo Darsky, des capteurs de température Onoff). Pour utiliser des données de la base SQL, il faut au préalable les avoir enregistrées depuis Domoticz : c’est le rôle de la bibliothèque Python :darkblue:`fabric` Pour l'installer (pip est déjà installé): .. code-block:: sudo pip3 install fabric Une fois un premier enregistrement crée, pour une température, dans la base, il suffit pour un nouvel enregistrement d’une autre t° d’ajouter dans le script LUA « évènement /:darkblue:`NOM_DU SCRIPT` » cette T° **Depuis la version 2023-2 de Domoticz le script a été réécrit en dzvent** *pour info Extrait du script en lua*: .. code-block:: package.path = package.path..";www/modules_lua/?.lua" require 'datas' require 'string_tableaux' data0=pression;data1=d_linky year = tonumber(os.date("%Y")); month = os.date("%m"); day = os.date("%d"); hour = os.date("%H"); min = os.date("%M"); sec = os.date("%S"); weekday = tonumber(os.date("%w")); time = os.date("%X"); datetime = year.."-"..month.."-"..day.." "..time; -- function write_datas(data0,data1) f = io.open("www/modules_lua/datas.lua", "w") f:write('pression='..data0..';d_linky='..data1) f:close() end function envoi_fab(don) print ("maj valeur:"..don); local command = "/bin/bash userdata/scripts/bash/./fabric.sh"..don.." > /home/michel/fab.log 2>&1"; os.execute(command); os.execute("python3 scripts/python/pushover.py "..txt.." >> /home/michel/push.log 2>&1"); end function round(num,numDecimal) local mult = 10^(numDecimal or 0) return math.floor(num * mult + 0.5) / mult end function Split(s, delimiter) result = {}; for match in (s..delimiter):gmatch("(.-)"..delimiter) do table.insert(result, match); end return result; end commandArray = {} t = {}; --donnees={['pression']='1.2'};write_datas('{["pression"]="'.."1.2"..';}') -- libelle=table#champ -- si 2 champs , ajouter ..'#champ2#"..split_str[2] après datetime.. -- exemple "don=" "..libelle.."#"..tostring(deviceValue).."#"..datetime.."#champ2#"..split_str[2] for deviceName,deviceValue in pairs(devicechanged) do if (deviceName=='pir_salon_temp') then print ("temp_salon:"..deviceValue); libelle="temp_salon#valeur";don=" "..libelle.."#"..tostring(deviceValue).."#"..datetime envoi_fab(don) ...... |image534| **Le script en DzVent "export_dev_sql"** .. code-block:: -- --[[ export_dev_sql]] -- package.path = package.path..";www/modules_lua/?.lua" require 'datas' require 'string_tableaux' data0=pression; year = tonumber(os.date("%Y")); month = os.date("%m"); day = os.date("%d"); hour = os.date("%H"); min = os.date("%M"); sec = os.date("%S"); weekday = tonumber(os.date("%w")); time = os.date("%X"); datetime = year.."-"..month.."-"..day.." "..time; function write_datas(data0) f = io.open("www/modules_lua/datas.lua", "w") f:write('pression='..data0) f:close() end function round(num,numDecimal) local mult = 10^(numDecimal or 0) return math.floor(num * mult + 0.5) / mult end function envoi_fab1(libelle,valeur) don=" "..libelle.."#"..valeur.."#"..datetime print("maj valeur:"..don); command = "/bin/bash userdata/scripts/bash/./fabric.sh "..don.." > /home/michel/fab.log 2>&1"; os.execute(command); end return { on = { devices = { 'temp_cave', 'temp_cuisine_ete', 'temp_cellier', 'TempHumBaro', 'pir_salon_temp', 'pir ar cuisine_temp', 'pression_chaudière', 'PH_Spa', 'Redox_Spa' } }, execute = function(domoticz, item) domoticz.log('item '..item.name..' was changed', domoticz.LOG_INFO) if (item.name=='temp_cuisine_ete') then -- choix nb decimales apres la virgule -- local temp=round(deviceValue, 1) valeur=tostring(round(item.temperature, 0)) if (domoticz.variables('temp_cuis_ete').value ~= valeur) then domoticz.variables('temp_cuis_ete').set(valeur) libelle="temp_cuis_ete#valeur"; envoi_fab1(libelle,valeur) end elseif (item.name=='temp_cave') then --local valeur=round(item.temperature, 1) valeur=tostring(round(item.temperature, 0)) if tostring(valeur)~=domoticz.variables('temp_cave').value then domoticz.variables('temp_cave').set(tostring(valeur)) libelle="temp_cave#valeur"; envoi_fab1(libelle,valeur) end elseif (item.name=='temp_cellier') then -- local valeur=round(deviceValue, 1) valeur=tostring(round(item.temperature, 0)) if tostring(valeur)~=domoticz.variables('temp_cellier').value then domoticz.variables('temp_cellier').set(tostring(valeur)) libelle="temp_cellier#valeur"; envoi_fab1(libelle,valeur) end elseif (item.name=='TempHumBaro') then valeur=tostring(round(item.temperature, 0)) if valeur~=domoticz.variables('temp_meteo').value then domoticz.variables('temp_meteo').set(valeur) libelle="temp_meteo#valeur"; envoi_fab1(libelle,valeur) end elseif (item.name=='pir_salon_temp') then valeur=tostring(round(item.temperature, 0)) if tostring(valeur)~=domoticz.variables('temp_salon').value then domoticz.variables('temp_salon').set(tostring(valeur)) libelle="temp_salon#valeur"; envoi_fab1(libelle,valeur) end elseif (item.name=='pir ar cuisine_temp') then valeur=tostring(round(item.temperature, 0)) if tostring(valeur)~=domoticz.variables('temp_ar_cuisine').value then domoticz.variables('temp_ar_cuisine').set(tostring(valeur)) libelle="temp_cuisine#valeur"; envoi_fab1(libelle,valeur) end ... ... end } |image783| .. important:: **Nom pour les fonctions DzVent** le même nom ne peut pas être utilisé pour 2 fonctions dans des scripts DzVents (mêmes différents). Pour limiter le nb d’enregistrements : |image535| Dans cet exemple, il a été créer plusieurs variables qui permettent des enregistrements dans la BD à chaque changement de valeurs limité au degré.: |image536| 6.2.1 fabric ============ .. admonition:: **Le script fabric.sh** installé ici dans le répertoire « scripts » de Domoticz |image537| .. code-block:: #!/bin/bash echo $1 echo $2 a="#" c=$1$a$2 echo $c cd /home/michel/python fab maintask --don=$c > /home/michel/fab.log 2>&1 Pour tester le script, il est plus facile de travailler dans le répertoire USER, c’est l’objet de la création du lien symbolique vers le dossier python de Domoticz |image538| |image539| .. admonition:: **Le script fabfile.py** |image540| .. code-block:: #!/usr/bin/env python2.7 # -*- coding: utf-8 -*- from fabric import Connection from fabric.tasks import task @task def subtask(ctx, donn): with ctx.cd("/www/monitor/python"): ctx.run(donn) @task( optional = ['don']) def maintask(ctx, don = None ): con = Connection(host = '192.168.1.7', user = 'michel', connect_kwargs = {'password':'PASS'}) file = "python3 sqlite_mysql.py " donn = file+don print(subtask(con,donn)) *Le script fabfile.py appelle sur le serveur qui héberge la BD le script sqlite_mysql.py*; **sqlite_mysql.py n’est exécuté que lorsqu’il est appelé, il n’écoute pas en permanence si des données sont envoyées** |image541| **POUR RESUMER** : sur le serveur de Domoticz - script LUA->MENU Domoticz évènements - script fabric.sh-> ../domoticz/scripts/ - script fabfile.py->../domoticz/scripts/python/ avec ls /home/USER/python/ - fab.log-> /home/USER 6.3 Sur le serveur de la base de données ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Le serveur Nginx avec aussi Monitor, réception des datas : Le script python :darkblue:`sqlite_mysql.py` : .. code-block:: #!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys import mysql.connector from mysql.connector import Error total_arg = len(sys.argv) if (total_arg>0) : x= str(sys.argv[1]) temp = x.split('#') table=temp[0] champ=temp[1] val1=temp[2] val=temp[3]+" "+temp[4] if (len(temp)==7) : champ2=temp[5] val2=temp[6] try: connection = mysql.connector.connect( host = "127.0.0.1", user = "michel", password = xxxxxxxx", database = "domoticz") if connection.is_connected(): db_Info = connection.get_server_info() print("Connected to MySQL Server version ", db_Info) cursor = connection.cursor() cursor.execute("select database();") record = cursor.fetchone() print("You're connected to database: ", record) if (len(temp)==7) : query = "INSERT INTO "+table+" (date,"+champ+","+champ2+") VALUES(%> values = (val, val1, val2) else : query = "INSERT INTO "+table+" (date,"+champ+") VALUES(%s, %s)" values = (val, val1) cursor.execute(query, values) connection.commit() print(cursor.rowcount, "Record inserted successfully into Laptop table") except Error as e: print("Error while connecting to MySQL", e) finally: if (connection.is_connected()): cursor.close() |image542| 6.4 Dans Monitor ^^^^^^^^^^^^^^^^ Le cache pour jpgraph est présent : |image543| Jpgraph est installé à la racine de monitor |image544| 6.4.1 la page graphique.php =========================== |image545| - *css* .. code-block:: #graphic{color:white;} graphique_img{max-width:700px;margin:0 1px 0 1px,;width:100%;} graphiques{background-color: green;} 6.4.2 la fonction graph ======================= **dans fonctions.php** et appelée par ajax.php, la fonction :darkblue:`graph()` .. code-block:: if ($app=="graph") {graph($device,$variable);} |image548| **L'accès à base de données**, le fichier PHP: :darkblue:`include/export_tab_sqli.php` et traitement des données par la BD https://raw.githubusercontent.com/mgrafr/monitor/main/include/export_tab_sqli.php |image549| *Suite de graph()* |image550| .. hint:: La documentation sur jpgraph : https://jpgraph.net/download/manuals/chunkhtml/index.html 6.4.3 autres fichiers PHP ========================= - index_loc.php (en général ne pas modifier) - config.php, header.php (en général ne pas modifier) Mettre la variable à « true » dans config.php |image551| 6.4.4 copies d’écran ==================== |image552| |image553| |image554| .. |image523| image:: ../media/image523.webp :width: 650px .. |image524| image:: ../media/image524.webp :width: 601px .. |image525| image:: ../media/image525.webp :width: 601px .. |image526| image:: ../media/image526.webp :width: 210px .. |image527| image:: ../media/image527.webp :width: 300px .. |image528| image:: ../media/image528.webp :width: 602px .. |image529| image:: ../media/image529.webp :width: 188px .. |image530| image:: ../media/image530.webp :width: 244px .. |image531| image:: ../media/image531.webp :width: 351px .. |image534| image:: ../media/image534.webp :width: 700px .. |image535| image:: ../media/image535.webp :width: 570px .. |image536| image:: ../media/image536.webp :width: 478px .. |image537| image:: ../media/image537.webp :width: 256px .. |image538| image:: ../media/image538.webp :width: 608px .. |image539| image:: ../media/image539.webp :width: 548px .. |image540| image:: ../media/image540.webp :width: 306px .. |image541| image:: ../media/image541.webp :width: 543px .. |image542| image:: ../media/image542.webp :width: 602px .. |image543| image:: ../media/image543.webp :width: 205px .. |image544| image:: ../media/image544.webp :width: 215px .. |image545| image:: ../media/image545.webp :width: 602px .. |image548| image:: ../media/image548.webp :width: 700px .. |image549| image:: ../media/image549.webp :width: 593px .. |image550| image:: ../media/image550.webp :width: 602px .. |image551| image:: ../media/image551.webp :width: 700px .. |image552| image:: ../media/image552.webp :width: 525px .. |image553| image:: ../media/image553.webp :width: 535px .. |image554| image:: ../media/image554.webp :width: 535px .. |image783| image:: ../media/image783.webp :width: 700px