Re: RpiNotify – Notifiche push allo smartphone [GUIDA]
Inviato: 04/02/2020, 13:14
grazie, credo proprio di aver fatto così dopo aver provato tante volte.
Il Forum NON UFFICIALE per raccogliere le novità riguardo ai software 123Solar e MeterN per il monitoraggio energetico e non solo ....
https://www.flanesi.it:443/forum/
Codice: Seleziona tutto
#!/usr/bin/python
#
# Script for RPI Power Pack Hat Pro V1.1 with MeterN
#
# enable I2C via raspi-config
# sudo apt-get install i2c-tools
# sudo apt-get install python-smbus
# sudo apt-get install python-requests
# ln -s /var/www/MyScripts/ups/upshat.py /usr/bin/ups
#
import struct
import smbus
import sys
import os
import time
import requests
# Inserire il token per l'invio dei messaggi con RPInotify - Lasciare vuoto se non si utilizza il servizio di notifica
TOKEN = ""
if len(sys.argv) == 2:
meterID = sys.argv[1] # MeterN ID per lo stato della batteria]
else:
print 'usage: ./upshat.py metern_id'
print 'example: ./upshat.py 8 - for use metern id 8 and write /dev/shm/metern8.txt'
sys.exit(1)
# da qui in poi non sono necessarie modifiche
file = "/dev/shm/metern"+ meterID +".txt"
control20 = 0
control95 = 0
sys.stdout=open(file,"w")
print "%s_1(0*V)" % meterID # Tensione
print "%s_2(0*%%)" % meterID # Capacita
sys.stdout.close()
while True:
def readVoltage(bus):
"This function returns as float the voltage from the Raspi UPS Hat via the provided SMBus object"
address = 0x62
read = bus.read_word_data(address, 2)
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
voltage = swapped * 305.0 /1000000
return voltage
def readCapacity(bus):
"This function returns as a float the remaining capacity of the battery connected to the Raspi UPS Hat via the provided SMBus object"
address = 0x62
read = bus.read_word_data(address, 4)
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
capacity = swapped/256
return capacity
bus = smbus.SMBus(1) # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
sys.stdout=open(file,"w")
print "%s_1({0:0.2f}*V)".format(readVoltage(bus)) % meterID # Tensione
print "%s_2({0:0.0f}*%%)".format(readCapacity(bus)) % meterID # Capacita %
sys.stdout.close()
# Notifiche con RPINotify
if TOKEN != "":
if readCapacity(bus) > 20:
control20 = 0
if readCapacity(bus) <= 20 and control20 == 0:
MSG = "RPI UPS Hat - ATTENZIONE Batteria residua 20%"
req = requests.post('https://api.rpinotify.it/message/' + TOKEN + '/', data={'text': MSG})
control20 = 1
#print(req.text)
if readCapacity(bus) > 95:
control95 = 0
if readCapacity(bus) <= 95 and control95 == 0:
MSG = "RPI UPS Hat - Funzionamento a batteria - Batteria residua 95%"
req = requests.post('https://api.rpinotify.it/message/' + TOKEN + '/', data={'text': MSG})
control95 = 1
# Shutdown system
if readCapacity(bus) < 5:
#print "System will shutdown now,bye!"
os.system("sudo poweroff")
sys.exit(1)
time.sleep(60)
Codice: Seleziona tutto
#!/usr/bin/php
<?php
/**
* Raspberry load check control & notify with RPInotify
*
* "loadcheck" Rev. 1.1
*
* Copyright (C) 2017 Flavio Anesi <www.flanesi.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* History
*
* 17-11-2017: Rilascio versione v1.0
*
* 24-05-2018: Vers. 1.1 : spostamento file di controllo .pid in cartella emporanea /dev/shm
*
* Descrizione
*
* Il presente script, esegue un controllo dei consumi domestici e in caso di superamento delle varie soglie di intervento
* (descrite nel seguito) del contatore elettronico invia una notifica sul cellulare mediante il servizio gratuito RpiNotify
* Per il funzionamento è quindi necessario avere in funzione il servizio di monitoraggio con MeterN ed aver effettuato
* una registrazione al servizio RpiNotify in modo da avere a disposizione il token rilasciato da RpiNotify e da inserire di seguito.
*
/**
* Creare il symlink con:
* ln -s /var/www/MyScripts/loadcheck.php /usr/bin/loadcheck
*
* Lo script puo' essere avviato in automatico all'avvio di MeterN (ver 0.8.4 e succ.), inserendo la seguente riga nel file
* "/var/www/metern/config/config_daemon.php" nella sessione Start
*
* exec("loadcheck > /dev/null 2>/dev/null &");
*
* mentre nella sessione Stop
*
* exec("pkill -f loadcheck > /dev/null 2>&1 &");
*
*
* A seguire alcune definizioni:
*
* Potenza contrattuale: la potenza richiesta all’atto della stipula di contratto e contrattualmente garantita.
Nel caso di utenze domestiche è abitualmente 3 kW;
* Potenza disponibile: potenza contrattuale più 10%, nel caso di specie corrispondente a 3,3 kW;
* Potenza limite: potenza disponibile + 27%, nel caso di specie 4,2 kW.
*
* Le regole di intervento del contatore elettronico monofase GEM sono state tratte da questa guida presente in rete:
* http:\\www.flanesi.it\blog\download\Il-nuovo-contatore-elettronico.pdf
*
*/
/**
* Imposta "error reporting"
*
* Turn off all error reporting ---> level: 0
* Report simple running errors ---> level: E_ERROR | E_WARNING | E_PARSE
* Reporting E_NOTICE can be good too ---> level: E_ERROR | E_WARNING | E_PARSE | E_NOTICE
* Report all errors except E_NOTICE ---> level: E_ALL & ~E_NOTICE
* Report all PHP errors ---> level: E_ALL
*/
error_reporting(0);
/** Nome dello script */
$filename = basename(__FILE__, ".php");
/** Percorso memorizzazione file pid */
$pathname = '/dev/shm/'.$filename;
/** Percorso e nome del "pid" file */
$pidfile = $pathname . ".pid";
/** Inizializza il "Task monitor signal event" */
declare(ticks = 1);
/** Registro funzione da eseguire all'arresto dell'esecuzione */
register_shutdown_function("fatalErrorShutdown");
/** Installo cattura dei segnali "Ctrl+C e kill SIGTERM" */
pcntl_signal(SIGTERM, "sigintShutdown");
pcntl_signal(SIGINT, "sigintShutdown");
/** Controllo esistenza altro processo attivo */
checkAlreadyRunning();
/*******************************************************************************************************************
* Configurazione script *
*******************************************************************************************************************/
/**
*
*
* Modificare la configurazione dello script in base alla propria installazione
*/
/**
* Modalita' di funzionamento
*
* 0 = Disattiva il debug su terminale.
* 1 = Attiva il debug su terminale se avvio dello script manualmente
*/
const DEBUG_MODE = 0;
/*******************************************************************************************************************
* Configurazione fornitura *
*******************************************************************************************************************/
/** Potenza contrattuale in kW */
const CONTRACT_POWER = 3;
/*******************************************************************************************************************
* Configurazione misuratori *
*******************************************************************************************************************/
/** Meter Id Prelievi */
const METERN_PRELIEVI = 3;
/** Tipo di misurazione dei Prelievi
* Impostare la variabile TYPE_IMP a seconda del metodo di calcolo/misura dei PRELIEVI
* 0 se si utilizza di eflow
* 1 se si utilizza virtmeter
* 2 se si utilizza un contatore per le misura di prelievi / immissioni
* 3 TEST - NO LETTURA (legge il valore da un file /var/www/MyScripts/testprel.txt)
(il contenuto del file testperl deve essere nel formato es: 3(2500*W) )
*/
$TYPE_IMP = 0;
/* Percorso e nome file destinazione per inserire un indicatore sullo stato dello script in MeterN
*/
$LOAD_FILE = $pathname . ".txt";
/* Percorso e nome file destinazione logger
*/
const METERN_FILE_LOGGER = "/var/www/metern/data/events.txt";
/*******************************************************************************************************************
* Configurazione RpiNotify *
*******************************************************************************************************************/
/** Token RpiNotify per notifiche push*/
const token = "yourtoken";
/*******************************************************************************************************************
* Fine configurazione script *
*******************************************************************************************************************/
/*******************************************************************************************************************
* Calcoli interni in base alla configurazione *
*******************************************************************************************************************/
/** Potenza disponibile (Kw) */
const AVAIL_POWER = CONTRACT_POWER * 1.1;
/** Potenza limite (kW) */
const LIMIT_POWER = AVAIL_POWER * 1.27;
/** Potenza max per ditacco (kW) */
const MAX_POWER = 14;
/** Se attivo DEBUG su terminale */
if(DEBUG_MODE == 1)
{
/** Stampo informazioni di debug su terminale, visibili solo se avviato manualmente */
printf("#########################################################\n");
printf("Potenza CONTRATTUALE : %8.02f kW\n", CONTRACT_POWER);
printf("Potenza DISPONIBILE : %8.02f kW\n", AVAIL_POWER);
printf("Potenza LIMITE : %8.02f kW\n", LIMIT_POWER);
printf("Potenza MAX PRELEVABILE : %8.02f kW\n", MAX_POWER);
}
/*******************************************************************************************************************
* Fine calcoli interni in base alla configurazione *
*******************************************************************************************************************/
/** Logger event */
logevents("Started Load check Script");
/* stato load script */
$buffer = sprintf("load(1*X)\n");
/* Save file */
file_put_contents($LOAD_FILE, $buffer);
/** Loop infinito stile servizio */
while (true)
{
/** Inizializzo prelievi */
$prelievi = get_prelievi($TYPE_IMP);
/** Se attivo DEBUG su terminale */
if(DEBUG_MODE == 1)
{
printf("#########################################################\n");
printf("Prelievi : %8.02f W\n", $prelievi);
}
if ($prelievi > MAX_POWER*1000) // SUPERO POTENZA MASSIMA - distacco in 1-2 secondi
{
$msg = "Prelievi: " . $prelievi/1000 . "kW. Distacco imposto per supero potenza massima di " . MAX_POWER . "kW\n";
if(DEBUG_MODE == 1)
{
printf($msg);
}
rpinotify(token, $msg);
}
else
{
if ($prelievi > LIMIT_POWER*1000) // SUPERO POTENZA LIMITE - distacco in 2 min
{
$limit_time_start = microtime(true);
limit_power_control($limit_time_start, $TYPE_IMP);
}
else
{
if ($prelievi > AVAIL_POWER*1000 && $prelievi < LIMIT_POWER*1000) // SUPERO POTENZA DISPONIBILE - distacco in 182 min
{
$avail_time_start = microtime(true);
avail_power_control($avail_time_start, $TYPE_IMP);
}
}
}
/** Riduci il consumo della CPU, aggiungendo un ritardo fisso */
sleep (5);
}
/**
* Lettura prelievi istantanei
*
* @return float prelievi attuali
*/
function get_prelievi($TYPE_IMP)
{
/** Richiedi valori di potenza */
switch($TYPE_IMP)
{
case 0: $cmd = "eflowlive whin"; break;
case 1: $cmd = "virtmeter " . METERN_PRELIEVI . " 0 0 imppower 0"; break;
case 2: $cmd = "cat /dev/shm/metern".METERN_PRELIEVI.".txt | egrep \"^".METERN_PRELIEVI."\(\" | grep \"*W)\""; break;
case 3: $cmd = "cat /var/www/MyScripts/testprel.txt"; break;
}
/** Esegui il comando di richiesta */
$datareturn = shell_exec($cmd);
/** Rimuovi spazi ridondanti */
$datareturn = trim($datareturn);
/** Rimuovi intestazione */
$datareturn = preg_replace("/^" . METERN_PRELIEVI . "\(/i", "", $datareturn);
/** Rimuovi finale */
$datareturn = preg_replace("/\*[a-z0-9]+\)$/i", "", $datareturn);
/** Converto stringa in floating point */
$prelievi = floatval($datareturn);
/** Esco con lettura prelievi istantanei */
return $prelievi;
}
/**
* Controllo supero potenza limite LIMIT_POWER
*/
function limit_power_control($limit_time_start, $TYPE_IMP)
{
while (true)
{
$prelievi = get_prelievi($TYPE_IMP);
if ($prelievi > LIMIT_POWER*1000)
{
$perc = (($prelievi / (LIMIT_POWER*1000)) - 1) * 100;
$perc = round($perc, 0);
$time_dist = 120 - (microtime(true) - $limit_time_start);
$time_dist = round($time_dist, 0);
$msg = "Prelievi: " . $prelievi/1000 . "kW. Rischio distacco per supero potenza limite (" . LIMIT_POWER . "kW)del " . $perc . "% - Distacco entro " .$time_dist. " secondi";
if(DEBUG_MODE == 1)
{
printf("%s\n", $msg);
}
rpinotify(token, $msg);
sleep (9);
} else {
break;
}
}
}
/**
* Controllo potenza fra AVAIL_POWER e LIMIT_POWER
*/
function avail_power_control($avail_time_start, $TYPE_IMP)
{
$check1=0;
$check2=0;
while (true)
{
$prelievi = get_prelievi($TYPE_IMP);
if ($prelievi > AVAIL_POWER*1000 && $prelievi < LIMIT_POWER*1000)
{
$time_dist = (microtime(true) - $avail_time_start);
$time_dist = round($time_dist, 0);
if(DEBUG_MODE == 1)
{
printf("%u\n", $time_dist); //stampa a terminale i secondi trascorsi
}
if ($time_dist >= 120 && $check1 == 0 )
{
$msg = "Prelievi: " . $prelievi/1000 . "kW. Superata potenza disponibile di ". AVAIL_POWER ."kW. Possibile distacco fra 180 minuti. ";
$check1=1;
if(DEBUG_MODE == 1)
{
printf("%s\n", $msg); //stampa a terminale il messaggio di avviso
}
rpinotify(token, $msg);
}
if ($time_dist >= 5520 && $check2 == 0 )
{
$msg = "Prelievi:" . $prelievi/1000 . "kW. Superata potenza disponibile di ". AVAIL_POWER ."kW. Possibile distacco fra 90 minuti. ";
$check2=1;
if(DEBUG_MODE == 1)
{
printf("%s\n", $msg);
}
rpinotify(token, $msg);
}
sleep (5);
} else {
break;
}
}
}
/**
* RpiNotify
*/
function rpinotify($token, $msg)
{
$post['text'] = $msg;
$ch = curl_init('https://api.rpinotify.it/message/' . $token . '/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$output = curl_exec($ch);
if(DEBUG_MODE == 1)
{
printf("%s\n", $output);
}
curl_close($ch);
}
/**
* Log Event
*
* @param $stringData string Stringa di testo da loggare
*/
function logevents($stringData)
{
/** Data e ora attuale */
$now = date("d/m/Y H:i:s");
/** Se il file per logger non esiste */
if (!file_exists(METERN_FILE_LOGGER))
{
/** Creo nuovo file vuoto */
FileWriteWithLock(METERN_FILE_LOGGER, "");
}
/** Leggo il file interamente */
$content = file_get_contents(METERN_FILE_LOGGER);
/** Controllo lettura contenuto */
if($content !== false)
{
/** Appendo all'inizio la nuova stringa di testo */
$content = $now . "\t" . $stringData . " (" . getmypid() . ")\n\n" . $content;
/** Salvo il file con accesso esclusivo */
FileWriteWithLock(METERN_FILE_LOGGER, $content);
}
}
/**
* WriteWithLock
*
* Funzione per scrivere file con apertura esclusiva
*
* @param string $file Nome del file da scrivere
* @param string $mode Modalita' di accesso al file
* @param string $content Contenuto del file
*
* @return bool Stato della scrittura
*/
function WriteWithLock($file, $mode, $content)
{
/** Apro il file nella modalita' richiesta */
if ($handle = fopen($file, $mode))
{
/** Tempo di inizio lock del file*/
$startTime = microtime(TRUE);
do
{
/** Serializza l'accesso in scrittura */
$canWrite = flock($handle, LOCK_EX);
/** Se il blocco non e' stato ottenuto attendi 0 - 100 millisecondi, per evitare collisione e limitare il carico della CPU */
if (!$canWrite)
{
/** Rallenta la CPU casualmente */
usleep(round(rand(0, 100) * 1000));
}
}
while ((!$canWrite) and ((microtime(TRUE) - $startTime) < 5));
/** Il file e' stato bloccato in modo da poter memorizzare le informazioni */
if ($canWrite)
{
/** Scri il file */
fwrite($handle, $content);
/** Scarico buffer scrittura */
fflush($handle);
/** Togli il lock dal file */
flock($handle, LOCK_UN);
/** Chiud il file */
fclose($handle);
/** Esci con successo */
return (true);
}
/** Chiud il file */
fclose($handle);
/** Esci con error */
return (false);
}
/** Esci con error */
return (false);
}
/**
* FileWriteWithLock
*
* Funzione per scrivere file con apertura esclusiva
*
* @param string $file Nome del file da scrivere
* @param string $content Contenuto del file
*
* @return bool Stato della scrittura
*/
function FileWriteWithLock($file, $content)
{
/** Scrivi il vile in modalita "scrittura" */
return WriteWithLock($file, "w", $content);
}
/**
* Funzione per controllare processo univoco
*/
function checkAlreadyRunning()
{
/** Variabili globali */
global $filename, $pidfile;
/** Inizializzo procesos non in running */
$pid = false;
$pid_running = false;
/** Controllo esistenza "Process Id" file */
if (file_exists($pidfile))
{
/** Se il file esiste, estrazione "Process Id" */
$data = file($pidfile);
/** Per ogni "Process Id", controllo se attualmente attivo */
foreach ($data as $pid)
{
/** Estrazione "Process Id" */
$pid = (int)$pid;
/** Se "Process Id" valido e processo attivo */
if ( ($pid > 0) && (file_exists("/proc/" . $pid)) )
{
/** Salvo "Process Id" */
$pid_running = $pid;
/** Interrompo ricerca */
break;
}
}
}
/** Se trovato processo attivo */
if ($pid_running && ($pid_running != getmypid()) )
{
/** Controllo esistenza "Process Id" file */
if (file_exists($pidfile))
{
/** Inizializzo file "Process Id" */
file_put_contents($pidfile, $pid);
}
/** Logger */
print($filename . " already running as pid " . $pid . " so stopping now" . PHP_EOL);
/** Esci */
exit();
}
else
{
/** Ricavo "Process Id" */
$pid = getmypid();
/** Scrivo file "Process Id" */
file_put_contents($pidfile, $pid);
/** Logger */
print($filename . " running with pid: " . $pid . PHP_EOL);
}
}
/**
* Funzione eseguita quando si verifica un errore fatale
*/
function fatalErrorShutdown()
{
/** Estrazione nome del file */
$filename = basename(__FILE__, ".php");
/** Ricava ultimo errore */
$lastError = error_get_last();
/** Se presente errore, esci */
if (!is_null($lastError) && $lastError['type'] === E_ERROR)
{
/** Logger */
print($filename . " receive unexpected error: " . $lastError['message'] . PHP_EOL);
/** Esci in modo sicuro */
shutdown();
}
}
/**
* Funzione eseguita se lo script e' stato ucciso da:
*
* SIGINT: Ctrl+C
* SIGTERM: kill
*
* @param int $signal
*/
function sigintShutdown($signal)
{
/** Estrazione nome del file */
$filename = basename(__FILE__, ".php");
/** Ricevuto segnale SIGINT o SIGTERM */
if ($signal === SIGINT || $signal === SIGTERM)
{
/** Logger */
print($filename . " receive SIGINT or SIGTERM." . PHP_EOL);
/** Esci */
shutdown();
}
}
/**
* Funzione chiamata per uscita in modo sicuro
*/
function shutdown()
{
/** Variabili globali */
global $filename, $pidfile, $boiler_consumption;
/** Controllo esistenza "Process Id" file */
if (file_exists($pidfile))
{
/** Cancello "Process Id" file */
unlink($pidfile);
}
/** Logger */
print($filename . " quitting." . PHP_EOL);
logevents("Stopped Load check Script");
/* stato load script */
$buffer = sprintf("load(0*X)\n");
/* Save file */
file_put_contents($LOAD_FILE, $buffer);
/** Uscita */
exit();
}
?>
Codice: Seleziona tutto
TOKEN = ""
Codice: Seleziona tutto
TOKEN = "inserisci_qui_il_tuo_token"
CHATID="inserisci_il_numero_chat"
Codice: Seleziona tutto
req = requests.post('https://api.rpinotify.it/message/' + TOKEN + '/', data={'text': MSG})
Codice: Seleziona tutto
req = requests.post('https://api.telegram.org/bot' + TOKEN + '/sendMessage', data={'text': MSG,'chat_id':CHATID})