forked from cesco78/domotica-telegram
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtvcc.py
466 lines (379 loc) · 20.1 KB
/
tvcc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
#!/usr/bin/python
import telepot
import pprint
import time
import sys
import os
import json
import requests
import ConfigParser
import datetime
import traceback
import sqlite3
# "Domotibot" e' un bot di telegram che permette la gestione di alcune funzionalita' all'interno
# di una rete domestica. Viene usata la libreria "telepot" per le connessioni alle API di Telegram
#
# Programma di Francesco Tucci
# Versione 1.04 del 17/04/2016
#
# Il programma e' rilasciato con licenza GPL v.3
#
# genero un timestamp per l'inserimento nel file di log all'inizio di ogni riga
# ritorna il timestamp nel formato dd-mm-aaaa hh:mm:ss
def adesso():
ts = time.time()
st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
return st
# qualche nota sul file di log:
# - il timestamp nel formato dd-mm-aaaa hh:mm:ss messo all'inizio della riga e' generato dalla funzione adesso()
# - dopo il timestamp metto tre caratteri che riguardano il tipo si messaggio
# 0 = [INF] informazione
# 1 = [AVV] avviso
# 2 = [ERR] errore
# cosi' posso filtrare il log alla ricerca di errori senza vedere tutti i messaggi meno gravi
def logga(livello, messaggio):
# apro il file di log in append
log = open (ConfigSectionMap("Sistema")['log'], "a")
# inizio mettendo il timestamp
stringa = adesso()
# aggiungo il livello di gravita'
if livello == 0:
stringa = stringa + " [INF]"
elif livello == 1:
stringa = stringa + " [AVV]"
else:
stringa = stringa + " [ERR]"
# inserisco il messaggio
stringa = stringa + " " + messaggio
# lo scrivo nel file
log.write(stringa + "\n")
# chiudo il file di log
log.close()
# funzione per la memorizzazione di tutti i parametri nel file di configrazione
# per poter accedere al parametro basta usare il comando
# x = ConfigSectionMap("nome_sezione)['nome_parametro']
# ritorna un array con tutti i valori della sezione richiesta
def ConfigSectionMap(section):
dict1 = {}
options = Config.options(section)
for option in options:
try:
dict1[option] = Config.get(section, option)
if dict1[option] == -1:
print("skip: %s" % option)
except:
print("exception on %s!" % option)
dict1[option] = None
return dict1
# scrivo un file di semaforo con data e ora di accensione, per evitare la falsa rilevazione
# tutte le volte che il sistema si accende
def scrivi_semaforo():
quando_acceso = open (ConfigSectionMap("Sistema")['log_file_accensione'], "w")
quando_acceso.write("adesso")
quando_acceso.close
# per verificare se il processo di motion e' attivo
# ritorna il numero di occorrenze del processo "motion" attive nel sistema
# (attenzione, se si sta lavorando con il file di configurazione di motion aperto il conteggio e' falsato)
def verifica_motion():
processname = 'motion'
tmp = os.popen("ps -Af").read()
proccount = tmp.count(processname)
return proccount
# cosa fare quando si riceve un messaggio
def handle(msg):
# questi sono i dati del messaggio in arrivo
id_utente = msg['from']['id'] # id utente per rispondere male a chi non e' ablitato
nome_utente = msg['from']['first_name'] # nome utente per rispondere con gentilezza ai comandi
if 'last_name' in msg:
cognome_utente = msg['from']['last_name'] # cognome dell'utente che ha inviato il messaggio
else:
cognome_utente = ""
#cognome_utente = "n.d."
id_chat = msg['chat']['id'] # id della chat a cui rispondere
testo = msg['text'].lower() # testo dei messaggi ricevuti, convertito tutto in minuscole per facilitare il lavoro con il parse
# debug, per verificare cosa e' effettivamente arrivato al bot
logga(0, "L'utente " + nome_utente + " (" +str(id_utente) + ") ha scritto <<" + testo + ">> nella chat " + str(id_chat))
# comando per reimpostare la tastiera standard e quello per creare la tastiera personalizzata
hide_keyboard = {'hide_keyboard': True}
show_keyboard = {'keyboard': [['TVcc ON','TVcc OFF', 'TVcc?', 'Now'],['Temp?', 'Watt?']]}
# controllo che i comandi arrivino dagli utenti abilitati
utenti_abilitati = [ConfigSectionMap("Sistema")['utente_1'], ConfigSectionMap("Sistema")['utente_2'], ConfigSectionMap("Sistema")['utente_3'], ConfigSectionMap("Sistema")['utente_4'], ConfigSectionMap("Sistema")['utente_5']]
utente_abilitato = False
for index in range(len(utenti_abilitati)):
if utenti_abilitati[index] != "no":
if id_utente == int(utenti_abilitati[index]):
utente_abilitato = True
if utente_abilitato == False:
bot.sendMessage(id_utente, "Spiacente, bot non attivo")
#mando un messaggio all'amministratore del sistema per informare che un altro utete ha provato a scrivere
messaggio = "Attenzione l'utente " + nome_utente + " " + cognome_utente + " (id " + str(id_utente) + ") ha scritto questo: <<" + testo + ">>"
bot.sendMessage(ConfigSectionMap("Sistema")['utente_1'], messaggio)
logga(1, "Messaggio da utente non autorizzato! --> " + nome_utente + " " + cognome_utente + " (id " + str(id_utente) + ") ha scritto questo: <<" + testo + ">>")
else:
# questo e' il comando per iniziare ad interagire
if testo == "/ciao" or testo == "/ciao@[nomedelbot]":
messaggio = "Ciao " + nome_utente + ", cosa posso fare per te?"
# alla risposta aggiunge la tastiera personalizzata
bot.sendMessage(id_chat, messaggio, reply_markup=show_keyboard)
# *******
# voglio accendere il sistema di videosorveglianza
# *******
elif testo == "tvcc on":
logga(0, "Accendo motion come da richiesta")
# controllo se il processo 'motion' e' attivo o no
motion_on = verifica_motion()
# se il processo 'motion' non e' attivo lo avvio e avviso che e' attivato
if motion_on == 0:
# attivo il processo
os.system("/home/pi/motion-mmal/motion")
scrivi_semaforo()
# gli lascio il tempo di attivarsi
time.sleep(5)
# controllo se e' in esecuzione
motion_on = verifica_motion()
if motion_on == 0:
messaggio = "Sistema TVcc NON attivato, potrebbe esserci un problema"
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
logga(2, "Motion non si e' avviato")
else:
messaggio = "Sistema TVcc attivato con successo!"
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
logga(0, "Motion regolarmente attivo")
# se il processo era gia' attivo, avviso che non ho fatto nulla
else:
messaggio = "Il Sistema TVcc era gia' attivo, non ho fatto nulla."
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
logga(1, "Motion non attivato, era gia' attivo")
# *******
# voglio spegnere il sistema di videosorveglianza
# *******
elif testo == 'tvcc off':
logga(0, "Spengo motion come richiesto")
# controllo se il processo 'motion' e' attivo o no
motion_on = verifica_motion()
# se il processo 'motion' e' attivo lo uccido e avviso che e' disattivato
if motion_on != 0:
os.system("pkill motion")
time.sleep(5)
motion_on = verifica_motion()
# se il processo 'motion' non e' attivo va bene, e' ammazzato davvero
if motion_on == 0:
messaggio = "Confermo che il sistema TVcc e' stato disattivato"
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
logga(0, "Motion disattivato")
else:
messaggio = "Non sono riuscito a spegnere il sistema TVcc!"
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
logga(3, "Motion ancora attivo, ci sono " + str(motion_on) + " processi attivi")
else:
messaggio = "Il sistema TVcc era gia' disattivato, non ho fatto niente."
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
logga (0, "Motion gia' spento, nessun intervento")
# *******
# voglio sapere lo stato del sisterma di videosorveglianza
# *******
elif testo == 'tvcc?':
# controllo se il processo 'motion' e' attivo o no
motion_on = verifica_motion()
logga(0, "Verifica dello stato di motion")
if motion_on == 0:
messaggio = "Il sistema TVcc e' DISATTIVATO"
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
logga(0, "motion spento")
else:
messaggio = "Il sistema TVcc e' ATTIVO"
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
logga(0, "motion acceso")
# *******
# voglio fare un foto dell'area (solo se il sistema non e' attivo)
# *******
elif testo == 'now':
# controllo se il processo 'motion' e' attivo o no
motion_on = verifica_motion()
# se il processo 'motion' e' attivo non posso fare la foto
if motion_on != 0:
messaggio = "Posso fare la foto solo con il sistema TVcc non attivo, se ti serve farla adesso devi prima disattivarlo"
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
logga(1, "Richiesta foto istantanea non ammissibile con motion attivo")
else:
os.system("raspistill -w 1600 -h 1200 -ex verylong -t 1 -o /home/pi/Pictures/SingoloClick.jpg")
path_image = '/home/pi/Pictures/SingoloClick.jpg'
foto = open(path_image, 'rb')
messaggio = "Ecco la foto che mi hai chiesto"
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
bot.sendPhoto(id_chat, foto)
os.system("rm /home/pi/Pictures/SingoloClick.jpg")
logga(0, "Scattata la foto istantanea")
# *******
# Voglio un report sulla temperatura in casa
# *******
elif testo == "temp?":
# inizializzo la variabile di connessione
connessione = None
try:
# connessione al DB
connessione = sqlite3.connect('/[cartella]/[database].db')
# generazione del cursore
cursore = connessione.cursor()
# leggo l'ultimo valore memorizzato
cursore.execute("SELECT ID, strftime('%H:%M', Timestamp, 'localtime'), Luogo, Temp, Umid FROM Temperature WHERE Luogo = 1 ORDER BY ID DESC LIMIT 1;")
# memorizzo tutti i valore recuperati
righe = cursore.fetchall()
# compongo il messaggio
for row in righe:
messaggio = "*Ultima lettura delle ore " + str(row[1]) + "*\nT: " + str(row[3]) + " - Umid.: " + str(row[4]) + "%\n\n"
# cerco il valore massimo delle ultime 24h
cursore.execute("SELECT MAX(Temp), strftime('%d/%m %H:%M', Timestamp, 'localtime') from temperature where timestamp >= datetime('now','-1 day');")
righe = cursore.fetchall()
for row in righe:
messaggio = messaggio + "*Nelle ultime 24 ore:*\n"
messaggio = messaggio + "Massima: " + str(row[0]) + " (" + str(row[1]) + ")\n"
# cerco il valore minimo delle ultime 24h
cursore.execute("SELECT MIN(Temp), strftime('%d/%m %H:%M', Timestamp, 'localtime') from temperature where timestamp >= datetime('now','-1 day');")
righe = cursore.fetchall()
for row in righe:
messaggio = messaggio + "Minima: " + str(row[0]) + " (" + str(row[1]) + ")\n"
bot.sendMessage(id_chat, messaggio, parse_mode='Markdown', reply_markup=hide_keyboard)
except sqlite3.Error, e:
return "Error %s:" % e.args[0]
finally:
if connessione:
connessione.commit()
connessione.close()
# *******
# Voglio un report sul consumo di corrente
# *******
elif testo == "watt?":
# inizializzo la variabile di connessione
connessione = None
try:
# connessione al DB
connessione = sqlite3.connect('/[cartella]/[database].db')
# generazione del cursore
cursore = connessione.cursor()
# leggo l'ultimo valore memorizzato
cursore.execute("SELECT ID, strftime('%H:%M', Timestamp, 'localtime'), Luogo, Consumo FROM Corrente WHERE Luogo = 1 ORDER BY ID DESC LIMIT 1;")
# memorizzo tutti i valore recuperati
righe = cursore.fetchall()
# compongo il messaggio
for row in righe:
messaggio = "*Ultima lettura delle ore " + str(row[1]) + "*\nPotenza: " + str(row[3]) + " W\n\n"
# cerco il valore massimo delle ultime 24h
cursore.execute("SELECT MAX(Consumo), strftime('%d/%m %H:%M', Timestamp, 'localtime') from Corrente where timestamp >= datetime('now','-1 day');")
righe = cursore.fetchall()
for row in righe:
messaggio = messaggio + "*Nelle ultime 24 ore:*\n"
messaggio = messaggio + "Picco massimo: " + str(row[0]) + " W (" + str(row[1]) + ")\n"
# cerco il valore minimo delle ultime 24h
cursore.execute("SELECT MIN(Consumo), strftime('%d/%m %H:%M', Timestamp, 'localtime') from Corrente where timestamp >= datetime('now','-1 day');")
righe = cursore.fetchall()
for row in righe:
messaggio = messaggio + "Consumo minimo: " + str(row[0]) + " W (" + str(row[1]) + ")\n"
# cerco il valore media delle ultime 24h
cursore.execute("SELECT AVG(Consumo), strftime('%d/%m %H:%M', Timestamp, 'localtime') from Corrente where timestamp >= datetime('now','-1 day');")
righe = cursore.fetchall()
for row in righe:
messaggio = messaggio + "Consumo medio: " + str(int(row[0])) + " W\n"
bot.sendMessage(id_chat, messaggio, parse_mode='Markdown', reply_markup=hide_keyboard)
except sqlite3.Error, e:
return "Error %s:" % e.args[0]
finally:
if connessione:
connessione.commit()
connessione.close()
else:
messaggio = "Ciao " + nome_utente + ", per interagire con me scrivi '/ciao' e segui le istruzioni"
bot.sendMessage(id_chat, messaggio, reply_markup=hide_keyboard)
loggo(1, "Messaggio non riconosciuto")
# qui inizia il codice principale (non c'e' il 'main' in python)
# che viene eseguto solo all'avvio del sistema
# leggo il file di configurazione per recuparare tutti i parametri di funzionamento del sistema
# la cosa migliore sarebbe avere questo file in /etc/tvcc.conf per rispettare le convenzioni in linux
# io lo tengo nella cartella dove lavoro per questione di comodita'
Config = ConfigParser.ConfigParser()
Config.read("/home/pi/domotica_tucci/tvcc.conf")
# registro l'avvio del sistema
logga(0, "Sistema avviato")
# all'avvio del sistema notifico la cosa al gruppo, ma prima devo verificare
# che la connessione ad Internet sia operativa
# cerco di ottenere l'IP pubblico
# aggiunta la gestione delle eccezioni per l'avvio se la rete manca (27-01-2016)
connesso = False
while connesso == False:
try:
req = requests.get("http://httpbin.org/ip")
connesso = True
logga(0, "Internet c'e")
except Exception, err:
logga(3, "Manca Internet " + str(traceback.format_exc()))
time.sleep(30)
# ci provo fino a che la richiesta non mi da' messaggio http200
while req.status_code != 200:
time.sleep(30)
req = requests.get("http://httpbin.org/ip")
logga(2, "http status code: " + str(req.status_code))
# se la richiesta e' http 200, vuol dire che e' buona, quindi procedo
if req.status_code == 200:
# change the HTTP response body into a JSON type
text = json.loads(req.text)
# retreive value by key using dict
ip = text['origin']
logga(0, "IP pubblico: " + ip)
# ottengo l'IP della lan e della wlan
indirizzo_eth0 = os.popen("/sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'").read()
indirizzo_eth0 = indirizzo_eth0.replace('\n', '')
#indirizzo_wlan0 = os.popen("/sbin/ifconfig wlan00 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'").read()
logga (0, "IP di LAN: " + indirizzo_eth0)
# creo il bot e mi collego a Telegram usando la mia chiave univoca
# che mi e' stata comunicata da The Botfather (si deve rispettare la famiglia)
# aggiunta la gestione delle eccezioni per capire che fare quando manca connessione o i server Telegram sono giu' (27/01/2016)
connessione_telegram = False
while connessione_telegram == False:
try:
bot = telepot.Bot(ConfigSectionMap("Sistema")['id_bot'])
utente = bot.getMe()
logga(0, "Connessione a Telegram avvenuta! ID utente del bot: " + str(utente['id']))
connessione_telegram = True
except Exception, err:
logga(3, "Connessione a Telegram fallita o caduta")
logga(3, traceback.format_exc())
time.sleep(30)
# mando i messaggi informativi alla chat
messaggio = "Ciao, sono stato appena riavviato, per sicurezza adesso avvio la videosorveglianza e ti mostro alcune informazioni\n"
messaggio = messaggio + "IP pubblico del tuo sistema: " + ip + "\n"
messaggio = messaggio + "IP di LAN: " + indirizzo_eth0
bot.sendMessage(ConfigSectionMap("Sistema")['id_chat'], messaggio)
# a questo punto avvio Motion solo se non e' indicato il parametro 'off' nella riga di comando
if len(sys.argv) >=2:
if sys.argv[1] == 'off':
messaggio = "Mi hai chiesto di non avviare la TVcc, quindi non l'ho fatto"
bot.sendMessage(ConfigSectionMap("Sistema")['id_chat'], messaggio)
logga(0, "motion non attivato per switch OFF a riga di comando")
else:
os.system("/home/pi/motion-mmal/motion")
logga(0, "Comando avvio motion al boot del sistema")
scrivi_semaforo()
# gli do il tempo di avviarsi
time.sleep(5)
# controllo se il processo 'motion' si e' attivato
motion_on = verifica_motion()
# se non si e' attivato lo segnalo
if motion_on == 0:
messaggio = "ATTENZIONE, il sistema TVcc non ha risposto al comando di attivazione!"
bot.sendMessage(ConfigSectionMap("Sistema")['id_chat'], messaggio)
logga(2, "Motion non e' partito")
# se invece si e' attivato lo notifico
else:
messaggio = "Confermo che il sistema TVcc e' adesso attivo"
bot.sendMessage(ConfigSectionMap("Sistema")['id_chat'], messaggio)
logga(0, "Motion e' partito al boot del sistema")
# controllo comandi in arrivo
try:
bot.notifyOnMessage(handle)
except Exception, err:
logga(3, "Connessione a Telegram fallita o caduta")
logga(3, traceback.format_exc())
# da qui in poi ci sono i comandi per tenere sotto controllo tutta la
# parte di domotica e sensoristica del sistema, con avviso in caso di problemi
while 1:
time.sleep(10)