Je reprends un article que j'ai écris un peu à l'arrache aujourd'hui sur mon blog.
Regarder tous les jours, les beaux graphiques réalisés pour nous par domoticz ça va bien un moment. Recevoir des alertes par sms en cas de problème c’est du déjà vu. Je recherchais le moyen de recevoir par mail un rapport en pdf sur l’état de santé (à un instant T) de mon serveur domoticz ainsi qu'un certain nombre d'informations utiles pour moi.
Les utilisateurs domoticz trouveront ici l'information sur la date et l'heure de la mise en route du service ainsi que son pid et la conso mémoire.
Je laisse libre à chacun de juger l'utilité de ce type de rapport. L'idée de ce partage est de permettre d'explorer de nouvelles pistes.
Le rapport est un document pdf, tramis par mail via des tâches planifiées (cron).
Les goûts et les couleurs ne se discutent pas. Y'a forcément mieux, l'idée ici je le répète le partage.
Le programme principal se nomme rapport.py (source plus bas - attention à certain décalage de lignes).
Dans la première partie, on retrouve des infos sur la mémoire, cpu, disque etc.
Dans la seconde, je place des informations pratiques. D'ailleurs, si j'oublie de commander des granulés ça va être ma fête
ça marche comment ?
La partie système est mise en œuvre grâce au module psutil https://github.com/giampaolo/psutil de python dont l'emploi le plus connu est le logiciel glances http://blog.nicolargo.com/2011/12/prese ... ances.htmlde Nicolargo.
Un script python lance des requêtes json au serveur domoticz pour la partie granulés, edf, batterie des modules z-wave.
Enfin, le pdf est créé grâce à la librairie fpdf http://www.fpdf.org/
Le code du programme rapport.py
Code : Tout sélectionner
#!/usr/bin/env python
# -*- coding:Utf-8 -*-
#
# Ce programme python prépare, met en forme et envoi par mail un document pdf reprenant des informations du serveur domoticz
# Utilise les modules psutil, request fpdf
# Version 1 de février 2015 par José
#
# Basé sur le travail de Christophe CARON http://www.caron.ws/ pour la partie supervision psutils https://github.com/giampaolo/psutil
# Utilise le module de recherche de Patrice du site easydomoticz.fr
# Le code de la partie mail vient du site http://astuces.absolacom.com/developpement/envoyer-un-mail-avec-pieces-jointes-en-python/
# Merci à eux.
# Les paramètres liés à domoticz se trouvent sur un fichier (config.py) situé dans le même répertoire
#
##########
#
# Il faut préalablement installer les modules suivants:
# installer python-pip
# par sudo apt-get install python-pip
#
# installer request
# sudo pip install requests
#
# installer psutils
# sudo pip install psutil
#
# installer fpdf
# sudo apt-get install pypdf
#
###########
from fpdf import FPDF
import data
import datetime
import sys
import config
import time
import requests
# Récupération et formatage de la date pour donner le nom du fichier
datejour = datetime.datetime.now()
datejour = datejour.strftime('%d-%m-%Y %H-%M-%S')
title=u"Rapport d'état du serveur Domoticz"
# Classe PDF de pypdf
class PDF(FPDF):
def header(self):
#Arial bold 15
self.set_font('Arial','B',15)
#Calculate width of title and position
w=self.get_string_width(title)+6
self.set_x((210-w)/2)
#Colors of frame, background and text
self.set_draw_color(0,0,204)
self.set_fill_color(255,153,102)
self.set_text_color(0,0,0)
#Thickness of frame (1 mm)
self.set_line_width(1)
#Title avec logo
self.image('img/logo.jpg',10,6,30);
self.cell(w,9,title,1,1,'C',1)
#Line break
self.ln(10)
def footer(self):
#Position at 1.5 cm from bottom
self.set_y(-15)
#Arial italic 8
self.set_font('Arial','I',8)
#Text color in gray
self.set_text_color(128)
#Page number
self.cell(0,10,'Rapport du '+ datejour + ' - P'+str(self.page_no()),0,0,'C')
def chapter_title(self,num,label):
#Arial 11
self.set_font('Arial','',11)
#Background color
self.set_fill_color(255,255,255)
#Title avec image du rapsberry pi
self.cell(0,5,label,0,1,'L',1)
self.image('img/pi.jpg',110,42,65);
#Line break
self.ln(2)
def chapter_body(self,name):
#Arial 11
self.set_font('Arial','',11)
#Output justified text
self.multi_cell(0,5,name)
#Line break
self.ln()
#Mention in italics
self.set_font('','I')
def print_chapter(self,num,title,name):
self.chapter_title(num,title)
self.chapter_body(name)
def chapitre_general_titre(self,num,texte):
#Arial 11
self.set_font('Arial','',11)
#Background color
self.set_fill_color(255,255,255)
# Placement suivant l'image
if num == 20:
self.image('img/edf.jpg',10,135,20)
self.set_y(136)
self.set_x(36)
self.cell(0,5,texte,0,1,'L',1)
if num == 21:
self.image('img/eo2.jpg',10,152,15)
self.set_y(153)
self.set_x(36)
self.cell(0,5,texte,0,1,'L',1)
if num == 22:
self.image('img/cuve.jpg',10,187,20)
self.set_y(188)
self.set_x(36)
self.cell(0,5,texte,0,1,'L',1)
if num == 23:
self.image('img/oregon.jpg',10,220,20)
self.set_y(220)
self.set_x(36)
self.cell(0,5,texte,0,1,'L',1)
#Line break
self.ln(2)
self.set_x(36)
def chapitre_general(self,texte):
#Arial 11
self.set_font('Arial','',11)
#Background color
self.set_fill_color(255,255,255)
#Abcisse
self.set_x(36)
#Texte à mettre dans le pdf
self.cell(0,5,texte,0,1,'L',1)
#Line break
self.ln(2)
self.set_x(36)
# Module de recherche de Patrice
def recherche(idx, recherche, decal, nb_car):
# URL Domoticz a interroger pour recuperer les infos est type=devices&rid=XXX
json_url='/json.htm?type=devices&rid='
requete='http://'+config.domoticz_ip+':'+config.domoticz_port+json_url+str(idx)
r=requests.get (requete)
ch= r.text
lgterme=len(str(recherche))
trouve_a= ch.find(str(recherche))
debut=trouve_a+lgterme
resultat=ch[debut+int(decal):debut+int(decal)+int(nb_car)]
return resultat
# Lance les requêtes au moyen du programme collecte
# Récupération EDF
# recherche(idx,recherche,decal,nb_car)
edf=recherche('180', 'Data', '5', '4')
# Récupération du nombre de granulés en stock
# Calcul du nombre de sac par rapport à la variable du fichier config
granules=recherche('164', 'Counter', '5', '2')
humidite=recherche('171', 'Humidity', '4', '2')
stock=int(config.qte_granules_total) - int(granules)
poids=int(granules) * int(config.poids_sac_granules)
# Récupération du volume et de la hauteur de la cuve
volume=recherche('177', 'Counter', '5', '4')
hauteur=recherche('176', 'Data', '5', '3')
temp=recherche('101', 'Data', '5', '3')
# Récupération de l'état des batteries
batsdb=recherche('169', 'BatteryLevel', '4', '3')
# Création du PDF
pdf=PDF()
pdf.set_title(title)
pdf.set_author(u'José')
pdf.add_page()
# Mise en forme des données système
pdf.print_chapter(1,u'Information mémoire (Disponible ' + data.mpourcent + u" %)","Total = " + data.mtotal + u" - Utilisée = " + data.mutilise + " - Libre = " + data.mlibre)
pdf.print_chapter(2,u'Information mémoire swap (Utilisé ' + data.mspourcent + u" %)","Total = " + data.mstotal + u" - Utilisée = " + data.msutilise + " - Libre = " + data.mslibre)
pdf.print_chapter(3,u'Information CPU',"Charge = " + data.usagecpu + u" % - Température = "+str(data.cpu_temperature)+u" °C")
pdf.print_chapter(4,u'Espace disque carte SD (utilisée à '+ data.pourcent + "%)","Total = " + data.total + u" - Utilisé = " + data.utilise + " - Libre = " + data.libre)
pdf.print_chapter(5,u'Le module Domoticz (pid ' + str(data.pid) + ') fonctionne depuis le ' + str(data.boot) + ' - Status : ' + data.status,u'Ce soft utilise '+ str(data.mem) + u' de mémoire.')
pdf.print_chapter(6,u'Temps de fonctionnement du serveur : '+data.demarrage,'')
# Trace une ligne pour séparer les données système du reste
pdf.dashed_line(10,130,200,130,2,2)
# Mise en forme des données générales
# EDF
pdf.chapitre_general_titre(20,u"La valeur de la consommation EDF de la journée d'hier s'élève à " + edf + u" euros (hors abo).")
# Granulés
pdf.chapitre_general_titre(21,u"Il reste " + str(stock) + u" sacs de granulés en stock sur " + str(config.qte_granules_total) + '.')
pdf.chapitre_general(u"Représente une consommation de "+ str(granules) + u" sacs à ce jour, soit " + str(poids) + " kg." )
pdf.chapitre_general(u"Le niveau d'humidité ambiante du stockage est de " + humidite + u" %.")
# Ajoute un message d'alerte pour lancer la commande
if int(stock) < 20:
# Place la couleur du texte en rouge
pdf.set_text_color(204,0,0)
pdf.chapitre_general(u"Attention le stock est trop bas. Il faut lancer une commande !")
# Remet la couleur en noir
pdf.set_text_color(0,0,0)
# Cuve
pdf.chapitre_general_titre(22,u'Le volume de la cuve est de '+ volume + " m3.")
pdf.chapitre_general(u"La hauteur d'eau est de " + hauteur + " cm.")
pdf.chapitre_general(u"La température de l'eau est de "+ temp + u"°C.")
# Batteries des sondes Oregon
pdf.chapitre_general_titre(23,u'Etat batterie sonde salle de bains: '+ batsdb + " %.")
pdf.chapitre_general(u"Etat batterie sonde cabane de jardin: "+ batsdb + " %.")
pdf.chapitre_general(u"Etat batterie sonde congélateur: "+ batsdb + " %.")
# Lancement de la création du rapport
pdf.output('Rapport.pdf','F')Code : Tout sélectionner
#!/usr/bin/env python
# -*- coding:Utf-8 -*-
#
# Récupération du code de la page
# http://astuces.absolacom.com/developpement/envoyer-un-mail-avec-pieces-jointes-en-python/
# Merci à son auteur
#
########
import smtplib
import os
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders
# Variables
files = ['Rapport.pdf'] # Pièces jointes avec leur chemin
destinataires = ['destinataire1@free.fr', 'destinataire2@free.fr'] # Mettre des [] Adresses mail séparées par des virgules
expediteur = 'expediteur@free.fr'
titre = u"Rapport journalier Domoticz"
message = u"Bonjour a tous, \nVoici votre rapport journalier du systeme domoticz. \nBonne journee."
smtp = 'smtp.free.fr'
def send_mail(send_from, send_to, subject, text, files=[], server='localhost'):
assert type(send_to)==list
assert type(files)==list
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = COMMASPACE.join(send_to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach( MIMEText(text) )
for f in files:
part = MIMEBase('application', "octet-stream")
#print "Transmission du mail avec la pièce jointe : " + f
part.set_payload( open(f,"rb").read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
msg.attach(part)
smtp = smtplib.SMTP(server)
smtp.sendmail(send_from, send_to, msg.as_string())
smtp.close()
# Module d'envoi de mail
send_mail(expediteur, destinataires, titre, message, files, smtp)Code : Tout sélectionner
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
#########
#
# Utilisation du travail de Christophe CARON
# http://www.caron.ws/
#
# Récupération des données pour le module rapport de domoticz
#
# Jose - Février 2015 - Ajout de la partie domoticz
#
#########
import psutil
import os
import sys
import string
import datetime
from subprocess import PIPE, Popen
from datetime import timedelta
#convertion des unites en Ko,Go,To
def bytes2human(n):
# http://code.activestate.com/recipes/578019
# >>> bytes2human(10000)
# '9.8K'
# >>> bytes2human(100001221)
# '95.4M'
symbols = (' Ko', ' Mo', ' Go', ' To', ' Po', 'Eo', 'Zo', 'Yo')
prefix = {}
for i, s in enumerate(symbols):
prefix[s] = 1 << (i+1)*10
for s in reversed(symbols):
if n >= prefix[s]:
value = float(n) / prefix[s]
return '%.1f%s' % (value, s)
return "%sB" % n
# Lecture de la temperature
def get_cpu_temperature():
process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE)
output, _error = process.communicate()
return float(output[output.index('=') + 1:output.rindex("'")])
# Détermine la date et l'heure du dernier reboot de domoticz
def domo():
for p in psutil.process_iter():
try:
pi = p.as_dict(attrs=['pid', 'name', 'create_time', 'memory_info', 'status'])
except:
pass
else:
if pi['name'] == 'domoticz':
pid = pi['pid']
#print 'Pid correspondant: ', pi['pid']
boot = datetime.datetime.fromtimestamp(pi['create_time']).strftime("%d-%m-%Y %H:%M:%S")
#print 'Heure de la mise en service domoticz', boot
mem = pi['memory_info'][0]+pi['memory_info'][1]
#print 'Mémoire :', mem
status = pi['status']
#print status
return boot, pid, mem, status
# Uptime
with open('/proc/uptime', 'r') as f:
uptime_seconds = round(float(f.readline().split()[0]),0)
demarrage = str(timedelta(seconds = uptime_seconds))
demarrage = demarrage.replace('days','Jours',1)
demarrage = demarrage.replace(',',' et',1)
demarrage = demarrage.replace('day','Jour',1)
demarrage = demarrage.replace(':','h ',1)
demarrage = demarrage.replace(':','mn ',1)
demarrage = demarrage+ 's '
# Attribution des valeurs aux variables
# SD card
valeur = psutil.disk_usage('/')
part = psutil.disk_partitions(all=False)
infopart = (part[0])[2] # extration de l'extension
total = bytes2human(valeur.total)
utilise = bytes2human(valeur.used)
libre = bytes2human(valeur.free)
pourcent = str(valeur.percent)
# CPU
usagecpu = str(psutil.cpu_percent(interval=1))
p = psutil.Process(os.getpid())
# Heure mise a jour
dateheure = datetime.datetime.now()
dateheure = dateheure.strftime('%d/%m/%Y')
#dateheure = datetime.datetime.fromtimestamp(p.create_time).strftime("%d-%m-%Y %H:%M")
# Temperature
cpu_temperature = get_cpu_temperature()
p = psutil.Process(os.getpid())
# Memoire Vive RAM
valeur = psutil.virtual_memory()
mtotal = bytes2human(valeur.total)
mdispo = bytes2human(valeur.available)
mutilise = bytes2human(valeur.used)
mlibre = bytes2human(valeur.free)
mpourcent = str(valeur.percent)
# Memoire SWAP
valeur = psutil.swap_memory()
mstotal = bytes2human(valeur.total)
msutilise = bytes2human(valeur.used)
mslibre = bytes2human(valeur.free)
mspourcent = str(valeur.percent)
# Date et heure de boot domoticz et pid système
boot, pid, mem, status = domo()
# Mise en forme de la mémoire
mem = bytes2human(mem)Les programmes data.py et config.py sont chargés comme modules du programme rapport.py. Ce dernier récupère toutes les données à mettre dans le rapport et procède à sa création grâce à la classe fpdf. Une tâche planifiée réalise l'opération puis transmet le rapport par mail (gare au spam, je le répète le blacklistage n'est pas loin).
Les images sont stockées dans un répertoire "img" afin que ça ne devienne pas le bazar.
Allez, je file commander mes granulés. Si je suis allé trop vite, n'hésitez pas.