93 Commits

Author SHA1 Message Date
4d2cdc81ef Merge pull request 'roberto-patch-1' (#2) from roberto-patch-1 into master
Reviewed-on: #2
2025-07-09 19:01:16 +02:00
a8800280f3 sensor_include.py 2025-07-09 18:57:32 +02:00
cb6220a059 Delete cron_import.py 2025-07-09 18:56:43 +02:00
cdeb049b06 rain_include.py 2025-07-09 18:53:12 +02:00
390dd41dac event_include.py 2025-07-09 18:50:48 +02:00
a11db80ac0 drv.include.py 2025-07-09 18:47:54 +02:00
caf698bfd5 cron_include.py 2025-07-09 18:43:25 +02:00
566b982e00 Add cron_import.py 2025-07-09 18:40:59 +02:00
412c58e054 Delete my_server.py 2025-07-08 18:33:06 +02:00
cde121ced7 Delete Soket_server_test.py 2025-07-08 18:32:58 +02:00
9d76bb3456 Update Python/Socket_server_x_test.py 2025-07-08 18:30:45 +02:00
ecef7fb368 Update Python/socket.include.py 2025-07-08 18:29:50 +02:00
d81834131b Upload files to "Python" 2025-07-08 18:28:08 +02:00
fa30f253be Update Soket_server_test.py 2025-07-08 18:27:28 +02:00
6326ec01fe Upload files to "/"
Signed-off-by: roberto <robalda@ik.me>
2025-07-08 18:25:55 +02:00
9cf3ee0785 Upload files to "/"
Signed-off-by: roberto <robalda@ik.me>
2025-07-08 18:22:23 +02:00
fbd578f502 Upload files to "/" 2025-07-08 18:21:10 +02:00
82a637d6d2 Python 2025-07-08 18:17:47 +02:00
lejubila
10045d5a11 Fix path of url weather icons 2022-07-16 16:26:38 +02:00
lejubila
3d0d36077d Update CHANGELOG 2021-10-03 10:04:30 +02:00
lejubila
a30e1cb6ef fix return value when get value of sensor 2021-08-28 12:17:22 +02:00
lejubila
81ce03e90a Add autoclose of the solenoid when the soil moisture has reached the set level 2021-08-28 11:38:05 +02:00
lejubila
2001b09937 Add support for sensor: moisture, temperature, fertility, illuminance. Add event ev_not_open_for_moisture, sensor_set_state_before, sensor_set_state_after. Added zone humidity management: when a zone has reached the humidity defined in EVx_SENSOR_MOISTURE it does not start irrigation or interrupts it if it is already active. In case of rain it does not stop irrigation if the soil humidity has not reached the configured value. 2021-08-27 22:52:01 +02:00
lejubila
c91128cbb4 Add command line: sensor_status, sensor_status_all, sensor_status_set. Add api command: sensor_status_set. 2021-08-22 15:19:34 +02:00
lejubila
7242b1c2ab Add command last_rain_sensor_timestamp, last_rain_online_timestamp, reset_last_rain_sensor_timestamp, reset_last_rain_online_timestamp. Add socket server api for reset_last_rain_sensor_timestamp, reset_last_rain_online_timestamp 2021-08-10 23:39:38 +02:00
lejubila
5152252653 Aggiornato driver rainsensorqty alla versione 0.2.5c 2021-04-24 09:55:57 +02:00
lejubila
50fc8a72b2 Change number of version 2020-09-06 08:28:04 +02:00
lejubila
9183bbcef5 Add support for send log to piGardenWeb 2020-09-06 08:24:26 +02:00
lejubila
bdab0fb6fe Fix changelog 2020-05-16 18:14:21 +02:00
lejubila
6ed0510c7f Published version 0.6.0: - Add support for enable all cron fron api
- Update rainsensorqty driver to version 0.2.5b
2020-05-16 18:11:51 +02:00
lejubila
15e36e95a3 Update rainsensorqty to version 0.2.5 2020-04-08 22:17:14 +02:00
lejubila
3a5c3ef934 Add support for enable all cron fron api 2020-04-07 23:20:41 +02:00
lejubila
f3c11a7fee Corretto repositori git da cui scaricare wiringPi 2020-04-01 12:45:58 +02:00
lejubila
ac95194b3e Rimosso file con riferimento alla vecchia versione di rainsensorqty 2019-09-24 18:47:39 +02:00
lejubila
a125ff31d8 Aggiornato changelog alla version 0.5.14 2019-09-24 17:12:25 +02:00
lejubila
bb435f0ee6 Merge branch 'rainsensorqty' into develop
Eseguito merge del brench rainsensorqty nel brench develop
2019-09-24 17:06:25 +02:00
lejubila
b8daa7acab Cambiato numero di versione 2019-09-24 17:05:50 +02:00
lejubila
dd289f8b31 Adattato alla versione 0.3.3 i nomi zone presente nelle schedulazione mostrate nello stato in formato json 2019-09-24 16:58:23 +02:00
androtto
2add0c725f versione 0.2.3 2019-09-21 23:00:50 +02:00
androtto
0b34b7010f version 0.2.1 2019-09-02 14:42:29 +02:00
lejubila
c527702c72 Aggiunto elenco elettrovalvole con relativi alias nello status json 2019-09-01 22:22:42 +02:00
lejubila
b6de96debe Aggiunto api per gestire le schedulazioni di piGardenSched 2019-08-29 19:00:42 +02:00
lejubila
ea681816fe Aggiunto nello status json la lettura delle schedulazioni gestite con piGardenSched 2019-08-25 17:25:12 +02:00
lejubila
8ca698d4ee rimosso alcune linee commentate 2019-08-24 10:04:46 +02:00
lejubila
70943c5f5e Rimosso alcune linee commentate 2019-08-24 09:59:18 +02:00
lejubila
aa9799cdaf Rimosso alcune linee commentate 2019-08-24 09:52:46 +02:00
lejubila
6d4f522f21 Aggiunto changelog per nuova versione, e file di configurazione di esempio per il nuovo driver rainsensorqty 2019-08-12 08:40:55 +02:00
lejubila
51e4391237 Merge branch 'rainsensorqty' into develop 2019-08-12 08:26:28 +02:00
lejubila
e2eda96f2c Aggiornato driver rainsensorqty alla versione 0.2.0 2019-08-12 08:24:57 +02:00
lejubila
aa12f5a8a5 Aggiunto compressione file di log dei driver 2019-06-23 12:44:33 +02:00
lejubila
40ab2e3b25 Aggiunto implementazione driver rainsensorqty railizzato da @androtto 2018-12-09 12:49:09 +01:00
lejubila
40eb0b2429 Iniziato integrazione codice di @androtto nel driver rainsensorqty 2018-11-17 09:20:24 +01:00
lejubila
9d9ef42679 Aggiornato numero versione 2018-11-13 12:47:26 +01:00
lejubila
70c4e063f8 corretto bug su driver openweathermap che in alcuni casi causa una malformazione dello status json e impediva la comunicazione con piGardenWeb 2018-11-13 12:45:28 +01:00
lejubila
4d4b35c3cc corretto bug su driver openweathermap che in alcuni casi causa una malformazione dello status json e impediva la comunicazione con piGardenWeb 2018-11-13 12:43:26 +01:00
lejubila
71e91cc861 Corretto bug che impediva la pubblicazione del topic mqtt per ogni evento 2018-11-12 23:15:34 +01:00
David Bigagli
e26292fe36 Delete mqtt_status.sh 2018-11-11 23:39:22 +01:00
lejubila
d99566ef93 aggiunto scheletro driver rainsensorqty 2018-11-11 23:31:13 +01:00
lejubila
a76c797b22 Corretto nome variabile errata EV1_MONOSTAVLE nel file di configurazione 2018-11-11 15:34:28 +01:00
lejubila
07a594e799 aggiunto possibilità di disabilitare servizio meteo online 2018-11-11 11:57:59 +01:00
lejubila
dfa68c2b83 Implementato possibilità di disabilitare servizio meteo online impostando il valore none in WEATHER_SERVICE 2018-11-11 11:49:35 +01:00
lejubila
e284bbcb25 Fix bug in single monostable solenodid management caused from wrong variable name EV_IS_MONOSTAVLE 2018-11-11 11:14:28 +01:00
lejubila
d324faf8bb Fix bug in single monostable solenodid management caused from wrong variable name EV_IS_MONOSTAVLE 2018-11-11 10:13:21 +01:00
lejubila
383cbc44a9 Fix bug in single monostable solenodid management caused from wrong variable name EV_IS_MONOSTAVLE 2018-11-11 10:09:02 +01:00
lejubila
9864709b44 Cambiato changelog 2018-11-01 12:03:07 +01:00
lejubila
6d8c82f0a3 Modificato gestione di invio messaggi tramite socket server 2018-10-14 22:47:43 +02:00
lejubila
a997081aa9 Rimosso mqttconnector 2018-09-22 08:23:13 +02:00
lejubila
c145171306 Aggiunto nel json_status il tipo di evento attualmente triggerato 2018-09-22 08:20:54 +02:00
lejubila
e9a676188f Aggiunto supporto mqtt per la pubblicazione dello stato in formato json verso il broker 2018-09-20 10:53:40 +02:00
lejubila
0d748031fd variato file di configurazione di mqttconnector 2018-08-15 12:09:47 +02:00
lejubila
3ed46d80ad Merge branch 'master' of https://github.com/lejubila/piGarden 2018-07-18 23:10:29 +02:00
lejubila
05a73720bb Inserito data nel CHANGELOG prima della pubbilicazione della release, aggiunto prima implementazione sperimentale di mqttconnector 2018-07-18 23:08:16 +02:00
lejubila
2ea6588c32 Implementato controllo meteo su servizio online tramite driver, implementato driver per interfacciarsi con le api di openweathermap e wunderground 2018-07-15 23:18:59 +02:00
David Bigagli
fc717f18a2 Update README.md 2018-06-05 18:00:25 +02:00
David Bigagli
50117a5a96 Update README.md 2018-06-05 17:57:14 +02:00
David Bigagli
309c209b1b Update README.md 2018-06-05 17:56:35 +02:00
David Bigagli
9a81f0fbba Update README.md 2018-06-05 17:53:36 +02:00
lejubila
419695b420 Implementato driver subsistem per il controllo delle condizioni meteo tramite servizio online. Implementato driver 'wunderground' per il controllo meteo tramite il servizio wunderground 2018-06-05 16:31:45 +02:00
lejubila
3239006922 Modificato file README dei driver, variato numero di versione di piGarden 2018-06-01 22:16:26 +02:00
lejubila
24baa97dad Corretto nome di funzione nel driver sonoff_tasmota_http 2018-05-24 22:06:12 +02:00
lejubila
c354f72c0c Eseguito correzioni su driver sonoff_tasmota_http 2018-05-23 17:57:24 +02:00
lejubila
6f80b8ea21 Corretto problema parsing json su driver sonoff_tasmota_http 2018-05-22 12:53:57 +02:00
lejubila
87edbbbe7a Eseguito correzioni su driver sonoff_tasmota_http 2018-05-21 22:21:27 +02:00
lejubila
3eb45fd1e9 Aggiunto prima implementazione del driver sonoff_tesmota_http, aggiunto implementazione parametro EVx_MONOSTABLE per potere definire singolarmente le elettrovamvole monostabili, corretto alcuni commenti 2018-05-20 17:14:58 +02:00
lejubila
7c96a6afd1 Modificato output di notifiche eventi dello script rpinotify.sh, aggiornato CHANGELOG per nuova versione 2018-05-04 21:17:32 +02:00
lejubila
c664bea16a corretto timestamp su eventi ev_not_open_for_rain, ev_not_open_for_rain_online, ev_not_open_for_rain_sensor 2018-05-03 12:02:48 +02:00
lejubila
78fa176848 Implementato eventi events/ev_not_open_for_rain, events/ev_not_open_for_rain_sensor, events/ev_not_open_for_rain_online. Aggiornato script di invio mail notifiche 2018-05-03 11:45:47 +02:00
lejubila
5c00853e39 Aggiunto driver 'remote' per gestire da un'installazione master di piGarden più installazioni remote 2018-03-25 12:32:39 +02:00
lejubila
dd3cab12c2 Aggiornaeto numero di release 2017-12-13 15:47:06 +01:00
lejubila
38ecd92d7a Corretto problema di inizializzazione variabile LOG_OUTPUT_DRV_FILE 2017-12-13 15:43:06 +01:00
lejubila
22ec16cc9d Aggunto eventi cron_add_before, cron_add_after, cron_del_before, cron_del_after, ev_open_in_before, ev_open_in_after, exec_poweroff_before, exec_poweroff_after, exec_reboot_before, exec_reboot_after ed eseguito bugfix vari 2017-11-19 23:24:48 +01:00
lejubila
8d4849db75 Merge branch 'master' into develop 2017-11-01 18:10:41 +01:00
lejubila
80bcbbc92a corretto parametri in sendmail.sh 2017-11-01 18:03:52 +01:00
83 changed files with 7989 additions and 216 deletions

View File

@@ -1,3 +1,74 @@
# 0.6.4 - 03/10/2021
- Fix path of url weather icons
# 0.6.4 - 03/10/2021
- Add support for sensor: moisture, temperature, fertility, illuminance
- Add command line: sensor_status, sensor_status_all, sensor_status_set
- Add api command: sensor_status_set
- Add event ev_not_open_for_moisture, sensor_set_state_before, sensor_set_state_after
- Added zone humidity management: when a zone has reached the humidity defined in EVx_SENSOR_MOISTURE it does not start irrigation or interrupts it if it is already active. In case of rain it does not stop irrigation if the soil humidity has not reached the configured value
# 0.6.3 - 10/08/2021
- Add command last_rain_sensor_timestamp, last_rain_online_timestamp, reset_last_rain_sensor_timestamp, reset_last_rain_online_timestamp
- Add socket server api for reset_last_rain_sensor_timestamp, reset_last_rain_online_timestamp
# 0.6.2 - 24/04/2021
- Update rainsensorqty driver to version 0.2.5c
# 0.6.1 - 06/09/2020
- Add support for send log to piGardenWeb
# 0.6.0 - 16/05/2020
- Add support for enable all cron fron api
- Update rainsensorqty driver to version 0.2.5b
# 0.5.14 - 24/09/2019
- Updated rainsensorqty driver to version 0.2.3
- Added api and command for manage piGardenSched scheduling
# 0.5.13 - 12/08/2019
- Added driver rainsensorqty for menage rainfall detection based on quantity
# 0.5.12.1 - 23/06/2019
- Added zip log drver file when exceeding the size limit
# 0.5.12 - 13/11/2018
- Fixed a bug that prevented the publication of the mqtt topic for each event
- Fixed a bug on openweathermap driver which in some cases causes a malformation of the json status and prevented communication with piGardenWeb
# 0.5.11 - 11/11/2018
- Added ability to disable online weather service by defining WEATHER_SERVICE="none" in the configuration file
# 0.5.10 - 11/11/2018
- Fix bug in single monostable solenodid management caused from wrong variable name EV_IS_MONOSTAVLE
# 0.5.9 - 01/11/2018
- Added mqtt support for publishing status to broker
# 0.5.8 - 19/07/2018
- Added "openweathermap" driver for impement check weather condition from openweatermap api
# 0.5.7 - 01/06/2018
- Added "sonoff_tasmota_http" driver for interfacin with Sonoff module with Tasmota firmware over http protocol
# 0.5.6 - 04/05/2018
- Added events ev_not_open_for_rain, ev_not_open_for_rain_sensor, ev_not_open_for_rain_online
- Added script rpinotify.sh for notificate events to telegram
# 0.5.5 - 25/03/2018
- Added "remote" driver to control remote pigarden
# 0.5.4 - 13/11/2017
- Fix bad initialization LOG_OUTPUT_DRV_FILE variable if not defined in config file
# 0.5.3 - 19/11/2017
- Fix send parameter on event init_before and init_after
- Added WEATHER argument in check_rain_sensor_after and check_rain_sensor_change event
- Added events cron_add_before, cron_add_after, cron_del_before, cron_del_after, ev_open_in_before, ev_open_in_after, exec_poweroff_before, exec_poweroff_after, exec_reboot_before, exec_reboot_after
- Added to sendmail.sh argument passed form check_rain_sensor_after and check_rain_sensor_change event
- Added to sendmail.sh new events
- Fix wrong state on event script sendmail.sh
# 0.5.2 - 01/11/2017
- Fix problem inconsistent return value in drv_rain_sensor_get
- Fix get parameter in event script sendmail.sh

View File

@@ -0,0 +1,15 @@
import socket
HOST = 'localhost'
PORT = 12345
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
# Se l'autenticazione è abilitata
s.sendall(b'admin\n')
s.sendall(b'password123\n')
s.sendall(b'status\n') # Invia il comando
data = s.recv(1024)
print(f"Received: {data.decode()}")

434
Python/cron_include.py Normal file
View File

@@ -0,0 +1,434 @@
import os
import re
import logging
from crontab import CronTab, CronItem # Importa le classi necessarie da python-crontab
# --- Mock/Placeholder per le dipendenze esterne ---
# In un'applicazione reale, queste funzioni verrebbero fornite dal tuo sistema piGarden principale.
# Configura un logger di base per le funzioni di log
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def log_write(log_type, level, message):
"""
Simula la funzione log_write dal tuo script Bash.
In un'applicazione reale, useresti il modulo logging di Python.
"""
if level == "info":
logging.info(f"[{log_type}] {message}")
elif level == "warning":
logging.warning(f"[{log_type}] {message}")
elif level == "error":
logging.error(f"[{log_type}] {message}")
else:
logging.debug(f"[{log_type}] {message}")
def trigger_event(event_name, *args):
"""
Simula la funzione trigger_event dal tuo script Bash.
"""
log_write("event", "info", f"Triggered event: {event_name} with args: {args}")
# Qui potresti aggiungere la logica per chiamare handler di eventi reali
def alias_exists(alias_name):
"""
Simula la funzione alias_exists.
Dovrebbe essere integrata con la tua configurazione delle elettrovalvole.
Per questo esempio, restituisce True solo per alias "1" a "6".
"""
try:
num = int(alias_name)
return 1 <= num <= EV_TOTAL # Assumiamo EV_TOTAL sia definito globalmente o passato
except ValueError:
return False
# EV_TOTAL deve essere definito o passato, simuliamo un valore qui
EV_TOTAL = 6
# Percorso dello script principale (es. piGarden.py)
# Questo dovrebbe essere il percorso assoluto del tuo script piGarden principale
# In un'applicazione reale, lo passeresti dalla tua classe PiGarden
PI_GARDEN_SCRIPT_PATH = "/home/pi/piGarden/piGarden.py"
# --- Classe CronManager ---
class CronManager:
def __init__(self, script_path, ev_total_val, log_writer, event_trigger, alias_checker):
self.script_path = script_path
self.ev_total = ev_total_val
self.log_write = log_writer
self.trigger_event = event_trigger
self.alias_exists = alias_checker
self.cron_user = True # Gestisce il crontab dell'utente corrente
def _get_crontab(self):
"""Ottiene l'oggetto CronTab per l'utente corrente."""
try:
return CronTab(user=self.cron_user)
except Exception as e:
self.log_write("cron", "error", f"Impossibile accedere al crontab: {e}")
raise
def cron_del(self, cron_type, cron_arg=""):
"""
Elimina una tipologia di schedulazione dal crontab dell'utente.
:param cron_type: Tipologia del cron (es. "init", "open", "close").
:param cron_arg: Argomento della tipologia (es. alias dell'elettrovalvola).
"""
if not cron_type:
self.log_write("cron", "error", "Tipo cron vuoto")
print("Tipo cron vuoto", file=os.sys.stderr)
return False
crontab = self._get_crontab()
jobs_to_remove = []
# Il tuo script Bash usa commenti START/END.
# Possiamo cercare lavori che contengono questi commenti.
# Alternativamente, si potrebbe assegnare un commento specifico ad ogni job creato.
start_comment_pattern = re.compile(rf"^# START cron {re.escape(cron_type)} {re.escape(cron_arg)}$")
end_comment_pattern = re.compile(rf"^# END cron {re.escape(cron_type)} {re.escape(cron_arg)}$")
found_block = False
in_block = False
for job in list(crontab.jobs): # Iterate over a copy because we might modify
if start_comment_pattern.match(job.comment or ""):
in_block = True
found_block = True
jobs_to_remove.append(job) # Include the START comment job itself
elif end_comment_pattern.match(job.comment or ""):
if in_block:
jobs_to_remove.append(job) # Include the END comment job itself
in_block = False
elif in_block:
jobs_to_remove.append(job)
if not found_block:
print(f"{cron_type} {cron_arg} cron non presente", file=os.sys.stderr)
return True # Considerato un successo se non c'è nulla da eliminare
self.trigger_event("cron_del_before", cron_type, cron_arg)
for job in jobs_to_remove:
crontab.remove(job)
try:
crontab.write()
self.log_write("cron", "info", f"Cron '{cron_type} {cron_arg}' eliminato con successo.")
self.trigger_event("cron_del_after", cron_type, cron_arg)
return True
except Exception as e:
self.log_write("cron", "error", f"Errore durante la scrittura del crontab: {e}")
print(f"Errore durante la scrittura del crontab: {e}", file=os.sys.stderr)
return False
def _get_cron_command(self, cron_type, cron_arg, cron_arg2):
"""Determina il comando Bash da eseguire per il cron job."""
base_command = f"{self.script_path}"
if cron_type == "init":
return f"{base_command} init"
elif cron_type == "start_socket_server":
return f"{base_command} start_socket_server force"
elif cron_type == "check_rain_online":
return f"{base_command} check_rain_online 2> /tmp/check_rain_online.err"
elif cron_type == "check_rain_sensor":
return f"{base_command} check_rain_sensor 2> /tmp/check_rain_sensor.err"
elif cron_type == "close_all_for_rain":
return f"{base_command} close_all_for_rain 2> /tmp/close_all_for_rain.err 1> /dev/null"
elif cron_type == "open":
return f"{base_command} open {cron_arg}"
elif cron_type == "open_in":
return f"{base_command} open {cron_arg} {cron_arg2}"
elif cron_type == "open_in_stop":
return f"{base_command} close {cron_arg}"
elif cron_type == "close":
return f"{base_command} close {cron_arg}"
else:
self.log_write("cron", "error", f"Tipo cron errato: {cron_type}")
print(f"Tipo cron errato: {cron_type}", file=os.sys.stderr)
raise ValueError(f"Tipo cron errato: {cron_type}")
def cron_add(self, cron_type, minute="*", hour="*", dom="*", month="*", dow="*", cron_arg="", cron_arg2=""):
"""
Aggiunge una schedulazione nel crontab dell'utente.
:param cron_type: Tipologia del cron.
:param minute: Minuto (0-59, *, */n, @reboot).
:param hour: Ora (0-23, *, */n).
:param dom: Giorno del mese (1-31, *, */n).
:param month: Mese (1-12, *, */n).
:param dow: Giorno della settimana (0-6, *, */n).
:param cron_arg: Primo argomento specifico della tipologia.
:param cron_arg2: Secondo argomento specifico della tipologia (es. "disabled").
"""
if not cron_type:
self.log_write("cron", "error", "Tipo cron vuoto")
print("Tipo cron vuoto", file=os.sys.stderr)
return False
# Elimina prima qualsiasi blocco esistente per garantire l'idempotenza
self.cron_del(cron_type, cron_arg)
crontab = self._get_crontab()
# Determina il comando e se deve essere disabilitato
cron_command = self._get_cron_command(cron_type, cron_arg, cron_arg2)
cron_disabled = (cron_arg2 == "disabled")
# Crea i commenti START e END per il blocco
start_comment = f"# START cron {cron_type} {cron_arg}"
end_comment = f"# END cron {cron_type} {cron_arg}"
# Aggiungi il commento START
job_start = crontab.new(command=f"echo '{start_comment}'", comment=start_comment)
job_start.minute.every(1) # Un cron job fittizio per il commento START
job_start.enabled = False # Disabilita il job commento
# Aggiungi il job principale
job = crontab.new(command=cron_command)
if minute == "@reboot":
job.set_every("reboot")
else:
job.minute.on(minute)
job.hour.on(hour)
job.dom.on(dom)
job.month.on(month)
job.dow.on(dow)
job.enabled = not cron_disabled
job.comment = f"piGarden {cron_type} {cron_arg}" # Un commento più descrittivo per il job reale
# Aggiungi il commento END
job_end = crontab.new(command=f"echo '{end_comment}'", comment=end_comment)
job_end.minute.every(1) # Un cron job fittizio per il commento END
job_end.enabled = False # Disabilita il job commento
try:
crontab.write()
self.log_write("cron", "info", f"Cron '{cron_type} {cron_arg}' aggiunto con successo: {job.render()}")
self.trigger_event("cron_add_after", cron_type, cron_arg, job.render())
return True
except Exception as e:
self.log_write("cron", "error", f"Errore durante la scrittura del crontab: {e}")
print(f"Errore durante la scrittura del crontab: {e}", file=os.sys.stderr)
return False
def cron_get(self, cron_type, cron_arg=""):
"""
Legge una tipologia di schedulazione dal crontab dell'utente.
:param cron_type: Tipologia del cron.
:param cron_arg: Argomento della tipologia.
:return: Stringa contenente le schedulazioni trovate, separate da newline.
"""
if not cron_type:
self.log_write("cron", "error", "Tipo cron vuoto")
print("Tipo cron vuoto", file=os.sys.stderr)
return ""
crontab = self._get_crontab()
found_jobs = []
# Cerca i job principali che corrispondono al tipo e all'argomento
for job in crontab.jobs:
if job.comment and job.comment.startswith(f"piGarden {cron_type} {cron_arg}"):
found_jobs.append(job.render())
return "\n".join(found_jobs)
# --- Funzioni wrapper per tipi di cron specifici ---
def set_cron_init(self):
self.cron_del("init") # Assicurati che non ci siano duplicati
self.cron_add("init", minute="@reboot")
def del_cron_init(self):
self.cron_del("init")
def set_cron_start_socket_server(self):
self.cron_del("start_socket_server")
self.cron_add("start_socket_server", minute="@reboot")
def del_cron_start_socket_server(self):
self.cron_del("start_socket_server")
def set_cron_check_rain_sensor(self):
self.cron_del("check_rain_sensor")
self.cron_add("check_rain_sensor", minute="*") # Ogni minuto
def del_cron_check_rain_sensor(self):
self.cron_del("check_rain_sensor")
def set_cron_check_rain_online(self):
self.cron_del("check_rain_online")
self.cron_add("check_rain_online", minute="*/3") # Ogni 3 minuti
def del_cron_check_rain_online(self):
self.cron_del("check_rain_online")
def set_cron_close_all_for_rain(self):
self.cron_del("close_all_for_rain")
self.cron_add("close_all_for_rain", minute="*/5") # Ogni 5 minuti
def del_cron_close_all_for_rain(self):
self.cron_del("close_all_for_rain")
def add_cron_open(self, alias, minute, hour, dom, month, dow, disabled=""):
if not self.alias_exists(alias):
self.log_write("cron", "error", f"Alias '{alias}' non trovato")
print(f"Alias '{alias}' non trovato", file=os.sys.stderr)
return False
self.cron_add("open", minute, hour, dom, month, dow, alias, disabled)
return True
def del_cron_open(self, alias):
if not self.alias_exists(alias):
self.log_write("cron", "error", f"Alias '{alias}' non trovato")
print(f"Alias '{alias}' non trovato", file=os.sys.stderr)
return False
self.cron_del("open", alias)
return True
def get_cron_open(self, alias):
if not self.alias_exists(alias):
self.log_write("cron", "error", f"Alias '{alias}' non trovato")
print(f"Alias '{alias}' non trovato", file=os.sys.stderr)
return ""
return self.cron_get("open", alias)
def del_cron_open_in(self, alias):
if not self.alias_exists(alias):
self.log_write("cron", "error", f"Alias '{alias}' non trovato")
print(f"Alias '{alias}' non trovato", file=os.sys.stderr)
return False
self.cron_del("open_in", alias)
self.cron_del("open_in_stop", alias)
return True
def get_cron_close(self, alias):
if not self.alias_exists(alias):
self.log_write("cron", "error", f"Alias '{alias}' non trovato")
print(f"Alias '{alias}' non trovato", file=os.sys.stderr)
return ""
return self.cron_get("close", alias)
def add_cron_close(self, alias, minute, hour, dom, month, dow, disabled=""):
if not self.alias_exists(alias):
self.log_write("cron", "error", f"Alias '{alias}' non trovato")
print(f"Alias '{alias}' non trovato", file=os.sys.stderr)
return False
self.cron_add("close", minute, hour, dom, month, dow, alias, disabled)
return True
def del_cron_close(self, alias):
if not self.alias_exists(alias):
self.log_write("cron", "error", f"Alias '{alias}' non trovato")
print(f"Alias '{alias}' non trovato", file=os.sys.stderr)
return False
self.cron_del("close", alias)
return True
def cron_disable_all_open_close(self):
crontab = self._get_crontab()
for i in range(1, self.ev_total + 1):
alias = str(i) # Assumendo che gli alias siano i numeri delle EV
# Disabilita le schedulazioni di apertura
for job in list(crontab.jobs):
if job.comment and job.comment.startswith(f"piGarden open {alias}") and job.enabled:
job.enabled = False
self.log_write("cron", "info", f"Disabilitato cron 'open' per alias {alias}: {job.render()}")
# Disabilita le schedulazioni di chiusura
for job in list(crontab.jobs):
if job.comment and job.comment.startswith(f"piGarden close {alias}") and job.enabled:
job.enabled = False
self.log_write("cron", "info", f"Disabilitato cron 'close' per alias {alias}: {job.render()}")
try:
crontab.write()
self.log_write("cron", "info", "Tutte le schedulazioni di apertura/chiusura disabilitate.")
return True
except Exception as e:
self.log_write("cron", "error", f"Errore durante la disabilitazione dei cron: {e}")
return False
def cron_enable_all_open_close(self):
crontab = self._get_crontab()
for i in range(1, self.ev_total + 1):
alias = str(i) # Assumendo che gli alias siano i numeri delle EV
# Abilita le schedulazioni di apertura
for job in list(crontab.jobs):
if job.comment and job.comment.startswith(f"piGarden open {alias}") and not job.enabled:
job.enabled = True
self.log_write("cron", "info", f"Abilitato cron 'open' per alias {alias}: {job.render()}")
# Abilita le schedulazioni di chiusura
for job in list(crontab.jobs):
if job.comment and job.comment.startswith(f"piGarden close {alias}") and not job.enabled:
job.enabled = True
self.log_write("cron", "info", f"Abilitato cron 'close' per alias {alias}: {job.render()}")
try:
crontab.write()
self.log_write("cron", "info", "Tutte le schedulazioni di apertura/chiusura abilitate.")
return True
except Exception as e:
self.log_write("cron", "error", f"Errore durante l'abilitazione dei cron: {e}")
return False
# --- Esempio di utilizzo (per testare la classe CronManager) ---
if __name__ == "__main__":
# Inizializza il gestore cron con le dipendenze mock
cron_manager = CronManager(
script_path=PI_GARDEN_SCRIPT_PATH,
ev_total_val=EV_TOTAL,
log_writer=log_write,
event_trigger=trigger_event,
alias_checker=alias_exists
)
print("--- Test Cron Manager ---")
# Esempio: Aggiungi un cron per l'inizializzazione
print("\nAggiungo cron 'init'...")
cron_manager.set_cron_init()
# Esempio: Aggiungi un cron per aprire l'elettrovalvola "1" ogni giorno alle 7:00
print("\nAggiungo cron 'open' per EV 1 alle 07:00...")
cron_manager.add_cron_open("1", "0", "7", "*", "*", "*")
# Esempio: Aggiungi un cron per chiudere l'elettrovalvola "2" ogni 5 minuti (disabilitato)
print("\nAggiungo cron 'close' per EV 2 ogni 5 minuti (disabilitato)...")
cron_manager.add_cron_close("2", "*/5", "*", "*", "*", "*", "disabled")
# Esempio: Ottieni i cron per l'elettrovalvola "1"
print("\nCron 'open' per EV 1:")
print(cron_manager.get_cron_open("1"))
# Esempio: Ottieni i cron per l'elettrovalvola "2"
print("\nCron 'close' per EV 2:")
print(cron_manager.get_cron_close("2"))
# Esempio: Disabilita tutti i cron di apertura/chiusura
print("\nDisabilito tutti i cron di apertura/chiusura...")
cron_manager.cron_disable_all_open_close()
# Verifica lo stato dopo la disabilitazione
print("\nCron 'open' per EV 1 dopo disabilitazione:")
print(cron_manager.get_cron_open("1")) # Dovrebbe mostrare il job ma disabilitato
# Esempio: Abilita tutti i cron di apertura/chiusura
print("\nAbilito tutti i cron di apertura/chiusura...")
cron_manager.cron_enable_all_open_close()
# Verifica lo stato dopo l'abilitazione
print("\nCron 'open' per EV 1 dopo abilitazione:")
print(cron_manager.get_cron_open("1")) # Dovrebbe mostrare il job abilitato
# Esempio: Elimina un cron specifico
print("\nElimino cron 'init'...")
cron_manager.del_cron_init()
print("\nElimino cron 'open' per EV 1...")
cron_manager.del_cron_open("1")
print("\nElimino cron 'close' per EV 2...")
cron_manager.del_cron_close("2")
print("\n--- Test Completato ---")
print("Controlla il tuo crontab con 'crontab -l' per vedere le modifiche.")

487
Python/drv.include.py Normal file
View File

@@ -0,0 +1,487 @@
import os
import re
import logging
import subprocess # Per eseguire comandi esterni come 'gpio' se necessario
# --- Mock/Placeholder per le dipendenze esterne e configurazione ---
# In un'applicazione reale, queste verrebbero fornite dal tuo sistema piGarden principale.
# Configura un logger di base per le funzioni di log
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def log_write(log_type, level, message):
"""Simula la funzione log_write dal tuo script Bash."""
if level == "info":
logging.info(f"[{log_type}] {message}")
elif level == "warning":
logging.warning(f"[{log_type}] {message}")
elif level == "error":
logging.error(f"[{log_type}] {message}")
else:
logging.debug(f"[{log_type}] {message}")
def message_write(msg_type, message):
"""Simula la funzione message_write dal tuo script Bash."""
if msg_type == 'info':
logging.info(f"[message] INFO: {message}")
elif msg_type == 'warning':
logging.warning(f"[message] WARNING: {message}")
elif msg_type == 'success':
logging.info(f"[message] SUCCESS: {message}")
# Variabili di configurazione simulate (dovrebbero venire dal piGarden.conf)
# Queste saranno passate alla classe DriverManager
mock_config = {
"EV_TOTAL": 6,
"SUPPLY_GPIO_1": 2,
"SUPPLY_GPIO_2": 3,
"RAIN_GPIO": 25,
"WEATHER_SERVICE": "drv:openweathermap", # Esempio di driver di servizio meteo
"RELE_GPIO_CLOSE": 0,
"RELE_GPIO_OPEN": 1,
"SUPPLY_GPIO_POS": 0,
"SUPPLY_GPIO_NEG": 1,
"GPIO": "/usr/local/bin/gpio", # Percorso al comando gpio (wiringPi)
"CUT": "/usr/bin/cut", # Percorso al comando cut
"LOG_OUTPUT_DRV_FILE": "/tmp/piGarden.drv.log", # Percorso per il log dei driver
# Elettrovalvole di esempio per setup_drv
"EV1_GPIO": "17",
"EV2_GPIO": "drv:custom_rele", # Esempio di un GPIO gestito da un driver custom
"EV3_GPIO": "22",
"EV4_GPIO": "18",
"EV5_GPIO": "23",
"EV6_GPIO": "24",
}
# --- Classe DriverManager ---
class DriverManager:
def __init__(self, config, log_writer, message_writer):
self.config = config
self.log_write = log_writer
self.message_write = message_writer
self.list_drv = [] # Lista dei driver attivi rilevati
# Percorsi degli strumenti esterni (dal config)
self.gpio_cmd = self.config.get("GPIO")
self.cut_cmd = self.config.get("CUT")
self.log_output_drv_file = self.config.get("LOG_OUTPUT_DRV_FILE")
# Inizializza il file di log dei driver se non esiste
if not os.path.exists(self.log_output_drv_file):
open(self.log_output_drv_file, 'a').close()
# Configura un logger specifico per l'output dei driver, come nello script Bash
self.drv_logger = logging.getLogger('driver_output')
self.drv_logger.setLevel(logging.INFO)
# Rimuovi handler esistenti per evitare duplicati se chiamato più volte
if not self.drv_logger.handlers:
drv_handler = logging.FileHandler(self.log_output_drv_file, mode='a')
drv_formatter = logging.Formatter('%(asctime)s %(message)s')
drv_handler.setFormatter(drv_formatter)
self.drv_logger.addHandler(drv_handler)
self.drv_logger.propagate = False # Evita che i log vadano al logger root
# Placeholder per le funzioni GPIO dirette (sostituire con RPi.GPIO o gpiozero)
# Esempio con subprocess per il comando 'gpio' (meno Pythonico ma più fedele al Bash)
self._gpio_write = lambda gpio_id, value: self._run_gpio_command("write", gpio_id, value)
self._gpio_mode = lambda gpio_id, mode: self._run_gpio_command("mode", gpio_id, mode)
self._gpio_read = lambda gpio_id: self._run_gpio_command("read", gpio_id)
# Inizializza i driver al momento della creazione dell'istanza
self.setup_drv()
def _run_gpio_command(self, action, gpio_id, value=None):
"""Esegue un comando 'gpio' tramite subprocess."""
cmd = [self.gpio_cmd, "-g", action, str(gpio_id)]
if value is not None:
cmd.append(str(value))
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
self.drv_logger.info(f"GPIO command '{' '.join(cmd)}' output: {result.stdout.strip()}")
return result.stdout.strip()
except subprocess.CalledProcessError as e:
self.log_write("drv", "error", f"Errore GPIO command '{' '.join(cmd)}': {e.stderr.strip()}")
self.message_write("warning", f"Errore GPIO: {e.stderr.strip()}")
return None # O solleva un'eccezione
def setup_drv(self):
"""
Funzione eseguita ad ogni avvio, include i driver e lancia le funzioni di setup.
"""
self.list_drv = [] # Azzera la lista dei driver
# Raccoglie i nomi dei driver utilizzati per le elettrovalvole
ev_total = self.config.get("EV_TOTAL", 0)
for i in range(1, ev_total + 1):
gpio_val = self.config.get(f"EV{i}_GPIO", "")
if gpio_val.startswith("drv:"):
drv = gpio_val.split(":")[1]
if drv not in self.list_drv:
self.list_drv.append(drv)
# Raccoglie i nomi dei driver utilizzati per gli altri gpio e servizi
for key in ["SUPPLY_GPIO_1", "SUPPLY_GPIO_2", "RAIN_GPIO", "WEATHER_SERVICE"]:
gpio_val = self.config.get(key, "")
if isinstance(gpio_val, str) and gpio_val.startswith("drv:"):
drv = gpio_val.split(":")[1]
if drv not in self.list_drv:
self.list_drv.append(drv)
# Simula l'inclusione dei file dei driver e l'esecuzione della funzione di setup
for drv in self.list_drv:
# In un'applicazione reale, qui potresti caricare moduli Python specifici
# per ogni driver o chiamare metodi dedicati.
# Per ora, simuliamo la chiamata a drv_<drv>_setup
setup_func_name = f"drv_{drv}_setup"
if hasattr(self, setup_func_name) and callable(getattr(self, setup_func_name)):
self.drv_logger.info(f"{setup_func_name}")
try:
getattr(self, setup_func_name)()
except Exception as e:
self.log_write("drv", "error", f"Errore in {setup_func_name}: {e}")
else:
self.drv_logger.info(f"Nessuna funzione di setup trovata per driver: {drv}")
def get_driver_callback(self, function_name, driver_id):
"""
Restituisce il nome del metodo interno da richiamare per una specifica funzione del driver.
"""
if isinstance(driver_id, str) and driver_id.startswith("drv:"):
drv = driver_id.split(":")[1]
if drv not in self.list_drv:
return "drvnotfound"
return f"drv_{drv}_{function_name}"
return None # Nessun driver specifico, useremo il GPIO diretto
# --- Implementazioni delle funzioni drv_* ---
# Esempio di un driver custom (simulato)
def drv_custom_rele_rele_init(self, gpio_id):
self.drv_logger.info(f"Custom Relè Driver: Inizializzazione {gpio_id}")
# Logica specifica per il relè custom
# Esempio: self.custom_rele_board.init(gpio_id)
return True
def drv_custom_rele_rele_close(self, gpio_id):
self.drv_logger.info(f"Custom Relè Driver: Chiusura {gpio_id}")
# Logica specifica per il relè custom
# Esempio: self.custom_rele_board.set_state(gpio_id, 'closed')
return True
def drv_custom_rele_rele_open(self, gpio_id):
self.drv_logger.info(f"Custom Relè Driver: Apertura {gpio_id}")
# Logica specifica per il relè custom
# Esempio: self.custom_rele_board.set_state(gpio_id, 'open')
return True
def drv_openweathermap_rain_online_get(self, driver_id):
self.drv_logger.info(f"OpenWeatherMap Driver: Recupero dati meteo online per {driver_id}")
# Qui faresti una chiamata API reale a OpenWeatherMap
# Esempio:
# import requests
# api_key = self.config.get("OPENWEATHERMAP_KEY")
# location = self.config.get("OPENWEATHERMAP_LOCATION")
# url = f"http://api.openweathermap.org/data/2.5/weather?{location}&appid={api_key}"
# try:
# response = requests.get(url)
# response.raise_for_status() # Solleva un'eccezione per errori HTTP
# data = response.json()
# # Estrai lo stato della pioggia da 'data'
# if 'rain' in data and data['rain']:
# return "1" # Indica pioggia
# return "0" # Nessuna pioggia
# except requests.exceptions.RequestException as e:
# self.log_write("drv", "error", f"Errore OpenWeatherMap API: {e}")
# self.message_write("warning", "Errore servizio meteo online")
# return ""
return "0" # Mock return
def drv_rele_init(self, gpio_id):
"""Inizializza un relè e lo porta nello stato aperto."""
fnc_name = self.get_driver_callback("rele_init", gpio_id)
if fnc_name is None: # Nessun driver specifico, usa GPIO diretto
self._gpio_write(gpio_id, self.config.get("RELE_GPIO_OPEN"))
self._gpio_mode(gpio_id, "out")
elif fnc_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {gpio_id}")
self.message_write("warning", f"Driver non trovato: {gpio_id}")
else:
# Chiama la funzione del driver dinamico
if hasattr(self, fnc_name) and callable(getattr(self, fnc_name)):
self.drv_logger.info(f"{fnc_name} arg:{gpio_id}")
try:
getattr(self, fnc_name)(gpio_id)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc_name}' non implementata.")
def drv_rele_close(self, gpio_id):
"""Chiude un relè."""
fnc_name = self.get_driver_callback("rele_close", gpio_id)
if fnc_name is None:
self._gpio_write(gpio_id, self.config.get("RELE_GPIO_CLOSE"))
elif fnc_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {gpio_id}")
self.message_write("warning", f"Driver non trovato: {gpio_id}")
return False # Fallimento
else:
if hasattr(self, fnc_name) and callable(getattr(self, fnc_name)):
self.drv_logger.info(f"{fnc_name} arg:{gpio_id}")
try:
getattr(self, fnc_name)(gpio_id)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc_name}: {e}")
return False
else:
self.log_write("drv", "error", f"Funzione driver '{fnc_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc_name}' non implementata.")
return False
return True # Successo
def drv_rele_open(self, gpio_id):
"""Apre un relè."""
fnc_name = self.get_driver_callback("rele_open", gpio_id)
if fnc_name is None:
self._gpio_write(gpio_id, self.config.get("RELE_GPIO_OPEN"))
elif fnc_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {gpio_id}")
self.message_write("warning", f"Driver non trovato: {gpio_id}")
return False
else:
if hasattr(self, fnc_name) and callable(getattr(self, fnc_name)):
self.drv_logger.info(f"{fnc_name} arg:{gpio_id}")
try:
getattr(self, fnc_name)(gpio_id)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc_name}: {e}")
return False
else:
self.log_write("drv", "error", f"Funzione driver '{fnc_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc_name}' non implementata.")
return False
return True
def drv_supply_bistable_init(self, idx1, idx2):
"""Inizializza i relè che gestiscono l'alimentazione per le valvole bistabili."""
fnc1_name = self.get_driver_callback("supply_bistable_init", idx1)
fnc2_name = self.get_driver_callback("supply_bistable_init", idx2)
if fnc1_name is None:
self._gpio_write(idx1, 0)
self._gpio_mode(idx1, "out")
elif fnc1_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {idx1}")
self.message_write("warning", f"Driver non trovato: {idx1}")
return
else:
if hasattr(self, fnc1_name) and callable(getattr(self, fnc1_name)):
self.drv_logger.info(f"{fnc1_name} arg:{idx1}")
try:
getattr(self, fnc1_name)(idx1)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc1_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc1_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc1_name}' non implementata.")
if fnc2_name is None:
self._gpio_write(idx2, 0)
self._gpio_mode(idx2, "out")
elif fnc2_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {idx2}")
self.message_write("warning", f"Driver non trovato: {idx2}")
else:
if hasattr(self, fnc2_name) and callable(getattr(self, fnc2_name)):
self.drv_logger.info(f"{fnc2_name} arg:{idx2}")
try:
getattr(self, fnc2_name)(idx2)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc2_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc2_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc2_name}' non implementata.")
def drv_supply_positive(self, idx1, idx2):
"""Imposta la tensione positiva per le elettrovalvole bistabili."""
fnc1_name = self.get_driver_callback("supply_positive", idx1)
fnc2_name = self.get_driver_callback("supply_positive", idx2)
if fnc1_name is None:
self._gpio_write(idx1, self.config.get("SUPPLY_GPIO_POS"))
elif fnc1_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {idx1}")
self.message_write("warning", f"Driver non trovato: {idx1}")
return
else:
if hasattr(self, fnc1_name) and callable(getattr(self, fnc1_name)):
self.drv_logger.info(f"{fnc1_name} arg:{idx1}")
try:
getattr(self, fnc1_name)(idx1)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc1_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc1_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc1_name}' non implementata.")
if fnc2_name is None:
self._gpio_write(idx2, self.config.get("SUPPLY_GPIO_POS"))
elif fnc2_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {idx2}")
self.message_write("warning", f"Driver non trovato: {idx2}")
else:
if hasattr(self, fnc2_name) and callable(getattr(self, fnc2_name)):
self.drv_logger.info(f"{fnc2_name} arg:{idx2}")
try:
getattr(self, fnc2_name)(idx2)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc2_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc2_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc2_name}' non implementata.")
def drv_supply_negative(self, idx1, idx2):
"""Imposta la tensione negativa per le elettrovalvole bistabili."""
fnc1_name = self.get_driver_callback("supply_negative", idx1)
fnc2_name = self.get_driver_callback("supply_negative", idx2)
if fnc1_name is None:
self._gpio_write(idx1, self.config.get("SUPPLY_GPIO_NEG"))
elif fnc1_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {idx1}")
self.message_write("warning", f"Driver non trovato: {idx1}")
return
else:
if hasattr(self, fnc1_name) and callable(getattr(self, fnc1_name)):
self.drv_logger.info(f"{fnc1_name} arg:{idx1}")
try:
getattr(self, fnc1_name)(idx1)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc1_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc1_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc1_name}' non implementata.")
if fnc2_name is None:
self._gpio_write(idx2, self.config.get("SUPPLY_GPIO_NEG"))
elif fnc2_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {idx2}")
self.message_write("warning", f"Driver non trovato: {idx2}")
else:
if hasattr(self, fnc2_name) and callable(getattr(self, fnc2_name)):
self.drv_logger.info(f"{fnc2_name} arg:{idx2}")
try:
getattr(self, fnc2_name)(idx2)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc2_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc2_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc2_name}' non implementata.")
def drv_rain_sensor_init(self, gpio_id):
"""Inizializza il sensore della pioggia."""
fnc_name = self.get_driver_callback("rain_sensor_init", gpio_id)
if fnc_name is None:
self._gpio_mode(gpio_id, "in")
elif fnc_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {gpio_id}")
self.message_write("warning", f"Driver non trovato: {gpio_id}")
else:
if hasattr(self, fnc_name) and callable(getattr(self, fnc_name)):
self.drv_logger.info(f"{fnc_name} arg:{gpio_id}")
try:
getattr(self, fnc_name)(gpio_id)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc_name}' non implementata.")
def drv_rain_sensor_get(self, gpio_id):
"""Legge lo stato del sensore della pioggia."""
fnc_name = self.get_driver_callback("rain_sensor_get", gpio_id)
vret = ""
if fnc_name is None:
vret = self._gpio_read(gpio_id)
elif fnc_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato: {gpio_id}")
self.message_write("warning", f"Driver non trovato: {gpio_id}")
else:
if hasattr(self, fnc_name) and callable(getattr(self, fnc_name)):
self.drv_logger.info(f"{fnc_name} arg:{gpio_id}")
try:
vret = getattr(self, fnc_name)(gpio_id)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc_name}' non implementata.")
return vret
def drv_rain_online_get(self, driver_id):
"""Legge lo stato delle condizioni meteo dal servizio online."""
fnc_name = self.get_driver_callback("rain_online_get", driver_id)
vret = ""
if fnc_name is None or fnc_name == "drvnotfound":
self.log_write("drv", "error", f"Driver non trovato o non specificato per il servizio meteo: {driver_id}")
self.message_write("warning", f"Driver non trovato per il servizio meteo: {driver_id}")
else:
if hasattr(self, fnc_name) and callable(getattr(self, fnc_name)):
self.drv_logger.info(f"{fnc_name} arg:{driver_id}")
try:
vret = getattr(self, fnc_name)(driver_id)
except Exception as e:
self.log_write("drv", "error", f"Errore in {fnc_name}: {e}")
else:
self.log_write("drv", "error", f"Funzione driver '{fnc_name}' non implementata.")
self.message_write("warning", f"Funzione driver '{fnc_name}' non implementata.")
return vret
# --- Esempio di utilizzo ---
if __name__ == "__main__":
print("--- Test DriverManager ---")
# Inizializza il DriverManager con la configurazione mock e le funzioni di log/messaggio
driver_manager = DriverManager(mock_config, log_write, message_write)
print("\n--- Test setup_drv (eseguito all'inizializzazione) ---")
print(f"Driver rilevati: {driver_manager.list_drv}")
print("\n--- Test drv_rele_init (GPIO diretto) ---")
driver_manager.drv_rele_init("17") # EV1_GPIO = 17
print("\n--- Test drv_rele_close (GPIO diretto) ---")
driver_manager.drv_rele_close("17")
print("\n--- Test drv_rele_open (GPIO diretto) ---")
driver_manager.drv_rele_open("17")
print("\n--- Test drv_rele_init (Custom Driver) ---")
driver_manager.drv_rele_init("drv:custom_rele") # EV2_GPIO = drv:custom_rele
print("\n--- Test drv_supply_positive ---")
driver_manager.drv_supply_positive(mock_config["SUPPLY_GPIO_1"], mock_config["SUPPLY_GPIO_2"])
print("\n--- Test drv_rain_sensor_init ---")
driver_manager.drv_rain_sensor_init(mock_config["RAIN_GPIO"])
print("\n--- Test drv_rain_sensor_get ---")
rain_sensor_state = driver_manager.drv_rain_sensor_get(mock_config["RAIN_GPIO"])
print(f"Stato sensore pioggia: {rain_sensor_state}")
print("\n--- Test drv_rain_online_get (OpenWeatherMap Driver) ---")
online_rain_state = driver_manager.drv_rain_online_get(mock_config["WEATHER_SERVICE"])
print(f"Stato pioggia online: {online_rain_state}")
print("\n--- Test completato ---")
print(f"Controlla il file di log dei driver: {driver_manager.log_output_drv_file}")

255
Python/event_include.py Normal file
View File

@@ -0,0 +1,255 @@
import os
import subprocess
import time
import threading
import logging
# --- Mock/Placeholder per le dipendenze esterne e configurazione ---
# In un'applicazione reale, queste verrebbero fornite dal tuo sistema piGarden principale.
# Configura un logger di base per le funzioni di log
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def log_write(log_type, level, message):
"""Simula la funzione log_write dal tuo script Bash."""
if level == "info":
logging.info(f"[{log_type}] {message}")
elif level == "warning":
logging.warning(f"[{log_type}] {message}")
elif level == "error":
logging.error(f"[{log_type}] {message}")
else:
logging.debug(f"[{log_type}] {message}")
def message_write(msg_type, message):
"""Simula la funzione message_write dal tuo script Bash."""
if msg_type == 'info':
logging.info(f"[message] INFO: {message}")
elif msg_type == 'warning':
logging.warning(f"[message] WARNING: {message}")
elif msg_type == 'success':
logging.info(f"[message] SUCCESS: {message}")
def mqtt_status_mock(*args):
"""Simula la funzione mqtt_status. In un'applicazione reale, chiamerebbe il tuo modulo MQTT."""
log_write("mqtt", "info", f"Simulando mqtt_status con args: {args}")
# Variabili di configurazione simulate
mock_config = {
"EVENT_DIR": "/tmp/piGarden_events", # Assicurati che questa directory esista per i test
}
# --- Classe EventManager ---
class EventManager:
def __init__(self, event_dir, log_writer, mqtt_status_func, message_writer_func=None):
self.event_dir = event_dir
self.log_write = log_writer
self.mqtt_status = mqtt_status_func
self.message_write = message_writer_func if message_writer_func else log_writer # Fallback if not provided
self.CURRENT_EVENT = ""
self.CURRENT_EVENT_ALIAS = ""
# Assicurati che la directory degli eventi esista per i test
os.makedirs(self.event_dir, exist_ok=True)
def _build_script_args(self, event, *args):
"""
Costruisce la lista di argomenti per lo script esterno basandosi sul tipo di evento.
"""
script_args = [event] # Il primo argomento è sempre il nome dell'evento
# Aggiungi il timestamp corrente come ultimo argomento per tutti i casi
current_timestamp = str(int(time.time()))
if event in ["ev_open_before", "ev_open_after"]:
# ALIAS="$2", FORCE="$3"
alias = args[0] if len(args) > 0 else ""
force = args[1] if len(args) > 1 else ""
self.CURRENT_EVENT_ALIAS = alias
script_args.extend([alias, force, current_timestamp])
elif event == "ev_open_in_before":
# ALIAS="$2", FORCE="$3", MINUTE_START="$4", MINUTE_STOP="$5"
alias = args[0] if len(args) > 0 else ""
force = args[1] if len(args) > 1 else ""
minute_start = args[2] if len(args) > 2 else ""
minute_stop = args[3] if len(args) > 3 else ""
self.CURRENT_EVENT_ALIAS = alias
script_args.extend([alias, force, minute_start, minute_stop, current_timestamp])
elif event == "ev_open_in_after":
# ALIAS="$2", FORCE="$3", CRON_START="$4", CRON_STOP="$5"
alias = args[0] if len(args) > 0 else ""
force = args[1] if len(args) > 1 else ""
cron_start = args[2] if len(args) > 2 else ""
cron_stop = args[3] if len(args) > 3 else ""
self.CURRENT_EVENT_ALIAS = alias
script_args.extend([alias, force, cron_start, cron_stop, current_timestamp])
elif event in ["ev_close_before", "ev_close_after"]:
# ALIAS="$2"
alias = args[0] if len(args) > 0 else ""
self.CURRENT_EVENT_ALIAS = alias
script_args.extend([alias, current_timestamp])
elif event in ["check_rain_sensor_before", "check_rain_sensor_after", "check_rain_sensor_change"]:
# STATE="$2"
state = args[0] if len(args) > 0 else ""
script_args.extend([state, current_timestamp])
elif event == "check_rain_online_before":
# STATE="$2"
state = args[0] if len(args) > 0 else ""
script_args.extend([state, current_timestamp])
elif event in ["check_rain_online_after", "check_rain_online_change"]:
# STATE="$2", WEATHER="$3"
state = args[0] if len(args) > 0 else ""
weather = args[1] if len(args) > 1 else ""
script_args.extend([state, weather, current_timestamp])
elif event in ["init_before", "init_after", "exec_poweroff_before", "exec_poweroff_after",
"exec_reboot_before", "exec_reboot_after"]:
# Nessun argomento specifico oltre l'evento, ma il Bash script passa $2 come CAUSE (spesso vuoto)
# e il timestamp. Qui passiamo solo il timestamp.
script_args.extend([current_timestamp])
elif event in ["cron_add_before", "cron_add_after"]:
# CRON_TYPE="$2", CRON_ARG="$3", CRON_ELEMENT="$4"
cron_type = args[0] if len(args) > 0 else ""
cron_arg = args[1] if len(args) > 1 else ""
cron_element = args[2] if len(args) > 2 else ""
script_args.extend([cron_type, cron_arg, cron_element, current_timestamp])
elif event in ["cron_del_before", "cron_del_after"]:
# CRON_TYPE="$2", CRON_ARG="$3"
cron_type = args[0] if len(args) > 0 else ""
cron_arg = args[1] if len(args) > 1 else ""
script_args.extend([cron_type, cron_arg, current_timestamp])
else: # Caso generico
# EVENT="$1", CAUSE="$2"
cause = args[0] if len(args) > 0 else ""
script_args.extend([cause, current_timestamp])
return script_args
def trigger_event(self, event, *args):
"""
Attiva un evento ed esegue gli script associati.
:param event: Nome dell'evento da attivare.
:param args: Argomenti aggiuntivi da passare agli script dell'evento.
:return: Codice di uscita dell'ultimo script eseguito, o 0 se tutto ok.
"""
self.log_write("event", "info", f"Triggering event: {event} with args: {args}")
current_event_dir = os.path.join(self.event_dir, event)
return_code = 0
if os.path.isdir(current_event_dir):
# Ordina i file per garantire un ordine di esecuzione consistente
files_in_dir = sorted(os.listdir(current_event_dir))
for f_name in files_in_dir:
script_path = os.path.join(current_event_dir, f_name)
# Controlla se è un file eseguibile
if os.path.isfile(script_path) and os.access(script_path, os.X_OK):
script_args = self._build_script_args(event, *args)
self.log_write("event", "info", f"Executing event script: {script_path} with args: {script_args}")
try:
# Esegue lo script esterno, reindirizzando stdout/stderr a DEVNULL per replicare &> /dev/null
result = subprocess.run(
[script_path] + script_args,
capture_output=True,
text=True,
check=True, # Solleva CalledProcessError per codici di uscita non zero
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
return_code = result.returncode
except subprocess.CalledProcessError as e:
return_code = e.returncode
self.log_write("event", "error",
f"Script evento '{script_path}' fallito con codice {return_code}. "
f"Output: {e.stdout.strip()} Errore: {e.stderr.strip()}")
# Aggiorna CURRENT_EVENT e chiama mqtt_status in background
self.CURRENT_EVENT = event
threading.Thread(target=self.mqtt_status, args=(self.CURRENT_EVENT,)).start()
self.log_write("event", "error",
f"Stop catena di eventi per codice di uscita {return_code} in {script_path}")
return return_code # Ferma l'esecuzione degli script successivi
except FileNotFoundError:
self.log_write("event", "error", f"Script evento non trovato: {script_path}")
return_code = 127 # Codice di errore comune per "comando non trovato"
break # Ferma l'esecuzione degli script successivi
except Exception as e:
self.log_write("event", "error", f"Errore generico durante l'esecuzione di {script_path}: {e}")
return_code = 1 # Errore generico
break # Ferma l'esecuzione degli script successivi
else:
self.log_write("event", "info", f"Nessuna directory eventi trovata per: {event}")
# Aggiorna CURRENT_EVENT e chiama mqtt_status in background, indipendentemente dal successo
self.CURRENT_EVENT = event
threading.Thread(target=self.mqtt_status, args=(self.CURRENT_EVENT,)).start()
return return_code
# --- Esempio di utilizzo ---
if __name__ == "__main__":
print("--- Test EventManager ---")
# Crea una directory di test per gli eventi e alcuni script fittizi
test_event_dir = mock_config["EVENT_DIR"]
os.makedirs(os.path.join(test_event_dir, "test_event"), exist_ok=True)
os.makedirs(os.path.join(test_event_dir, "error_event"), exist_ok=True)
os.makedirs(os.path.join(test_event_dir, "ev_open_before"), exist_ok=True)
# Script fittizio che ha successo
with open(os.path.join(test_event_dir, "test_event", "script_successo.sh"), "w") as f:
f.write("#!/bin/bash\n")
f.write("echo 'Script di successo eseguito per $1'\n")
f.write("exit 0\n")
os.chmod(os.path.join(test_event_dir, "test_event", "script_successo.sh"), 0o755)
# Script fittizio che fallisce
with open(os.path.join(test_event_dir, "error_event", "script_fallimento.sh"), "w") as f:
f.write("#!/bin/bash\n")
f.write("echo 'Script di fallimento eseguito per $1'\n")
f.write("exit 10\n") # Codice di uscita non zero
os.chmod(os.path.join(test_event_dir, "error_event", "script_fallimento.sh"), 0o755)
# Script fittizio per ev_open_before
with open(os.path.join(test_event_dir, "ev_open_before", "log_open_before.sh"), "w") as f:
f.write("#!/bin/bash\n")
f.write("echo 'ev_open_before: Evento=$1, Alias=$2, Force=$3, Timestamp=$4' >> /tmp/event_test.log\n")
f.write("exit 0\n")
os.chmod(os.path.join(test_event_dir, "ev_open_before", "log_open_before.sh"), 0o755)
event_manager = EventManager(
event_dir=mock_config["EVENT_DIR"],
log_writer=log_write,
mqtt_status_func=mqtt_status_mock,
message_writer_func=message_write
)
print("\n--- Esecuzione di un evento di successo ---")
result = event_manager.trigger_event("test_event", "some_cause")
print(f"Codice di ritorno: {result}")
print(f"CURRENT_EVENT: {event_manager.CURRENT_EVENT}")
print(f"CURRENT_EVENT_ALIAS: {event_manager.CURRENT_EVENT_ALIAS}")
print("\n--- Esecuzione di un evento che fallisce ---")
result = event_manager.trigger_event("error_event", "another_cause")
print(f"Codice di ritorno: {result}")
print(f"CURRENT_EVENT: {event_manager.CURRENT_EVENT}")
print(f"CURRENT_EVENT_ALIAS: {event_manager.CURRENT_EVENT_ALIAS}")
print("\n--- Esecuzione di un evento specifico (ev_open_before) ---")
result = event_manager.trigger_event("ev_open_before", "Zona_1", "true")
print(f"Codice di ritorno: {result}")
print(f"CURRENT_EVENT: {event_manager.CURRENT_EVENT}")
print(f"CURRENT_EVENT_ALIAS: {event_manager.CURRENT_EVENT_ALIAS}")
print("Controlla /tmp/event_test.log per l'output dello script.")
print("\n--- Test completato ---")
# Pulisci le directory di test (opzionale)
# import shutil
# shutil.rmtree(test_event_dir)

1616
Python/piGarden.py Normal file

File diff suppressed because it is too large Load Diff

432
Python/rain_include.py Normal file
View File

@@ -0,0 +1,432 @@
import os
import time
import json
import logging
# --- Mock/Placeholder per le dipendenze esterne e configurazione ---
# In un'applicazione reale, queste verrebbero fornite dal tuo sistema piGarden principale.
# Configura un logger di base per le funzioni di log
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def log_write(log_type, level, message):
"""Simula la funzione log_write dal tuo script Bash."""
if level == "info":
logging.info(f"[{log_type}] {message}")
elif level == "warning":
logging.warning(f"[{log_type}] {message}")
elif level == "error":
logging.error(f"[{log_type}] {message}")
else:
logging.debug(f"[{log_type}] {message}")
# Mock per la classe EventManager (dal file event_manager_py)
class MockEventManager:
def __init__(self):
self.CURRENT_EVENT = ""
self.CURRENT_EVENT_ALIAS = ""
def trigger_event(self, event, *args):
log_write("event", "info", f"Mock EventManager: Triggered event: {event} with args: {args}")
self.CURRENT_EVENT = event
# Simula un codice di ritorno di successo
return 0
# Mock per la classe DriverManager (dal file driver_manager_py)
class MockDriverManager:
def __init__(self, config):
self.config = config
def drv_rain_online_get(self, service_id):
"""Simula il recupero dello stato pioggia online."""
# Restituisce un timestamp se piove, un valore negativo se non piove, o 0 in caso di errore
# Per i test, simuliamo che non piova (-1) o che piova (timestamp attuale)
if "openweathermap" in service_id:
# Simula pioggia 50% delle volte
if time.time() % 2 == 0:
# Scrivi un mock di dati meteo online per il test
mock_weather_data = {
"weather": [{"description": "light rain"}],
"main": {"temp": 280, "humidity": 90}
}
with open(os.path.join(self.config["STATUS_DIR"], "last_weather_online"), "w") as f:
json.dump(mock_weather_data, f)
return str(int(time.time())) # Simula pioggia (timestamp)
else:
return "-1" # Simula non pioggia
return "0" # Errore o servizio non riconosciuto
def drv_rain_sensor_get(self, gpio_id):
"""Simula il recupero dello stato dal sensore pioggia."""
# Restituisce lo stato del GPIO (0 o 1)
# Per i test, simuliamo lo stato del sensore
if time.time() % 3 == 0:
return str(self.config.get("RAIN_GPIO_STATE", 0)) # Simula pioggia
return str(1 - self.config.get("RAIN_GPIO_STATE", 0)) # Simula non pioggia
# Mock per la classe PiGarden (per le dipendenze ev_*)
class MockPiGarden:
def __init__(self, config):
self.config = config
self.solenoid_states = {} # Mappa alias a stato (0=chiuso, 1=aperto, 2=forzato)
# Inizializza stati fittizi
for i in range(1, self.config.get("EV_TOTAL", 0) + 1):
self.solenoid_states[str(i)] = 0 # Tutti chiusi di default
def ev_status(self, alias):
"""Simula ev_status, restituisce lo stato dell'elettrovalvola."""
return self.solenoid_states.get(alias, 0) # 0 se non trovato o chiuso
def ev_close(self, alias):
"""Simula ev_close, imposta lo stato dell'elettrovalvola a chiuso."""
self.solenoid_states[alias] = 0
log_write("irrigate", "info", f"MockPiGarden: Elettrovalvola '{alias}' chiusa.")
def ev_check_moisture(self, ev_num):
"""
Simula ev_check_moisture.
Ritorna 0 se l'umidità non è stata raggiunta (bisogno d'acqua),
>0 se l'umidità massima è stata raggiunta.
"""
# Per i test, simuliamo che l'umidità sia raggiunta per EV1, altrimenti no
if ev_num == 1:
return 1 # Umidità raggiunta
return 0 # Umidità non raggiunta
def ev_check_moisture_autoclose(self, ev_num):
"""
Simula ev_check_moisture_autoclose.
Ritorna 0 se non deve chiudere automaticamente, >0 se sì.
"""
# Per i test, simuliamo che l'autochiusura sia attiva per EV1 e l'umidità sia raggiunta
if self.config.get(f"EV{ev_num}_SENSOR_MOISTURE_AUTOCLOSE", "0") == "1":
return self.ev_check_moisture(ev_num) # Usa la logica di check_moisture
return 0
def ev_number2norain(self, ev_num):
"""Simula ev_number2norain."""
return self.config.get(f"EV{ev_num}_NORAIN", "0") == "1"
# Variabili di configurazione simulate (dal piGarden.conf)
mock_config_rain = {
"WEATHER_SERVICE": "drv:openweathermap", # Esempio di driver di servizio meteo
"RAIN_GPIO": 25,
"RAIN_GPIO_STATE": 0, # Stato del GPIO che indica pioggia
"NOT_IRRIGATE_IF_RAIN_ONLINE": 86400, # 24 ore in secondi
"NOT_IRRIGATE_IF_RAIN_SENSOR": 86400, # 24 ore in secondi
"EV_TOTAL": 6,
"STATUS_DIR": "/tmp/piGarden_status", # Directory per i file di stato
# Elettrovalvole di esempio per close_all_for_rain
"EV1_ALIAS": "Zona_1", "EV1_NORAIN": "0", "EV1_SENSOR_MOISTURE_AUTOCLOSE": "1",
"EV2_ALIAS": "Zona_2", "EV2_NORAIN": "1", # Questa zona non si chiude per pioggia
"EV3_ALIAS": "Zona_3", "EV3_NORAIN": "0",
"EV4_ALIAS": "Zona_4", "EV4_NORAIN": "0",
"EV5_ALIAS": "Zona_5", "EV5_NORAIN": "0",
"EV6_ALIAS": "Zona_6", "EV6_NORAIN": "0",
}
# Assicurati che la directory di stato esista per i test
os.makedirs(mock_config_rain["STATUS_DIR"], exist_ok=True)
# --- Classe RainManager ---
class RainManager:
def __init__(self, config, log_writer, event_manager, driver_manager, pigarden_core):
self.config = config
self.log_write = log_writer
self.event_manager = event_manager
self.driver_manager = driver_manager
self.pigarden_core = pigarden_core # Istanza della classe PiGarden principale
self.status_dir = self.config.get("STATUS_DIR")
self.weather_service = self.config.get("WEATHER_SERVICE")
self.rain_gpio = self.config.get("RAIN_GPIO")
self.rain_gpio_state = self.config.get("RAIN_GPIO_STATE")
self.not_irrigate_if_rain_online = self.config.get("NOT_IRRIGATE_IF_RAIN_ONLINE")
self.not_irrigate_if_rain_sensor = self.config.get("NOT_IRRIGATE_IF_RAIN_SENSOR")
self.ev_total = self.config.get("EV_TOTAL")
# Inizializza i file di stato se non esistono
self._init_status_files()
def _init_status_files(self):
"""Assicura che i file di stato esistano per evitare errori FileNotFoundError."""
for filename in ["last_state_rain_online", "last_rain_online",
"last_state_rain_sensor", "last_rain_sensor",
"last_weather_online"]:
file_path = os.path.join(self.status_dir, filename)
if not os.path.exists(file_path):
# Crea il file vuoto o con un valore di default appropriato
with open(file_path, 'w') as f:
if filename.startswith("last_rain_"):
f.write("0") # Timestamp 0
elif filename.startswith("last_state_rain_"):
f.write("unknown")
elif filename == "last_weather_online":
f.write("{}") # JSON vuoto
def _read_status_file(self, filename, default=""):
"""Legge il contenuto di un file di stato."""
file_path = os.path.join(self.status_dir, filename)
try:
with open(file_path, 'r') as f:
content = f.read().strip()
return content if content else default
except FileNotFoundError:
return default
except Exception as e:
self.log_write("rain", "error", f"Errore lettura {filename}: {e}")
return default
def _write_status_file(self, filename, content):
"""Scrive il contenuto in un file di stato."""
file_path = os.path.join(self.status_dir, filename)
try:
with open(file_path, 'w') as f:
f.write(str(content))
except Exception as e:
self.log_write("rain", "error", f"Errore scrittura {filename}: {e}")
def _delete_status_file(self, filename):
"""Elimina un file di stato."""
file_path = os.path.join(self.status_dir, filename)
try:
if os.path.exists(file_path):
os.remove(file_path)
except Exception as e:
self.log_write("rain", "error", f"Errore eliminazione {filename}: {e}")
def check_rain_online(self):
"""
Esegue il controllo meteo tramite il servizio online configurato.
"""
if self.weather_service == "none":
self.log_write("rain", "warning", "check_rain_online - servizio online disabilitato")
return
self.event_manager.trigger_event("check_rain_online_before", "")
local_epoch_str = self.driver_manager.drv_rain_online_get(self.weather_service)
current_state_rain_online = ""
last_state_rain_online = self._read_status_file("last_state_rain_online", default="norain")
weather_json = "{}" # Default a JSON vuoto
if local_epoch_str and local_epoch_str.lstrip('-').isdigit(): # Controlla se è un numero (anche negativo)
local_epoch = int(local_epoch_str)
if local_epoch == 0:
self.log_write("rain", "error", "check_rain_online - fallita lettura dati online (valore 0)")
else:
if local_epoch > 0:
current_state_rain_online = 'rain'
self._write_status_file("last_rain_online", local_epoch)
else:
current_state_rain_online = 'norain'
# Leggi il JSON meteo, se esiste e valido
weather_data_str = self._read_status_file("last_weather_online", default="{}")
try:
weather_json = json.loads(weather_data_str)
# Estrai solo la parte "weather" se presente, altrimenti l'intero JSON
weather_display = json.dumps(weather_json.get("weather", weather_json))
except json.JSONDecodeError:
weather_display = "null" # Se il file non è un JSON valido
self.log_write("rain", "info", f"check_rain_online - weather={weather_display}, local_epoch={local_epoch}")
if current_state_rain_online != last_state_rain_online:
self._write_status_file("last_state_rain_online", current_state_rain_online)
self.event_manager.trigger_event("check_rain_online_change", current_state_rain_online, weather_display)
else:
self.log_write("rain", "error", "check_rain_online - fallita lettura dati online (non un numero)")
self.event_manager.trigger_event("check_rain_online_after", current_state_rain_online, weather_json)
def check_rain_sensor(self):
"""
Controlla se piove tramite sensore hardware.
"""
if not self.rain_gpio:
self.log_write("rain", "warning", "Sensore pioggia non presente")
return
self.event_manager.trigger_event("check_rain_sensor_before", "")
current_state_rain_sensor = ""
last_state_rain_sensor = self._read_status_file("last_state_rain_sensor", default="norain")
s_str = self.driver_manager.drv_rain_sensor_get(self.rain_gpio)
s = int(s_str) if s_str and s_str.isdigit() else -1 # Converte in int, default a -1 se non valido
if s == self.rain_gpio_state: # Confronta con lo stato configurato per la pioggia
current_state_rain_sensor = 'rain'
local_epoch = int(time.time())
self._write_status_file("last_rain_sensor", local_epoch)
self.log_write("rain", "info", f"check_rain_sensor - ora sta piovendo ({local_epoch})")
else:
current_state_rain_sensor = 'norain'
self.log_write("rain", "info", "check_rain_sensor - ora non sta piovendo")
if current_state_rain_sensor != last_state_rain_sensor:
self._write_status_file("last_state_rain_sensor", current_state_rain_sensor)
self.event_manager.trigger_event("check_rain_sensor_change", current_state_rain_sensor)
self.event_manager.trigger_event("check_rain_sensor_after", current_state_rain_sensor)
def close_all_for_rain(self):
"""
Chiude tutte le elettrovalvole se sta piovendo o se hanno raggiunto l'umidità massima.
"""
# Chiude le elettrovalvole che hanno raggiunto l'umidità del terreno impostata
for i in range(1, self.ev_total + 1):
alias = self.config.get(f"EV{i}_ALIAS")
if not alias: continue # Salta se l'alias non è definito
state = self.pigarden_core.ev_status(alias)
moisture = self.pigarden_core.ev_check_moisture_autoclose(i)
# Se l'elettrovalvola è aperta (stato 1) e l'umidità massima è stata raggiunta (moisture > 0)
if state == 1 and moisture > 0:
self.pigarden_core.ev_close(alias)
self.log_write("irrigate", "warning", f"close_all_for_rain - Chiusa elettrovalvola '{alias}' perché l'umidità massima del terreno è stata raggiunta")
# Chiude le elettrovalvole in caso di pioggia (online o sensore)
close_all_flag = False
now = int(time.time())
# Controllo pioggia online
if self.not_irrigate_if_rain_online > 0:
last_rain_online_str = self._read_status_file("last_rain_online", default="0")
try:
last_rain_online = int(last_rain_online_str)
if now - last_rain_online < self.not_irrigate_if_rain_online:
close_all_flag = True
except ValueError:
pass # Ignora se il timestamp non è un numero
# Controllo pioggia sensore
if self.not_irrigate_if_rain_sensor > 0:
last_rain_sensor_str = self._read_status_file("last_rain_sensor", default="0")
try:
last_rain_sensor = int(last_rain_sensor_str)
if now - last_rain_sensor < self.not_irrigate_if_rain_sensor:
close_all_flag = True
except ValueError:
pass # Ignora se il timestamp non è un numero
if close_all_flag:
# Piove: valuta se chiudere le elettrovalvole
for i in range(1, self.ev_total + 1):
alias = self.config.get(f"EV{i}_ALIAS")
if not alias: continue
state = self.pigarden_core.ev_status(alias)
ev_norain = self.pigarden_core.ev_number2norain(i) # True se non deve chiudere per pioggia
moisture = self.pigarden_core.ev_check_moisture(i) # 0 se non ha raggiunto l'umidità ottimale
# Se l'elettrovalvola è aperta (stato 1), NON è impostata per ignorare la pioggia (ev_norain è False),
# E l'umidità non è ancora ottimale (moisture è 0 o non ha raggiunto il max)
# La logica Bash `[ "$moisture" -ne 0 ]` significa "se l'umidità NON è zero",
# che nel contesto di `ev_check_moisture` (che ritorna 0 per "non raggiunta", >0 per "raggiunta")
# significherebbe "se l'umidità è stata raggiunta".
# Tuttavia, il commento nello script Bash `if [ $moisture -gt 0 ]; then message_write "warning" "solenoid not open because maximum soil moisture has been reached"`
# suggerisce che >0 significa "umidità massima raggiunta".
# Quindi, per chiudere per pioggia, l'umidità NON deve essere già al massimo.
# Se `ev_check_moisture` ritorna 0 per "non raggiunto" e >0 per "raggiunto",
# allora `moisture == 0` significa "ha ancora bisogno d'acqua".
# Quindi, la condizione per chiudere per pioggia dovrebbe essere:
# `state == 1` (aperta) AND `not ev_norain` (non ignora pioggia) AND `moisture == 0` (ha ancora bisogno d'acqua)
# Il Bash `[ "$moisture" -ne 0 ]` nella seconda loop di close_all_for_rain è contro-intuitivo
# se `ev_check_moisture` ritorna >0 per "raggiunto".
# Assumo che `moisture -ne 0` in quel contesto significhi "se l'umidità non è perfetta, chiudi".
# Se `ev_check_moisture` ritorna 0 per "umidità OK/raggiunta" e 1 per "non OK", allora -ne 0 ha senso.
# Basandomi sulla funzione `ev_open` che usa `moisture -gt 0` per bloccare l'apertura (umidità già alta),
# `moisture -ne 0` qui dovrebbe significare "se l'umidità non è ancora ottimale/non è zero".
# Se 0 significa "umidità OK", allora -ne 0 significa "umidità NON OK".
# Adotterò la traduzione letterale di `moisture != 0` e lascerò al mock di `ev_check_moisture` di definire il comportamento.
if state == 1 and not ev_norain and moisture != 0:
self.pigarden_core.ev_close(alias)
self.log_write("irrigate", "warning", f"close_all_for_rain - Chiusa elettrovalvola '{alias}' per pioggia")
def last_rain_sensor_timestamp(self):
"""Mostra il timestamp dell'ultima pioggia rilevato dal sensore."""
return self._read_status_file("last_rain_sensor", default="0")
def last_rain_online_timestamp(self):
"""Mostra il timestamp dell'ultima pioggia rilevato dal servizio online."""
return self._read_status_file("last_rain_online", default="0")
def reset_last_rain_sensor_timestamp(self):
"""Resetta il timestamp dell'ultima pioggia rilevato dal sensore."""
self.event_manager.trigger_event("reset_last_rain_sensor_timestamp_before", "")
self._delete_status_file("last_rain_sensor")
self.event_manager.trigger_event("reset_last_rain_sensor_timestamp_after", "")
self.log_write("rain", "info", "reset_last_rain_sensor_timestamp")
def reset_last_rain_online_timestamp(self):
"""Resetta il timestamp dell'ultima pioggia rilevato dal servizio online."""
self.event_manager.trigger_event("reset_last_rain_online_timestamp_before", "")
self._delete_status_file("last_rain_online")
# Il Bash script chiama trigger_event due volte con _before, correggo a _after
self.event_manager.trigger_event("reset_last_rain_online_timestamp_after", "")
self.log_write("rain", "info", "reset_last_rain_online_timestamp")
# --- Esempio di utilizzo ---
if __name__ == "__main__":
print("--- Test RainManager ---")
# Inizializza le dipendenze mock
mock_event_manager = MockEventManager()
mock_driver_manager = MockDriverManager(mock_config_rain)
mock_pigarden_core = MockPiGarden(mock_config_rain)
# Inizializza RainManager
rain_manager = RainManager(
config=mock_config_rain,
log_writer=log_write,
event_manager=mock_event_manager,
driver_manager=mock_driver_manager,
pigarden_core=mock_pigarden_core
)
# --- Test check_rain_online ---
print("\n--- Test: check_rain_online (potrebbe simulare pioggia o no) ---")
rain_manager.check_rain_online()
print(f"Stato pioggia online (file): {rain_manager.last_rain_online_timestamp()}")
print(f"Stato ultimo rilevamento online (file): {rain_manager._read_status_file('last_state_rain_online')}")
# --- Test check_rain_sensor ---
print("\n--- Test: check_rain_sensor (potrebbe simulare pioggia o no) ---")
rain_manager.check_rain_sensor()
print(f"Stato pioggia sensore (file): {rain_manager.last_rain_sensor_timestamp()}")
print(f"Stato ultimo rilevamento sensore (file): {rain_manager._read_status_file('last_state_rain_sensor')}")
# --- Test close_all_for_rain ---
print("\n--- Test: close_all_for_rain ---")
# Imposta un'elettrovalvola aperta per il test
mock_pigarden_core.solenoid_states["Zona_1"] = 1
mock_pigarden_core.solenoid_states["Zona_3"] = 1
print(f"Stato iniziale Zona_1: {mock_pigarden_core.ev_status('Zona_1')}")
print(f"Stato iniziale Zona_3: {mock_pigarden_core.ev_status('Zona_3')}")
# Simula una pioggia recente per attivare la chiusura
rain_manager._write_status_file("last_rain_online", int(time.time()) - 100) # 100 secondi fa
rain_manager._write_status_file("last_rain_sensor", int(time.time()) - 50) # 50 secondi fa
rain_manager.close_all_for_rain()
print(f"Stato finale Zona_1: {mock_pigarden_core.ev_status('Zona_1')} (dovrebbe essere 0 se autoclose è attivo o piove)")
print(f"Stato finale Zona_3: {mock_pigarden_core.ev_status('Zona_3')} (dovrebbe essere 0 se piove)")
# --- Test reset timestamp ---
print("\n--- Test: reset_last_rain_sensor_timestamp ---")
rain_manager.reset_last_rain_sensor_timestamp()
print(f"Timestamp sensore dopo reset: {rain_manager.last_rain_sensor_timestamp()}")
print("\n--- Test: reset_last_rain_online_timestamp ---")
rain_manager.reset_last_rain_online_timestamp()
print(f"Timestamp online dopo reset: {rain_manager.last_rain_online_timestamp()}")
print("\n--- Test completato ---")
# Pulisci le directory di test (opzionale)
# import shutil
# shutil.rmtree(mock_config_rain["STATUS_DIR"])

432
Python/sensor_include.py Normal file
View File

@@ -0,0 +1,432 @@
import os
import time
import json
import logging
# --- Mock/Placeholder per le dipendenze esterne e configurazione ---
# In un'applicazione reale, queste verrebbero fornite dal tuo sistema piGarden principale.
# Configura un logger di base per le funzioni di log
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def log_write(log_type, level, message):
"""Simula la funzione log_write dal tuo script Bash."""
if level == "info":
logging.info(f"[{log_type}] {message}")
elif level == "warning":
logging.warning(f"[{log_type}] {message}")
elif level == "error":
logging.error(f"[{log_type}] {message}")
else:
logging.debug(f"[{log_type}] {message}")
# Mock per la classe EventManager (dal file event_manager_py)
class MockEventManager:
def __init__(self):
self.CURRENT_EVENT = ""
self.CURRENT_EVENT_ALIAS = ""
def trigger_event(self, event, *args):
log_write("event", "info", f"Mock EventManager: Triggered event: {event} with args: {args}")
self.CURRENT_EVENT = event
# Simula un codice di ritorno di successo
return 0
# Mock per la classe DriverManager (dal file driver_manager_py)
class MockDriverManager:
def __init__(self, config):
self.config = config
def drv_rain_online_get(self, service_id):
"""Simula il recupero dello stato pioggia online."""
# Restituisce un timestamp se piove, un valore negativo se non piove, o 0 in caso di errore
# Per i test, simuliamo che non piova (-1) o che piova (timestamp attuale)
if "openweathermap" in service_id:
# Simula pioggia 50% delle volte
if time.time() % 2 == 0:
# Scrivi un mock di dati meteo online per il test
mock_weather_data = {
"weather": [{"description": "light rain"}],
"main": {"temp": 280, "humidity": 90}
}
with open(os.path.join(self.config["STATUS_DIR"], "last_weather_online"), "w") as f:
json.dump(mock_weather_data, f)
return str(int(time.time())) # Simula pioggia (timestamp)
else:
return "-1" # Simula non pioggia
return "0" # Errore o servizio non riconosciuto
def drv_rain_sensor_get(self, gpio_id):
"""Simula il recupero dello stato dal sensore pioggia."""
# Restituisce lo stato del GPIO (0 o 1)
# Per i test, simuliamo lo stato del sensore
if time.time() % 3 == 0:
return str(self.config.get("RAIN_GPIO_STATE", 0)) # Simula pioggia
return str(1 - self.config.get("RAIN_GPIO_STATE", 0)) # Simula non pioggia
# Mock per la classe PiGarden (per le dipendenze ev_*)
class MockPiGarden:
def __init__(self, config):
self.config = config
self.solenoid_states = {} # Mappa alias a stato (0=chiuso, 1=aperto, 2=forzato)
# Inizializza stati fittizi
for i in range(1, self.config.get("EV_TOTAL", 0) + 1):
self.solenoid_states[str(i)] = 0 # Tutti chiusi di default
def ev_status(self, alias):
"""Simula ev_status, restituisce lo stato dell'elettrovalvola."""
return self.solenoid_states.get(alias, 0) # 0 se non trovato o chiuso
def ev_close(self, alias):
"""Simula ev_close, imposta lo stato dell'elettrovalvola a chiuso."""
self.solenoid_states[alias] = 0
log_write("irrigate", "info", f"MockPiGarden: Elettrovalvola '{alias}' chiusa.")
def ev_check_moisture(self, ev_num):
"""
Simula ev_check_moisture.
Ritorna 0 se l'umidità non è stata raggiunta (bisogno d'acqua),
>0 se l'umidità massima è stata raggiunta.
"""
# Per i test, simuliamo che l'umidità sia raggiunta per EV1, altrimenti no
if ev_num == 1:
return 1 # Umidità raggiunta
return 0 # Umidità non raggiunta
def ev_check_moisture_autoclose(self, ev_num):
"""
Simula ev_check_moisture_autoclose.
Ritorna 0 se non deve chiudere automaticamente, >0 se sì.
"""
# Per i test, simuliamo che l'autochiusura sia attiva per EV1 e l'umidità sia raggiunta
if self.config.get(f"EV{ev_num}_SENSOR_MOISTURE_AUTOCLOSE", "0") == "1":
return self.ev_check_moisture(ev_num) # Usa la logica di check_moisture
return 0
def ev_number2norain(self, ev_num):
"""Simula ev_number2norain."""
return self.config.get(f"EV{ev_num}_NORAIN", "0") == "1"
# Variabili di configurazione simulate (dal piGarden.conf)
mock_config_rain = {
"WEATHER_SERVICE": "drv:openweathermap", # Esempio di driver di servizio meteo
"RAIN_GPIO": 25,
"RAIN_GPIO_STATE": 0, # Stato del GPIO che indica pioggia
"NOT_IRRIGATE_IF_RAIN_ONLINE": 86400, # 24 ore in secondi
"NOT_IRRIGATE_IF_RAIN_SENSOR": 86400, # 24 ore in secondi
"EV_TOTAL": 6,
"STATUS_DIR": "/tmp/piGarden_status", # Directory per i file di stato
# Elettrovalvole di esempio per close_all_for_rain
"EV1_ALIAS": "Zona_1", "EV1_NORAIN": "0", "EV1_SENSOR_MOISTURE_AUTOCLOSE": "1",
"EV2_ALIAS": "Zona_2", "EV2_NORAIN": "1", # Questa zona non si chiude per pioggia
"EV3_ALIAS": "Zona_3", "EV3_NORAIN": "0",
"EV4_ALIAS": "Zona_4", "EV4_NORAIN": "0",
"EV5_ALIAS": "Zona_5", "EV5_NORAIN": "0",
"EV6_ALIAS": "Zona_6", "EV6_NORAIN": "0",
}
# Assicurati che la directory di stato esista per i test
os.makedirs(mock_config_rain["STATUS_DIR"], exist_ok=True)
# --- Classe RainManager ---
class RainManager:
def __init__(self, config, log_writer, event_manager, driver_manager, pigarden_core):
self.config = config
self.log_write = log_writer
self.event_manager = event_manager
self.driver_manager = driver_manager
self.pigarden_core = pigarden_core # Istanza della classe PiGarden principale
self.status_dir = self.config.get("STATUS_DIR")
self.weather_service = self.config.get("WEATHER_SERVICE")
self.rain_gpio = self.config.get("RAIN_GPIO")
self.rain_gpio_state = self.config.get("RAIN_GPIO_STATE")
self.not_irrigate_if_rain_online = self.config.get("NOT_IRRIGATE_IF_RAIN_ONLINE")
self.not_irrigate_if_rain_sensor = self.config.get("NOT_IRRIGATE_IF_RAIN_SENSOR")
self.ev_total = self.config.get("EV_TOTAL")
# Inizializza i file di stato se non esistono
self._init_status_files()
def _init_status_files(self):
"""Assicura che i file di stato esistano per evitare errori FileNotFoundError."""
for filename in ["last_state_rain_online", "last_rain_online",
"last_state_rain_sensor", "last_rain_sensor",
"last_weather_online"]:
file_path = os.path.join(self.status_dir, filename)
if not os.path.exists(file_path):
# Crea il file vuoto o con un valore di default appropriato
with open(file_path, 'w') as f:
if filename.startswith("last_rain_"):
f.write("0") # Timestamp 0
elif filename.startswith("last_state_rain_"):
f.write("unknown")
elif filename == "last_weather_online":
f.write("{}") # JSON vuoto
def _read_status_file(self, filename, default=""):
"""Legge il contenuto di un file di stato."""
file_path = os.path.join(self.status_dir, filename)
try:
with open(file_path, 'r') as f:
content = f.read().strip()
return content if content else default
except FileNotFoundError:
return default
except Exception as e:
self.log_write("rain", "error", f"Errore lettura {filename}: {e}")
return default
def _write_status_file(self, filename, content):
"""Scrive il contenuto in un file di stato."""
file_path = os.path.join(self.status_dir, filename)
try:
with open(file_path, 'w') as f:
f.write(str(content))
except Exception as e:
self.log_write("rain", "error", f"Errore scrittura {filename}: {e}")
def _delete_status_file(self, filename):
"""Elimina un file di stato."""
file_path = os.path.join(self.status_dir, filename)
try:
if os.path.exists(file_path):
os.remove(file_path)
except Exception as e:
self.log_write("rain", "error", f"Errore eliminazione {filename}: {e}")
def check_rain_online(self):
"""
Esegue il controllo meteo tramite il servizio online configurato.
"""
if self.weather_service == "none":
self.log_write("rain", "warning", "check_rain_online - servizio online disabilitato")
return
self.event_manager.trigger_event("check_rain_online_before", "")
local_epoch_str = self.driver_manager.drv_rain_online_get(self.weather_service)
current_state_rain_online = ""
last_state_rain_online = self._read_status_file("last_state_rain_online", default="norain")
weather_json = "{}" # Default a JSON vuoto
if local_epoch_str and local_epoch_str.lstrip('-').isdigit(): # Controlla se è un numero (anche negativo)
local_epoch = int(local_epoch_str)
if local_epoch == 0:
self.log_write("rain", "error", "check_rain_online - fallita lettura dati online (valore 0)")
else:
if local_epoch > 0:
current_state_rain_online = 'rain'
self._write_status_file("last_rain_online", local_epoch)
else:
current_state_rain_online = 'norain'
# Leggi il JSON meteo, se esiste e valido
weather_data_str = self._read_status_file("last_weather_online", default="{}")
try:
weather_json = json.loads(weather_data_str)
# Estrai solo la parte "weather" se presente, altrimenti l'intero JSON
weather_display = json.dumps(weather_json.get("weather", weather_json))
except json.JSONDecodeError:
weather_display = "null" # Se il file non è un JSON valido
self.log_write("rain", "info", f"check_rain_online - weather={weather_display}, local_epoch={local_epoch}")
if current_state_rain_online != last_state_rain_online:
self._write_status_file("last_state_rain_online", current_state_rain_online)
self.event_manager.trigger_event("check_rain_online_change", current_state_rain_online, weather_display)
else:
self.log_write("rain", "error", "check_rain_online - fallita lettura dati online (non un numero)")
self.event_manager.trigger_event("check_rain_online_after", current_state_rain_online, weather_json)
def check_rain_sensor(self):
"""
Controlla se piove tramite sensore hardware.
"""
if not self.rain_gpio:
self.log_write("rain", "warning", "Sensore pioggia non presente")
return
self.event_manager.trigger_event("check_rain_sensor_before", "")
current_state_rain_sensor = ""
last_state_rain_sensor = self._read_status_file("last_state_rain_sensor", default="norain")
s_str = self.driver_manager.drv_rain_sensor_get(self.rain_gpio)
s = int(s_str) if s_str and s_str.isdigit() else -1 # Converte in int, default a -1 se non valido
if s == self.rain_gpio_state: # Confronta con lo stato configurato per la pioggia
current_state_rain_sensor = 'rain'
local_epoch = int(time.time())
self._write_status_file("last_rain_sensor", local_epoch)
self.log_write("rain", "info", f"check_rain_sensor - ora sta piovendo ({local_epoch})")
else:
current_state_rain_sensor = 'norain'
self.log_write("rain", "info", "check_rain_sensor - ora non sta piovendo")
if current_state_rain_sensor != last_state_rain_sensor:
self._write_status_file("last_state_rain_sensor", current_state_rain_sensor)
self.event_manager.trigger_event("check_rain_sensor_change", current_state_rain_sensor)
self.event_manager.trigger_event("check_rain_sensor_after", current_state_rain_sensor)
def close_all_for_rain(self):
"""
Chiude tutte le elettrovalvole se sta piovendo o se hanno raggiunto l'umidità massima.
"""
# Chiude le elettrovalvole che hanno raggiunto l'umidità del terreno impostata
for i in range(1, self.ev_total + 1):
alias = self.config.get(f"EV{i}_ALIAS")
if not alias: continue # Salta se l'alias non è definito
state = self.pigarden_core.ev_status(alias)
moisture = self.pigarden_core.ev_check_moisture_autoclose(i)
# Se l'elettrovalvola è aperta (stato 1) e l'umidità massima è stata raggiunta (moisture > 0)
if state == 1 and moisture > 0:
self.pigarden_core.ev_close(alias)
self.log_write("irrigate", "warning", f"close_all_for_rain - Chiusa elettrovalvola '{alias}' perché l'umidità massima del terreno è stata raggiunta")
# Chiude le elettrovalvole in caso di pioggia (online o sensore)
close_all_flag = False
now = int(time.time())
# Controllo pioggia online
if self.not_irrigate_if_rain_online > 0:
last_rain_online_str = self._read_status_file("last_rain_online", default="0")
try:
last_rain_online = int(last_rain_online_str)
if now - last_rain_online < self.not_irrigate_if_rain_online:
close_all_flag = True
except ValueError:
pass # Ignora se il timestamp non è un numero
# Controllo pioggia sensore
if self.not_irrigate_if_rain_sensor > 0:
last_rain_sensor_str = self._read_status_file("last_rain_sensor", default="0")
try:
last_rain_sensor = int(last_rain_sensor_str)
if now - last_rain_sensor < self.not_irrigate_if_rain_sensor:
close_all_flag = True
except ValueError:
pass # Ignora se il timestamp non è un numero
if close_all_flag:
# Piove: valuta se chiudere le elettrovalvole
for i in range(1, self.ev_total + 1):
alias = self.config.get(f"EV{i}_ALIAS")
if not alias: continue
state = self.pigarden_core.ev_status(alias)
ev_norain = self.pigarden_core.ev_number2norain(i) # True se non deve chiudere per pioggia
moisture = self.pigarden_core.ev_check_moisture(i) # 0 se non ha raggiunto l'umidità ottimale
# Se l'elettrovalvola è aperta (stato 1), NON è impostata per ignorare la pioggia (ev_norain è False),
# E l'umidità non è ancora ottimale (moisture è 0 o non ha raggiunto il max)
# La logica Bash `[ "$moisture" -ne 0 ]` significa "se l'umidità NON è zero",
# che nel contesto di `ev_check_moisture` (che ritorna 0 per "non raggiunta", >0 per "raggiunta")
# significherebbe "se l'umidità è stata raggiunta".
# Tuttavia, il commento nello script Bash `if [ $moisture -gt 0 ]; then message_write "warning" "solenoid not open because maximum soil moisture has been reached"`
# suggerisce che >0 significa "umidità massima raggiunta".
# Quindi, per chiudere per pioggia, l'umidità NON deve essere già al massimo.
# Se `ev_check_moisture` ritorna 0 per "non raggiunto" e >0 per "raggiunto",
# allora `moisture == 0` significa "ha ancora bisogno d'acqua".
# Quindi, la condizione per chiudere per pioggia dovrebbe essere:
# `state == 1` (aperta) AND `not ev_norain` (non ignora pioggia) AND `moisture == 0` (ha ancora bisogno d'acqua)
# Il Bash `[ "$moisture" -ne 0 ]` nella seconda loop di close_all_for_rain è contro-intuitivo
# se `ev_check_moisture` ritorna >0 per "raggiunto".
# Assumo che `moisture -ne 0` in quel contesto significhi "se l'umidità non è perfetta, chiudi".
# Se `ev_check_moisture` ritorna 0 per "umidità OK/raggiunta" e 1 per "non OK", allora -ne 0 ha senso.
# Basandomi sulla funzione `ev_open` che usa `moisture -gt 0` per bloccare l'apertura (umidità già alta),
# `moisture -ne 0` qui dovrebbe significare "se l'umidità non è ancora ottimale/non è zero".
# Se 0 significa "umidità OK", allora -ne 0 significa "umidità NON OK".
# Adotterò la traduzione letterale di `moisture != 0` e lascerò al mock di `ev_check_moisture` di definire il comportamento.
if state == 1 and not ev_norain and moisture != 0:
self.pigarden_core.ev_close(alias)
self.log_write("irrigate", "warning", f"close_all_for_rain - Chiusa elettrovalvola '{alias}' per pioggia")
def last_rain_sensor_timestamp(self):
"""Mostra il timestamp dell'ultima pioggia rilevato dal sensore."""
return self._read_status_file("last_rain_sensor", default="0")
def last_rain_online_timestamp(self):
"""Mostra il timestamp dell'ultima pioggia rilevato dal servizio online."""
return self._read_status_file("last_rain_online", default="0")
def reset_last_rain_sensor_timestamp(self):
"""Resetta il timestamp dell'ultima pioggia rilevato dal sensore."""
self.event_manager.trigger_event("reset_last_rain_sensor_timestamp_before", "")
self._delete_status_file("last_rain_sensor")
self.event_manager.trigger_event("reset_last_rain_sensor_timestamp_after", "")
self.log_write("rain", "info", "reset_last_rain_sensor_timestamp")
def reset_last_rain_online_timestamp(self):
"""Resetta il timestamp dell'ultima pioggia rilevato dal servizio online."""
self.event_manager.trigger_event("reset_last_rain_online_timestamp_before", "")
self._delete_status_file("last_rain_online")
# Il Bash script chiama trigger_event due volte con _before, correggo a _after
self.event_manager.trigger_event("reset_last_rain_online_timestamp_after", "")
self.log_write("rain", "info", "reset_last_rain_online_timestamp")
# --- Esempio di utilizzo ---
if __name__ == "__main__":
print("--- Test RainManager ---")
# Inizializza le dipendenze mock
mock_event_manager = MockEventManager()
mock_driver_manager = MockDriverManager(mock_config_rain)
mock_pigarden_core = MockPiGarden(mock_config_rain)
# Inizializza RainManager
rain_manager = RainManager(
config=mock_config_rain,
log_writer=log_write,
event_manager=mock_event_manager,
driver_manager=mock_driver_manager,
pigarden_core=mock_pigarden_core
)
# --- Test check_rain_online ---
print("\n--- Test: check_rain_online (potrebbe simulare pioggia o no) ---")
rain_manager.check_rain_online()
print(f"Stato pioggia online (file): {rain_manager.last_rain_online_timestamp()}")
print(f"Stato ultimo rilevamento online (file): {rain_manager._read_status_file('last_state_rain_online')}")
# --- Test check_rain_sensor ---
print("\n--- Test: check_rain_sensor (potrebbe simulare pioggia o no) ---")
rain_manager.check_rain_sensor()
print(f"Stato pioggia sensore (file): {rain_manager.last_rain_sensor_timestamp()}")
print(f"Stato ultimo rilevamento sensore (file): {rain_manager._read_status_file('last_state_rain_sensor')}")
# --- Test close_all_for_rain ---
print("\n--- Test: close_all_for_rain ---")
# Imposta un'elettrovalvola aperta per il test
mock_pigarden_core.solenoid_states["Zona_1"] = 1
mock_pigarden_core.solenoid_states["Zona_3"] = 1
print(f"Stato iniziale Zona_1: {mock_pigarden_core.ev_status('Zona_1')}")
print(f"Stato iniziale Zona_3: {mock_pigarden_core.ev_status('Zona_3')}")
# Simula una pioggia recente per attivare la chiusura
rain_manager._write_status_file("last_rain_online", int(time.time()) - 100) # 100 secondi fa
rain_manager._write_status_file("last_rain_sensor", int(time.time()) - 50) # 50 secondi fa
rain_manager.close_all_for_rain()
print(f"Stato finale Zona_1: {mock_pigarden_core.ev_status('Zona_1')} (dovrebbe essere 0 se autoclose è attivo o piove)")
print(f"Stato finale Zona_3: {mock_pigarden_core.ev_status('Zona_3')} (dovrebbe essere 0 se piove)")
# --- Test reset timestamp ---
print("\n--- Test: reset_last_rain_sensor_timestamp ---")
rain_manager.reset_last_rain_sensor_timestamp()
print(f"Timestamp sensore dopo reset: {rain_manager.last_rain_sensor_timestamp()}")
print("\n--- Test: reset_last_rain_online_timestamp ---")
rain_manager.reset_last_rain_online_timestamp()
print(f"Timestamp online dopo reset: {rain_manager.last_rain_online_timestamp()}")
print("\n--- Test completato ---")
# Pulisci le directory di test (opzionale)
# import shutil
# shutil.rmtree(mock_config_rain["STATUS_DIR"])

525
Python/socket.include.py Normal file
View File

@@ -0,0 +1,525 @@
import os
import subprocess
import threading
import socket
import socketserver
import time
import sys
# --- CONFIGURAZIONE (DA ADATTARE ALLE TUE ESIGENZE) ---
# Queste variabili dovrebbero essere configurate in un file di configurazione separato
# o passate come argomenti al tuo script Python.
TCPSERVER_PID_FILE = "/var/run/my_socket_server.pid"
TCPSERVER_IP = "0.0.0.0" # Ascolta su tutte le interfacce
TCPSERVER_PORT = 12345 # Porta del server socket
TCPSERVER_USER = "admin" # Credenziali opzionali per l'autenticazione
TCPSERVER_PWD = "password123"
# Variabili usate internamente dallo script Bash, che in Python diventano parametri o logica
RUN_FROM_TCPSERVER = False # Sarà True quando il comando è eseguito dal server socket
# Definisci i percorsi per i comandi esterni (se non sono nel PATH di sistema)
# In un ambiente di produzione, è meglio specificare percorsi assoluti.
# Ad esempio, TR_COMMAND = "/usr/bin/tr"
TR_COMMAND = "tr"
CUT_COMMAND = "cut"
READLINK_COMMAND = "readlink"
# --- FUNZIONI UTILITY (PLACEHOLDERS) ---
# Queste sono le funzioni che erano richiamate dallo script Bash
# Dovrai implementarle in Python o collegarle alle tue librerie esistenti.
def log_write(source, level, message):
"""Placeholder per la funzione di logging."""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
print(f"[{timestamp}] [{source}] [{level.upper()}] {message}")
def json_error(code, message):
"""Placeholder per la funzione che genera un JSON di errore."""
print(f'{{"status": "error", "code": {code}, "message": "{message}"}}')
def json_status(*args):
"""Placeholder per la funzione che genera un JSON di stato."""
if args:
print(f'{{"status": "success", "data": "{", ".join(args)}"}}')
else:
print('{"status": "success"}')
def message_write(level, message):
"""Placeholder per la funzione che scrive un messaggio."""
print(f"[{level.upper()}] {message}")
def ev_open(alias, param=None):
"""Placeholder per la funzione di apertura valvola."""
log_write("socket_server", "info", f"Executing ev_open for alias: {alias}, param: {param}")
# Qui andrebbe la logica per aprire la valvola
pass
def ev_open_in(arg2, arg3, arg4, arg5):
"""Placeholder per la funzione di apertura valvola in un intervallo."""
log_write("socket_server", "info", f"Executing ev_open_in with args: {arg2}, {arg3}, {arg4}, {arg5}")
# Qui andrebbe la logica per aprire la valvola in un intervallo
pass
def ev_close(alias):
"""Placeholder per la funzione di chiusura valvola."""
log_write("socket_server", "info", f"Executing ev_close for alias: {alias}")
# Qui andrebbe la logica per chiudere la valvola
pass
def close_all():
"""Placeholder per la funzione di chiusura di tutte le valvole."""
log_write("socket_server", "info", "Executing close_all")
# Qui andrebbe la logica per chiudere tutte le valvole
pass
def cron_disable_all_open_close():
"""Placeholder per la funzione di disabilitazione scheduling cron."""
log_write("socket_server", "info", "Executing cron_disable_all_open_close")
# Qui andrebbe la logica per disabilitare lo scheduling
pass
def cron_enable_all_open_close():
"""Placeholder per la funzione di abilitazione scheduling cron."""
log_write("socket_server", "info", "Executing cron_enable_all_open_close")
# Qui andrebbe la logica per abilitare lo scheduling
pass
def set_cron_init():
"""Placeholder per la funzione set_cron_init."""
log_write("socket_server", "info", "Executing set_cron_init")
return "" # Simula output vuoto per successo
def set_cron_start_socket_server():
"""Placeholder per la funzione set_cron_start_socket_server."""
log_write("socket_server", "info", "Executing set_cron_start_socket_server")
return ""
def set_cron_check_rain_sensor():
"""Placeholder per la funzione set_cron_check_rain_sensor."""
log_write("socket_server", "info", "Executing set_cron_check_rain_sensor")
return ""
def set_cron_check_rain_online():
"""Placeholder per la funzione set_cron_check_rain_online."""
log_write("socket_server", "info", "Executing set_cron_check_rain_online")
return ""
def set_cron_close_all_for_rain():
"""Placeholder per la funzione set_cron_close_all_for_rain."""
log_write("socket_server", "info", "Executing set_cron_close_all_for_rain")
return ""
def del_cron_open(arg):
"""Placeholder per la funzione del_cron_open."""
log_write("socket_server", "info", f"Executing del_cron_open with arg: {arg}")
return ""
def del_cron_open_in(arg):
"""Placeholder per la funzione del_cron_open_in."""
log_write("socket_server", "info", f"Executing del_cron_open_in with arg: {arg}")
return ""
def del_cron_close(arg):
"""Placeholder per la funzione del_cron_close."""
log_write("socket_server", "info", f"Executing del_cron_close with arg: {arg}")
return ""
def add_cron_open(arg2, arg3, arg4, arg5, arg6, arg7, arg8):
"""Placeholder per la funzione add_cron_open."""
log_write("socket_server", "info", f"Executing add_cron_open with args: {arg2}, {arg3}, {arg4}, {arg5}, {arg6}, {arg7}, {arg8}")
return ""
def add_cron_close(arg2, arg3, arg4, arg5, arg6, arg7, arg8):
"""Placeholder per la funzione add_cron_close."""
log_write("socket_server", "info", f"Executing add_cron_close with args: {arg2}, {arg3}, {arg4}, {arg5}, {arg6}, {arg7}, {arg8}")
return ""
def cmd_pigardensched(arg2, arg3, arg4, arg5, arg6):
"""Placeholder per la funzione cmd_pigardensched."""
log_write("socket_server", "info", f"Executing cmd_pigardensched with args: {arg2}, {arg3}, {arg4}, {arg5}, {arg6}")
return ""
def reset_last_rain_sensor_timestamp():
"""Placeholder per la funzione reset_last_rain_sensor_timestamp."""
log_write("socket_server", "info", "Executing reset_last_rain_sensor_timestamp")
pass
def reset_last_rain_online_timestamp():
"""Placeholder per la funzione reset_last_rain_online_timestamp."""
log_write("socket_server", "info", "Executing reset_last_rain_online_timestamp")
pass
def sensor_status_set(arg2, arg3, arg4):
"""Placeholder per la funzione sensor_status_set."""
log_write("socket_server", "info", f"Executing sensor_status_set with args: {arg2}, {arg3}, {arg4}")
pass
# --- FUNZIONI DI GESTIONE DEI PROCESSI ---
def list_descendants(pid):
"""
Simula la logica di 'list_descendants' Bash.
In un sistema reale, dovresti usare librerie come 'psutil'
per ottenere i processi figli. Qui è solo una simulazione.
"""
try:
# psutil è l'opzione migliore, se disponibile
# import psutil
# parent = psutil.Process(pid)
# return [child.pid for child in parent.children(recursive=True)]
# Fallback semplice per simulazione (non accurato per processi reali)
# Se non hai psutil, questa funzione è difficile da replicare accuratamente in modo generico.
# Potrebbe essere necessario un approccio specifico per il tuo sistema operativo.
return [] # Nessun discendente noto senza psutil
except Exception as e:
log_write("process_manager", "error", f"Errore nel listing dei discendenti di {pid}: {e}")
return []
def get_script_path():
"""Restituisce il percorso assoluto dello script corrente."""
return os.path.abspath(sys.argv[0])
# --- CLASSE GESTORE RICHIESTE SOCKET ---
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
La classe MyTCPHandler gestirà ogni nuova connessione client.
Sovrascrive il metodo handle() per implementare la logica del server.
"""
def handle(self):
global RUN_FROM_TCPSERVER
RUN_FROM_TCPSERVER = True
# TCPREMOTEIP in Python è self.client_address[0]
client_ip = self.client_address[0]
log_write("socket_server", "info", f"Nuova connessione da: {client_ip}")
try:
# Autenticazione (se configurata)
if TCPSERVER_USER and TCPSERVER_PWD:
# Leggi utente (timeout 3 secondi)
self.request.settimeout(3)
try:
user_line = self.request.recv(1024).decode('utf-8').strip()
password_line = self.request.recv(1024).decode('utf-8').strip()
except socket.timeout:
log_write("socket_server", "warning", f"socket connection from: {client_ip} - Timeout during credentials read")
json_error(0, "Authentication timeout")
return
if user_line != TCPSERVER_USER or password_line != TCPSERVER_PWD:
log_write("socket_server", "warning", f"socket connection from: {client_ip} - Bad socket server credentials - user:{user_line}")
self.request.sendall(f'{{"status": "error", "code": 0, "message": "Bad socket server credentials"}}\n'.encode('utf-8'))
return
else:
log_write("socket_server", "info", f"socket connection from: {client_ip} - Authentication successful")
# Leggi il comando
command_line = self.request.recv(4096).decode('utf-8').strip()
# Parsing del comando
args = command_line.split(' ')
arg1 = args[0] if len(args) > 0 else ""
arg2 = args[1] if len(args) > 1 else ""
arg3 = args[2] if len(args) > 2 else ""
arg4 = args[3] if len(args) > 3 else ""
arg5 = args[4] if len(args) > 4 else ""
arg6 = args[5] if len(args) > 5 else ""
arg7 = args[6] if len(args) > 6 else ""
arg8 = args[7] if len(args) > 7 else ""
log_write("socket_server", "info", f"socket connection from: {client_ip} - command: {command_line}")
# Esegui il comando
response = self.execute_command(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
# Invia la risposta al client (assumendo che le funzioni json_error/json_status stampino su stdout)
# Potresti voler catturare l'output di quelle funzioni e inviarlo qui.
# Per semplicità, qui si presuppone che inviino direttamente al client,
# ma in un sistema reale dovresti costruire la risposta JSON e inviarla.
self.request.sendall(response.encode('utf-8') + b'\n')
except socket.timeout:
log_write("socket_server", "warning", f"socket connection from: {client_ip} - Timeout waiting for command.")
self.request.sendall(f'{{"status": "error", "code": 0, "message": "Timeout waiting for command"}}\n'.encode('utf-8'))
except Exception as e:
log_write("socket_server", "error", f"Errore durante la gestione della connessione da {client_ip}: {e}")
self.request.sendall(f'{{"status": "error", "code": -1, "message": "Internal server error"}}\n'.encode('utf-8'))
finally:
self.request.close() # Chiudi la connessione
RUN_FROM_TCPSERVER = False # Reset per la prossima connessione o per operazioni interne
def execute_command(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8):
"""Esegue il comando ricevuto dal socket server."""
# Questo metodo replicherà la logica della case statement Bash
vret = "" # Per catturare i risultati delle funzioni
if arg1 == "status":
json_status(arg2, arg3, arg4, arg5, arg6, arg7)
elif arg1 == "open":
if not arg2: # "empty$arg2" == "empty"
json_error(0, "Alias solenoid not specified")
else:
ev_open(arg2, arg3) # &> /dev/null è gestito non catturando l'output
json_status(f"get_cron_open_in:{arg2}")
elif arg1 == "open_in":
ev_open_in(arg2, arg3, arg4, arg5)
json_status(f"get_cron_open_in:{arg4}")
elif arg1 == "close":
if not arg2:
json_error(0, "Alias solenoid not specified")
else:
ev_close(arg2)
json_status(f"get_cron_open_in:{arg2}")
elif arg1 == "close_all":
if arg2 == "disable_scheduling":
cron_disable_all_open_close()
close_all()
message_write("success", "All solenoid closed")
json_status()
elif arg1 == "cron_enable_all_open_close":
cron_enable_all_open_close()
message_write("success", "All solenoid enabled")
json_status()
elif arg1 == "set_general_cron":
# Per i comandi di set_general_cron, chiami le funzioni e concateni i risultati
# Ho ipotizzato che i risultati vuoti indichino successo e stringhe non vuote errori
funcs_to_call = {
"set_cron_init": set_cron_init,
"set_cron_start_socket_server": set_cron_start_socket_server,
"set_cron_check_rain_sensor": set_cron_check_rain_sensor,
"set_cron_check_rain_online": set_cron_check_rain_online,
"set_cron_close_all_for_rain": set_cron_close_all_for_rain,
}
# Qui si itera sugli argomenti come nello script Bash
for i_arg in [arg for arg in [arg2, arg3, arg4, arg5, arg6, arg7] if arg]:
if i_arg in funcs_to_call:
ret_val = funcs_to_call[i_arg]()
if ret_val: # Se la funzione restituisce qualcosa (un errore in Bash)
vret += ret_val
if vret: # Se c'è stato qualche errore in una delle chiamate
json_error(0, "Cron set failed")
log_write("socket_server", "error", f"Cron set failed: {vret}")
else:
message_write("success", "Cron set successful")
json_status()
elif arg1 == "del_cron_open":
vret = del_cron_open(arg2)
if vret:
json_error(0, "Cron set failed")
log_write("socket_server", "error", f"Cron del failed: {vret}")
else:
message_write("success", "Cron set successful")
json_status()
elif arg1 == "del_cron_open_in":
vret = del_cron_open_in(arg2)
if vret:
json_error(0, "Cron del failed")
log_write("socket_server", "error", f"Cron del failed: {vret}")
else:
message_write("success", "Scheduled start successfully deleted")
json_status(f"get_cron_open_in:{arg2}")
elif arg1 == "del_cron_close":
vret = del_cron_close(arg2)
if vret:
json_error(0, "Cron set failed")
log_write("socket_server", "error", f"Cron set failed: {vret}")
else:
message_write("success", "Cron set successful")
json_status()
elif arg1 == "add_cron_open":
vret = add_cron_open(arg2, arg3, arg4, arg5, arg6, arg7, arg8)
if vret:
json_error(0, "Cron set failed")
log_write("socket_server", "error", f"Cron set failed: {vret}")
else:
message_write("success", "Cron set successful")
json_status()
elif arg1 == "add_cron_close":
vret = add_cron_close(arg2, arg3, arg4, arg5, arg6, arg7, arg8)
if vret:
json_error(0, "Cron set failed")
log_write("socket_server", "error", f"Cron set failed: {vret}")
else:
message_write("success", "Cron set successful")
json_status()
elif arg1 == "cmd_pigardensched":
vret = cmd_pigardensched(arg2, arg3, arg4, arg5, arg6)
if vret:
json_error(0, "piGardenSched command failed")
log_write("socket_server", "error", f"piGardenSched command failed: {vret}")
else:
message_write("success", "Schedule set successful")
json_status()
elif arg1 == "reboot":
message_write("warning", "System reboot is started")
json_status()
current_script_path = get_script_path()
# Esegui il reboot in un sottoprocesso separato per non bloccare il server
# e con nohup-like behavior.
# Questo è un esempio, la gestione di reboot/poweroff in Python è delicata.
# Potresti voler chiamare un comando di sistema come `sudo reboot`.
subprocess.Popen([current_script_path, "reboot_system_internal_cmd"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
preexec_fn=os.setsid) # setsid per disassociare dal gruppo di processi
elif arg1 == "poweroff":
message_write("warning", "System shutdown is started")
json_status()
current_script_path = get_script_path()
subprocess.Popen([current_script_path, "poweroff_system_internal_cmd"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
preexec_fn=os.setsid) # setsid per disassociare dal gruppo di processi
elif arg1 == "reset_last_rain_sensor_timestamp":
reset_last_rain_sensor_timestamp()
message_write("success", "Timestamp of last sensor rain successful reset")
json_status()
elif arg1 == "reset_last_rain_online_timestamp":
reset_last_rain_online_timestamp()
message_write("success", "Timestamp of last online rain successful reset")
json_status()
elif arg1 == "sensor_status_set":
if not arg2:
json_error(0, "Alias sensor not specified")
else:
sensor_status_set(arg2, arg3, arg4)
json_status()
else:
json_error(0, "invalid command")
# Le funzioni json_error, json_status, message_write stampano direttamente su stdout
# In un contesto di server reale, dovresti catturare il loro output e inviarlo al client.
# Per questa conversione, sto assumendo che tu voglia replicare il comportamento Bash
# di "stampa e poi la pipeline gestisce l'invio". Potrebbe richiedere un refactoring
# delle funzioni json_error/status per restituire stringhe invece di stampare.
# Per ora, restituisco una stringa vuota o un placeholder.
return "" # Potresti voler restituire il JSON o il messaggio qui
# --- FUNZIONI PRINCIPALI DEL SERVER ---
def start_socket_server():
"""Avvia il server socket."""
# Rimuovi il file PID esistente
if os.path.exists(TCPSERVER_PID_FILE):
os.remove(TCPSERVER_PID_FILE)
# Scrivi il PID dello script principale nel file PID
# In un sistema reale, dovresti scrivere il PID del processo del server,
# che potrebbe essere diverso se usi un manager di processi come systemd.
current_pid = os.getpid()
with open(TCPSERVER_PID_FILE, "w") as f:
f.write(str(current_pid))
log_write("socket_server", "info", f"Server PID {current_pid} scritto in {TCPSERVER_PID_FILE}")
try:
# Crea il server TCP
# ThreadingTCPServer gestisce ogni richiesta in un thread separato
with socketserver.ThreadingTCPServer((TCPSERVER_IP, TCPSERVER_PORT), MyTCPHandler) as server:
log_write("socket_server", "info", f"Server socket avviato su {TCPSERVER_IP}:{TCPSERVER_PORT}")
# Avvia il server, rimarrà in ascolto indefinitamente
server.serve_forever()
except Exception as e:
log_write("socket_server", "error", f"Errore all'avvio del server socket: {e}")
if os.path.exists(TCPSERVER_PID_FILE):
os.remove(TCPSERVER_PID_FILE) # Pulisci il PID file in caso di errore
sys.exit(1)
def stop_socket_server():
"""Ferma il server socket."""
if not os.path.exists(TCPSERVER_PID_FILE):
print("Daemon is not running")
sys.exit(1)
log_write("socket_server", "info", "stop socket server")
with open(TCPSERVER_PID_FILE, "r") as f:
try:
pid = int(f.read().strip())
except ValueError:
print(f"Errore: Il file PID '{TCPSERVER_PID_FILE}' contiene un PID non valido.")
sys.exit(1)
# Tentativo di killare i processi discendenti (se list_descendants è implementato)
descendants = list_descendants(pid)
for d_pid in descendants:
try:
os.kill(d_pid, 9) # SIGKILL
log_write("socket_server", "info", f"Terminato processo discendente {d_pid}")
except ProcessLookupError:
pass # Il processo non esiste
# Tenta di killare il processo principale del server
try:
os.kill(pid, 9) # SIGKILL
log_write("socket_server", "info", f"Terminato processo server {pid}")
except ProcessLookupError:
print(f"Processo con PID {pid} non trovato.")
except Exception as e:
log_write("socket_server", "error", f"Errore durante l'uccisione del processo server {pid}: {e}")
# Rimuovi il file PID
if os.path.exists(TCPSERVER_PID_FILE):
os.remove(TCPSERVER_PID_FILE)
log_write("socket_server", "info", "File PID rimosso.")
# --- FUNZIONE PRINCIPALE PER LA GESTIONE DEGLI ARGOMENTI DELLA CLI ---
if __name__ == "__main__":
if len(sys.argv) > 1:
command = sys.argv[1]
if command == "start_socket_server":
# Per avviare in background come un vero daemon, dovresti implementare
# la demonizzazione (forking) qui o usare una libreria come `python-daemon`.
# Per ora, questo lo avvia nel terminale corrente.
log_write("main", "info", "Richiesta di avvio del socket server.")
start_socket_server()
elif command == "stop_socket_server":
log_write("main", "info", "Richiesta di stop del socket server.")
stop_socket_server()
elif command == "socket_server_command":
# Questa parte dovrebbe essere gestita dal server TCP stesso.
# Non viene chiamata direttamente dalla riga di comando in Python in questo modo.
# Se hai bisogno di testare la logica 'socket_server_command' isolatamente,
# dovresti chiamare MyTCPHandler.execute_command() con argomenti di test.
print("Errore: 'socket_server_command' non può essere chiamato direttamente come script.")
print("Questa logica è gestita internamente dal server TCP.")
sys.exit(1)
elif command == "reboot_system_internal_cmd":
# Comando interno per il riavvio effettivo
log_write("main", "warning", "Eseguo il riavvio del sistema...")
# In un sistema Linux, questo è il modo per riavviare
subprocess.run(["sudo", "reboot"])
# Assicurati che l'utente che esegue lo script abbia i permessi sudo senza password per 'reboot'
elif command == "poweroff_system_internal_cmd":
# Comando interno per lo spegnimento effettivo
log_write("main", "warning", "Eseguo lo spegnimento del sistema...")
# In un sistema Linux, questo è il modo per spegnere
subprocess.run(["sudo", "poweroff"])
# Assicurati che l'utente che esegue lo script abbia i permessi sudo senza password per 'poweroff'
else:
print(f"Comando non riconosciuto: {command}")
print("Utilizzo: python your_script_name.py [start_socket_server|stop_socket_server]")
sys.exit(1)
else:
print("Nessun comando specificato.")
print("Utilizzo: python your_script_name.py [start_socket_server|stop_socket_server]")
sys.exit(1)

View File

@@ -4,7 +4,7 @@ Bash script to manage an irrigation system built with a Raspberry Pi
## Official documentation
Documentation of piGarden and build system irrigation with Raspberry Pi can be found on the [www.lejubila.net/tag/pigarden/](http://www.lejubila.net/tag/pigarden/)
Documentation of piGarden and build system irrigation with Raspberry Pi can be found on the [www.lejubila.net/pigarden/](http://www.lejubila.net/pigarden/)
## License
@@ -41,8 +41,8 @@ sudo make install
``` bash
cd
git clone git://git.drogon.net/wiringPi
cd wiringPi
git clone https://github.com/WiringPi/WiringPi.git
cd WiringPi
git pull origin
./build
```
@@ -66,5 +66,3 @@ sudo cp piGarden/conf/piGarden.conf.example /etc/piGarden.conf
Customize the configuration file.
For more information see
[www.lejubila.net/2015/12/impianto-di-irrigazione-con-raspberry-pi-pigarden-lo-script-di-gestione-quinta-parte/](https://www.lejubila.net/2015/12/impianto-di-irrigazione-con-raspberry-pi-pigarden-lo-script-di-gestione-quinta-parte/)
and
[www.lejubila.net/2017/04/pigarden-0-2-easter-egg/](https://www.lejubila.net/2017/04/pigarden-0-2-easter-egg/)

View File

@@ -2,6 +2,14 @@
LOG_FILE="/home/pi/piGarden/log/piGarden.log"
LOG_FILE_MAX_SIZE=1048576 # 1MB
#
# Enable send log to piGardenWeb
#
#LOG_URL="http://url_of_your_pigardenweb/api/log"
#LOG_API_TOKEN="token_of_pigardenweb_user"
#LOG_CURL_PARAM="--data-urlencode -k"
# Log file for driver output
LOG_OUTPUT_DRV_FILE="/tmp/piGarden.drv.log"
@@ -53,8 +61,16 @@ READLINK="/bin/readlink"
# Percorso stat
STAT="/usr/bin/stat"
# Percorso mosquito_pub
# # Installare con apt install mosquitto-clients
MOSQUITTO_PUB="/usr/bin/mosquitto_pub"
# Percorso di piGardenSched, se non definito non verranno gestire le schedulazioni di piGardenSched tramite api
#PIGARDENSCHED_PATH="/home/pi/piGardenSched/piGardenSched.sh"
# Se impostato con il valore 1, indica che il sistema gestisce elettrovalvole monostabili,
# se impostato a 0 il sistema gestirà elettrovalvole bisstabili
# se impostato a 0 il sistema gestirà elettrovalvole bistabili
EV_MONOSTABLE=0
# Id gpio usati per simulare il doppio deviatore con cui eseguire l'alimentazione alle elettrovalvole
@@ -90,6 +106,10 @@ EV_TOTAL=6
EV1_ALIAS="1" #
EV1_GPIO=17 # Physical 11 - wPi 0
#EV1_NORAIN=1 # Non interrompe l'irrigazione di questa zona in caso di pioggia
#EV1_MONOSTABLE=1 # L'elettrovalvola è monostabile
#EV1_SENSOR_ALIAS=Mi_Flora # Nome del sensore (definito in SENSORx_ALIAS) per stabilire l'umidità del terreno
#EV1_SENSOR_MOISTURE=50 # Percentule di umidità ottimale
#EV1_SENSOR_MOISTURE_AUTOCLOSE=1 # Chiude automaticamente l'elettrovalvola quando supera l'umidità impostata in EVx_MONOSTABLE
EV2_ALIAS="2" #
EV2_GPIO=27 # Physical 13 - wPi 2
@@ -106,9 +126,31 @@ EV5_GPIO=23 # Physical 16 - wPi 4
EV6_ALIAS="6" #
EV6_GPIO=24 # Physical 18 - wPi 5
# Definisce l'api key e il luogo per recuperare lo stato meteo online
# Numero di sensori
SENSOR_TOTAL=1
# Definizione sensori
SENSOR1_ALIAS=Mi_Flora
# Definisci il servizio online da utilizzare per il controllo delle condizioni meteo, puoi scegliere openweathermap oppure wunderground.
# Se non vuoi configurare nessun servizio imposta il vale "none"
WEATHER_SERVICE="openweathermap"
#WEATHER_SERVICE="none" # Nessun servizio configurato
# Parametri di openweathermap, definisce l'api key e il luogo per recuperare lo stato meteo online
OPENWEATHERMAP_KEY=""
OPENWEATHERMAP_LOCATION="q=Pieve%20a%20Nievole,it" # https://openweathermap.org/current
OPENWEATHERMAP_TZ="Europe/Rome" # Time zone
# Parametri di wunderground, definisce l'api key e il luogo per recuperare lo stato meteo online
WUNDERGROUND_KEY=""
WUNDERGROUND_LOCATION="IY/Monsummano" # http://www.wunderground.com/weather/api/d/docs?d=resources/country-to-iso-matching&MR=1
WUNDERGROUND_LOCATION="IT/Pieve%20a%20Nievole" # http://www.wunderground.com/weather/api/d/docs?d=resources/country-to-iso-matching&MR=1
# Blocca l'irrigazione se l'ultima pioggia rilevata online è avvenuta nell'ultima quantità di tempo inserita.
# Il tempo è espresso in secondi. Quindi inserendo 86400, se nelle ultime 24 ore ha piovuto viene bloccata l'irrigazione. Inserendo il valore zero non viene eseguito nessun controllo.
@@ -128,3 +170,12 @@ TCPSERVER_PWD=""
# Con impostato il valore 1 non invia l'identificativi per statistiche di utilizzo
NO_SEND_IDENTIFIER=0
# Parametri connessione al broker mqtt
MQTT_ENABLE=0 # set 1 to enable
MQTT_HOST=you_host_mqtt
MQTT_PORT=1883
MQTT_USER=your_user_mqtt
MQTT_PWD=yout_password_mqtt
MQTT_TOPIC="pigarden/result"
MQTT_CLIENT_ID=piGarden

View File

@@ -0,0 +1,174 @@
# Log file
LOG_FILE="/home/pi/piGarden/log/piGarden.log"
LOG_FILE_MAX_SIZE=1048576 # 1MB
#
# Enable send log to piGardenWeb
#
#LOG_URL="http://url_of_your_pigardenweb/api/log"
#LOG_API_TOKEN="token_of_pigardenweb_user"
#LOG_CURL_PARAM="--data-urlencode -k"
# Log file for driver output
LOG_OUTPUT_DRV_FILE="/tmp/piGarden.drv.log"
# Status directory
STATUS_DIR="/home/pi/piGarden/state"
# Event directory
EVENT_DIR="/home/pi/piGarden/events"
# Posizione gpio
GPIO="/usr/local/bin/gpio"
# Posizione js
JQ="/usr/local/bin/jq"
# Percorso curl
CURL="/usr/bin/curl"
# Percorso wc
WC="/usr/bin/wc"
# Percorso gzip
GZIP="/bin/gzip"
# Percorso mv
MV="/bin/mv"
# Percorso di tr
TR="/usr/bin/tr"
# Percorso di cut
CUT="/usr/bin/cut"
# Percorso tcpserver
TCPSERVER="/usr/bin/tcpserver"
# Percorso cron
CRONTAB="/usr/bin/crontab"
# Percorso grep
GREP="/bin/grep"
# Percorsp sed
SED="/bin/sed"
# Percorso readlink
READLINK="/bin/readlink"
# Percorso stat
STAT="/usr/bin/stat"
# Percorso mosquito_pub
# # Installare con apt install mosquitto-clients
MOSQUITTO_PUB="/usr/bin/mosquitto_pub"
# Percorso di piGardenSched, se non definito non verranno gestire le schedulazioni di piGardenSched tramite api
#PIGARDENSCHED_PATH="/home/pi/piGardenSched/piGardenSched.sh"
# Se impostato con il valore 1, indica che il sistema gestisce elettrovalvole monostabili,
# se impostato a 0 il sistema gestirà elettrovalvole bistabili
EV_MONOSTABLE=0
# Id gpio usati per simulare il doppio deviatore con cui eseguire l'alimentazione alle elettrovalvole
SUPPLY_GPIO_1=2 # Physical 3 - wPi 8
SUPPLY_GPIO_2=3 # Physical 5 - wPi 9
# Stato dei due gpio per impartire l'alimentazione positiva alle elettrovalvole (aperta)
SUPPLY_GPIO_POS=0
# Stato dei due gpio per impartire l'alimentazione negativa alle elettrovalvole (chiusa)
SUPPLY_GPIO_NEG=1
# Stato di ingresso da assegnare al gpio per chiudere il rele
RELE_GPIO_CLOSE=0
# Stato di ingresso da assegnare al gpio per aprire il rele
RELE_GPIO_OPEN=1
# Id del gpio usato per collegare il sensore di rilevamento pioggia
# attraverso il driver rainsensorqty
RAIN_GPIO="drv:rainsensorqty:25" # Physical 22 - wPi 6
# Valore in ingresso sul gpio definito in RAIN_GPIO che indica lo stato di pioggia
# variabile usata anche da drv:rainsensorqty, se e' a 1, significa che il reed contact e' collegato ad un circuito pull-down, vuol dire che attende l'impulso a 1 per contare le vaschette, normalmente e' a 0.
# se e' a 0, significa che il reed contact e' collegato ad un circuito pull-up, vuol dire che attende l'impulso a 0 per contare le vaschette, normalmente e' a 0.
RAIN_GPIO_STATE=0
RAINSENSORQTY_LOOPSFORSETRAINING=16 # dopo 16 impulsi, 16 vaschette riempite si considera pioggia
RAINSENSORQTY_SECSBETWEENRAINEVENT=10800 # =3h, significa che dopo 3 ore si resetta il numero di vaschette riempire e solo dopo un nuovo ciclo di riempimento si considera una nuova pioggia
RAINSENSORQTY_MMEACH=0.33 # see RAINSENSORQTY driver readme for details
# Numero totale di elettrovalvole
EV_TOTAL=6
# Definizione delle elettrovalvole
EV1_ALIAS="1" #
EV1_GPIO=17 # Physical 11 - wPi 0
#EV1_NORAIN=1 # Non interrompe l'irrigazione di questa zona in caso di pioggia
#EV1_MONOSTABLE=1 # L'elettrovalvola è monostabile
EV2_ALIAS="2" #
EV2_GPIO=27 # Physical 13 - wPi 2
EV3_ALIAS="3" #
EV3_GPIO=22 # Physical 15 - wPi 3
EV4_ALIAS="4" #
EV4_GPIO=18 # Physical 12 - wPi 1
EV5_ALIAS="5" #
EV5_GPIO=23 # Physical 16 - wPi 4
EV6_ALIAS="6" #
EV6_GPIO=24 # Physical 18 - wPi 5
# Definisci il servizio online da utilizzare per il controllo delle condizioni meteo, puoi scegliere openweathermap oppure wunderground.
# Se non vuoi configurare nessun servizio imposta il vale "none"
WEATHER_SERVICE="openweathermap"
#WEATHER_SERVICE="none" # Nessun servizio configurato
# Parametri di openweathermap, definisce l'api key e il luogo per recuperare lo stato meteo online
OPENWEATHERMAP_KEY=""
OPENWEATHERMAP_LOCATION="q=Pieve%20a%20Nievole,it" # https://openweathermap.org/current
OPENWEATHERMAP_TZ="Europe/Rome" # Time zone
# Parametri di wunderground, definisce l'api key e il luogo per recuperare lo stato meteo online
WUNDERGROUND_KEY=""
WUNDERGROUND_LOCATION="IT/Pieve%20a%20Nievole" # http://www.wunderground.com/weather/api/d/docs?d=resources/country-to-iso-matching&MR=1
# Blocca l'irrigazione se l'ultima pioggia rilevata online è avvenuta nell'ultima quantità di tempo inserita.
# Il tempo è espresso in secondi. Quindi inserendo 86400, se nelle ultime 24 ore ha piovuto viene bloccata l'irrigazione. Inserendo il valore zero non viene eseguito nessun controllo.
NOT_IRRIGATE_IF_RAIN_ONLINE=86400
# Il parametro è simile a quello precedente, il controllo però anziché essere fatto attingendo a wunderground, viene eseguito direttamente sul sensore se installato. Inserendo il valore zero non viene eseguito nessun controllo.
NOT_IRRIGATE_IF_RAIN_SENSOR=86400
# Indirizzo ip e porta di ascolto del socket server
TCPSERVER_IP="127.0.0.1"
TCPSERVER_PORT="8084"
# Utente e password che i clients devono utilizzare per stabilire una connessione tramite socket server
TCPSERVER_USER=""
TCPSERVER_PWD=""
# Con impostato il valore 1 non invia l'identificativi per statistiche di utilizzo
NO_SEND_IDENTIFIER=0
# Parametri connessione al broker mqtt
MQTT_ENABLE=0 # set 1 to enable
MQTT_HOST=you_host_mqtt
MQTT_PORT=1883
MQTT_USER=your_user_mqtt
MQTT_PWD=yout_password_mqtt
MQTT_TOPIC="pigarden/result"
MQTT_CLIENT_ID=piGarden

View File

@@ -0,0 +1,177 @@
# Log file
LOG_FILE="/home/pi/piGarden/log/piGarden.log"
LOG_FILE_MAX_SIZE=1048576 # 1MB
#
# Enable send log to piGardenWeb
#
#LOG_URL="http://url_of_your_pigardenweb/api/log"
#LOG_API_TOKEN="token_of_pigardenweb_user"
#LOG_CURL_PARAM="--data-urlencode -k"
# Log file for driver output
#LOG_OUTPUT_DRV_FILE="/tmp/piGarden.drv.log"
# Status directory
STATUS_DIR="/home/pi/piGarden/state"
# Event directory
EVENT_DIR="/home/pi/piGarden/events"
# Posizione gpio
GPIO="/usr/local/bin/gpio"
# Posizione js
JQ="/usr/local/bin/jq"
# Percorso curl
CURL="/usr/bin/curl"
# Percorso wc
WC="/usr/bin/wc"
# Percorso gzip
GZIP="/bin/gzip"
# Percorso mv
MV="/bin/mv"
# Percorso di tr
TR="/usr/bin/tr"
# Percorso di cut
CUT="/usr/bin/cut"
# Percorso tcpserver
TCPSERVER="/usr/bin/tcpserver"
# Percorso cron
CRONTAB="/usr/bin/crontab"
# Percorso grep
GREP="/bin/grep"
# Percorsp sed
SED="/bin/sed"
# Percorso readlink
READLINK="/bin/readlink"
# Percorso stat
STAT="/usr/bin/stat"
# Percorso mosquito_pub
# Installare con apt install mosquitto-clients
MOSQUITTO_PUB="/usr/bin/mosquitto_pub"
# Percorso di piGardenSched, se non definito non verranno gestire le schedulazioni di piGardenSched tramite api
#PIGARDENSCHED_PATH="/home/pi/piGardenSched/piGardenSched.sh"
# Se impostato con il valore 1, indica che il sistema gestisce elettrovalvole monostabili,
# se impostato a 0 il sistema gestirà elettrovalvole bisstabili
EV_MONOSTABLE=0
# Id gpio usati per simulare il doppio deviatore con cui eseguire l'alimentazione alle elettrovalvole
SUPPLY_GPIO_1=2
SUPPLY_GPIO_2=3
# Stato dei due gpio per impartire l'alimentazione positiva alle elettrovalvole (aperta)
SUPPLY_GPIO_POS=0
# Stato dei due gpio per impartire l'alimentazione negativa alle elettrovalvole (chiusa)
SUPPLY_GPIO_NEG=1
# Stato di ingresso da assegnare al gpio per chiudere il rele
RELE_GPIO_CLOSE=0
# Stato di ingresso da assegnare al gpio per aprire il rele
RELE_GPIO_OPEN=1
# Id del gpio usato per collegare il sensore di rilevamento pioggia
RAIN_GPIO=25 # Physical 22 - wPi 6
# Valore in ingresso sul gpio definito in RAIN_GPIO che indica lo stato di pioggia
RAIN_GPIO_STATE=0
# Numero totale di elettrovalvole
EV_TOTAL=4
# Definizione delle elettrovalvole
EV1_ALIAS="Giardino_Posteriore_DX" #
EV1_GPIO="drv:remote:PIREMOTE1:Giardino_Posteriore_DX"
#EV1_NORAIN=1 # Non interrompe l'irrigazione di questa zona in caso di pioggia
EV2_ALIAS="Giardino_Posteriore_CN" #
EV2_GPIO="drv:remote:PIREMOTE2:Giardino_Posteriore_CN"
EV3_ALIAS="Giardino_Posteriore_SX" #
EV3_GPIO="drv:remote:PIREMOTE2:Giardino_Posteriore_SX"
EV4_ALIAS="Giardino_Posteriore_GPIO" #
EV4_GPIO="18"
# Definisci il servizio online da utilizzare per il controllo delle condizioni meteo, puoi scegliere openweathermap oppure wunderground.
# Se non vuoi configurare nessun servizio imposta il vale "none"
WEATHER_SERVICE="openweathermap"
#WEATHER_SERVICE="none" # Nessun servizio configurato
# Parametri di openweathermap, definisce l'api key e il luogo per recuperare lo stato meteo online
OPENWEATHERMAP_KEY=""
OPENWEATHERMAP_LOCATION="q=Pieve%20a%20Nievole,it" # https://openweathermap.org/current
OPENWEATHERMAP_TZ="Europe/Rome" # Time zone
# Parametri di wunderground, definisce l'api key e il luogo per recuperare lo stato meteo online
WUNDERGROUND_KEY=""
WUNDERGROUND_LOCATION="IT/Pieve%20a%20Nievole" # http://www.wunderground.com/weather/api/d/docs?d=resources/country-to-iso-matching&MR=1
# Blocca l'irrigazione se l'ultima pioggia rilevata online è avvenuta nell'ultima quantità di tempo inserita.
# Il tempo è espresso in secondi. Quindi inserendo 86400, se nelle ultime 24 ore ha piovuto viene bloccata l'irrigazione. Inserendo il valore zero non viene eseguito nessun controllo.
NOT_IRRIGATE_IF_RAIN_ONLINE=86400
# Il parametro è simile a quello precedente, il controllo però anziché essere fatto attingendo a wunderground, viene eseguito direttamente sul sensore se installato. Inserendo il valore zero non viene eseguito nessun controllo.
NOT_IRRIGATE_IF_RAIN_SENSOR=86400
# Indirizzo ip e porta di ascolto del socket server
TCPSERVER_IP="127.0.0.1"
TCPSERVER_PORT="8084"
# Utente e password che i clients devono utilizzare per stabilire una connessione tramite socket server
TCPSERVER_USER=""
TCPSERVER_PWD=""
# Con impostato il valore 1 non invia l'identificativi per statistiche di utilizzo
NO_SEND_IDENTIFIER=0
# Parametri connessione al broker mqtt
MQTT_ENABLE=0 # set 1 to enable
MQTT_HOST=you_host_mqtt
MQTT_PORT=1883
MQTT_USER=your_user_mqtt
MQTT_PWD=yout_password_mqtt
MQTT_TOPIC="pigarden/result"
MQTT_CLIENT_ID=piGarden
#
# Configurazione piGarden remoti
#
PIREMOTE1_IP="192.168.1.51"
PIREMOTE1_PORT="8084"
PIREMOTE1_USER=""
PIREMOTE1_PWD=""
PIREMOTE2_IP="192.168.1.52"
PIREMOTE2_PORT="8084"
PIREMOTE2_USER=""
PIREMOTE2_PWD=""

View File

@@ -0,0 +1,177 @@
# Log file
LOG_FILE="/home/pi/piGarden/log/piGarden.log"
LOG_FILE_MAX_SIZE=1048576 # 1MB
#
# Enable send log to piGardenWeb
#
#LOG_URL="http://url_of_your_pigardenweb/api/log"
#LOG_API_TOKEN="token_of_pigardenweb_user"
#LOG_CURL_PARAM="--data-urlencode -k"
# Log file for driver output
#LOG_OUTPUT_DRV_FILE="/tmp/piGarden.drv.log"
# Status directory
STATUS_DIR="/home/pi/piGarden/state"
# Event directory
EVENT_DIR="/home/pi/piGarden/events"
# Posizione gpio
GPIO="/usr/local/bin/gpio"
# Posizione js
JQ="/usr/local/bin/jq"
# Percorso curl
CURL="/usr/bin/curl"
# Percorso wc
WC="/usr/bin/wc"
# Percorso gzip
GZIP="/bin/gzip"
# Percorso mv
MV="/bin/mv"
# Percorso di tr
TR="/usr/bin/tr"
# Percorso di cut
CUT="/usr/bin/cut"
# Percorso tcpserver
TCPSERVER="/usr/bin/tcpserver"
# Percorso cron
CRONTAB="/usr/bin/crontab"
# Percorso grep
GREP="/bin/grep"
# Percorsp sed
SED="/bin/sed"
# Percorso readlink
READLINK="/bin/readlink"
# Percorso stat
STAT="/usr/bin/stat"
# Percorso mosquito_pub
# # Installare con apt install mosquitto-clients
MOSQUITTO_PUB="/usr/bin/mosquitto_pub"
# Percorso di piGardenSched, se non definito non verranno gestire le schedulazioni di piGardenSched tramite api
#PIGARDENSCHED_PATH="/home/pi/piGardenSched/piGardenSched.sh"
# Se impostato con il valore 1, indica che il sistema gestisce elettrovalvole monostabili,
# se impostato a 0 il sistema gestirà elettrovalvole bisstabili
EV_MONOSTABLE=0
# Id gpio usati per simulare il doppio deviatore con cui eseguire l'alimentazione alle elettrovalvole
SUPPLY_GPIO_1=2
SUPPLY_GPIO_2=3
# Stato dei due gpio per impartire l'alimentazione positiva alle elettrovalvole (aperta)
SUPPLY_GPIO_POS=0
# Stato dei due gpio per impartire l'alimentazione negativa alle elettrovalvole (chiusa)
SUPPLY_GPIO_NEG=1
# Stato di ingresso da assegnare al gpio per chiudere il rele
RELE_GPIO_CLOSE=0
# Stato di ingresso da assegnare al gpio per aprire il rele
RELE_GPIO_OPEN=1
# Id del gpio usato per collegare il sensore di rilevamento pioggia
RAIN_GPIO=25 # Physical 22 - wPi 6
# Valore in ingresso sul gpio definito in RAIN_GPIO che indica lo stato di pioggia
RAIN_GPIO_STATE=0
# Numero totale di elettrovalvole
EV_TOTAL=4
# Definizione delle elettrovalvole
EV1_ALIAS="Giardino_Posteriore_DX"
EV1_GPIO="drv:sonoff_tasmota_http:SONOFF1:Power1"
EV1_MONOSTABLE=1
EV2_ALIAS="Giardino_Posteriore_CN"
EV2_GPIO="drv:sonoff_tasmota_http:SONOFF1:Power2"
EV2_MONOSTABLE=1
EV3_ALIAS="Giardino_Posteriore_SX"
EV3_GPIO="drv:sonoff_tasmota_http:SONOFF1:Power3"
EV3_MONOSTABLE=1
EV4_ALIAS="Giardino_Anteriore"
EV4_GPIO="drv:sonoff_tasmota_http:SONOFF2:Power1"
# Definisci il servizio online da utilizzare per il controllo delle condizioni meteo, puoi scegliere openweathermap oppure wunderground.
# Se non vuoi configurare nessun servizio imposta il vale "none"
WEATHER_SERVICE="openweathermap"
#WEATHER_SERVICE="none" # Nessun servizio configurato
# Parametri di openweathermap, definisce l'api key e il luogo per recuperare lo stato meteo online
OPENWEATHERMAP_KEY=""
OPENWEATHERMAP_LOCATION="q=Pieve%20a%20Nievole,it" # https://openweathermap.org/current
OPENWEATHERMAP_TZ="Europe/Rome" # Time zone
# Parametri di wunderground, definisce l'api key e il luogo per recuperare lo stato meteo online
WUNDERGROUND_KEY=""
WUNDERGROUND_LOCATION="IT/Pieve%20a%20Nievole" # http://www.wunderground.com/weather/api/d/docs?d=resources/country-to-iso-matching&MR=1
# Blocca l'irrigazione se l'ultima pioggia rilevata online è avvenuta nell'ultima quantità di tempo inserita.
# Il tempo è espresso in secondi. Quindi inserendo 86400, se nelle ultime 24 ore ha piovuto viene bloccata l'irrigazione. Inserendo il valore zero non viene eseguito nessun controllo.
NOT_IRRIGATE_IF_RAIN_ONLINE=86400
# Il parametro è simile a quello precedente, il controllo però anziché essere fatto attingendo a wunderground, viene eseguito direttamente sul sensore se installato. Inserendo il valore zero non viene eseguito nessun controllo.
NOT_IRRIGATE_IF_RAIN_SENSOR=86400
# Indirizzo ip e porta di ascolto del socket server
TCPSERVER_IP="127.0.0.1"
TCPSERVER_PORT="8084"
# Utente e password che i clients devono utilizzare per stabilire una connessione tramite socket server
TCPSERVER_USER=""
TCPSERVER_PWD=""
# Con impostato il valore 1 non invia l'identificativi per statistiche di utilizzo
NO_SEND_IDENTIFIER=0
# Parametri connessione al broker mqtt
MQTT_ENABLE=0 # set 1 to enable
MQTT_HOST=you_host_mqtt
MQTT_PORT=1883
MQTT_USER=your_user_mqtt
MQTT_PWD=yout_password_mqtt
MQTT_TOPIC="pigarden/result"
MQTT_CLIENT_ID=piGarden
#
# Configurazione moduli sonoff
#
SONOFF1_IP="192.168.1.1"
SONOFF1_USER="user"
SONOFF1_PWD="pwd"
SONOFF2_IP="192.168.1.21"
SONOFF2_USER=""
SONOFF2_PWD=""

View File

@@ -2,6 +2,14 @@
LOG_FILE="/home/pi/piGarden/log/piGarden.log"
LOG_FILE_MAX_SIZE=1048576 # 1MB
#
# Enable send log to piGardenWeb
#
#LOG_URL="http://url_of_your_pigardenweb/api/log"
#LOG_API_TOKEN="token_of_pigardenweb_user"
#LOG_CURL_PARAM="--data-urlencode -k"
# Log file for driver output
#LOG_OUTPUT_DRV_FILE="/tmp/piGarden.drv.log"
@@ -53,6 +61,14 @@ READLINK="/bin/readlink"
# Percorso stat
STAT="/usr/bin/stat"
# Percorso mosquito_pub
# Installare con apt install mosquitto-clients
MOSQUITTO_PUB="/usr/bin/mosquitto_pub"
# Percorso di piGardenSched, se non definito non verranno gestire le schedulazioni di piGardenSched tramite api
#PIGARDENSCHED_PATH="/home/pi/piGardenSched/piGardenSched.sh"
# Se impostato con il valore 1, indica che il sistema gestisce elettrovalvole monostabili,
# se impostato a 0 il sistema gestirà elettrovalvole bisstabili
EV_MONOSTABLE=1
@@ -472,9 +488,20 @@ EV128_ALIAS="Zona_128" #
EV128_GPIO="drv:spb16ch:128"
# Definisce l'api key e il luogo per recuperare lo stato meteo online
# Definisci il servizio online da utilizzare per il controllo delle condizioni meteo, puoi scegliere openweathermap oppure wunderground.
# Se non vuoi configurare nessun servizio imposta il vale "none"
WEATHER_SERVICE="openweathermap"
#WEATHER_SERVICE="none" # Nessun servizio configurato
# Parametri di openweathermap, definisce l'api key e il luogo per recuperare lo stato meteo online
OPENWEATHERMAP_KEY=""
OPENWEATHERMAP_LOCATION="q=Pieve%20a%20Nievole,it" # https://openweathermap.org/current
OPENWEATHERMAP_TZ="Europe/Rome" # Time zone
# Parametri di wunderground, definisce l'api key e il luogo per recuperare lo stato meteo online
WUNDERGROUND_KEY=""
WUNDERGROUND_LOCATION="IY/Monsummano" # http://www.wunderground.com/weather/api/d/docs?d=resources/country-to-iso-matching&MR=1
WUNDERGROUND_LOCATION="IT/Pieve%20a%20Nievole" # http://www.wunderground.com/weather/api/d/docs?d=resources/country-to-iso-matching&MR=1
# Blocca l'irrigazione se l'ultima pioggia rilevata online è avvenuta nell'ultima quantità di tempo inserita.
# Il tempo è espresso in secondi. Quindi inserendo 86400, se nelle ultime 24 ore ha piovuto viene bloccata l'irrigazione. Inserendo il valore zero non viene eseguito nessun controllo.
@@ -494,6 +521,15 @@ TCPSERVER_PWD=""
# Con impostato il valore 1 non invia l'identificativi per statistiche di utilizzo
NO_SEND_IDENTIFIER=0
# Parametri connessione al broker mqtt
MQTT_ENABLE=0 # set 1 to enable
MQTT_HOST=you_host_mqtt
MQTT_PORT=1883
MQTT_USER=your_user_mqtt
MQTT_PWD=yout_password_mqtt
MQTT_TOPIC="pigarden/result"
MQTT_CLIENT_ID=piGarden
#
# Configurazione schede spb16ch

View File

@@ -0,0 +1,13 @@
# Driver for check wather condition with openweathermap online service
# Example of configuration in piGarden.conf
```bash
WEATHER_SERVICE="openweathermap"
OPENWEATHERMAP_KEY="d4d22ea805788420267971135563b4cc" # Set with your api key of openweathermap
OPENWEATHERMAP_LOCATION="q=Pieve%20a%20Nievole,it" # Set with location identify, see https://openweathermap.org/current
OPENWEATHERMAP_TZ="Europe/Rome" # Set with your time zone
```

View File

@@ -0,0 +1,123 @@
#
# Funzioni comuni utilizzate dal driver
#
#
# Recupera la descrizione della condizione meteo
#
# $1 condizione meteo recuperata dalle api
#
function drv_openweathermap_get_wather {
declare -A w
w["thunderstorm with light rain"]="Thunderstorms and Rain"
w["thunderstorm with rain"]="Thunderstorms and Rain"
w["thunderstorm with heavy rain"]="Thunderstorms and Rain"
w["light thunderstorm"]="Thunderstorm"
w["thunderstorm"]="Thunderstorm"
w["heavy thunderstorm"]="Thunderstorm"
w["ragged thunderstorm"]="Thunderstorm"
w["thunderstorm with light drizzle"]="Thunderstorms and Rain"
w["thunderstorm with drizzle"]="Thunderstorms and Rain"
w["thunderstorm with heavy drizzle"]="Thunderstorms and Rain"
w["light intensity drizzle"]="Drizzle"
w["drizzle"]="Drizzle"
w["heavy intensity drizzle"]="Drizzle"
w["light intensity drizzle rain"]="Drizzle"
w["drizzle rain"]="Drizzle"
w["heavy intensity drizzle rain"]="Drizzle"
w["shower rain and drizzle"]="Drizzle"
w["heavy shower rain and drizzle"]="Drizzle"
w["shower drizzle"]="Drizzle"
w["light rain"]="Rain Mist"
w["moderate rain"]="Rain"
w["heavy intensity rain"]="Rain"
w["very heavy rain"]="Rain"
w["extreme rain"]="Rain"
w["freezing rain"]="Freezing Rain"
w["light intensity shower rain"]="Rain"
w["shower rain"]="Rain"
w["heavy intensity shower rain"]="Rain"
w["ragged shower rain"]="Rain"
w["light snow"]="Snow"
w["snow"]="Snow"
w["heavy snow"]=""
w["sleet"]="Snow Grains"
w["shower sleet"]="Snow Grains"
w["light rain and snow"]="Snow"
w["rain and snow"]="Snow"
w["light shower snow"]="Snow"
w["shower snow"]="Snow"
w["heavy shower snow"]="Snow"
w["mist"]="Mist"
w["smoke"]="Smoke"
w["haze"]="Haze"
w["sand, dust whirls"]="Dust Whirls"
w["fog"]="Fog"
w["sand"]="Sand"
w["dust"]="Widespread Dust"
w["volcanic ash"]="Volcanic Ash"
w["squalls"]="Squalls"
w["tornado"]="Tornado"
w["clear sky"]="Clear"
w["few clouds"]="Partly Cloudy"
w["scattered clouds"]="Scattered Clouds"
w["broken clouds"]="Partly Cloudy"
w["overcast clouds"]="Mostly Cloudy"
local weather=${w[$1]}
if [ -z "$weather" ]; then
weather="$1"
fi
echo $weather
}
#
# Recupera la l'icona rappresentativa delle condizione meteo
#
# $1 nome dell'icona recuperato delle api weather.icon
#
function drv_openweathermap_get_ico {
declare -A w
w["01d"]="http://www.wunderground.com/static/i/c/k/clear.gif"
w["01n"]="http://www.wunderground.com/static/i/c/k/nt_clear.gif"
w["02d"]="http://www.wunderground.com/static/i/c/k/partlycloudy.gif"
w["02n"]="http://www.wunderground.com/static/i/c/k/nt_partlycloudy.gif"
w["03d"]="http://www.wunderground.com/static/i/c/k/cloudy.gif"
w["03n"]="http://www.wunderground.com/static/i/c/k/nt_cloudy.gif"
w["04d"]="http://www.wunderground.com/static/i/c/k/cloudy.gif"
w["04n"]="http://www.wunderground.com/static/i/c/k/nt_cloudy.gif"
w["09d"]="http://www.wunderground.com/static/i/c/k/sleet.gif"
w["09n"]="http://www.wunderground.com/static/i/c/k/nt_sleet.gif"
w["10d"]="http://www.wunderground.com/static/i/c/k/rain.gif"
w["10n"]="http://www.wunderground.com/static/i/c/k/nt_rain.gif"
w["11d"]="http://www.wunderground.com/static/i/c/k/tstorms.gif"
w["11n"]="http://www.wunderground.com/static/i/c/k/nt_tstorms.gif"
w["13d"]="http://www.wunderground.com/static/i/c/k/snow.gif"
w["13n"]="http://www.wunderground.com/static/i/c/k/nt_snow.gif"
w["50d"]="http://www.wunderground.com/static/i/c/k/fog.gif"
w["50n"]="http://www.wunderground.com/static/i/c/k/nt_fog.gif"
local ico=${w[$1]}
if [ -z "$ico" ]; then
ico="$1"
fi
echo $ico
}

View File

@@ -0,0 +1,74 @@
#
# Ritorna lo stato delle condizioni meteo interrogando il servizio online
#
# $i identificativo gpio del sensore di pioggia
#
# return output: 0 - errore durante il recupero delle condizioni meteo
# >0 - rilevato pioggia, timestamp del rilevamento
# <0 - rilevato nessuna pioggia, timestamp del rilevamento
function drv_openweathermap_rain_online_get {
# http://www.wunderground.com/weather/api/d/docs?d=resources/phrase-glossary&MR=1
$CURL "http://api.openweathermap.org/data/2.5/weather?$OPENWEATHERMAP_LOCATION&units=metric&appid=$OPENWEATHERMAP_KEY" > $TMP_PATH/check_rain_online.openweathermap.json
local weather=`cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -M ".weather[0].main"`
local wind_deg=$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".wind.deg")
local wind_speed=$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".wind.speed")
if [ "$wind_speed" == "null" ]; then
#wind_speed=$($JQ -n $wind_speed*3600/1000)
wind_speed=0
fi
local weather="$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".weather[0].description")"
local ico=$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".weather[0].icon")
weather=$(drv_openweathermap_get_wather "$weather")
ico=$(drv_openweathermap_get_ico "$ico")
local current_observation=$(cat <<EOF
{
"display_location": {
"city": "$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".name")"
},
"observation_epoch": "$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".dt")",
"local_epoch": "$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".dt")",
"local_tz_long": "$OPENWEATHERMAP_TZ",
"weather": "$weather",
"temp_c": "$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".main.temp")",
"relative_humidity": "$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".main.humidity")%",
"wind_dir": "$(deg2dir $wind_deg)",
"wind_degrees": "$wind_deg",
"wind_kph": "$wind_speed",
"wind_gust_kph": "--",
"pressure_mb": "$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".main.pressure")",
"dewpoint_c": "--",
"feelslike_c": "--",
"icon_url": "$ico"
}
EOF
)
# "icon_url": "http://openweathermap.org/img/w/$(cat $TMP_PATH/check_rain_online.openweathermap.json | $JQ -r -M ".weather[0].icon").png"
#local current_observation=`cat $TMP_PATH/check_rain_online.json | $JQ -M ".current_observation"`
if [ "$weather" = "null" ]; then
echo "0"
else
echo "$current_observation" > "$STATUS_DIR/last_weather_online"
local local_epoch=`cat $STATUS_DIR/last_weather_online | $JQ -M -r ".local_epoch"`
if [[ "$weather" == *"Rain"* ]] ||
[[ "$weather" == *"Snow"* ]] ||
[[ "$weather" == *"Hail"* ]] ||
[[ "$weather" == *"Ice"* ]] ||
[[ "$weather" == *"Thunderstorm"* ]] ||
[[ "$weather" == *"Drizzle"* ]];
then
echo $local_epoch
else
echo "-$local_epoch"
fi
fi
}

4
drv/rainsensorqty/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
.set_var
# Except this file
!.gitignore

124
drv/rainsensorqty/README.md Normal file
View File

@@ -0,0 +1,124 @@
#
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file README.md
# Version: 0.2.5
# Data: 07/Apr/2020
FILE DI CONFIGURAZIONE /etc/piGarden.conf:
-----------------------------------------
NOTA: vedere nella directory conf_example il file esempio piu' aggiornato!
per attivare il driver e' necessario inserire la seguente variabile nel file di configurazion
RAIN_GPIO="drv:rainsensorqty:25" # Physical 22 - wPi 6
la variabile seguente e' impiegata anche da drv_rainsensorqty per rilevare la chiusura del contatto magnetico che, in un circuito pull-up, e' lo stato 1.
# Valore in ingresso sul gpio definito in RAIN_GPIO che indica lo stato di pioggia
RAIN_GPIO_STATE=1
le seguenti variabili controllano il driver come descritto:
RAINSENSORQTY_LOOPSFORSETRAINING=16 # dopo 16 impulsi, 16 vaschette riempite si considera pioggia
RAINSENSORQTY_SECSBETWEENRAINEVENT=10800 # =3h, significa che dopo 3 si resetta il numero di vaschette da riempire e solo dopo il riempimento del nuovo numero di vaschette si considera una nuova pioggia
infine la variabile seguente e' la quantita' di acqua espressa in mm di precipitazioni:
RAINSENSORQTY_MMEACH=0.33 # see RAINSENSORQTY driver readme for details
CALIBRAZIONE SENSORE PIOGGIA
----------------------------
secondo il seguente processo da me effettuato sul mio misuratore di pioggia:
Ho erogato 18 ml di acqua nel rain gauge che hanno prodotto 10 impulsi; pertanto il riempimento di 1.8 ml ha causato un impulso, 1 ml = 1000 mmc (mm cubici),
1.8 ml sono pari a 1800 mmc
la superficie della vaschetta in mmq e' pari a 110 mm x 55 mm = 5500 mmq
volume / superficie mi da' l'altezza, quindi 1800 mmc / 5500 mmq = 0.32727273 mm
se fossero stati 1.7 ml di acqua per ogni impulso/vaschetta riempita, la variabile sarebbe stata impostata a 0.30909091 mm
COMANDI SPECIALI
----------------
nella sottodirectory command sono presenti:
commands/rainsensorqty_CHECK.sh
chiama la funzione di verifica pioggia, la medesima chiamata da check_rain_sensor
commands/rainsensorqty_HISTORY.sh
visualizza lo storico della pioggia consultando il file $RAINSENSORQTY_HISTORY
puo' essere lanciato con l''opzione -force per ricostruire il file $RAINSENSORQTY_HISTORY dal $RAINSENSORQTY_HISTORYRAW
e con un argomento X, che permette di evidenziare solo gli ultimi X eventi
commands/rainsensorqty_HISTORYRAW.sh
visualizza lo storico della pioggia consultando il file $RAINSENSORQTY_HISTORYRAW
puo' essere lanciato senza argomenti per processare l'intero file, chiede conferma visto che i tempi sono potenzialmente lunghi
con un argomento X, che permette di evidenziare solo gli ultimi X eventi
commands/rainsensorqty_INIT.sh
inizializza il driver eseguendo lo script di monitoring - normalmente tale processo avviene da piGarden.sh
utile quando si vuole testare dei cambiamenti o se necessario riavviare dopo il kill del comando successivo
commands/rainsensorqty_KILL.sh
killa i processi di monitoring ed eventuali figli
commands/rainsensorqty_RAINNOW.sh
simula una pioggia registrandola in $RAINSENSORQTY_LASTRAIN $RAINSENSORQTY_HISTORY $RAINSENSORQTY_HISTORYRAW
puo' essere lanciato in 3 modi:
senza argomenti: genera una pioggia per un numero di loop pari al valore della variabile RAINSENSORQTY_LOOPSFORSETRAINING, quindi una pioggia completa
con un argomento: che indica quanti loop
con due argomenti: quanti loop e quanti secondi tra uno e l'altro
commands/rainsensorqty_REMOVELASTRAIN.sh
rimuove dai file $RAINSENSORQTY_LASTRAIN $RAINSENSORQTY_HISTORY $RAINSENSORQTY_HISTORYRAW l'ultima pioggia registrata
commands/rainsensorqty_RESET.sh
invia il SIGUSR1 al processo di monitor per resettare i cicli. Viene visualizzato il reset solo dopo il successivo PULSE, questo perche' non e' possibile per lo script ricevere il trap in quanto il processo $GPIO e' attivo in attesa del PULSE
questi i timestamps alla data del file:
-rwxr-xr-x 1 pi pi 1044 Sep 24 18:26 rainsensorqty_CHECK.sh
-rwxr-xr-x 1 pi pi 1139 Nov 28 00:17 rainsensorqty_HISTORYRAW.sh
-rwxr-xr-x 1 pi pi 1462 Nov 19 00:19 rainsensorqty_HISTORY.sh
-rwxr-xr-x 1 pi pi 596 Sep 24 18:26 rainsensorqty_INIT.sh
-rwxr-xr-x 1 pi pi 1300 Sep 24 18:26 rainsensorqty_KILL.sh
-rwxr-xr-x 1 pi pi 1111 Nov 27 00:05 rainsensorqty_RAINNOW.sh
-rwxr-xr-x 1 pi pi 1252 Nov 22 00:18 rainsensorqty_REMOVELASTRAIN.sh
-rwxr-xr-x 1 pi pi 897 Sep 24 18:26 rainsensorqty_RESET.sh
ULTERIORI VARIABILI in config.include.sh
----------------------------------------
esistono ulteriori variabili che potrebbe essere necessario variare
RAINSENSOR_DEBOUNCE=0.3 # 0.3 seconds for manage debounce of reed contact
serve per ritardare la lettura di un secondo impulso falso causato dal rimbalzo del contatto magnetico
RAINSENSORQTY_verbose="yes"
aumenta il livello di verbosita' nei file di log
RAINSENSORQTY_LASTRAIN="$STATUS_DIR/rainsensorqty_lastrain"
memorizza l'ultima pioggia
RAINSENSORQTY_HISTORY="$STATUS_DIR/rainsensorqty_history"
memorizza tutte le piogge permettendo di visualizzare lo storico (commands/rainsensorqty_HISTORY.sh)
RAINSENSORQTY_HISTORYRAW="$STATUS_DIR/rainsensorqty_history.raw"
memorizza in formato grezzo (raw) tutti gli impulsi registrati (formato: "secondi:impulsi" ) - consultabile il contenuto con commands/rainsensorqty_HISTORYRAW.sh
RAINSENSORQTY_MONITORLOG="$DIR_SCRIPT/log/rainsensorqty_monitor.log"
log dello script di monitoring, popolato solo se RAINSENSORQTY_verbose="yes"
RAINSENSORQTY_MONPID="$TMPDIR/rainsensorqty_monitor.pid"
file che viene popolato con il pid dello script di monitoring
RAINSENSORQTY_DIR="$DIR_SCRIPT/drv/rainsensorqty"
home directory del driver
monitor_sh="$RAINSENSORQTY_DIR/drv_rainsensorqty_monitor.sh"
script di monitoring
lo script eredita le variabili di ambiente da $RAINSENSORQTY_VAR ($TMPDIR/.rainsensorqty_var)
NOTA: $TMPDIR e' /tmp e lo script visualizza un warning se non e' un tmpfs
# internal gpio resistor, 3 values: pull-up, pull-down, none
# pull-up/down if rain gauge is connected directly to raspberry
# none if connected through an optocoupler circuit
GPIO_RESISTOR="none" #pull-up|pull-down|none
enable pull-up or pull-down resistor: https://raspberry-projects.com/pi/pi-hardware/raspberry-pi-model-b-plus/model-b-plus-io-pins
Pull-up is 50K min - 65K max.
Pull-down is 50K min - 60K max.
#rising means waiting for 1 status (from 0)
#falling means waiting for 0 status (from 1)
#RAINSENSORQTY_PULSE=rising # pull-down circuit (rest status is 0)
#RAINSENSORQTY_PULSE=falling # pull-up circuit (rest status is 1)
(( RAIN_GPIO_STATE == 1 )) && RAINSENSORQTY_PULSE=rising # pull-down circuit (rest status is 0)
(( RAIN_GPIO_STATE == 0 )) && RAINSENSORQTY_PULSE=falling # pull-up circuit (rest status is 1)
lo script di monitoring ascolta il cambiamento di stato da quello di riposo allo stato di impulso (chiusura del contatto reed).
dipendentemente dal circuto implementato, se lo stato di riposo e' 0, lo script attende la variazione verso 1 (rising)
se lo stato di riposo e' 1, lo script attende la variazione verso 0 (falling)
la variabile RAINSENSORQTY_PULSE viene impostata secondo il valore di RAIN_GPIO_STATE presente in /etc/piGarden.conf
cioe' il valore che ci si aspetta per registrare il riempimento della vaschetta dello stato di pioggia

View File

View File

@@ -0,0 +1,35 @@
#!/bin/bash
#
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "rainsensorqty_CHECK.sh"
# test script for checking rain status using drv_rainsensorqty_rain_sensor_get function
# Version: 0.2.0
# Data: 11/Aug/2019
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
SCRIPTNAME=${0##*/}
cd $SCRIPTDIR/.. # command is a subdirectory of driver
DIR_SCRIPT=/home/pi/piGarden # home directory of piGarden
CONFIG_ETC="/etc/piGarden.conf"
LOG_OUTPUT_DRV_FILE="$DIR_SCRIPT/log/$LOG_OUTPUT_DRV_FILE"
. $CONFIG_ETC
. ./common.include.sh
. ./config.include.sh
. ./init.include.sh
. ./rainsensor.include.sh
# restituisce 0 se piove, e nell'output di testo il valore di "$RAIN_GPIO_STATE"
# restituisce 99 se non piove, output "norain"
# esce con 1 se non c'e' il monitoring, output "ERROR"
drv_rainsensorqty_rain_sensor_get
case $? in
0) echo "NORMAL: it's raining" ;;
99) echo "NORMAL: it's not raining" ;;
1) echo "ERROR: monitor process $DIR_SCRIPT/drv/rainsensorqty/drv_rainsensorqty_monitor.sh is not running" ;;
esac

View File

@@ -0,0 +1,59 @@
#!/bin/bash
#
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "rainsensorqty_CHECK.sh"
# test script for checking rain status using drv_rainsensorqty_rain_sensor_get function
# Version: 0.2.5
# Data: 08/Jan/2020
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
SCRIPTNAME=${0##*/}
cd $SCRIPTDIR/.. # command is a subdirectory of driver
DIR_SCRIPT=/home/pi/piGarden # home directory of piGarden
CONFIG_ETC="/etc/piGarden.conf"
LOG_OUTPUT_DRV_FILE="$DIR_SCRIPT/log/$LOG_OUTPUT_DRV_FILE"
. $CONFIG_ETC
. ./common.include.sh
. ./config.include.sh
. ./init.include.sh
. ./rainsensor.include.sh
if [[ $1 = "-force" ]] ; then
if [[ -s $RAINSENSORQTY_HISTORY ]] ; then
echo backup $RAINSENSORQTY_HISTORY to ${RAINSENSORQTY_HISTORY}.old$$
cp $RAINSENSORQTY_HISTORY ${RAINSENSORQTY_HISTORY}.old$$
fi
echo "generate all rain events to $RAINSENSORQTY_HISTORY"
if ! rainevents > ${RAINSENSORQTY_HISTORY} ; then
echo "WARNING: rainevents function had error"
fi
shift
fi
if ! rain_history tmp ; then # update rain history with last rain if not
echo "WARNING: rain_history function had error"
fi
cmd="cat"
if [[ $# > 0 ]] ; then
if (( $1 >= 1 )) ; then
echo "processing last $1 lines of $RAINSENSORQTY_HISTORYRAW file"
cmd="tail -$1"
else
echo "argument not recognized - exit"
exit 1
fi
fi
echo -e "\n\n"
if [[ -s $RAINSENSORQTY_HISTORY ]] ; then
echo "RAIN HISTORY"
cat $RAINSENSORQTY_HISTORY $RAINSENSORQTY_HISTORYTMP | $cmd | rain_when_amount
else
echo "WARNING: no \$RAINSENSORQTY_HISTORY file"
fi

View File

@@ -0,0 +1,50 @@
#!/bin/bash
#
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "rainsensorqty_CHECK.sh"
# test script for checking rain status using drv_rainsensorqty_rain_sensor_get function
# Version: 0.2.5
# Data: 07/Apr/2020
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
SCRIPTNAME=${0##*/}
cd $SCRIPTDIR/.. # command is a subdirectory of driver
DIR_SCRIPT=/home/pi/piGarden # home directory of piGarden
CONFIG_ETC="/etc/piGarden.conf"
LOG_OUTPUT_DRV_FILE="$DIR_SCRIPT/log/$LOG_OUTPUT_DRV_FILE"
. $CONFIG_ETC
. ./common.include.sh
. ./config.include.sh
. ./init.include.sh
. ./rainsensor.include.sh
if [[ $# = 0 ]] ; then
cmd=cat
echo "processing all entire $RAINSENSORQTY_HISTORYRAW file, will go on? (y/n)"
read answer
echo $answer
[[ $answer = [yY] ]] || exit 1
else
if (( $1 >= 1 )) ; then
echo "processing $1 lines of $RAINSENSORQTY_HISTORYRAW file"
cmd="tail -$1"
else
echo "argument not recognized - exit"
exit 1
fi
fi
echo -e "\n\n"
$cmd $RAINSENSORQTY_HISTORYRAW | while read line
do
set -- ${line//:/ }
secs=$1
counter=$2
echo "$(sec2date $1):$counter"
done

View File

@@ -0,0 +1,25 @@
#!/bin/bash
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "rainsensorqty_INIT.sh"
# test script for initialize driver and executing monitor process
# Version: 0.2.0
# Data: 11/Aug/2019
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
SCRIPTNAME=${0##*/}
cd $SCRIPTDIR/.. # command is a subdirectory of driver
DIR_SCRIPT=/home/pi/piGarden # home directory of piGarden
CONFIG_ETC="/etc/piGarden.conf"
. $CONFIG_ETC
. ./config.include.sh
. ./common.include.sh
. ./init.include.sh
. ./rainsensor.include.sh
drv_rainsensorqty_rain_sensor_init
drv_rainsensorqty_init

View File

@@ -0,0 +1,52 @@
#!/bin/bash
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "rainsensorqty_KILL.sh"
# script for killing monitor process(es)
# Version: 0.2.0a
# Data: 29/Aug/2019
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
SCRIPTNAME=${0##*/}
cd $SCRIPTDIR/.. # command is a subdirectory of driver
DIR_SCRIPT=/home/pi/piGarden # home directory of piGarden
CONFIG_ETC="/etc/piGarden.conf"
. $CONFIG_ETC
. ./config.include.sh
. ./common.include.sh
. ./init.include.sh
. ./rainsensor.include.sh
# check if rain monitor process is running...
if [[ -f "$RAINSENSORQTY_MONPID" ]] ; then
pid="$( < "$RAINSENSORQTY_MONPID" )"
if ps -fp $pid >/dev/null ; then
echo "process $pid pid and its child(ren)"
children_pid="$(ps -ef| awk "\$3==$pid {print \$2}")"
ps -fp $pid
ps -fp $children_pid | tail +2
echo -e "\nsending TERM signal to $pid and its child(ren)"
echo kill $children_pid
echo kill $pid
kill $children_pid
kill $pid
echo -e "\nchecking $pid pid and its child(ren) are still alive"
for process in $pid $children_pid
do
if ps -fp $process >/dev/null ; then
echo "$process is still alive"
else
echo "$process is dead"
fi
done
else
echo "no RAIN process alive"
fi
else
echo "no RAIN process alive"
fi

View File

@@ -0,0 +1,67 @@
#!/bin/bash
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "rainsensorqty_RAINNOW.sh"
# test script for simulate rain ... now!
# Version: 0.2.5
# Data: 07/Apr/2020
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
SCRIPTNAME=${0##*/}
cd $SCRIPTDIR/.. # command is a subdirectory of driver
DIR_SCRIPT=/home/pi/piGarden # home directory of piGarden
CONFIG_ETC="/etc/piGarden.conf"
. $CONFIG_ETC
. ./config.include.sh
. ./common.include.sh
. ./init.include.sh
. ./rainsensor.include.sh
wait=0
timestart=$( date +%s)
if [[ $# -ne 0 ]] ; then
if [[ $# = 1 ]] ; then
howmany=$1
echo "one argument passed: rain event for $howmany loops"
(( time = timestart - howmany ))
elif [[ $# = 2 ]] ; then
howmany=$1
wait=$2
echo "two arguments passed: rain event for $howmany loops every $wait seconds"
(( time = timestart ))
else
echo "too many arguments... exit"
exit 1
fi
else
howmany=$RAINSENSORQTY_LOOPSFORSETRAINING
(( time = timestart - $RAINSENSORQTY_LOOPSFORSETRAINING ))
fi
echo "RAIN now! (for $howmany loops)"
for (( c=1; c<=$howmany; c++ ))
do
if (( wait > 0 )) ; then
time=$( date +%s)
else
(( time+= 1 ))
fi
linetoadd="$time:$c"
echo $linetoadd >> $RAINSENSORQTY_HISTORYRAW
sleep $wait
echo -e ".\c"
done
echo
if ! rain_history ; then # update rain history with last rain if not
echo "WARNING: rain_history function had error"
fi
echo $linetoadd > ${RAINSENSORQTY_LASTRAIN}
echo "file ${RAINSENSORQTY_LASTRAIN} updated."
echo "last 2 rain events:"
tail -2 $RAINSENSORQTY_HISTORY | rain_when_amount

View File

@@ -0,0 +1,44 @@
#!/bin/bash
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "test_rainsensorqty_CHECK.sh"
# test script for checking rain status using drv_rainsensorqty_rain_sensor_get function
# Version: 0.2.5
# Data: 07/Apr/2020
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
SCRIPTNAME=${0##*/}
cd $SCRIPTDIR/.. # command is a subdirectory of driver
DIR_SCRIPT=/home/pi/piGarden # home directory of piGarden
CONFIG_ETC="/etc/piGarden.conf"
LOG_OUTPUT_DRV_FILE="$DIR_SCRIPT/log/$LOG_OUTPUT_DRV_FILE"
. $CONFIG_ETC
. ./common.include.sh
. ./config.include.sh
. ./init.include.sh
. ./rainsensor.include.sh
# two variables for store rain data
# RAINSENSORQTY_LASTRAIN
# RAINSENSORQTY_HISTORY
echo "RAIN HISTORY - last two events"
tail -2 $RAINSENSORQTY_HISTORY
tail -2 $RAINSENSORQTY_HISTORY | rain_when_amount
echo "...removing last event"
removelastrain
echo "...rebuilding ${RAINSENSORQTY_HISTORY} from ${RAINSENSORQTY_HISTORYRAW}"
if ! rainevents > ${RAINSENSORQTY_HISTORY} ; then
echo "WARNING: rainevents function had error"
fi
tail -1 ${RAINSENSORQTY_HISTORYRAW} > ${RAINSENSORQTY_LASTRAIN}
echo -e "\nnew RAIN HISTORY - last two events"
tail -2 $RAINSENSORQTY_HISTORY
tail -2 $RAINSENSORQTY_HISTORY | rain_when_amount

View File

@@ -0,0 +1,35 @@
#!/bin/bash
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "rainsensorqty_RESET.sh"
# script for reset counter in monitor script
# Version: 0.2.0a
# Data: 29/Aug/2019
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
SCRIPTNAME=${0##*/}
cd $SCRIPTDIR/.. # command is a subdirectory of driver
DIR_SCRIPT=/home/pi/piGarden # home directory of piGarden
CONFIG_ETC="/etc/piGarden.conf"
. $CONFIG_ETC
. ./config.include.sh
. ./common.include.sh
. ./init.include.sh
. ./rainsensor.include.sh
# check if rain monitor process is running...
if [[ -f "$RAINSENSORQTY_MONPID" ]] ; then
pid="$( < "$RAINSENSORQTY_MONPID" )"
if ps -fp $pid >/dev/null ; then
echo "sending SIGUSR1 to $pid"
kill -SIGUSR1 $pid
echo -e "sent SIGUSR1 - reset will be shown after next cycle"
else
echo "no RAIN process alive"
fi
else
echo "no RAIN process alive"
fi

View File

@@ -0,0 +1 @@
echo 1 > /tmp/tick ; sleep 0.9 ; echo 0 > /tmp/tick

View File

@@ -0,0 +1,203 @@
#
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "common.include.sh"
# common functions used by driver
# Version: 0.2.5
# Data: 08/Jan/2020
#note:
#RAINSENSORQTY_MONPID="$TMPDIR/rainsensorqty_monitor.pid"
#
sec2date()
{
date --date="@$1"
}
d() # short date & time
{
date '+%X-%x'
}
drv_rainsensorqty_writelog()
{
#2 variables - $1 function, $2 message
if [[ $2 =~ ERROR || $2 =~ WARNING || $2 =~ RAIN || $RAINSENSORQTY_verbose = yes ]] ; then
echo -e "$1 - `d`\t\t$2" >> "$LOG_OUTPUT_DRV_FILE"
# if [[ $($WC -c <"$LOG_OUTPUT_DRV_FILE") > $LOG_FILE_MAX_SIZE )) ; then
# $GZIP "$LOG_OUTPUT_DRV_FILE"
# $MV "${LOG_OUTPUT_DRV_FILE}.gz" "${LOG_OUTPUT_DRV_FILE}.$(date +%Y%m%d%H%M).gz"
# fi
fi
}
drv_rainsensorqty_check()
{
local f="drv_rainsensorqty_check"
if [[ -f "$RAINSENSORQTY_MONPID" ]] ; then
local pid=$( < "$RAINSENSORQTY_MONPID" )
drv_rainsensorqty_writelog $f "NORMAL: checking if $pid pid is running"
if ps -fp $pid >/dev/null ; then
drv_rainsensorqty_writelog $f "NORMAL: $pid pid is running"
return 0
else
drv_rainsensorqty_writelog $f "ERROR: $pid pid monitor process NOT running - $RAINSENSORQTY_MONPID file contains $pid"
return 1
fi
else
drv_rainsensorqty_writelog $f "ERROR: no raining monitor process file \$RAINSENSORQTY_MONPID"
return 1
fi
}
en_echo() # enhanched echo - check verbose variable
{
[[ $RAINSENSORQTY_verbose = yes ]] && echo "$(d) $*"
}
check_incomplete_loop()
{
[[ ! -f $RAINSENSORQTY_HISTORYRAW ]] && return 1
[[ ! -f $RAINSENSORQTY_HISTORY ]] && touch $RAINSENSORQTY_HISTORY
> $RAINSENSORQTY_HISTORYTMP
if lastrainevent=$( rainevents 1 ) ; then
: # done ok
else
echo "WARNING: rainevents function had errors"
return 1
fi
set -- ${lastrainevent//:/ }
local started=$1
local before=$2
local counter=$3
wrongevent=$(awk -F ":" '$1=="'$started'" && $2!="'$before'" {print $0}' $RAINSENSORQTY_HISTORY)
if [[ -n $wrongevent ]] ; then
echo "ERROR: wrong last rain event found: $wrongevent , right one should be: $lastrainevent"
return 2
fi
if grep -q ^${lastrainevent}$ $RAINSENSORQTY_HISTORY ; then
: # already present
return 0
else
: # missing and fixed
if [[ $1 == tmp ]] ; then
echo $lastrainevent > $RAINSENSORQTY_HISTORYTMP
else
echo $lastrainevent >> $RAINSENSORQTY_HISTORY
fi
return 1
fi
}
#next function is not used anymore
rain_history()
{
[[ ! -f $RAINSENSORQTY_HISTORYRAW ]] && return 1
[[ ! -f $RAINSENSORQTY_HISTORY ]] && touch $RAINSENSORQTY_HISTORY
> $RAINSENSORQTY_HISTORYTMP
if lastrainevent=$( rainevents 1 ) ; then
: # done ok
else
echo "WARNING: rainevents function had errors"
return 1
fi
#old #if grep -q ^$(<$RAINSENSORQTY_LASTRAIN)$ $RAINSENSORQTY_HISTORY ; then
if grep -q ^${lastrainevent}$ $RAINSENSORQTY_HISTORY ; then
: # already present
return 0
else
: # missing and fixed
if [[ $1 == tmp ]] ; then
echo $lastrainevent > $RAINSENSORQTY_HISTORYTMP
else
echo $lastrainevent >> $RAINSENSORQTY_HISTORY
fi
return 0
fi
}
rain_when_amount()
{
# from standard input
# format $time:$endtime:$endsequence
cat - | while read line
do
set -- ${line//:/ }
start=$1
stop=$2
howmuch=$3
printf "RAINED for %7.2f mm between %s and %s\n" $( $JQ -n "$howmuch * $RAINSENSORQTY_MMEACH" ) "$(date --date="@$start")" "$(date --date="@$stop")"
done
}
check_TMPDIR()
{
if [[ $(df | awk '$NF=="/tmp" {print $1}') != "tmpfs" ]] ; then
echo "WARNING: /tmp isn't a tmp file system"
echo -e "\tplease add to your /etc/fstab file:\n\ttmpfs /tmp tmpfs defaults,noatime,nosuid 0 0"
fi
}
rainevents()
{
if [[ ! -f $RAINSENSORQTY_HISTORYRAW ]] ; then
#echo "WARNING: no \$RAINSENSORQTY_HISTORYRAW file"# cannot echo, redirected output
return 1
fi
case $1 in
[0-9]|[0-9][0-9]) howmanyevent=$1 ;;
# -1) skiplast=true ;;
*) howmanyevent=-1 ;;
esac
newloop=yes
tac $RAINSENSORQTY_HISTORYRAW | while read line
do
set -- ${line//:/ }
time=$1
sequence=$2
if [[ $newloop == yes ]] ; then
endtime=$time
endsequence=$sequence
newloop=no
fi
if (( sequence == 1 )) ; then
# [[ $skiplast=true ]] && { skiplast=false ; continue ; }
echo $time:$endtime:$endsequence
newloop=yes
(( event +=1 ))
fi
(( howmanyevent == event )) && break
done | sort -k1n
}
removelastrain()
{
if [[ ! -f $RAINSENSORQTY_HISTORYRAW ]] ; then
echo "WARNING: no \$RAINSENSORQTY_HISTORYRAW file"
return 1
fi
next=false
tac $RAINSENSORQTY_HISTORYRAW | while read line
do
set -- ${line//:/ }
time=$1
sequence=$2
[[ $next = true ]] && echo $line
(( sequence == 1 )) && next=true
done | tac > ${RAINSENSORQTY_HISTORYRAW}_$$
mv ${RAINSENSORQTY_HISTORYRAW}_$$ $RAINSENSORQTY_HISTORYRAW
}

View File

@@ -0,0 +1,66 @@
#
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "config.include.sh"
# specific driver config file
# Version: 0.2.5
# Data: 08/Jan/2020
export TMPDIR=/tmp
RAINSENSOR_DEBOUNCE=0.3 # 0.3 seconds for manage debounce of reed contact
RAINSENSORQTY_verbose="yes" # yes/no
RAINSENSORQTY_LASTRAIN="$STATUS_DIR/rainsensorqty_lastrain"
RAINSENSORQTY_HISTORY="$STATUS_DIR/rainsensorqty_history"
RAINSENSORQTY_HISTORYRAW="$STATUS_DIR/rainsensorqty_history.raw"
RAINSENSORQTY_HISTORYTMP="$TMPDIR/rainsensorqty_history.tmp"
RAINSENSORQTY_MONITORLOG="$DIR_SCRIPT/log/rainsensorqty_monitor.log"
RAINSENSORQTY_MONPID="$TMPDIR/rainsensorqty_monitor.pid"
RAINSENSORQTY_STATE="$TMPDIR/rainsensorqty_state"
RAINSENSORQTY_STATE_HIST="$TMPDIR/rainsensorqty_state.history"
RAINSENSORQTY_DIR="$DIR_SCRIPT/drv/rainsensorqty"
monitor_sh="$RAINSENSORQTY_DIR/drv_rainsensorqty_monitor.sh"
# internal gpio resistor, 3 values: pull-up, pull-down, none
# pull-up/down if rain gauge is connected directly to raspberry
# none if connected through an optocoupler circuit
GPIO_RESISTOR="pull-up" #pull-up|pull-down|none
#rising means waiting for 1 status (from 0)
#falling means waiting for 0 status (from 1)
#RAINSENSORQTY_PULSE=rising # pull-down circuit (rest status is 0)
#RAINSENSORQTY_PULSE=falling # pull-up circuit (rest status is 1)
(( RAIN_GPIO_STATE == 1 )) && RAINSENSORQTY_PULSE=rising # pull-down circuit (rest status is 0)
(( RAIN_GPIO_STATE == 0 )) && RAINSENSORQTY_PULSE=falling # pull-up circuit (rest status is 1)
config_check()
{
var2check="RAINSENSOR_DEBOUNCE RAINSENSORQTY_verbose RAINSENSORQTY_LASTRAIN RAINSENSORQTY_HISTORY RAINSENSORQTY_MONITORLOG RAINSENSORQTY_MONPID RAINSENSORQTY_DIR monitor_sh"
for var in $var2check
do
#${!a}
if [[ -z ${!var} ]] ; then
echo "ERROR: \$$var not set"
exit 1
fi
done
if [[ -z $RAINSENSORQTY_PULSE ]] ; then
echo "ERROR: RAIN_GPIO_STATE not set in piGarden.conf"
exit 1
fi
return 0
case $GPIO_RESISTOR in
pull-up|pull-down|none) return 0 ;;
*) echo "ERROR: GPIO_RESISTOR not set correctly - values are \"pull-up|pull-down|none\" "
exit 1
;;
esac
}

View File

@@ -0,0 +1,14 @@
# cfg file version 0.2.0
# Id del gpio usato per collegare il sensore di rilevamento pioggia
# attraverso il driver rainsensorqty
RAIN_GPIO="drv:rainsensorqty:25" # Physical 22 - wPi 6
# Valore in ingresso sul gpio definito in RAIN_GPIO che indica lo stato di pioggia
# variabile usata anche da drv:rainsensorqty, se e' a 1, significa che il reed contact e' collegato ad un circuito pull-down, vuol dire che attende l'impulso a 1 per contare le vaschette, normalmente e' a 0.
# se e' a 0, significa che il reed contact e' collegato ad un circuito pull-up, vuol dire che attende l'impulso a 0 per contare le vaschette, normalmente e' a 0.
RAIN_GPIO_STATE=0
RAINSENSORQTY_LOOPSFORSETRAINING=16 # dopo 16 impulsi, 16 vaschette riempite si considera pioggia
RAINSENSORQTY_SECSBETWEENRAINEVENT=10800 # =3h, significa che dopo 3 ore si resetta il numero di vaschette riempire e solo dopo un nuovo ciclo di riempimento si considera una nuova pioggia
RAINSENSORQTY_MMEACH=0.33 # see RAINSENSORQTY driver readme for details

View File

@@ -0,0 +1,66 @@
#
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "config.include.sh"
# specific driver config file
# Version: 0.2.5
# Data: 08/Jan/2020
export TMPDIR=/tmp
RAINSENSOR_DEBOUNCE=0.3 # 0.3 seconds for manage debounce of reed contact
RAINSENSORQTY_verbose="yes" # yes/no
RAINSENSORQTY_LASTRAIN="$STATUS_DIR/rainsensorqty_lastrain"
RAINSENSORQTY_HISTORY="$STATUS_DIR/rainsensorqty_history"
RAINSENSORQTY_HISTORYRAW="$STATUS_DIR/rainsensorqty_history.raw"
RAINSENSORQTY_HISTORYTMP="$TMPDIR/rainsensorqty_history.tmp"
RAINSENSORQTY_MONITORLOG="$DIR_SCRIPT/log/rainsensorqty_monitor.log"
RAINSENSORQTY_MONPID="$TMPDIR/rainsensorqty_monitor.pid"
RAINSENSORQTY_STATE="$TMPDIR/rainsensorqty_state"
RAINSENSORQTY_STATE_HIST="$TMPDIR/rainsensorqty_state.history"
RAINSENSORQTY_DIR="$DIR_SCRIPT/drv/rainsensorqty"
monitor_sh="$RAINSENSORQTY_DIR/drv_rainsensorqty_monitor.sh"
# internal gpio resistor, 3 values: pull-up, pull-down, none
# pull-up/down if rain gauge is connected directly to raspberry
# none if connected through an optocoupler circuit
GPIO_RESISTOR="pull-up" #pull-up|pull-down|none
#rising means waiting for 1 status (from 0)
#falling means waiting for 0 status (from 1)
#RAINSENSORQTY_PULSE=rising # pull-down circuit (rest status is 0)
#RAINSENSORQTY_PULSE=falling # pull-up circuit (rest status is 1)
(( RAIN_GPIO_STATE == 1 )) && RAINSENSORQTY_PULSE=rising # pull-down circuit (rest status is 0)
(( RAIN_GPIO_STATE == 0 )) && RAINSENSORQTY_PULSE=falling # pull-up circuit (rest status is 1)
config_check()
{
var2check="RAINSENSOR_DEBOUNCE RAINSENSORQTY_verbose RAINSENSORQTY_LASTRAIN RAINSENSORQTY_HISTORY RAINSENSORQTY_MONITORLOG RAINSENSORQTY_MONPID RAINSENSORQTY_DIR monitor_sh"
for var in $var2check
do
#${!a}
if [[ -z ${!var} ]] ; then
echo "ERROR: \$$var not set"
exit 1
fi
done
if [[ -z $RAINSENSORQTY_PULSE ]] ; then
echo "ERROR: RAIN_GPIO_STATE not set in piGarden.conf"
exit 1
fi
return 0
case $GPIO_RESISTOR in
pull-up|pull-down|none) return 0 ;;
*) echo "ERROR: GPIO_RESISTOR not set correctly - values are \"pull-up|pull-down|none\" "
exit 1
;;
esac
}

View File

@@ -0,0 +1,113 @@
#!/bin/bash
#
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "drv_rainsensorqty_monitor.sh"
# monitor script
# Version: 0.2.5c
# Data: 08/Dec/2020
resetcounter()
{
(( counter = 0 ))
drv_rainsensorqty_writelog $f "SIGUSR1 received after last PULSE - counter resetted" &
echo "SIGUSR1 received after last PULSE - counter resetted"
}
# DEBUG FUNCTION:
testloop()
{
touch /tmp/tick
while true
do
if [[ $( < /tmp/tick ) = "1" ]] ; then
break
fi
sleep 1
done
}
###############
# MAIN #
###############
trap "resetcounter" SIGUSR1
DIRNAME="$( dirname $0 )"
f="$(basename $0)"
. $DIRNAME/common.include.sh
RAINSENSORQTY_VAR=$TMPDIR/.rainsensorqty_var
if [[ -f "$RAINSENSORQTY_VAR" ]] ; then
en_echo "NORMAL: file $RAINSENSORQTY_VAR found - getting variables"
. "$RAINSENSORQTY_VAR"
else
echo "ERROR: $RAINSENSORQTY_VAR not found"
exit 1
fi
#drvt="$( echo $RAIN_GPIO | $CUT -f 1 -d: )"
#drv="$( echo $RAIN_GPIO | $CUT -f 2 -d: )"
gpio_port="$( echo $RAIN_GPIO | $CUT -f 3 -d: )"
# check if no other rain monitor process running...
if [[ -f "$RAINSENSORQTY_MONPID" ]] ; then
pid="$( < "$RAINSENSORQTY_MONPID" )"
if ps -fp $pid >/dev/null ; then
drv_rainsensorqty_writelog $f "ERROR monitor process already running\n$( ps -fp $pid )"
exit 1
fi
fi
echo $$ > $RAINSENSORQTY_MONPID
drv_rainsensorqty_writelog $f "NORMAL - $$ pid monitor process started - see $RAINSENSORQTY_MONPID"
echo ""
en_echo "---- NEW RUN ----"
# check if monitor script was killed before writing a complete rain event
check_incomplete_loop
case $? in
2) echo "\$RAINSENSORQTY_HISTORY - wrong entry found - no fix possible" ;;
1) echo "\$RAINSENSORQTY_HISTORY - fixed incomplete loop" ;;
0) echo "\$RAINSENSORQTY_HISTORY - no incomplete loop needed to be fixed" ;;
esac
# init variables
MMEACH="$RAINSENSORQTY_MMEACH"
(( counter=0 ))
before="-1"
en_echo "WAITING FOR $RAINSENSORQTY_PULSE PULSE"
# loop forever
while true
do
sleep $RAINSENSOR_DEBOUNCE
#DEBUG: testloop #DEBUG
$GPIO -g wfi $gpio_port $RAINSENSORQTY_PULSE
now=`date +%s`
(( elapsed = now - before ))
if (( elapsed >= RAINSENSORQTY_SECSBETWEENRAINEVENT )) ; then
last_event="$started:$before:$counter"
(( counter=0 ))
drv_rainsensorqty_writelog $f "first drops after $elapsed seconds since last rain ( greater than $RAINSENSORQTY_SECSBETWEENRAINEVENT )- new cycle - waiting for $( $JQ -n "$RAINSENSORQTY_LOOPSFORSETRAINING * $MMEACH" ) mm of rain" &
(( before > 0 )) && echo $last_event >> $RAINSENSORQTY_HISTORY
en_echo "---- NEW CYCLE ----"
fi
(( counter+=1 ))
(( counter == 1 )) && (( started = now ))
echo "$now:$counter" >> ${RAINSENSORQTY_HISTORYRAW} &
MMWATER=$( $JQ -n "$counter*$MMEACH" )
en_echo $( printf "%s PULSE #%d RECEIVED (%.2f mm)" $RAINSENSORQTY_PULSE $counter $MMWATER )
text=$(printf "%.2f mm height (#%d pulse)" $MMWATER $counter )
if (( counter >= RAINSENSORQTY_LOOPSFORSETRAINING )) ; then
drv_rainsensorqty_writelog $f "RAINING - $text" &
echo "$now:$counter" > ${RAINSENSORQTY_LASTRAIN}
else
drv_rainsensorqty_writelog $f "now is $text" &
fi
(( before = now ))
done

View File

@@ -0,0 +1,37 @@
#
# Driver rainsensorqty - driver for measure the rain volume
# Author: androtto
# file "init.include.sh"
# Inizializza il driver, viene richiamata nella funzione init di piGarden
# Version: 0.2.0
# Data: 11/Aug/2019
function drv_rainsensorqty_init {
local f="drv_rainsensorqty_init"
# format RAIN_GPIO="drv:rainsensorqty:25"
drv_rainsensorqty_writelog $f "NORMAL: executing $monitor_sh"
# esegue rainmonitor
if [ -x "$monitor_sh" ] ; then
nohup "$monitor_sh" >> $RAINSENSORQTY_MONITORLOG 2>&1 &
sleep 1
drv_rainsensorqty_writelog $f "NORMAL: $monitor_sh has pid $( < $RAINSENSORQTY_MONPID)"
else
drv_rainsensorqty_writelog $f "ERROR: cannot find \"\$monitor_sh \" "
fi
}
if ! config_check ; then
echo "ERROR in config_check function"
exit 1
else
: #echo "config_check ok"
fi
check_TMPDIR
RAINSENSORQTY_VAR=$TMPDIR/.rainsensorqty_var
set | $GREP -e ^GPIO -e ^LOG -e ^CUT -e ^JQ -e ^RAIN -e ^SCR -e ^TMP > $RAINSENSORQTY_VAR

View File

@@ -0,0 +1,104 @@
#
# Driver rainsensorqty - driver for measure the rain volume, for rain meter, for rain gauge
# Author: androtto
# file "rainsensor.include.sh"
# functions called by piGarden.sh
# Version: 0.1.2
# Data: 19/Mar/2019
# fixed output drv_rainsensorqty_rain_sensor_get
#
# Inizializza il sensore di rilevamento pioggia
#
# $1 identificativo gpio del sensore di pioggia
#
drv_rainsensorqty_rain_sensor_init()
{
local f=drv_rainsensorqty_rain_sensor_init
drv_rainsensorqty_writelog "launched: $f" $1
local drvt="$( echo $RAIN_GPIO | $CUT -f 1 -d: )"
local drv="$( echo $RAIN_GPIO | $CUT -f 2 -d: )"
local gpio_port="$( echo $RAIN_GPIO | $CUT -f 3 -d: )"
if $GPIO -g mode $gpio_port in ; then
drv_rainsensorqty_writelog $f "NORMAL: '$GPIO -g mode $gpio_port in' set correctly"
else
drv_rainsensorqty_writelog $f "ERROR: '$GPIO -g mode $gpio_port in' has an error"
exit 1
fi
case $GPIO_RESISTOR in
pull-up) gpio_arg=up
message="NORMAL: '$GPIO -g mode $gpio_port up' set internal pull-up resistor"
;;
pull-down) gpio_arg=down
message="NORMAL: '$GPIO -g mode $gpio_port down' set internal pull-down resistor"
;;
none) gpio_arg=tri
message="NORMAL: '$GPIO -g mode $gpio_port tri' set none to internal resistor"
;;
*) echo "ERROR: GPIO_RESISTOR not set correctly - values are \"pull-up|pull-down|none\" "
drv_rainsensorqty_writelog "drv_rainsensorqty_rain_sensor_init" "ERROR: GPIO_RESISTOR not set correctly - values are \"pull-up|pull-down|none\" "
exit 1
;;
esac
if $GPIO -g mode $gpio_port $gpio_arg ; then
drv_rainsensorqty_writelog $f "$message"
else
drv_rainsensorqty_writelog $f "ERROR: '$GPIO -g mode $gpio_port $gpio_arg' command"
exit 1
fi
}
#
# Ritorna in output lo stato del sensore di rilevamento pioggia
#
# $1 identificativo gpio del sensore di pioggia
#
# restituisce 0 se piove, e nell'output di testo il valore di "$RAIN_GPIO_STATE"
# restituisce 99 se non piove, output "norain"
# esce con 1 se non c'e' il monitoring, output "ERROR"
drv_rainsensorqty_rain_sensor_get()
{
local now=$(date +%s)
local interval=60 # because check_rain_sensor is scheduled once a minute ... to changed if schedule is modified, from crontab:
#* * * * * /home/pi/piGarden/piGarden.sh check_rain_sensor 2> /tmp/check_rain_sensor.err
local f="drv_rainsensorqty_check"
# script called with:
#drv_rainsensorqty_writelog $f $1
# ignora il parametro di $1, lo recupera dal file di configurazione
# verifica se lo script di monitoring e' attivo
if drv_rainsensorqty_check ; then
drv_rainsensorqty_writelog $f "NORMAL - drv_rainsensorqty_check ok, monitor process running"
if [ -f "$RAINSENSORQTY_LASTRAIN" ] ; then
local lastrain="$( cat "$RAINSENSORQTY_LASTRAIN" | $CUT -f 1 -d: )"
local counter="$( cat "$RAINSENSORQTY_LASTRAIN" | $CUT -f 2 -d: )"
LEVEL=$( $JQ -n "$counter/$RAINSENSORQTY_LOOPSFORSETRAINING" | $JQ 'floor' )
(( diff = now - lastrain ))
drv_rainsensorqty_writelog $f "NORMAL: last rain $( date --date="@$lastrain" ) - LEVEL $LEVEL rain"
drv_rainsensorqty_writelog $f "NORMAL: check rain $( date --date="@$now" ) "
if (( diff <= interval )) ; then
drv_rainsensorqty_writelog $f "RAIN - return \$RAIN_GPIO_STATE = $RAIN_GPIO_STATE as output"
drv_rainsensorqty_writelog $f "DEBUG : check rain - diff $diff < $interval - return $RAIN_GPIO_STATE"
msg="$RAIN_GPIO_STATE"
echo $msg
return 0
else
drv_rainsensorqty_writelog $f "NO_RAIN - return \"norain\" as output"
drv_rainsensorqty_writelog $f "DEBUG : check rain - diff $diff < $interval - return 99"
msg="norain"
echo $msg
return 99
fi
fi
else
drv_rainsensorqty_writelog $f "ERROR: drv_rainsensorqty_check failed, no monitor process running ($monitor_sh)"
msg="ERROR"
echo $msg
return 1
fi
}

14
drv/remote/README.md Normal file
View File

@@ -0,0 +1,14 @@
# Driver for controlling remote pigarden on the network from a pigarden master installation
For more information see https://www.lejubila.net/2018/03/pigardent-0-5-5-driver-master-per-implementare-un-architettura-master-slave/
# Example of zone configuration in piGarden.conf
```bash
EV1_ALIAS="Giarino_Posteriore_DX" #
EV1_GPIO="drv:remote:PIREMOTE1:Giardino_Posteriore_DX"
PIREMOTE1_IP="192.168.1.51"
PIREMOTE1_PORT="8084"
PIREMOTE1_USER=""
PIREMOTE1_PWD=""
```

View File

@@ -0,0 +1,38 @@
#
# Funzioni comuni utilizzate dal driver
#
#
# Esegue un comando su un pigarden remoto tramite socket
#
# $1 identificativo pigarden remoto
# $2 comando da eseguire
#
function drv_remote_command {
local remote="$1"
local command="$2"
local remote_ip_var=$remote"_IP"
local remote_port_var=$remote"_PORT"
local remote_user_var=$remote"_USER"
local remote_pwd_var=$remote"_PWD"
local remote_ip="${!remote_ip_var}"
local remote_port="${!remote_port_var}"
local remote_user="${!remote_user_var}"
local remote_pwd="${!remote_pwd_var}"
exec 5<>/dev/tcp/$remote_ip/$remote_port
if [[ ! -z $remote_user ]] && [[ ! -z $remote_pwd ]]; then
command="$remote_user\n$remote_pwd\n$command"
fi
echo -e "$command" >&5
cat <&5
}

View File

@@ -0,0 +1,7 @@
#
# File di configurazione del driver
#
# Dichiarazione variabile di esempio
#declare -g SAMPLE_FOO
#SAMPLE_FOO="bar"

View File

@@ -0,0 +1,9 @@
#
# Questa funzione viene inviocata dalla funzione "init" di piGarden se sono presenti elettrovalvole o sensori che utilizzano questo driver
#
function drv_remote_init {
local FOO=bar
}

View File

@@ -0,0 +1,23 @@
#
# Inizializza il sensore di rilevamento pioggia
#
# $i identificativo gpio del sensore di pioggia
#
function drv_remote_rain_sensor_init {
local FOO="bar"
}
#
# Ritorna lo stato del sensore di rilevamento pioggia
#
# $i identificativo gpio del sensore di pioggia
#
function drv_remote_rain_sensor_get {
local FOO="bar"
}

View File

@@ -0,0 +1,76 @@
#
# Inizializzazione rele
#
# $1 identificativo relè da inizializzare
#
function drv_remote_rele_init {
drv_remote_rele_open "$1"
}
#
# Apertura rele
#
# $1 identificativo relè da aprire (chiude l'elettrovalvola)
#
function drv_remote_rele_open {
local remote=`echo $1 | $CUT -d':' -f3,3`
local remote_alias=`echo $1 | $CUT -d':' -f4,4`
local command="close $remote_alias"
echo "remote=$remote"
echo "remote_alias=$remote_alias"
echo "command=$command"
local response=$(drv_remote_command "$remote" "$command")
echo "response=$response"
local result=$(echo $response|$JQ -M ".error.description")
echo "result=$result"
if [[ "$result" != "\"\"" ]]; then
local error=$result
error="${error%\"}"
error="${error#\"}"
echo "error=$error"
log_write "drv_remote" "error" "Remote rele open error: $error"
message_write "warning" "Remote rele open error: $error"
return 1
fi
}
#
# Chiusura rele
#
# $1 identificativo relè da chiudere (apre l'elettrovalvola)
#
function drv_remote_rele_close {
local remote=`echo $1 | $CUT -d':' -f3,3`
local remote_alias=`echo $1 | $CUT -d':' -f4,4`
local command="open $remote_alias force"
echo "remote=$remote"
echo "remote_alias=$remote_alias"
echo "command=$command"
local response=$(drv_remote_command "$remote" "$command")
echo "response=$response"
local result=`echo $response|$JQ -M ".error.description"`
echo "result=$result"
if [[ "$result" != "\"\"" ]]; then
local error=$result
error="${error%\"}"
error="${error#\"}"
echo "error=$error"
log_write "drv_remoter" "error" "Remote rele close error: $error"
message_write "warning" "Remote rele close error: $error"
return 1
fi
}

View File

@@ -0,0 +1,27 @@
#
# Questa funzione viene invocata dalla funzione "setup_drv" di piGarden ad ogni avvio dello script
# e serve per eseguire l'eventuale setup del driver se necessario
#
function drv_remote_setup {
local all_remote=0
# Imposta le zone come remote
for i in $(seq $EV_TOTAL)
do
local a=EV"$i"_GPIO
local gpio="${!a}"
if [[ "$gpio" == drv:remote:* ]]; then
local varname=EV"$i"_REMOTE
declare -g $varname=1
all_remote=$((all_remote+1))
fi
done
# Se tutte le zone sono remote disabilita la gestione dell'alimentazione bistabile
if [ $all_remote -eq $EV_TOTAL ]; then
EV_MONOSTABLE=1
fi
}

View File

@@ -0,0 +1,34 @@
#
# Inizializza i rele che gestiscono l'alimentazione per le valvole bistabili
#
# $1 identificativo relè
#
function drv_remote_supply_bistable_init {
local FOO="bar"
}
#
# Imposta l'alimentazione delle elettrovalvole con voltaggio positivo
#
# $1 identificativo relè
#
function drv_remote_supply_positive {
local FOO="bar"
}
#
# Imposta l'alimentazione delle elettrovalvole con voltaggio negativo
#
# $1 identificativo relè
#
function drv_remote_supply_negative {
local FOO="bar"
}

View File

@@ -5,7 +5,7 @@
#
# Funzione di esempio
#
function sample_foo {
function drv_sample_foo {
echo "bar"

View File

@@ -0,0 +1,13 @@
#
# Ritorna lo stato delle condizioni meteo interrogando il servizio online
#
# return output: 0 - errore durante il recupero delle condizioni meteo
# >0 - rilevato pioggia, timestamp del rilevamento
# <0 - rilevato nessuna pioggia, timestamp del rilevamento
function drv_sample_rain_online_get {
local FOO="bar"
}

View File

@@ -1,6 +1,6 @@
#
# Questa funzione viene invocata dalla funzione "setup_drv" di piGarden ad ogni avvio dello script
# e serve per eseguire l'evenutale setup del driver se necessario
# e serve per eseguire l'eventuale setup del driver se necessario
#
function drv_sample_setup {

View File

@@ -0,0 +1,22 @@
# Driver for controlling Sonoff remote relays with Tasmota firmware via http protocol
More information on Sonoff Tasmota firmware: https://github.com/arendst/Sonoff-Tasmota
# Example of zone configuration in piGarden.conf
```bash
EV1_ALIAS="Giardino_Posteriore_DX"
EV1_GPIO="drv:sonoff_tasmota_http:SONOFF1:Power1"
EV1_MONOSTABLE=1
EV2_ALIAS="Giardino_Posteriore_CN"
EV2_GPIO="drv:sonoff_tasmota_http:SONOFF1:Power2"
EV2_MONOSTABLE=1
SONOFF1_IP="192.168.1.1"
SONOFF1_USER="user"
SONOFF1_PWD="pwd"
```
More information for configuration: https://www.lejubila.net/2018/06/pigarden-0-5-7-gestisci-le-tue-elettrovalvole-con-i-moduli-sonoff-grazie-al-nuovo-driver-sonoff_tasmota_http

View File

@@ -0,0 +1,50 @@
#
# Funzioni comuni utilizzate dal driver
#
#
# Invia un comando al modulo sonoff tramite http
#
# $1 identificativo modulo sonoff
# $2 comando da eseguire
#
function drv_sonoff_tasmota_http_command {
local remote="$1"
local command="$2"
local remote_ip_var=$remote"_IP"
local remote_user_var=$remote"_USER"
local remote_pwd_var=$remote"_PWD"
local remote_ip="${!remote_ip_var}"
local remote_user="${!remote_user_var}"
local remote_pwd="${!remote_pwd_var}"
local url="http://$remote_ip/cm"
local credentials=""
local response=""
if [[ ! -z $remote_user ]] && [[ ! -z $remote_pwd ]]; then
credentials="user=$remote_user&password=$remote_pwd&"
fi
url="$url?$credentials$command"
echo "url api sonoff: $url" >> "$LOG_OUTPUT_DRV_FILE"
$CURL --connect-timeout 5 -sb -H "$url"
local res=$?
echo "curl code response: $res" >> "$LOG_OUTPUT_DRV_FILE"
if [ "$res" != "0" ]; then
echo "The curl command failed with: $res - url: $url"
fi
#echo "{\"POWER1\":\"ON\"}"
}

View File

@@ -0,0 +1,7 @@
#
# File di configurazione del driver
#
# Dichiarazione variabile di esempio
#declare -g SAMPLE_FOO
#SAMPLE_FOO="bar"

View File

@@ -0,0 +1,9 @@
#
# Questa funzione viene inviocata dalla funzione "init" di piGarden se sono presenti elettrovalvole o sensori che utilizzano questo driver
#
function drv_sonoff_tasmota_http_init {
local FOO=bar
}

View File

@@ -0,0 +1,23 @@
#
# Inizializza il sensore di rilevamento pioggia
#
# $i identificativo gpio del sensore di pioggia
#
function drv_sonoff_tasmota_http_rain_sensor_init {
local FOO="bar"
}
#
# Ritorna lo stato del sensore di rilevamento pioggia
#
# $i identificativo gpio del sensore di pioggia
#
function drv_sonoff_tasmota_http_rain_sensor_get {
local FOO="bar"
}

View File

@@ -0,0 +1,78 @@
#
# Inizializzazione rele
#
# $1 identificativo relè da inizializzare
#
function drv_sonoff_tasmota_http_rele_init {
drv_sonoff_tasmota_http_rele_open "$1"
}
#
# Apertura rele
#
# $1 identificativo relè da aprire (chiude l'elettrovalvola)
#
function drv_sonoff_tasmota_http_rele_open {
local remote=`echo $1 | $CUT -d':' -f3,3`
local remote_alias=`echo $1 | $CUT -d':' -f4,4`
local command="cmnd=$remote_alias%20Off"
echo "remote=$remote"
echo "remote_alias=$remote_alias"
echo "command=$command"
local response=$(drv_sonoff_tasmota_http_command "$remote" "$command")
echo "response=$response"
local jskey=${remote_alias^^}
local result=$(echo $response|$JQ -M ".$jskey")
echo "result=$result"
if [[ "$result" != "\"OFF\"" ]]; then
local error="Command error: $response"
error="${error%\"}"
error="${error#\"}"
echo "error=$error"
log_write "drv_sonoff_tasmota_http" "error" "Remote rele open error: $error"
message_write "warning" "Remote rele open error: $error"
return 1
fi
}
#
# Chiusura rele
#
# $1 identificativo relè da chiudere (apre l'elettrovalvola)
#
function drv_sonoff_tasmota_http_rele_close {
local remote=`echo $1 | $CUT -d':' -f3,3`
local remote_alias=`echo $1 | $CUT -d':' -f4,4`
local command="cmnd=$remote_alias%20On"
echo "remote=$remote"
echo "remote_alias=$remote_alias"
echo "command=$command"
local response=$(drv_sonoff_tasmota_http_command "$remote" "$command")
echo "response=$response"
local jskey=${remote_alias^^}
local result=$(echo $response|$JQ -M ".$jskey")
echo "result=$result"
if [[ "$result" != "\"ON\"" ]]; then
local error="Command error: $response"
error="${error%\"}"
error="${error#\"}"
echo "error=$error"
log_write "drv_sonoff_tasmota_http" "error" "Remote rele close error: $error"
message_write "warning" "Remote rele close error: $error"
return 1
fi
}

View File

@@ -0,0 +1,10 @@
#
# Questa funzione viene invocata dalla funzione "setup_drv" di piGarden ad ogni avvio dello script
# e serve per eseguire l'eventuale setup del driver se necessario
#
function drv_sonoff_tasmota_http_setup {
local FOO="bar"
}

View File

@@ -0,0 +1,34 @@
#
# Inizializza i rele che gestiscono l'alimentazione per le valvole bistabili
#
# $1 identificativo relè
#
function drv_sonoff_tasmota_http_supply_bistable_init {
drv_sonoff_tasmota_http_supply_negative "$1"
}
#
# Imposta l'alimentazione delle elettrovalvole con voltaggio positivo
#
# $1 identificativo relè
#
function drv_sonoff_tasmota_http_supply_positive {
drv_sonoff_tasmota_http_rele_open "$1"
}
#
# Imposta l'alimentazione delle elettrovalvole con voltaggio negativo
#
# $1 identificativo relè
#
function drv_sonoff_tasmota_http_supply_negative {
drv_sonoff_tasmota_http_rele_close "$1"
}

View File

@@ -74,7 +74,7 @@ function drv_spb16ch_boards_id_load {
SPB16CH_USED_ID+=("$board_id")
done
else
log_write "spb16ch: file $SPB16CH_BOARD_ID_STORE_FILE not found: remember to run 'piGarden init' to generate the file"
log_write "drv_spb16ch" "error" "spb16ch: file $SPB16CH_BOARD_ID_STORE_FILE not found: remember to run 'piGarden init' to generate the file"
fi
}

View File

@@ -19,7 +19,7 @@ function drv_spb16ch_rele_open {
local rele_data=${SPB16CH_RELE_MAP[$rele_id]}
if [[ -z $rele_data ]]; then
local message="Error - Rele map not defined - rele_id=$rele_id - ($1)"
log_write "$message"
log_write "drv_spb16ch" "error" "$message"
message_write "warning" "$message"
fi
local address_num=${rele_data:0:2}
@@ -52,7 +52,7 @@ function drv_spb16ch_rele_close {
local rele_data=${SPB16CH_RELE_MAP[$rele_id]}
if [[ -z $rele_data ]]; then
local message="Error - Rele map not defined - rele_id=$rele_id - ($1)"
log_write "$message"
log_write "drv_spb16ch" "error" "$message"
message_write "warning" "$message"
fi
local address_num=${rele_data:0:2}

View File

@@ -0,0 +1,12 @@
# Driver for check wather condition with wunderground online service
# Example of configuration in piGarden.conf
```bash
WEATHER_SERVICE="wunderground"
WUNDERGROUND_KEY="" # Set with your api key of wunderground
WUNDERGROUND_LOCATION="IY/Monsummano" # Set with your location indentify, see http://www.wunderground.com/weather/api/d/docs?d=resources/country-to-iso-matching&MR=1
```

View File

@@ -0,0 +1,12 @@
#
# Funzioni comuni utilizzate dal driver
#
#
# Funzione di esempio
#
function sample_foo {
echo "bar"
}

View File

@@ -0,0 +1,36 @@
#
# Ritorna lo stato delle condizioni meteo interrogando il servizio online
#
# $i identificativo gpio del sensore di pioggia
#
# return output: 0 - errore durante il recupero delle condizioni meteo
# >0 - rilevato pioggia, timestamp del rilevamento
# <0 - rilevato nessuna pioggia, timestamp del rilevamento
function drv_wunderground_rain_online_get {
# http://www.wunderground.com/weather/api/d/docs?d=resources/phrase-glossary&MR=1
$CURL http://api.wunderground.com/api/$WUNDERGROUND_KEY/conditions/q/$WUNDERGROUND_LOCATION.json > $TMP_PATH/check_rain_online.json
local weather=`cat $TMP_PATH/check_rain_online.json | $JQ -M ".current_observation.weather"`
local current_observation=`cat $TMP_PATH/check_rain_online.json | $JQ -M ".current_observation"`
local local_epoch=`cat $TMP_PATH/check_rain_online.json | $JQ -M -r ".current_observation.local_epoch"`
if [ "$weather" = "null" ]; then
echo "0"
else
if [[ "$weather" == *"Rain"* ]] ||
[[ "$weather" == *"Snow"* ]] ||
[[ "$weather" == *"Hail"* ]] ||
[[ "$weather" == *"Ice"* ]] ||
[[ "$weather" == *"Thunderstorm"* ]] ||
[[ "$weather" == *"Drizzle"* ]];
then
echo $local_epoch
else
echo "-$local_epoch"
fi
echo "$current_observation" > "$STATUS_DIR/last_weather_online"
fi
}

4
events/cron_add_after/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
events/cron_add_before/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
events/cron_del_after/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
events/cron_del_before/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
events/ev_open_in_after/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
events/ev_open_in_before/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
events/exec_poweroff_after/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
events/exec_reboot_after/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
events/exec_reboot_before/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

111
events/scripts/rpinotify.sh Executable file
View File

@@ -0,0 +1,111 @@
#!/bin/bash
#
# Send telegram notificacion on triggered event
#
# $1 = event
# $2 = cause
# $3 = time
#
# To use this script, you must get your hash. Register for the rpinotify service. Get the hash and enter it below
#
# rpinotify token
TOKEN=""
EVENT="$1"
TO="mail@destination.com"
FROM="piGarden@your_domain.com"
SUBJECT="[piGarden notification mail] event $EVENT"
BODY=""
case "$EVENT" in
"init_before" | "init_after")
TIME=$2
BODY="PiGarden triggered new event --- EVENT: $EVENT --- TIME: $(/bin/date -d@$TIME)"
;;
"ev_open_before" | "ev_open_after")
ALIAS="$2"
FORCE="$3"
TIME=$4
BODY="PiGarden triggered new event --- EVENT: $EVENT --- ZONE: $ALIAS --- FORCED IRRIGATION: $FORCE --- TIME: $(/bin/date -d@$TIME)"
;;
"ev_open_in_before")
ALIAS="$2"
FORCE="$3"
MINUTE_START="$4"
MINUTE_STOP="$5"
TIME=$6
BODY="PiGarden triggered new event --- EVENT: $EVENT --- ZONE: $ALIAS --- FORCED IRRIGATION: $FORCE --- MINUTE START: $MINUTE_START --- MINUTE STOP: $MINUTE_STOP --- TIME: $(/bin/date -d@$TIME)"
;;
"ev_open_in_after")
ALIAS="$2"
FORCE="$3"
CRON_START="$4"
CRON_STOP="$5"
TIME=$6
BODY="PiGarden triggered new event --- EVENT: $EVENT --- ZONE: $ALIAS --- FORCED IRRIGATION: $FORCE --- CRON START: $CRON_START --- CRON STOP: $CRON_STOP --- TIME: $(/bin/date -d@$TIME)"
;;
"ev_close_before" | "ev_close_after")
ALIAS="$2"
TIME=$3
BODY="PiGarden triggered new event --- EVENT: $EVENT --- ZONE: $ALIAS --- TIME: $(/bin/date -d@$TIME);"
;;
"ev_not_open_for_rain" | "ev_not_open_for_rain_online" | "ev_not_open_for_rain_sensor")
ALIAS="$2"
TIME=$3
BODY="PiGarden triggered new event --- EVENT: $EVENT --- ZONE: $ALIAS --- TIME: $(/bin/date -d@$TIME)"
;;
"check_rain_sensor_before" | "check_rain_sensor_after" | "check_rain_sensor_change")
STATE="$2"
TIME=$3
BODY="PiGarden triggered new event --- EVENT: $EVENT --- STATE: $STATE --- TIME: $(/bin/date -d@$TIME)"
;;
"check_rain_online_before")
STATE="$2"
TIME=$3
BODY="PiGarden triggered new event --- EVENT: $EVENT --- STATE: $STATE --- TIME: $(/bin/date -d@$TIME)"
;;
"check_rain_online_after" | "check_rain_online_change")
STATE="$2"
WEATHER="$3"
TIME=$4
BODY="PiGarden triggered new event --- EVENT: $EVENT --- STATE: $STATE --- WEATHER: $WEARTHER --- TIME: $(/bin/date -d@$TIME)"
;;
"cron_add_before" | "cron_add_after")
CRON_TYPE="$2"
CRON_ARG="$3"
CRON_ELEMENT="$4"
TIME=$5
BODY="PiGarden triggered new event --- EVENT: $EVENT --- CRON TYPE: $CRON_TYPE --- CRON ARG: $CRON_ARG --- CRON ELEMENT: $CRON_ELEMENT\nTIME: $(/bin/date -d@$TIME)"
;;
"cron_del_before" | "cron_del_after")
CRON_TYPE="$2"
CRON_ARG="$3"
TIME=$4
BODY="PiGarden triggered new event --- EVENT: $EVENT --- CRON TYPE: $CRON_TYPE --- CRON ARG: $CRON_ARG --- TIME: $(/bin/date -d@$TIME)"
;;
"exec_poweroff_before" | "exec_poweroff_after" | "exec_reboot_before" | "exec_reboot_after")
TIME=$2
BODY="PiGarden triggered new event --- EVENT: $EVENT --- TIME: $(/bin/date -d@$TIME)"
;;
*)
exit
;;
esac
curl -X POST -F "text=$BODY" https://api.rpinotify.it/message/$TOKEN/

View File

@@ -24,13 +24,12 @@ EVENT="$1"
TO="mail@destination.com"
FROM="piGarden@your_domain.com"
SUBJECT="piGarden notification mail: event $EVENT"
SUBJECT="[piGarden notification mail] event $EVENT"
BODY=""
case "$EVENT" in
"init_before" | "init_after")
STATE="$2"
TIME=$3
TIME=$2
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nTIME: $(/bin/date -d@$TIME)"
;;
@@ -41,22 +40,74 @@ case "$EVENT" in
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nZONE: $ALIAS\nFORCED IRRIGATION: $FORCE\nTIME: $(/bin/date -d@$TIME)"
;;
"ev_open_in_before")
ALIAS="$2"
FORCE="$3"
MINUTE_START="$4"
MINUTE_STOP="$5"
TIME=$6
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nZONE: $ALIAS\nFORCED IRRIGATION: $FORCE\nMINUTE START: $MINUTE_START\nMINUTE STOP: $MINUTE_STOP\nTIME: $(/bin/date -d@$TIME)"
;;
"ev_open_in_after")
ALIAS="$2"
FORCE="$3"
CRON_START="$4"
CRON_STOP="$5"
TIME=$6
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nZONE: $ALIAS\nFORCED IRRIGATION: $FORCE\nCRON START: $CRON_START\nCRON STOP: $CRON_STOP\nTIME: $(/bin/date -d@$TIME)"
;;
"ev_close_before" | "ev_close_after")
ALIAS="$2"
TIME=$3
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nZONE: $ALIAS\nTIME: $(/bin/date -d@$TIME)"
;;
"ev_not_open_for_rain" | "ev_not_open_for_rain_online" | "ev_not_open_for_rain_sensor")
ALIAS="$2"
TIME=$3
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nZONE: $ALIAS\nTIME: $(/bin/date -d@$TIME)"
;;
"check_rain_sensor_before" | "check_rain_sensor_after" | "check_rain_sensor_change")
STATE="$2"
TIME=$3
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nSTATE: $ALIAS\nTIME: $(/bin/date -d@$TIME)"
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nSTATE: $STATE\nTIME: $(/bin/date -d@$TIME)"
;;
"check_rain_online_before" | "check_rain_online_after" | "check_rain_online_change")
"check_rain_online_before")
STATE="$2"
TIME=$3
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nSTATE: $ALIAS\nTIME: $(/bin/date -d@$TIME)"
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nSTATE: $STATE\nTIME: $(/bin/date -d@$TIME)"
;;
"check_rain_online_after" | "check_rain_online_change")
STATE="$2"
WEATHER="$3"
TIME=$4
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nSTATE: $STATE\nWEATHER: $WEARTHER\nTIME: $(/bin/date -d@$TIME)"
;;
"cron_add_before" | "cron_add_after")
CRON_TYPE="$2"
CRON_ARG="$3"
CRON_ELEMENT="$4"
TIME=$5
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nCRON TYPE: $CRON_TYPE\nCRON ARG: $CRON_ARG\nCRON ELEMENT: $CRON_ELEMENT\nTIME: $(/bin/date -d@$TIME)"
;;
"cron_del_before" | "cron_del_after")
CRON_TYPE="$2"
CRON_ARG="$3"
TIME=$4
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nCRON TYPE: $CRON_TYPE\nCRON ARG: $CRON_ARG\nTIME: $(/bin/date -d@$TIME)"
;;
"exec_poweroff_before" | "exec_poweroff_after" | "exec_reboot_before" | "exec_reboot_after")
TIME=$2
BODY="PiGarden triggered new event\n\nEVENT: $EVENT\nTIME: $(/bin/date -d@$TIME)"
;;
*)

View File

@@ -10,7 +10,7 @@ function cron_del {
if [ -z "$CRON_TYPE" ]; then
echo "Cron type is empty" >&2
log_write "Cron type is empty"
log_write "cron" "error" "Cron type is empty"
return 1
fi
@@ -25,24 +25,24 @@ function cron_del {
fi
if ! [[ $START =~ $re ]] ; then
echo "Cron start don't find" >&2
log_write "Cron start don't find"
log_write "cron" "error" "Cron start don't find"
return 1
fi
if ! [[ $END =~ $re ]] ; then
echo "Cron end cron don't find" >&2
log_write "Cron end cron don't find"
log_write "cron" "error" "Cron end cron don't find"
return 1
fi
if [ "$START" -gt "$END" ]; then
echo "Wrong position for start and end in cron" >&2
log_write "Wrong position for start and end in cron"
log_write "cron" "error" "Wrong position for start and end in cron"
return 1
fi
trigger_event "cron_del_before" "$1" "$2"
$SED "$START,${END}d" "$TMP_CRON_FILE" | $SED '$!N; /^\(.*\)\n\1$/!P; D' | $CRONTAB -
#$CRONTAB "$TMP_CRON_FILE"
rm "$TMP_CRON_FILE"
trigger_event "cron_del_after" "$1" "$2"
}
@@ -74,7 +74,7 @@ function cron_add {
if [ -z "$CRON_TYPE" ]; then
echo "Cron type is empty" >&2
log_write "Cron type is empty"
log_write "cron" "error" "Cron type is empty"
return 1
fi
@@ -91,12 +91,12 @@ function cron_add {
else
if ! [[ $START =~ $re ]] ; then
echo "Cron start don't find" >&2
log_write "Cron start don't find"
log_write "cron" "error" "Cron start don't find"
return 1
fi
if ! [[ $END =~ $re ]] ; then
echo "Cron end cron don't find" >&2
log_write "Cron end cron don't find"
log_write "cron" "error" "Cron end cron don't find"
return 1
fi
START=$(($START + 1))
@@ -104,7 +104,7 @@ function cron_add {
if [ "$START" -gt "$END" ]; then
echo "Wrong position for start and end in cron" >&2
log_write "Wrong position for start and end in cron"
log_write "cron" "error" "Wrong position for start and end in cron"
return 1
fi
@@ -183,7 +183,7 @@ function cron_add {
*)
echo "Wrong cron type: $CRON_TYPE"
log_write "Wrong cron type: $CRON_TYPE"
log_write "cron" "error" "Wrong cron type: $CRON_TYPE"
;;
esac
@@ -206,8 +206,10 @@ function cron_add {
echo "$CRON_DISABLED$CRON_M $CRON_H $CRON_DOM $CRON_MON $CRON_DOW $CRON_COMMAND" >> "$TMP_CRON_FILE2"
echo "# END cron $CRON_TYPE $CRON_ARG" >> "$TMP_CRON_FILE2"
trigger_event "cron_add_before" "$CRON_TYPE" "$CRON_ARG" "$CRON_DISABLED$CRON_M $CRON_H $CRON_DOM $CRON_MON $CRON_DOW $CRON_COMMAND"
$CRONTAB "$TMP_CRON_FILE2"
rm "$TMP_CRON_FILE" "$TMP_CRON_FILE2"
trigger_event "cron_add_after" "$CRON_TYPE" "$CRON_ARG" "$CRON_DISABLED$CRON_M $CRON_H $CRON_DOM $CRON_MON $CRON_DOW $CRON_COMMAND"
}
@@ -223,7 +225,7 @@ function cron_get {
if [ -z "$CRON_TYPE" ]; then
echo "Cron type is empty" >&2
log_write "Cron type is empty"
log_write "cron" "error" "Cron type is empty"
return 1
fi
@@ -239,12 +241,12 @@ function cron_get {
else
if ! [[ $START =~ $re ]] ; then
echo "Cron start don't find" >&2
log_write "Cron start don't find"
log_write "cron" "error" "Cron start don't find"
return 1
fi
if ! [[ $END =~ $re ]] ; then
echo "Cron end cron don't find" >&2
log_write "Cron end cron don't find"
log_write "cron" "error" "Cron end cron don't find"
return 1
fi
START=$(($START + 1))
@@ -252,7 +254,7 @@ function cron_get {
if [ "$START" -gt "$END" ]; then
echo "Wrong position for start and end in cron" >&2
log_write "Wrong position for start and end in cron"
log_write "cron" "error" "Wrong position for start and end in cron"
return 1
fi
@@ -368,7 +370,7 @@ function add_cron_open {
local exists=`alias_exists $1`
if [ "check $exists" = "check FALSE" ]; then
log_write "Alias $1 not found"
log_write "cron" "error" "Alias $1 not found"
echo "Alias $1 not found"
return 1
fi
@@ -385,7 +387,7 @@ function del_cron_open {
local exists=`alias_exists $1`
if [ "check $exists" = "check FALSE" ]; then
log_write "Alias $1 not found"
log_write "cron" "error" "Alias $1 not found"
echo "Alias $1 not found"
return 1
fi
@@ -402,7 +404,7 @@ function get_cron_open {
local exists=`alias_exists $1`
if [ "check $exists" = "check FALSE" ]; then
log_write "Alias $1 not found"
log_write "cron" "error" "Alias $1 not found"
echo "Alias $1 not found"
return 1
fi
@@ -419,7 +421,7 @@ function del_cron_open_in {
local exists=`alias_exists $1`
if [ "check $exists" = "check FALSE" ]; then
log_write "Alias $1 not found"
log_write "cron" "error" "Alias $1 not found"
echo "Alias $1 not found"
return 1
fi
@@ -437,7 +439,7 @@ function get_cron_close {
local exists=`alias_exists $1`
if [ "check $exists" = "check FALSE" ]; then
log_write "Alias $1 not found"
log_write "cron" "error" "Alias $1 not found"
echo "Alias $1 not found"
return 1
fi
@@ -460,7 +462,7 @@ function add_cron_close {
local exists=`alias_exists $1`
if [ "check $exists" = "check FALSE" ]; then
log_write "Alias $1 not found"
log_write "cron" "error" "Alias $1 not found"
echo "Alias $1 not found"
return 1
fi
@@ -477,7 +479,7 @@ function del_cron_close {
local exists=`alias_exists $1`
if [ "check $exists" = "check FALSE" ]; then
log_write "Alias $1 not found"
log_write "cron" "error" "Alias $1 not found"
echo "Alias $1 not found"
return 1
fi
@@ -557,7 +559,79 @@ function cron_disable_all_open_close {
fi
done
}
#
# Attiva tutte le schedulazioni di apertura e chiusura elettrovalvole
#
function cron_enable_all_open_close {
local a=""
local al=""
local cron=""
#
# Disabilita tutte le schedulazioni di apertura
#
for i in $(seq $EV_TOTAL)
do
a=EV"$i"_ALIAS
al=${!a}
local crons=`get_cron_open $al`
if [[ ! -z "$crons" ]]; then
del_cron_open $al
IFS=$'\n' # make newlines the only separator
for cron in $crons
do
#echo "-- $cron --"
CRON_M=`echo $cron | $CUT -d' ' -f1,1`
CRON_H=`echo $cron | $CUT -d' ' -f2,2`
CRON_DOM=`echo $cron | $CUT -d' ' -f3,3`
CRON_MON=`echo $cron | $CUT -d' ' -f4,4`
CRON_DOW=`echo $cron | $CUT -d' ' -f5,5`
if [[ ${CRON_M:0:1} == "#" ]]; then
CRON_M=${CRON_M:1:${#CRON_M}}
fi
#echo "++ $CRON_M $CRON_H $CRON_DOM $CRON_MON $CRON_DOW ++"
add_cron_open $al "$CRON_M" "$CRON_H" "$CRON_DOM" "$CRON_MON" "$CRON_DOW"
done
fi
done
#
# Disabilita tutte le schedulazioni di chiusura
#
for i in $(seq $EV_TOTAL)
do
a=EV"$i"_ALIAS
al=${!a}
local crons=`get_cron_close $al`
if [[ ! -z "$crons" ]]; then
del_cron_close $al
IFS=$'\n' # make newlines the only separator
for cron in $crons
do
#echo "-- $cron --"
CRON_M=`echo $cron | $CUT -d' ' -f1,1`
CRON_H=`echo $cron | $CUT -d' ' -f2,2`
CRON_DOM=`echo $cron | $CUT -d' ' -f3,3`
CRON_MON=`echo $cron | $CUT -d' ' -f4,4`
CRON_DOW=`echo $cron | $CUT -d' ' -f5,5`
if [[ ${CRON_M:0:1} == "#" ]]; then
CRON_M=${CRON_M:1:${#CRON_M}}
fi
#echo "++ $CRON_M $CRON_H $CRON_DOM $CRON_MON $CRON_DOW ++"
add_cron_close $al "$CRON_M" "$CRON_H" "$CRON_DOM" "$CRON_MON" "$CRON_DOW"
done
fi
done
}

View File

@@ -1,11 +1,15 @@
declare -a list_drv
#
# Funzione eseguita ad ogni avvio di piGarden.sh, si occupa includere tutti i file dei driver utilizzati e di lanciare
# la funzione di setup di ogni driver se esistente
#
function setup_drv {
#declare -a list_drv
list_drv=()
# Inizializza i driver per le elettrovalvole
# Raccoglie i nomi dei driver utilizzati per le elettrovalvole
for i in $(seq $EV_TOTAL)
do
local a=EV"$i"_GPIO
@@ -18,8 +22,8 @@ function setup_drv {
fi
done
# Inizializza i driver per gli altri gpio
for gpio in "$SUPPLY_GPIO_1" "$SUPPLY_GPIO_2" "$RAIN_GPIO"
# Raccoglie i nomi dei driver utilizzati per gli altri gpio
for gpio in "$SUPPLY_GPIO_1" "$SUPPLY_GPIO_2" "$RAIN_GPIO" "$WEATHER_SERVICE"
do
if [[ "$gpio" == drv:* ]]; then
local drv=`echo $gpio | $CUT -d':' -f2,2`
@@ -29,10 +33,13 @@ function setup_drv {
fi
done
#
# Esegue l'unclusione dei file dei driver e per ognuno lancia l'eventuale funzione di setup
#
local file_drv
for drv in "${list_drv[@]}"
do
for callback in config common init rele supply rainsensor setup
for callback in config common init rele supply rainsensor rainonline setup
do
file_drv="$DIR_SCRIPT/drv/$drv/$callback.include.sh"
if [ -f "$file_drv" ]; then
@@ -87,7 +94,7 @@ function drv_rele_init {
$GPIO -g mode $idx out # setta il gpio nella modalita di scrittura
# Il driver definito non è stato trovato
elif [ "$fnc" == "drvnotfound" ]; then
log_write "Driver not found: $idx"
log_write "drv" "error" "Driver not found: $idx"
message_write "warning" "Driver not found: $idx"
else
echo "$(date) $fnc arg:$idx" >> "$LOG_OUTPUT_DRV_FILE"
@@ -109,11 +116,15 @@ function drv_rele_close {
$GPIO -g write $idx $RELE_GPIO_CLOSE
# Il driver definito non è stato trovato
elif [ "$fnc" == "drvnotfound" ]; then
log_write "Driver not found: $idx"
log_write "drv" "error" "Driver not found: $idx"
message_write "warning" "Driver not found: $idx"
return 1
else
echo "$(date) $fnc arg:$idx" >> "$LOG_OUTPUT_DRV_FILE"
$fnc "$idx" >> "$LOG_OUTPUT_DRV_FILE" 2>&1
if [ $? -eq 1 ]; then
return 1
fi
fi
}
@@ -131,11 +142,15 @@ function drv_rele_open {
$GPIO -g write $idx $RELE_GPIO_OPEN
# Il driver definito non è stato trovato
elif [ "$fnc" == "drvnotfound" ]; then
log_write "Driver not found: $idx"
log_write "drv" "error" "Driver not found: $idx"
message_write "warning" "Driver not found: $idx"
return 1
else
echo "$(date) $fnc arg:$idx" >> "$LOG_OUTPUT_DRV_FILE"
$fnc "$idx" >> "$LOG_OUTPUT_DRV_FILE" 2>&1
if [ $? -eq 1 ]; then
return 1
fi
fi
}
@@ -157,7 +172,7 @@ function drv_supply_bistable_init {
$GPIO -g mode $idx1 out
# Il driver definito non è stato trovato
elif [ "$fnc1" == "drvnotfound" ]; then
log_write "Driver not found: $idx1"
log_write "drv" "error" "Driver not found: $idx1"
message_write "warning" "Driver not found: $idx1"
return
else
@@ -171,7 +186,7 @@ function drv_supply_bistable_init {
$GPIO -g mode $idx2 out
# Il driver definito non è stato trovato
elif [ "$fnc2" == "drvnotfound" ]; then
log_write "Driver not found: $idx2"
log_write "drv" "error" "Driver not found: $idx2"
message_write "warning" "Driver not found: $idx2"
else
echo "$(date) $fnc2 arg:$idx2" >> "$LOG_OUTPUT_DRV_FILE"
@@ -197,7 +212,7 @@ function drv_supply_positive {
$GPIO -g write $idx1 $SUPPLY_GPIO_POS
# Il driver definito non è stato trovato
elif [ "$fnc1" == "drvnotfound" ]; then
log_write "Driver not found: $idx1"
log_write "drv" "error" "Driver not found: $idx1"
message_write "warning" "Driver not found: $idx1"
return
else
@@ -210,7 +225,7 @@ function drv_supply_positive {
$GPIO -g write $idx2 $SUPPLY_GPIO_POS
# Il driver definito non è stato trovato
elif [ "$fnc2" == "drvnotfound" ]; then
log_write "Driver not found: $idx2"
log_write "drv" "error" "Driver not found: $idx2"
message_write "warning" "Driver not found: $idx2"
else
echo "$(date) $fnc2 arg:$idx2" >> "$LOG_OUTPUT_DRV_FILE"
@@ -236,7 +251,7 @@ function drv_supply_negative {
$GPIO -g write $idx1 $SUPPLY_GPIO_NEG
# Il driver definito non è stato trovato
elif [ "$fnc1" == "drvnotfound" ]; then
log_write "Driver not found: $idx1"
log_write "drv" "error" "Driver not found: $idx1"
message_write "warning" "Driver not found: $idx1"
return
else
@@ -249,7 +264,7 @@ function drv_supply_negative {
$GPIO -g write $idx2 $SUPPLY_GPIO_NEG
# Il driver definito non è stato trovato
elif [ "$fnc2" == "drvnotfound" ]; then
log_write "Driver not found: $idx2"
log_write "drv" "error" "Driver not found: $idx2"
message_write "warning" "Driver not found: $idx2"
else
echo "$(date) $fnc2 arg:$idx2" >> "$LOG_OUTPUT_DRV_FILE"
@@ -273,7 +288,7 @@ function drv_rain_sensor_init {
$GPIO -g mode $idx in
# Il driver definito non è stato trovato
elif [ "$fnc" == "drvnotfound" ]; then
log_write "Driver not found: $idx"
log_write "drv" "error" "Driver not found: $idx"
message_write "warning" "Driver not found: $idx"
else
echo "$(date) $fnc arg:$idx" >> "$LOG_OUTPUT_DRV_FILE"
@@ -297,7 +312,34 @@ function drv_rain_sensor_get {
vret=`$GPIO -g read $idx`
# Il driver definito non è stato trovato
elif [ "$fnc" == "drvnotfound" ]; then
log_write "Driver not found: $idx"
log_write "drv" "error" "Driver not found: $idx"
message_write "warning" "Driver not found: $idx"
else
echo "$(date) $fnc arg:$idx" >> "$LOG_OUTPUT_DRV_FILE"
vret=`$fnc "$idx"`
fi
echo "$vret"
}
#
# Legge lo stato le condizioni meteo dal servizio online
#
# $1 identificativo gpio sensore pioggia
#
function drv_rain_online_get {
local idx="$1"
local fnc=`get_driver_callback "rain_online_get" "$idx"`
local vret=""
# Nessun driver definito, esegue la lettura del sensore tramite gpio del raspberry
if [ -z "$fnc" ]; then
log_write "drv" "error" "Driver not found: $idx"
message_write "warning" "Driver not found: $idx"
# Il driver definito non è stato trovato
elif [ "$fnc" == "drvnotfound" ]; then
log_write "drv" "error" "Driver not found: $idx"
message_write "warning" "Driver not found: $idx"
else
echo "$(date) $fnc arg:$idx" >> "$LOG_OUTPUT_DRV_FILE"

View File

@@ -5,19 +5,6 @@
function trigger_event {
# check_rain_online_after
# check_rain_online_before
# check_rain_online_change
# check_rain_sensor_after
# check_rain_sensor_before
# check_rain_sensor_change
# ev_close_after
# ev_close_before
# ev_open_after
# ev_open_before
# init_after
# init_before
local EVENT="$1"
local CAUSE="$2"
local current_event_dir="$EVENT_DIR/$EVENT"
@@ -30,12 +17,32 @@ function trigger_event {
case "$EVENT" in
"ev_open_before" | "ev_open_after")
ALIAS="$2"
CURRENT_EVENT_ALIAS="$ALIAS"
FORCE="$3"
$f "$EVENT" "$ALIAS" "$FORCE" `date +%s` &> /dev/null
;;
"ev_open_in_before")
ALIAS="$2"
CURRENT_EVENT_ALIAS="$ALIAS"
FORCE="$3"
local MINUTE_START="$4"
local MINUTE_STOP="$5"
$f "$EVENT" "$ALIAS" "$FORCE" "$MINUTE_START" "$MINUTE_STOP" `date +%s` &> /dev/null
;;
"ev_open_in_after")
ALIAS="$2"
CURRENT_EVENT_ALIAS="$ALIAS"
FORCE="$3"
local CRON_START="$4"
local CRON_STOP="$5"
$f "$EVENT" "$ALIAS" "$FORCE" "$CRON_START" "$CRON_STOP" `date +%s` &> /dev/null
;;
"ev_close_before" | "ev_close_after")
ALIAS="$2"
CURRENT_EVENT_ALIAS="$ALIAS"
$f "$EVENT" "$ALIAS" `date +%s` &> /dev/null
;;
@@ -45,11 +52,40 @@ function trigger_event {
$f "$EVENT" "$STATE" `date +%s` &> /dev/null
;;
"check_rain_online_before" | "check_rain_online_after" | "check_rain_online_change")
"check_rain_online_before")
STATE="$2"
$f "$EVENT" "$STATE" `date +%s` &> /dev/null
;;
"check_rain_online_after" | "check_rain_online_change")
STATE="$2"
WEATHER="$3"
$f "$EVENT" "$STATE" "$WEATHER" `date +%s` &> /dev/null
;;
"init_before" | "init_after")
STATE="$2"
$f "$EVENT" `date +%s` &> /dev/null
;;
"cron_add_before" | "cron_add_after")
local CRON_TYPE="$2"
local CRON_ARG="$3"
local CRON_ELEMENT="$4"
$f "$EVENT" "$CRON_TYPE" "$CRON_ARG" "$CRON_ELEMENT" `date +%s` &> /dev/null
;;
"cron_del_before" | "cron_del_after")
local CRON_TYPE="$2"
local CRON_ARG="$3"
$f "$EVENT" "$CRON_TYPE" "$CRON_ARG" `date +%s` &> /dev/null
;;
"exec_poweroff_before" | "exec_poweroff_after" | "exec_reboot_before" | "exec_reboot_after" )
$f "$EVENT" `date +%s` &> /dev/null
;;
*)
$f "$EVENT" "$CAUSE" `date +%s` &> /dev/null
;;
@@ -57,7 +93,13 @@ function trigger_event {
local ec=$?
#echo "$EVENT ec=$ec" >> /tmp/piGarden.testevent
#CURRENT_EVENT="$EVENT"
#mqtt_status &
if [ $ec -ne 0 ]; then
CURRENT_EVENT="$EVENT"
mqtt_status &
log_write "Stop events chain for exit code $ec in $current_event_dir/$f"
return $ec
fi
@@ -66,6 +108,9 @@ function trigger_event {
fi
CURRENT_EVENT="$EVENT"
mqtt_status &
}

View File

@@ -1,47 +1,48 @@
#
# Controlla se se piove tramite http://api.wunderground.com/
# Esegue controllo meteo tramite servizio online configurato in WEATHER_SERVICE
#
function check_rain_online {
trigger_event "check_rain_online_before" ""
# http://www.wunderground.com/weather/api/d/docs?d=resources/phrase-glossary&MR=1
$CURL http://api.wunderground.com/api/$WUNDERGROUND_KEY/conditions/q/$WUNDERGROUND_LOCATION.json > $TMP_PATH/check_rain_online.json
local weather=`cat $TMP_PATH/check_rain_online.json | $JQ -M ".current_observation.weather"`
local current_observation=`cat $TMP_PATH/check_rain_online.json | $JQ -M ".current_observation"`
local local_epoch=`cat $TMP_PATH/check_rain_online.json | $JQ -M -r ".current_observation.local_epoch"`
local current_state_rain_online=""
local last_state_rain_online=`cat "$STATUS_DIR/last_state_rain_online" 2> /dev/null`
#echo $weather
#weather="[Light/Heavy] Drizzle"
if [ "$weather" = "null" ]; then
log_write "check_rain_online - failed read online data"
else
log_write "check_rain_online - weather=$weather, local_epoch=$local_epoch"
#if [[ "$weather" == *"Clear"* ]]; then
#if [[ "$weather" == *"Rain"* ]]; then
if [[ "$weather" == *"Rain"* ]] ||
[[ "$weather" == *"Snow"* ]] ||
[[ "$weather" == *"Hail"* ]] ||
[[ "$weather" == *"Ice"* ]] ||
[[ "$weather" == *"Thunderstorm"* ]] ||
[[ "$weather" == *"Drizzle"* ]];
then
current_state_rain_online='rain'
echo $local_epoch > "$STATUS_DIR/last_rain_online"
else
current_state_rain_online='norain'
fi
echo "$current_observation" > "$STATUS_DIR/last_weather_online"
if [ "$current_state_rain_online" != "$last_state_rain_online" ]; then
echo "$current_state_rain_online" > "$STATUS_DIR/last_state_rain_online"
trigger_event "check_rain_online_change" "$current_state_rain_online"
fi
if [ "$WEATHER_SERVICE" == "none" ]; then
log_write "rain" "warning" "check_rain_online - online service is disabled"
return
fi
trigger_event "check_rain_online_after" "$current_state_rain_online"
trigger_event "check_rain_online_before" ""
local local_epoch=`drv_rain_online_get $WEATHER_SERVICE`
local current_state_rain_online=""
local last_state_rain_online=`cat "$STATUS_DIR/last_state_rain_online" 2> /dev/null`
local weather="null"
if [[ $local_epoch =~ ^-?[0-9]+$ ]]; then
if [ $local_epoch -eq 0 ]; then
log_write "rain" "error" "check_rain_online - failed read online data"
else
if [ $local_epoch -gt 0 ]; then
current_state_rain_online='rain'
echo $local_epoch > "$STATUS_DIR/last_rain_online"
else
current_state_rain_online='norain'
fi
weather=$(cat "$STATUS_DIR/last_weather_online" | $JQ -M ".weather")
log_write "rain" "info" "check_rain_online - weather=$weather, local_epoch=$local_epoch"
if [ "$current_state_rain_online" != "$last_state_rain_online" ]; then
echo "$current_state_rain_online" > "$STATUS_DIR/last_state_rain_online"
trigger_event "check_rain_online_change" "$current_state_rain_online" "$weather"
fi
fi
else
log_write "rain" "error" "check_rain_online - failed read online data"
fi
trigger_event "check_rain_online_after" "$current_state_rain_online" "$weather"
}
#
# Controlla se se piove tramite sensore
#
@@ -56,11 +57,11 @@ function check_rain_sensor {
current_state_rain_sensor='rain'
local local_epoch=`date +%s`
echo $local_epoch > "$STATUS_DIR/last_rain_sensor"
log_write "check_rain_sensor - now it's raining ($local_epoch)"
log_write "rain" "info" "check_rain_sensor - now it's raining ($local_epoch)"
#return $local_epoch
else
current_state_rain_sensor='norain'
log_write "check_rain_sensor - now is not raining"
log_write "rain" "info" "check_rain_sensor - now is not raining"
fi
if [ "$current_state_rain_sensor" != "$last_state_rain_sensor" ]; then
echo "$current_state_rain_sensor" > "$STATUS_DIR/last_state_rain_sensor"
@@ -68,17 +69,38 @@ function check_rain_sensor {
fi
trigger_event "check_rain_sensor_after" "$current_state_rain_sensor"
else
log_write "Rain sensor not present"
log_write "rain" "warning" "Rain sensor not present"
fi
}
#
# Chiude tutte le elettrovalvole se sta piovendo
# Chiude tutte le elettrovalvole se sta piovendo oppure chiude quelle che hanno raggiunto l'mudità massima impostata
# Eseguie il controllo in tempo reale sul sensore hardware e sui dati dell'ultima chiamata eseguita online
#
function close_all_for_rain {
#
# Chiude le elettrovalvole che hanno raggiunto l'umidità del terreno impostata in EVx_SENSOR_MOISTURE
#
for i in $(seq $EV_TOTAL)
do
local a=EV"$i"_ALIAS
local al=${!a}
ev_status $al
local state=$?
local moisture=$(ev_check_moisture_autoclose $i)
if [ "$state" = "1" ] && [ "$moisture" -gt 0 ]; then
ev_close $al
log_write "irrigate" "warning" "close_all_for_rain - Close solenoid '$al' because maximum soil moisture has been reached"
fi
done
#
# Chiude le elettrovalvole in caso di pioggia
#
local close_all=0
local now=`date +%s`
@@ -101,6 +123,12 @@ function close_all_for_rain {
fi
if [ "$close_all" = "1" ]; then
#
# PIOVE
# Valuta se la sciare aperte le elettrovalvole in caso l'umidità del sensore non sia stata raggiunta
#
for i in $(seq $EV_TOTAL)
do
local a=EV"$i"_ALIAS
@@ -109,14 +137,51 @@ function close_all_for_rain {
local evnorain=${!a}
ev_status $al
local state=$?
local moisture=$(ev_check_moisture $i)
#echo "$al = $state"
if [ "$state" = "1" ] && [ "$evnorain" != "1" ]; then
if [ "$state" = "1" ] && [ "$evnorain" != "1" ] && [ "$moisture" -ne 0 ]; then
ev_close $al
log_write "close_all_for_rain - Close solenoid '$al' for rain"
log_write "irrigate" "warning" "close_all_for_rain - Close solenoid '$al' for rain"
fi
done
fi
}
#
# Mostra il timestamp dell'ultima pioggia rilevato dal sensore
#
function last_rain_sensor_timestamp {
cat "$STATUS_DIR/last_rain_sensor" 2> /dev/null
}
#
# Mostra il timestamp dell'ultima pioggia rilevato dal servizio online
#
function last_rain_online_timestamp {
cat "$STATUS_DIR/last_rain_online" 2> /dev/null
}
#
# Resetta il timestamp dell'ultima pioggia rilevato dal sensore
#
function reset_last_rain_sensor_timestamp {
trigger_event "reset_last_rain_sensor_timestamp_before" ""
rm "$STATUS_DIR/last_rain_sensor" 2> /dev/null
trigger_event "reset_last_rain_sensor_timestamp_after" ""
log_write "rain" "info" "reset_last_rain_sensor_timestamp"
}
#
# Resetta mostra il timestamp dell'ultima pioggia rilevato dal servizio online
#
function reset_last_rain_online_timestamp {
trigger_event "reset_last_rain_online_timestamp_before" ""
rm "$STATUS_DIR/last_rain_online" 2> /dev/null
trigger_event "reset_last_rain_online_timestamp_before" ""
log_write "rain" "info" "reset_last_rain_online_timestamp"
}

249
include/sensor.include.sh Normal file
View File

@@ -0,0 +1,249 @@
#
# Imposta lo stato di una elettrovalvola
# $1 numero del sensore
# $2 tipologia dello stato
# $3 stato da scrivere
#
function sensor_set_state {
trigger_event "sensor_set_state_before" $1 $2 $3
echo "$3" > "$STATUS_DIR/sensor$1_$2"
trigger_event "sensor_set_state_after" $1 $2 $3
}
#
# Legge lo stato di un sensore
# $1 numero del sensore
# $2 tipologia dello stato
#
function sensor_get_state {
if [ ! -f "$STATUS_DIR/sensor$1_$2" ]; then
sensor_set_state $1 $2 ""
fi
local state=$(cat "$STATUS_DIR/sensor$1_$2" 2> /dev/null)
echo $state
}
#
# Recupera il numero di un sensore in base all'alias
# $1 alias del sensore
#
function sensor_alias2number {
for i in $(seq $EV_TOTAL)
do
a=SENSOR"$i"_ALIAS
av=${!a}
if [ "$av" == "$1" ]; then
return $i
fi
done
log_write "general" "error" "ERROR sensor alias not found: $1"
message_write "warning" "Sensor alias not found"
mqtt_status
exit 1
}
#
# Verifica se un alias di un sensore esiste
# $1 alias dell'elettrovalvola
#
function sensor_alias_exists {
local vret='FALSE'
for i in $(seq $EV_TOTAL)
do
a=SENSOR"$i"_ALIAS
av=${!a}
if [ "$av" == "$1" ]; then
vret='TRUE'
fi
done
echo $vret
}
#
# Mostra lo stato di tutte le elettrovalvole
#
function sensor_status_all {
for i in $(seq $SENSOR_TOTAL)
do
a=SENSOR"$i"_ALIAS
av=${!a}
for t in $SENSOR_STATE_TYPE
do
local state=$(sensor_get_state $i $t)
echo -e "$av: $t $state"
done
done
}
#
# Mostra lo stato di un sensore
# $1 alias sensore
# $2 tipologia dello stato
#
function sensor_status {
sensor_alias2number $1
i=$?
if [ -z "$2" ]; then
for t in $SENSOR_STATE_TYPE
do
local state=$(sensor_get_state $i $t)
echo -e "$av: $t $state"
done
else
local state=$(sensor_get_state $i $2)
echo -e "$state"
fi
}
#
# Imposta lo stato di un sensore per alias
# $1 alias sensore
# $2 tipologia dello stato
# $3 stato da imopostare
#
function sensor_status_set {
sensor_alias2number $1
i=$?
sensor_set_state $i $2 $3
mqtt_status
}
#
# Stampa la lista degli alias dei sensori
#
function list_alias_sensor {
for i in $(seq $SENSOR_TOTAL)
do
local a=SENSOR"$i"_ALIAS
local al=${!a}
echo $al
done
}
#
# Stampa lo stato di tutti i sensori in formato json
#
function json_sensor_status_all {
local js=""
local js_item=""
local js_type=""
for i in $(seq $SENSOR_TOTAL)
do
a=SENSOR"$i"_ALIAS
av=${!a}
js_type=""
for t in $SENSOR_STATE_TYPE
do
local state=$(sensor_get_state $i $t)
js_type="$js_type \"$t\": \"$state\", "
done
js_type="${js_type::-2}"
js_item="$js_item \"$av\":{$js_type}, ";
done
if [[ ! -z $js_item ]]; then
js_item="${js_item::-2}"
fi
js="\"sensor\": {$js_item}"
echo $js
}
#
# Controlla se la zona comandata da un elettrovalvola ha raggiunto l'umidità necessaria per non fare partire l'irrigazione,
# faerla chiudere in caso di pioggia.
# Se è stata superata l'umidità indicata in EVx_SENSOR_MOISTURE ritorna l'umidità attuale del sensore relativo all'elettrovalvola
# in caso contrario ritorna 0, se no è impostato il parametro EV_xSENSOR_ALIAS o EVxSENSOR?MOISTURE ritorna il valore -1
#
# $1 numero elettrovalvola da controllare
#
function ev_check_moisture {
local s=EV"$1"_SENSOR_ALIAS
local sa=${!s}
if [[ -z $sa ]]; then
echo -1
return
fi
local moisture=$(sensor_status $sa moisture)
local s=EV"$1"_SENSOR_MOISTURE
local max_moisture=${!s}
if [ -z $max_moisture ]; then
echo -1
return
fi
if [ $moisture -gt $max_moisture ]; then
log_write "sensor" "info" "ev_check_moisture_autoclose: humidity of the \"$sa\" sensor reached: $moisture%"
echo $moisture
return $moisture
fi
echo 0
return 0
}
#
# Controlla se la zona comandata da un elettrovalvola ha raggiunto l'umidità necessaria per essere chiusa in automatico
# Se è stata superata l'umidità indicata in EVx_SENSOR_MOISTURE ritorna l'umidità attuale del sensore relativo all'elettrovalvola
# in caso contrario ritorna 0, se no è impostato il parametro EV_xSENSOR_ALIAS, EVxSENSOR_MOISTURE o
# EVxSENSOR_MOISTURE_AUTOCLOSE ritorna il valore -1
#
# $1 numero elettrovalvola da controllare
#
function ev_check_moisture_autoclose {
local s=EV"$1"_SENSOR_ALIAS
local sa=${!s}
if [[ -z $sa ]]; then
echo -1
return
fi
local moisture=$(sensor_status $sa moisture)
local s=EV"$1"_SENSOR_MOISTURE
local max_moisture=${!s}
if [ -z $max_moisture ]; then
echo -1
return
fi
local s=EV"$1"_SENSOR_MOISTURE_AUTOCLOSE
local autoclose=${!s}
if [ -z $autoclose ]; then
echo -1
return
fi
if [ $autoclose -ne 1 ]; then
echo -1
return
fi
if [ $moisture -gt $max_moisture ]; then
log_write "sensor" "info" "ev_check_moisture_autoclose: humidity of the \"$sa\" sensor reached: $moisture%"
echo $moisture
return $moisture
fi
echo 0
return 0
}

View File

@@ -19,7 +19,7 @@ function stop_socket_server {
exit 1
fi
log_write "stop socket server"
log_write "socket_server" "info" "stop socket server"
kill -9 $(list_descendants `cat "$TCPSERVER_PID_FILE"`) 2> /dev/null
kill -9 `cat "$TCPSERVER_PID_FILE"` 2> /dev/null
@@ -44,7 +44,7 @@ function socket_server_command {
user=$(echo "$user" | $TR -d '[\r\n]')
password=$(echo "$password" | $TR -d '[\r\n]')
if [ "$user" != "$TCPSERVER_USER" ] || [ "$password" != "$TCPSERVER_PWD" ]; then
log_write "socket connection from: $TCPREMOTEIP - Bad socket server credentials - user:$user"
log_write "socket_server" "warning" "socket connection from: $TCPREMOTEIP - Bad socket server credentials - user:$user"
json_error 0 "Bad socket server credentials"
return
fi
@@ -61,9 +61,9 @@ function socket_server_command {
arg7=$(echo "$line " | $CUT -d ' ' -f7)
arg8=$(echo "$line " | $CUT -d ' ' -f8)
log_write "socket connection from: $TCPREMOTEIP - command: $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 $arg7 $arg8"
log_write "socket_server" "info" "socket connection from: $TCPREMOTEIP - command: $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 $arg7 $arg8"
reset_messages &> /dev/null
#reset_messages &> /dev/null
case "$arg1" in
status)
@@ -102,6 +102,12 @@ function socket_server_command {
json_status
;;
cron_enable_all_open_close)
cron_enable_all_open_close &> /dev/null
message_write "success" "All solenoid enabled"
json_status
;;
set_general_cron)
local vret=""
for i in $arg2 $arg3 $arg4 $arg5 $arg6 $arg7
@@ -121,7 +127,7 @@ function socket_server_command {
if [[ ! -z $vret ]]; then
json_error 0 "Cron set failed"
log_write "Cron set failed: $vret"
log_write "socket_server" "error" "Cron set failed: $vret"
else
message_write "success" "Cron set successfull"
json_status
@@ -136,7 +142,7 @@ function socket_server_command {
if [[ ! -z $vret ]]; then
json_error 0 "Cron set failed"
log_write "Cron del failed: $vret"
log_write "socket_server" "error" "Cron del failed: $vret"
else
message_write "success" "Cron set successfull"
json_status
@@ -151,7 +157,7 @@ function socket_server_command {
if [[ ! -z $vret ]]; then
json_error 0 "Cron del failed"
log_write "Cron del failed: $vret"
log_write "socket_server" "error" "Cron del failed: $vret"
else
message_write "success" "Scheduled start successfully deleted"
json_status "get_cron_open_in:$arg2"
@@ -167,7 +173,7 @@ function socket_server_command {
if [[ ! -z $vret ]]; then
json_error 0 "Cron set failed"
log_write "Cron set failed: $vret"
log_write "socket_server" "error" "Cron set failed: $vret"
else
message_write "success" "Cron set successfull"
json_status
@@ -182,7 +188,7 @@ function socket_server_command {
if [[ ! -z $vret ]]; then
json_error 0 "Cron set failed"
log_write "Cron set failed: $vret"
log_write "socket_server" "error" "Cron set failed: $vret"
else
message_write "success" "Cron set successfull"
json_status
@@ -197,7 +203,7 @@ function socket_server_command {
if [[ ! -z $vret ]]; then
json_error 0 "Cron set failed"
log_write "Cron set failed: $vret"
log_write "socket_server" "error" "Cron set failed: $vret"
else
message_write "success" "Cron set successfull"
json_status
@@ -205,6 +211,21 @@ function socket_server_command {
;;
cmd_pigardensched)
local vret=""
vret=`cmd_pigardensched $arg2 $arg3 $arg4 $arg5 $arg6`
if [[ ! -z $vret ]]; then
json_error 0 "piGardenSched command failed"
log_write "socket_server" "error" "piGardenSched command failed: $vret"
else
message_write "success" "Schedule set successfull"
json_status
fi
;;
reboot)
message_write "warning" "System reboot is started"
json_status
@@ -219,13 +240,34 @@ function socket_server_command {
nohup $PATH_SCRIPT poweroff > /dev/null 2>&1 &
;;
reset_last_rain_sensor_timestamp)
reset_last_rain_sensor_timestamp
message_write "success" "Timestamp of last sensor rain successfull reset"
json_status
;;
reset_last_rain_online_timestamp)
reset_last_rain_online_timestamp
message_write "success" "Timestamp of last online rain successfull reset"
json_status
;;
sensor_status_set)
if [ "empty$arg2" == "empty" ]; then
json_error 0 "Alias sensor not specified"
else
sensor_status_set $arg2 $arg3 $arg4 &> /dev/null
json_status
fi
;;
*)
json_error 0 "invalid command"
;;
esac
reset_messages &> /dev/null
#reset_messages &> /dev/null
}

View File

@@ -10,7 +10,7 @@
#
function initialize {
log_write "Run initialize"
log_write "general" "info" "Run initialize"
unlock
@@ -26,10 +26,6 @@ function initialize {
# Imposta l'alimentazione con voltaggio negativo e setta i gpio in scrittura per le elettrovalvole bistabili
if [ "$EV_MONOSTABLE" != "1" ]; then
drv_supply_bistable_init "$SUPPLY_GPIO_1" "$SUPPLY_GPIO_2"
#$GPIO -g write $SUPPLY_GPIO_1 0
#$GPIO -g write $SUPPLY_GPIO_2 0
#$GPIO -g mode $SUPPLY_GPIO_1 out
#$GPIO -g mode $SUPPLY_GPIO_2 out
fi
# Elimina tutti gli stati delle elettrovalvole preesistenti
@@ -40,8 +36,6 @@ function initialize {
do
g=EV"$i"_GPIO
drv_rele_init "${!g}"
#$GPIO -g write ${!g} RELE_GPIO_OPEN # chiude l'alimentazione all'elettrovalvole
#$GPIO -g mode ${!g} out # setta il gpio nella modalita di scrittura
ev_set_state $i 0
done
@@ -56,21 +50,20 @@ function initialize {
# Inizializza il sensore di rilevamento pioggia
if [ -n "$RAIN_GPIO" ]; then
drv_rain_sensor_init "$RAIN_GPIO"
#$GPIO -g mode $RAIN_GPIO in
log_write "Rain sensor initialized"
log_write "rain" "info" "Rain sensor initialized"
else
log_write "Rain sensor not present"
log_write "rain" "info" "Rain sensor not present"
fi
trigger_event "init_after" ""
log_write "End initialize"
log_write "general" "info" "End initialize"
}
#
# Elimina i file contenente i messaggi da inserire nel json status
#
function reset_messages {
function reset_messages_old {
rm -f "$LAST_INFO_FILE.$!"
rm -f "$LAST_WARNING_FILE.$!"
rm -f "$LAST_SUCCESS_FILE.$!"
@@ -90,32 +83,53 @@ function ev_open {
local EVNUM=$?
local g=`ev_number2gpio $EVNUM`
local EVNORAIN=`ev_number2norain $EVNUM`
local EV_IS_REMOTE_VAR=EV"$EVNUM"_REMOTE
local EV_IS_REMOTE=${!EV_IS_REMOTE_VAR}
local EV_IS_MONOSTABLE_VAR=EV"$EVNUM"_MONOSTABLE
local EV_IS_MONOSTABLE=${!EV_IS_MONOSTABLE_VAR}
if [ ! "$2" = "force" ] && [ "$EVNORAIN" != "1" ]; then
if [[ "$NOT_IRRIGATE_IF_RAIN_ONLINE" -gt 0 && -f $STATUS_DIR/last_rain_online ]]; then
local last_rain=`cat $STATUS_DIR/last_rain_online`
local now=`date +%s`
local dif=0
let "dif = now - last_rain"
if [ $dif -lt $NOT_IRRIGATE_IF_RAIN_ONLINE ]; then
log_write "Solenoid '$1' not open for rain (online check)"
message_write "warning" "Solenoid not open for rain"
return
if [ ! "$2" = "force" ]; then
local moisture=$(ev_check_moisture $EVNUM)
if [ $moisture -gt 0 ]; then
message_write "warning" "solenoid not open because maximum soil moisture has been reached"
trigger_event "ev_not_open_for_moisture" "$1"
log_write "irrigate" "warning" "Solenoid '$1' not open because maximum soil moisture has been reached"
return
fi
if [ "$EVNORAIN" != "1" ]; then
if [[ "$NOT_IRRIGATE_IF_RAIN_ONLINE" -gt 0 && -f $STATUS_DIR/last_rain_online ]]; then
local last_rain=`cat $STATUS_DIR/last_rain_online`
local now=`date +%s`
local dif=0
let "dif = now - last_rain"
if [ $dif -lt $NOT_IRRIGATE_IF_RAIN_ONLINE ] && [ $moisture -ne 0 ]; then
message_write "warning" "Solenoid not open for rain"
trigger_event "ev_not_open_for_rain_online" "$1"
trigger_event "ev_not_open_for_rain" "$1"
log_write "irrigate" "warning" "Solenoid '$1' not open for rain (online check)"
return
fi
fi
check_rain_sensor
if [[ "$NOT_IRRIGATE_IF_RAIN_SENSOR" -gt 0 && -f $STATUS_DIR/last_rain_sensor && $moisture -ne 0 ]]; then
local last_rain=`cat $STATUS_DIR/last_rain_sensor`
local now=`date +%s`
local dif=0
let "dif = now - last_rain"
if [ $dif -lt $NOT_IRRIGATE_IF_RAIN_SENSOR ]; then
message_write "warning" "Solenoid not open for rain"
trigger_event "ev_not_open_for_rain_sensor" "$1"
trigger_event "ev_not_open_for_rain" "$1"
log_write "irrigate" "warning" "Solenoid '$1' not open for rain (sensor check)"
return
fi
fi
fi
check_rain_sensor
if [[ "$NOT_IRRIGATE_IF_RAIN_SENSOR" -gt 0 && -f $STATUS_DIR/last_rain_sensor ]]; then
local last_rain=`cat $STATUS_DIR/last_rain_sensor`
local now=`date +%s`
local dif=0
let "dif = now - last_rain"
if [ $dif -lt $NOT_IRRIGATE_IF_RAIN_SENSOR ]; then
log_write "Solenoid '$1' not open for rain (sensor check)"
message_write "warning" "Solenoid not open for rain"
return
fi
fi
fi
local state=1
@@ -125,34 +139,36 @@ function ev_open {
trigger_event "ev_open_before" "$1" "$2"
if [ $? -ne 0 ]; then
log_write "Solenoid '$1' not open due to external event"
log_write "irrigate" "warning" "Solenoid '$1' not open due to external event"
message_write 'warning' "Solenoid not open due to external event"
mqtt_status
return
fi
lock
# Gestisce l'apertura dell'elettrovalvola in base alla tipologia (monostabile / bistabile)
if [ "$EV_MONOSTABLE" == "1" ]; then
#$GPIO -g write $g $RELE_GPIO_CLOSE
if [ "$EV_MONOSTABLE" == "1" ] || [ "$EV_IS_REMOTE" == "1" ] || [ "$EV_IS_MONOSTABLE" == "1" ]; then
drv_rele_close "$g"
if [ $? -eq 1 ]; then
unlock
return
fi
else
supply_positive
#$GPIO -g write $g $RELE_GPIO_CLOSE
drv_rele_close "$g"
sleep 1
#$GPIO -g write $g $RELE_GPIO_OPEN
drv_rele_open "$g"
fi
ev_set_state $EVNUM $state
log_write "irrigate" "info" "Solenoid '$1' open"
message_write "success" "Solenoid open"
trigger_event "ev_open_after" "$1" "$2"
unlock
log_write "Solenoid '$1' open"
message_write "success" "Solenoid open"
}
#
@@ -173,23 +189,30 @@ function ev_open_in {
if ! [[ $minute_start =~ $re ]] ; then
echo -e "Time start of irrigation is wrong or not specified"
message_write "warning" "Time start of irrigation is wrong or not specified"
mqtt_status
return 1
fi
if ! [[ $minute_stop =~ $re ]] ; then
echo -e "Time stop of irrigation is wrong or not specified"
message_write "warning" "Time stop of irrigation is wrong or not specified"
mqtt_status
return 1
fi
if [ $minute_stop -lt "1" ] ; then
echo -e "Time stop of irrigation is wrong"
message_write "warning" "Time stop of irrigation is wrong"
mqtt_status
return 1
fi
if [ "empty$alias" == "empty" ]; then
echo -e "Alias solenoid not specified"
message_write "warning" "Alias solenoid not specified"
mqtt_status
return 1
fi
trigger_event "ev_open_in_before" "$3" "$4" "$1" "$2"
gpio_alias2number $alias > /dev/null 2>&1
minute_start=$(($minute_start + 1))
@@ -201,6 +224,7 @@ function ev_open_in {
if [ "$minute_start" -eq "1" ]; then
ev_open $alias $force
cron_start="- - - - -"
else
cron_add open_in $cron_start "$alias" "$force"
fi
@@ -210,8 +234,7 @@ function ev_open_in {
message_write "success" "Scheduled start successfully performed"
#echo $cron_start
#echo $cron_stop
trigger_event "ev_open_in_after" "$3" "$4" "$cron_start" "$cron_stop"
}
@@ -226,33 +249,36 @@ function ev_close {
ev_alias2number $1
EVNUM=$?
g=`ev_number2gpio $EVNUM`
local EV_IS_REMOTE_VAR=EV"$EVNUM"_REMOTE
local EV_IS_REMOTE=${!EV_IS_REMOTE_VAR}
trigger_event "ev_close_before" "$1"
lock
# Gestisce l'apertura dell'elettrovalvola in base alla tipologia (monostabile / bistabile)
if [ "$EV_MONOSTABLE" == "1" ]; then
#$GPIO -g write $g $RELE_GPIO_OPEN
if [ "$EV_MONOSTABLE" == "1" ] || [ "$EV_IS_REMOTE" == "1" ]; then
drv_rele_open "$g"
if [ $? -eq 1 ]; then
unlock
return
fi
else
supply_negative
#$GPIO -g write $g $RELE_GPIO_CLOSE
drv_rele_close "$g"
sleep 1
#$GPIO -g write $g $RELE_GPIO_OPEN
drv_rele_open "$g"
fi
ev_set_state $EVNUM 0
log_write "irrigate" "info" "Solenoid '$1' close"
message_write "success" "Solenoid close"
trigger_event "ev_close_after" "$1"
unlock
log_write "Solenoid '$1' close"
message_write "success" "Solenoid close"
cron_del open_in_stop $1 > /dev/null 2>&1
}
@@ -261,8 +287,6 @@ function ev_close {
#
function supply_positive {
drv_supply_positive "$SUPPLY_GPIO_1" "$SUPPLY_GPIO_2"
#$GPIO -g write $SUPPLY_GPIO_1 $SUPPLY_GPIO_POS
#$GPIO -g write $SUPPLY_GPIO_2 $SUPPLY_GPIO_POS
}
#
@@ -270,13 +294,13 @@ function supply_positive {
#
function supply_negative {
drv_supply_negative "$SUPPLY_GPIO_1" "$SUPPLY_GPIO_2"
#$GPIO -g write $SUPPLY_GPIO_1 $SUPPLY_GPIO_NEG
#$GPIO -g write $SUPPLY_GPIO_2 $SUPPLY_GPIO_NEG
}
#
# Scrive un messaggio nel file di log
# $1 log da scrivere
# $1 type
# $2 level
# $3 log da scrivere
#
function log_write {
if [ -e "$LOG_FILE" ]; then
@@ -287,7 +311,33 @@ function log_write {
fi
fi
echo -e "`date`\t\t$1" >> $LOG_FILE
if [ -e "$LOG_OUTPUT_DRV_FILE" ]; then
local actualsize=$($WC -c <"$LOG_OUTPUT_DRV_FILE")
if [ $actualsize -ge $LOG_FILE_MAX_SIZE ]; then
$GZIP $LOG_OUTPUT_DRV_FILE
$MV $LOG_OUTPUT_DRV_FILE.gz $LOG_OUTPUT_DRV_FILE.`date +%Y%m%d%H%M`.gz
fi
fi
echo -e "`date`\t\t$1\t$2\t$3" >> $LOG_FILE
log_send "$1" "$2" "`date '+%Y-%m-%d %H:%M:%S'`" "$3"
}
#
# Invia un log verso piGardenWeb
# $1 type
# $2 level
# $3 date time
# $4 messaggio
#
function log_send {
if [[ ! -z "$LOG_URL" ]]; then
$CURL $LOG_CURL_PARAM "$LOG_URL" -d "api_token=$LOG_API_TOKEN&type=$1&level=$2&datetime_log=$3&message=$4" &> /dev/null &
fi
}
#
@@ -296,6 +346,20 @@ function log_write {
# $2 messaggio
#
function message_write {
local file_message=""
if [ "$1" = 'info' ]; then
MESSAGE_INFO="$2"
elif [ "$1" = "warning" ]; then
MESSAGE_WARNING="$2"
elif [ "$1" = "success" ]; then
MESSAGE_SUCCESS="$2"
else
return
fi
}
function message_write_old {
local file_message=""
if [ "$1" = 'info' ]; then
file_message="$LAST_INFO_FILE.$!"
@@ -342,8 +406,9 @@ function gpio_alias2number {
fi
done
log_write "ERROR solenoid alias not found: $1"
log_write "general" "error" "ERROR solenoid alias not found: $1"
message_write "warning" "Solenoid alias not found"
mqtt_status
exit 1
}
@@ -361,8 +426,9 @@ function ev_alias2number {
fi
done
log_write "ERROR solenoid alias not found: $1"
log_write "general" "error" "ERROR solenoid alias not found: $1"
message_write "warning" "Solenoid alias not found"
mqtt_status
exit 1
}
@@ -447,7 +513,7 @@ function close_all {
#echo "$al = $state"
if [[ "$state" -gt "0" || "$1" = "force" ]]; then
ev_close $al
log_write "close_all - Close solenoid '$al' for rain"
log_write "irrigate" "info" "close_all - Close solenoid '$al' for rain"
fi
done
@@ -470,8 +536,9 @@ function list_alias {
#
# Stampa un json contanente lo status della centralina
# $1 .. $6 parametri opzionali
# - get_cron: aggiunge i dati relativi ai crontab delle scehdulazioni di apertura/chisura delle elettrovalvole
# - get_cron: aggiunge i dati relativi ai crontab delle scehdulazioni di apertura/chisura
# - get_cron_open_in: aggiunge i dati relativi ai crontab degli avvii ritardati eseguiti con il comando open_in
# - get_schedule: aggiunge i dati relativi alle schedulazioni di piGardenSched
#
function json_status {
local json=""
@@ -485,6 +552,13 @@ function json_status {
local last_success=""
local with_get_cron="0"
local with_get_cron_open_in="0"
local with_get_schedule="0"
local current_pid=$!
local json_event="\"event\": {\"event\": \"$CURRENT_EVENT\", \"alias\": \"$CURRENT_EVENT_ALIAS\"}"
if [ "$PARENT_PID" -gt "0" ]; then
current_pid=$PARENT_PID
fi
local vret=""
for i in $1 $2 $3 $4 $5 $6
@@ -497,9 +571,12 @@ function json_status {
with_get_cron_open_in="1"
elif [[ "$i" == get_cron_open_in:* ]]; then
with_get_cron_open_in="${i#get_cron_open_in:}"
elif [ $i = "get_schedule" ] && [[ $PIGARDENSCHED == "1" ]]; then
with_get_schedule="1"
fi
done
local json_ev=""
for i in $(seq $EV_TOTAL)
do
local a=EV"$i"_ALIAS
@@ -509,9 +586,14 @@ function json_status {
if [ -n "$json" ]; then
json="$json,"
fi
json="$json\"$i\":{\"name\":\"$av\",\"state\":$sv}"
json="$json\"$av\":{\"name\":\"$av\",\"state\":$sv}"
if [ -n "$json_ev" ]; then
json_ev="$json_ev,"
fi
json_ev="$json_ev\"$a\":{\"alias\":\"$av\"}"
done
json="\"zones\":{$json}"
json="\"zones\":{$json},\"ev\":{$json_ev}"
local last_rain_sensor=`cat "$STATUS_DIR/last_rain_sensor" 2> /dev/null`
local last_rain_online=`cat "$STATUS_DIR/last_rain_online" 2> /dev/null`
@@ -520,15 +602,10 @@ function json_status {
if [[ ! -z "$last_weather_online" ]]; then
json_last_weather_online=$last_weather_online
fi
if [ -f "$LAST_INFO_FILE.$!" ]; then
last_info=`cat "$LAST_INFO_FILE.$!"`
fi
if [ -f "$LAST_WARNING_FILE.$!" ]; then
last_warning=`cat "$LAST_WARNING_FILE.$!"`
fi
if [ -f "$LAST_SUCCESS_FILE.$!" ]; then
last_success=`cat "$LAST_SUCCESS_FILE.$!"`
fi
last_info="$MESSAGE_INFO"
last_warning="$MESSAGE_WARNING"
last_success="$MESSAGE_SUCCESS"
local json_last_weather_online="\"last_weather_online\":$json_last_weather_online"
local json_last_rain_sensor="\"last_rain_sensor\":\"$last_rain_sensor\""
local json_last_rain_online="\"last_rain_online\":\"$last_rain_online\""
@@ -569,6 +646,24 @@ function json_status {
fi
local json_cron="\"cron\":{$json_get_cron}"
local json_get_schedule=""
local json_schedule=""
if [ $with_get_schedule != "0" ]; then
json_get_schedule=""
for scheduled in `$PIGARDENSCHED_PATH sched`
do
local ev=$(echo $scheduled|$CUT -f1 -d";")
local evcomplete="$ev"_ALIAS
local al=${!evcomplete}
local json_get_schedule="\"$ev\": {\"alias\": \"$al\", \"entry\": \"$scheduled\"}, $json_get_schedule"
done
if [[ ! -z $json_get_schedule ]]; then
json_get_schedule="${json_get_schedule::-2}"
fi
local json_schedule=", \"schedule\":{$json_get_schedule}"
fi
local json_get_cron_open_in=""
if [ $with_get_cron_open_in != "0" ]; then
local values_open_in=""
@@ -600,9 +695,11 @@ function json_status {
json_get_cron_open_in="\"open_in\": {$values_open_in},\"open_in_stop\": {$values_open_in_stop}"
fi
local json_cron_open_in="\"cron_open_in\":{$json_get_cron_open_in}"
local json_cron_open_in="\"cron_open_in\":{$json_get_cron_open_in}"
local json_timestamp="\"timestamp\": $(date +%s)"
local json_sensor="$(json_sensor_status_all)"
json="{$json_version,$json,$json_last_weather_online,$json_error,$json_last_info,$json_last_warning,$json_last_success,$json_last_rain_online,$json_last_rain_sensor,$json_cron,$json_cron_open_in}"
json="{$json_version,$json_timestamp,$json_event,$json,$json_sensor,$json_last_weather_online,$json_error,$json_last_info,$json_last_warning,$json_last_success,$json_last_rain_online,$json_last_rain_sensor,$json_cron,$json_cron_open_in $json_schedule}"
echo "$json"
@@ -610,6 +707,26 @@ function json_status {
}
#
# Invia al broker mqtt il json contentente lo stato del sistema
#
# $1 parent pid (opzionale)
#
function mqtt_status {
if [ ! $MQTT_ENABLE -eq 1 ]; then
return
fi
if [ ! -z "$1" ]; then
PARENT_PID=$1
fi
local js=$(json_status)
$MOSQUITTO_PUB -h $MQTT_HOST -p $MQTT_PORT -u $MQTT_USER -P $MQTT_PWD -i $MQTT_CLIENT_ID -r -t "$MQTT_TOPIC" -m "$js"
}
#
# Mostra il i parametri dello script
#
@@ -624,9 +741,25 @@ function show_usage {
echo -e "\t$NAME_SCRIPT list_alias view list of aliases solenoid"
echo -e "\t$NAME_SCRIPT ev_status alias show status solenoid"
echo -e "\t$NAME_SCRIPT ev_status_all show status solenoids"
echo -e "\t$NAME_SCRIPT json_status [get_cron|get_cron_open_in] show status in json format"
echo -e "\t$NAME_SCRIPT check_rain_online check rain from http://api.wunderground.com/"
echo -e "\n"
echo -e "\t$NAME_SCRIPT list_alias_sensor view list of aliases sensor"
echo -e "\t$NAME_SCRIPT sensor_status alias [type] show status sensor (type: $SENSOR_STATE_TYPE)"
echo -e "\t$NAME_SCRIPT sensor_status_set alias type value set status of sensor (type: $SENSOR_STATE_TYPE)"
echo -e "\t$NAME_SCRIPT sensor_status_all show status of all sensors"
echo -e "\n"
echo -e "\t$NAME_SCRIPT last_rain_sensor_timestamp show timestamp of last rain sensor"
echo -e "\t$NAME_SCRIPT last_rain_online_timestamp show timestamp of last rain online"
echo -e "\t$NAME_SCRIPT reset_last_rain_sensor_timestamp show timestamp of last rain sensor"
echo -e "\t$NAME_SCRIPT reset_last_rain_online_timestamp show timestamp of last rain online"
echo -e "\n"
echo -e "\t$NAME_SCRIPT json_status [get_cron|get_cron_open_in|get_schedule] show status in json format"
echo -e "\t$NAME_SCRIPT mqtt_status send status in json format to mqtt broker"
echo -e "\n"
echo -e "\t$NAME_SCRIPT check_rain_online check rain from online api service"
echo -e "\t$NAME_SCRIPT check_rain_sensor check rain from hardware sensor"
echo -e "\n"
echo -e "\t$NAME_SCRIPT close_all_for_rain close all solenoid if it's raining"
echo -e "\t$NAME_SCRIPT close_all [force] close all solenoid"
echo -e "\n"
@@ -655,6 +788,8 @@ function show_usage {
echo -e "\t$NAME_SCRIPT del_cron_close alias remove all crontab for close a solenoid"
echo -e "\t$NAME_SCRIPT get_cron_close alias get all crontab for close a solenoid"
echo -e "\n"
echo -e "\t$NAME_SCRIPT cmd_pigardensched [prm1 [prm2 [prm3]...]] performs a pigardensched command"
echo -e "\n"
echo -e "\t$NAME_SCRIPT debug1 [parameter]|[parameter]|..] Run debug code 1"
echo -e "\t$NAME_SCRIPT debug2 [parameter]|[parameter]|..] Run debug code 2"
}
@@ -693,12 +828,12 @@ function lock {
local foo=bar
else
if [ "$current_time" -gt "$max_time" ]; then
log_write "Maximum locked time reached"
log_write "general" "error" "Maximum locked time reached"
sleep $max_time
unlock
exit 1
fi
log_write "Sleep 1 second for locked state"
log_write "general" "info" "Sleep 1 second for locked state"
sleep 1
lock $current_time
return
@@ -747,7 +882,7 @@ function send_identifier {
fi
echo "$ID" > "$FILE_ID"
log_write "Send installation identifier to collect usage"
log_write "general" "info" "Send installation identifier to collect usage"
$CURL https://www.lejubila.net/statistic/collect_usage/piGarden/$ID/$VERSION/$SUB_VERSION/$RELEASE_VERSION > /dev/null 2>&1
@@ -757,19 +892,135 @@ function send_identifier {
# Spenge il sistema
#
function exec_poweroff {
trigger_event "exec_poweroff_before"
local PATH_SCRIPT=`$READLINK -f "$DIR_SCRIPT/scripts/poweroff.sh"`
echo "$PATH_SCRIPT" > tmp/prova.txt
sleep 15
. $PATH_SCRIPT
trigger_event "exec_poweroff_after"
}
#
# Spenge il sistema
#
function exec_reboot {
trigger_event "exec_reboot_before"
local PATH_SCRIPT=`$READLINK -f "$DIR_SCRIPT/scripts/reboot.sh"`
sleep 15
. $PATH_SCRIPT
trigger_event "exec_reboot_after"
}
#
# Esegue un comando con piGardenSched
#
# $1 parametro 1
# $2 parametro 2
# $3 parametro 3
# $4 parametro 4
# $5 parametro 5
function cmd_pigardensched {
local foo="bar"
if [ $PIGARDENSCHED == 0 ]; then
echo "piGardenSched not configured in piGarden" >&2
log_write "piGardenSched" "error" "piGardenSched not configured in piGarden"
return
fi
$PIGARDENSCHED_PATH $1 $2 $3 $4 $5
if [ $? -ne 0 ]; then
echo "piGardenSched command failed"
fi
}
#
# Converte da gradi a direzione
#
# $1 gradi
#
function deg2dir {
local deg=$(echo $1 | $SED 's/\..*$//')
local dir=""
if [ "$deg" == "null" ]; then
echo ""
return
fi
# N 348.75 - 11.25
if [ $deg -le 11 ]; then
dir="North"
# NNE 11.25 - 33.75
elif [ $deg -le 33 ]; then
dir="NNE"
# NE 33.75 - 56.25
elif [ $deg -le 56 ]; then
dir="NE"
# ENE 56.25 - 78.75
elif [ $deg -le 78 ]; then
dir="ENE"
# E 78.75 - 101.25
elif [ $deg -le 101 ]; then
dir="East"
# ESE 101.25 - 123.75
elif [ $deg -le 123 ]; then
dir="ESE"
# SE 123.75 - 146.25
elif [ $deg -le 146 ]; then
dir="SE"
# SSE 146.25 - 168.75
elif [ $deg -le 168 ]; then
dir="SSE"
# S 168.75 - 191.25
elif [ $deg -le 191 ]; then
dir="South"
# SSW 191.25 - 213.75
elif [ $deg -le 213 ]; then
dir="SSW"
# SW 213.75 - 236.25
elif [ $deg -le 236 ]; then
dir="SW"
# WSW 236.25 - 258.75
elif [ $deg -le 258 ]; then
dir="WSW"
# W 258.75 - 281.25
elif [ $deg -le 281 ]; then
dir="West"
# WNW 281.25 - 303.75
elif [ $deg -le 303 ]; then
dir="WNW"
# NW 303.75 - 326.25
elif [ $deg -le 326 ]; then
dir="NW"
# NNW 326.25 - 348.75
elif [ $deg -le 348 ]; then
dir="NNW"
# N 348.75 - 11.25
else
dir="North"
fi
echo $dir
}
function debug1 {
@@ -781,8 +1032,8 @@ function debug2 {
}
VERSION=0
SUB_VERSION=5
RELEASE_VERSION=3
SUB_VERSION=6
RELEASE_VERSION=5
DIR_SCRIPT=`dirname $0`
NAME_SCRIPT=${0##*/}
@@ -808,20 +1059,42 @@ fi
. "$DIR_SCRIPT/include/cron.include.sh"
. "$DIR_SCRIPT/include/socket.include.sh"
. "$DIR_SCRIPT/include/rain.include.sh"
. "$DIR_SCRIPT/include/sensor.include.sh"
. "$DIR_SCRIPT/include/events.include.sh"
LAST_INFO_FILE="$STATUS_DIR/last_info"
LAST_WARNING_FILE="$STATUS_DIR/last_warning"
LAST_SUCCESS_FILE="$STATUS_DIR/last_success"
MESSAGE_INFO=""
MESSAGE_WARNING=""
MESSAGE_SUCCESS=""
CURRENT_EVENT=""
CURRENT_EVENT_ALIAS=""
SENSOR_STATE_TYPE="moisture temperature fertility illuminance"
PARENT_PID=0
if [ -z $SENSOR_TOTAL ]; then
SENSOR_TOTAL=0
fi
if [ -z $LOG_OUTPUT_DRV_FILE ]; then
$LOG_OUTPUT_DRV_FILE="/dev/null"
LOG_OUTPUT_DRV_FILE="/dev/null"
fi
if [ -z "$EVENT_DIR" ]; then
EVENT_DIR="$DIR_SCRIPT/events"
fi
if [ -z $WEATHER_SERVICE ]; then
WEATHER_SERVICE="drv:wunderground"
elif [ "$WEATHER_SERVICE" != "none" ]; then
WEATHER_SERVICE="drv:$WEATHER_SERVICE"
fi
PIGARDENSCHED="0"
if [[ -x "$PIGARDENSCHED_PATH" ]]; then
PIGARDENSCHED="1"
fi
# Elimina il file di lock se più vecchio di 11 secondi
if [ -f "$LOCK_FILE" ]; then
@@ -836,6 +1109,8 @@ fi
send_identifier &
setup_drv
#echo "EV_MONOSTABLE=$EV_MONOSTABLE"
case "$1" in
init)
initialize
@@ -872,10 +1147,50 @@ case "$1" in
ev_status_all
;;
list_alias_sensor)
list_alias_sensor
;;
sensor_status)
sensor_status $2 $3
;;
sensor_status_all)
sensor_status_all
;;
sensor_status_set)
sensor_status_set $2 $3 $4
;;
json_sensor_status_all)
json_sensor_status_all
;;
json_status)
json_status $2 $3 $4 $5 $6
;;
mqtt_status)
mqtt_status $2
;;
last_rain_sensor_timestamp)
last_rain_sensor_timestamp
;;
last_rain_online_timestamp)
last_rain_online_timestamp
;;
reset_last_rain_sensor_timestamp)
reset_last_rain_sensor_timestamp
;;
reset_last_rain_online_timestamp)
reset_last_rain_online_timestamp
;;
check_rain_online)
check_rain_online
;;
@@ -907,7 +1222,7 @@ case "$1" in
nohup $0 start_socket_server_daemon > /dev/null 2>&1 &
echo "Daemon is started widh pid $!"
log_write "start socket server with pid $!"
log_write "socket_server" "info" "start socket server with pid $!"
;;
start_socket_server_daemon)
@@ -989,6 +1304,10 @@ case "$1" in
get_cron_close)
get_cron_close $2
;;
cmd_pigardensched)
cmd_pigardensched $2 $3 $4 $5
;;
reboot)
exec_reboot
@@ -1017,4 +1336,4 @@ esac
rm "$TMP_CRON_FILE" 2> /dev/null
rm "$TMP_CRON_FILE-2" 2> /dev/null
reset_messages &> /dev/null
#reset_messages &> /dev/null