Compare commits
51 Commits
rainsensor
...
4d2cdc81ef
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d2cdc81ef | |||
| a8800280f3 | |||
| cb6220a059 | |||
| cdeb049b06 | |||
| 390dd41dac | |||
| a11db80ac0 | |||
| caf698bfd5 | |||
| 566b982e00 | |||
| 412c58e054 | |||
| cde121ced7 | |||
| 9d76bb3456 | |||
| ecef7fb368 | |||
| d81834131b | |||
| fa30f253be | |||
| 6326ec01fe | |||
| 9cf3ee0785 | |||
| fbd578f502 | |||
| 82a637d6d2 | |||
|
|
10045d5a11 | ||
|
|
3d0d36077d | ||
|
|
a30e1cb6ef | ||
|
|
81ce03e90a | ||
|
|
2001b09937 | ||
|
|
c91128cbb4 | ||
|
|
7242b1c2ab | ||
|
|
5152252653 | ||
|
|
50fc8a72b2 | ||
|
|
9183bbcef5 | ||
|
|
bdab0fb6fe | ||
|
|
6ed0510c7f | ||
|
|
15e36e95a3 | ||
|
|
3a5c3ef934 | ||
|
|
f3c11a7fee | ||
|
|
ac95194b3e | ||
|
|
a125ff31d8 | ||
|
|
bb435f0ee6 | ||
|
|
b8daa7acab | ||
|
|
dd289f8b31 | ||
|
|
c527702c72 | ||
|
|
b6de96debe | ||
|
|
ea681816fe | ||
|
|
8ca698d4ee | ||
|
|
70943c5f5e | ||
|
|
aa9799cdaf | ||
|
|
6d4f522f21 | ||
|
|
51e4391237 | ||
|
|
aa12f5a8a5 | ||
|
|
9d9ef42679 | ||
|
|
70c4e063f8 | ||
|
|
4d4b35c3cc | ||
|
|
71e91cc861 |
38
CHANGELOG.md
38
CHANGELOG.md
@@ -1,3 +1,41 @@
|
||||
# 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
|
||||
|
||||
|
||||
15
Python/Socket_server_x_test.py
Normal file
15
Python/Socket_server_x_test.py
Normal 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
434
Python/cron_include.py
Normal 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
487
Python/drv.include.py
Normal 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
255
Python/event_include.py
Normal 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
1616
Python/piGarden.py
Normal file
File diff suppressed because it is too large
Load Diff
432
Python/rain_include.py
Normal file
432
Python/rain_include.py
Normal 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
432
Python/sensor_include.py
Normal 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
525
Python/socket.include.py
Normal 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)
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -57,6 +65,9 @@ STAT="/usr/bin/stat"
|
||||
# # 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
|
||||
@@ -96,6 +107,9 @@ 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
|
||||
@@ -113,6 +127,16 @@ EV6_ALIAS="6" #
|
||||
EV6_GPIO=24 # Physical 18 - wPi 5
|
||||
|
||||
|
||||
|
||||
# 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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -57,6 +65,9 @@ STAT="/usr/bin/stat"
|
||||
# # 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
|
||||
@@ -79,15 +90,18 @@ RELE_GPIO_CLOSE=0
|
||||
RELE_GPIO_OPEN=1
|
||||
|
||||
# Id del gpio usato per collegare il sensore di rilevamento pioggia
|
||||
RAIN_GPIO="drv:rainsensorqty:25" # Physical 22 - wPi 6
|
||||
# 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_LOOPSFORSETRAINING=10 # dopo 10 impulsi, 10 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 numero di vaschette si considera una nuova pioggia
|
||||
|
||||
RAINSENSORQTY_MMEACH=0.33 # see RAINSENSORQTY driver readme for details
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -57,6 +65,9 @@ STAT="/usr/bin/stat"
|
||||
# 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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -57,6 +65,9 @@ STAT="/usr/bin/stat"
|
||||
# # 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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -57,6 +65,9 @@ STAT="/usr/bin/stat"
|
||||
# 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
|
||||
|
||||
@@ -91,24 +91,24 @@ function drv_openweathermap_get_ico {
|
||||
|
||||
declare -A w
|
||||
|
||||
w["01d"]="http://icons.wxug.com/i/c/k/clear.gif"
|
||||
w["01n"]="http://icons.wxug.com/i/c/k/nt_clear.gif"
|
||||
w["02d"]="http://icons.wxug.com/i/c/k/partlycloudy.gif"
|
||||
w["02n"]="http://icons.wxug.com/i/c/k/nt_partlycloudy.gif"
|
||||
w["03d"]="http://icons.wxug.com/i/c/k/cloudy.gif"
|
||||
w["03n"]="http://icons.wxug.com/i/c/k/nt_cloudy.gif"
|
||||
w["04d"]="http://icons.wxug.com/i/c/k/cloudy.gif"
|
||||
w["04n"]="http://icons.wxug.com/i/c/k/nt_cloudy.gif"
|
||||
w["09d"]="http://icons.wxug.com/i/c/k/sleet.gif"
|
||||
w["09n"]="http://icons.wxug.com/i/c/k/nt_sleet.gif"
|
||||
w["10d"]="http://icons.wxug.com/i/c/k/rain.gif"
|
||||
w["10n"]="http://icons.wxug.com/i/c/k/nt_rain.gif"
|
||||
w["11d"]="http://icons.wxug.com/i/c/k/tstorms.gif"
|
||||
w["11n"]="http://icons.wxug.com/i/c/k/nt_tstorms.gif"
|
||||
w["13d"]="http://icons.wxug.com/i/c/k/snow.gif"
|
||||
w["13n"]="http://icons.wxug.com/i/c/k/nt_snow.gif"
|
||||
w["50d"]="http://icons.wxug.com/i/c/k/fog.gif"
|
||||
w["50n"]="http://icons.wxug.com/i/c/k/nt_fog.gif"
|
||||
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]}
|
||||
|
||||
|
||||
@@ -34,11 +34,11 @@ function drv_openweathermap_rain_online_get {
|
||||
"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"),
|
||||
"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_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": "--",
|
||||
|
||||
4
drv/rainsensorqty/.gitignore
vendored
Normal file
4
drv/rainsensorqty/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
.set_var
|
||||
# Except this file
|
||||
!.gitignore
|
||||
@@ -2,8 +2,8 @@
|
||||
# Driver rainsensorqty - driver for measure the rain volume
|
||||
# Author: androtto
|
||||
# file README.md
|
||||
# Version: 0.2.0
|
||||
# Data: 11/Aug/2019
|
||||
# Version: 0.2.5
|
||||
# Data: 07/Apr/2020
|
||||
|
||||
|
||||
FILE DI CONFIGURAZIONE /etc/piGarden.conf:
|
||||
@@ -37,19 +37,38 @@ 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
|
||||
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
|
||||
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 l'ultima pioggia registrata
|
||||
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
|
||||
----------------------------------------
|
||||
@@ -65,14 +84,14 @@ 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_STATE="$TMPDIR/rainsensorqty_state"
|
||||
file che viene popolato con l'ultimo stato della vaschetta (formato timestamp:counter)
|
||||
|
||||
RAINSENSORQTY_DIR="$DIR_SCRIPT/drv/rainsensorqty"
|
||||
home directory del driver
|
||||
@@ -103,5 +122,3 @@ GPIO_RESISTOR="none" #pull-up|pull-down|none
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
# Author: androtto
|
||||
# file "rainsensorqty_CHECK.sh"
|
||||
# test script for checking rain status using drv_rainsensorqty_rain_sensor_get function
|
||||
# Version: 0.2.0a
|
||||
# Data: 13/Aug/2019
|
||||
# Version: 0.2.5
|
||||
# Data: 08/Jan/2020
|
||||
|
||||
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
|
||||
SCRIPTNAME=${0##*/}
|
||||
@@ -22,7 +22,38 @@ LOG_OUTPUT_DRV_FILE="$DIR_SCRIPT/log/$LOG_OUTPUT_DRV_FILE"
|
||||
. ./init.include.sh
|
||||
. ./rainsensor.include.sh
|
||||
|
||||
rain_history # update rain history file if not
|
||||
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 "RAIN HISTORY"
|
||||
cat $RAINSENSORQTY_HISTORY | rain_when_amount
|
||||
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
|
||||
|
||||
50
drv/rainsensorqty/commands/rainsensorqty_HISTORYRAW.sh
Executable file
50
drv/rainsensorqty/commands/rainsensorqty_HISTORYRAW.sh
Executable 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
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
# Author: androtto
|
||||
# file "rainsensorqty_RAINNOW.sh"
|
||||
# test script for simulate rain ... now!
|
||||
# Version: 0.2.0a
|
||||
# Data: 13/Aug/2019
|
||||
# Version: 0.2.5
|
||||
# Data: 07/Apr/2020
|
||||
|
||||
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
|
||||
SCRIPTNAME=${0##*/}
|
||||
@@ -19,8 +19,49 @@ CONFIG_ETC="/etc/piGarden.conf"
|
||||
. ./init.include.sh
|
||||
. ./rainsensor.include.sh
|
||||
|
||||
echo "RAIN now!"
|
||||
echo "$(date +%s):$RAINSENSORQTY_LOOPSFORSETRAINING" > ${RAINSENSORQTY_LASTRAIN}
|
||||
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 -e "\nLAST RAIN:"
|
||||
cat $RAINSENSORQTY_LASTRAIN | rain_when_amount
|
||||
|
||||
echo "last 2 rain events:"
|
||||
tail -2 $RAINSENSORQTY_HISTORY | rain_when_amount
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
# Author: androtto
|
||||
# file "test_rainsensorqty_CHECK.sh"
|
||||
# test script for checking rain status using drv_rainsensorqty_rain_sensor_get function
|
||||
# Version: 0.2.0a
|
||||
# Data: 13/Aug/2019
|
||||
# Version: 0.2.5
|
||||
# Data: 07/Apr/2020
|
||||
|
||||
SCRIPTDIR="$(cd `dirname $0` ; pwd )"
|
||||
SCRIPTNAME=${0##*/}
|
||||
@@ -26,26 +26,19 @@ LOG_OUTPUT_DRV_FILE="$DIR_SCRIPT/log/$LOG_OUTPUT_DRV_FILE"
|
||||
# RAINSENSORQTY_LASTRAIN
|
||||
# RAINSENSORQTY_HISTORY
|
||||
|
||||
rain_history # update rain history file if not
|
||||
|
||||
echo "RAIN HISTORY - last five events"
|
||||
tail -5 $RAINSENSORQTY_HISTORY | rain_when_amount
|
||||
|
||||
echo -e "\nLAST RAIN"
|
||||
cat $RAINSENSORQTY_LASTRAIN | rain_when_amount
|
||||
|
||||
#exit # for test
|
||||
|
||||
echo -e "\nbackup to .old files"
|
||||
cp -p $RAINSENSORQTY_HISTORY ${RAINSENSORQTY_HISTORY}.old
|
||||
cp -p $RAINSENSORQTY_LASTRAIN ${RAINSENSORQTY_LASTRAIN}.old
|
||||
echo "RAIN HISTORY - last two events"
|
||||
tail -2 $RAINSENSORQTY_HISTORY
|
||||
tail -2 $RAINSENSORQTY_HISTORY | rain_when_amount
|
||||
|
||||
echo "...removing last event"
|
||||
head -n-1 ${RAINSENSORQTY_HISTORY}.old > $RAINSENSORQTY_HISTORY
|
||||
tail -1 $RAINSENSORQTY_HISTORY > $RAINSENSORQTY_LASTRAIN
|
||||
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 five events"
|
||||
tail -5 $RAINSENSORQTY_HISTORY | rain_when_amount
|
||||
echo -e "\nnew RAIN HISTORY - last two events"
|
||||
tail -2 $RAINSENSORQTY_HISTORY
|
||||
tail -2 $RAINSENSORQTY_HISTORY | rain_when_amount
|
||||
|
||||
echo -e "\nnew LAST RAIN"
|
||||
cat $RAINSENSORQTY_LASTRAIN | rain_when_amount
|
||||
|
||||
1
drv/rainsensorqty/commands/test_pulse
Executable file
1
drv/rainsensorqty/commands/test_pulse
Executable file
@@ -0,0 +1 @@
|
||||
echo 1 > /tmp/tick ; sleep 0.9 ; echo 0 > /tmp/tick
|
||||
@@ -3,14 +3,19 @@
|
||||
# Author: androtto
|
||||
# file "common.include.sh"
|
||||
# common functions used by driver
|
||||
# Version: 0.2.0a
|
||||
# Data: 13/Aug/2019
|
||||
# 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'
|
||||
@@ -55,15 +60,70 @@ 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
|
||||
[[ ! -f $RAINSENSORQTY_LASTRAIN ]] && return 1
|
||||
if grep -q ^$(<$RAINSENSORQTY_LASTRAIN)$ $RAINSENSORQTY_HISTORY ; then
|
||||
: # do nothing
|
||||
return 2
|
||||
> $RAINSENSORQTY_HISTORYTMP
|
||||
|
||||
if lastrainevent=$( rainevents 1 ) ; then
|
||||
: # done ok
|
||||
else
|
||||
cat $RAINSENSORQTY_LASTRAIN >> $RAINSENSORQTY_HISTORY
|
||||
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
|
||||
}
|
||||
@@ -71,12 +131,14 @@ rain_history()
|
||||
rain_when_amount()
|
||||
{
|
||||
# from standard input
|
||||
# format $time:$endtime:$endsequence
|
||||
cat - | while read line
|
||||
do
|
||||
set -- ${line//:/ }
|
||||
when=$1
|
||||
howmuch=$2
|
||||
printf "RAINED on %s for %.2f mm\n" "$(date --date="@$1")" $( $JQ -n "$howmuch * $RAINSENSORQTY_MMEACH" )
|
||||
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
|
||||
}
|
||||
|
||||
@@ -88,3 +150,54 @@ check_TMPDIR()
|
||||
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
|
||||
}
|
||||
|
||||
66
drv/rainsensorqty/conf_example/config.include.sh
Normal file
66
drv/rainsensorqty/conf_example/config.include.sh
Normal 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
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
# Author: androtto
|
||||
# file "config.include.sh"
|
||||
# specific driver config file
|
||||
# Version: 0.2.0
|
||||
# Data: 11/Aug/2019
|
||||
# Version: 0.2.5
|
||||
# Data: 08/Jan/2020
|
||||
|
||||
export TMPDIR=/tmp
|
||||
|
||||
@@ -14,6 +14,8 @@ 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"
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
# Author: androtto
|
||||
# file "drv_rainsensorqty_monitor.sh"
|
||||
# monitor script
|
||||
# Version: 0.2.2
|
||||
# Data: 08/Sep/2019
|
||||
# Version: 0.2.5c
|
||||
# Data: 08/Dec/2020
|
||||
|
||||
resetcounter()
|
||||
{
|
||||
@@ -14,6 +14,19 @@ resetcounter()
|
||||
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 #
|
||||
###############
|
||||
@@ -51,34 +64,44 @@ 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 ))
|
||||
rain_history
|
||||
before="-1"
|
||||
|
||||
echo ""
|
||||
en_echo "---- NEW RUN ----"
|
||||
en_echo "WAITING FOR $RAINSENSORQTY_PULSE PULSE"
|
||||
|
||||
# loop forever
|
||||
while true
|
||||
do
|
||||
before=`date +%s`
|
||||
sleep $RAINSENSOR_DEBOUNCE
|
||||
en_echo "WAITING FOR $RAINSENSORQTY_PULSE PULSE"
|
||||
#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 ----"
|
||||
rain_history &
|
||||
fi
|
||||
(( counter+=1 ))
|
||||
en_echo "$RAINSENSORQTY_PULSE PULSE #$counter RECEIVED"
|
||||
echo "$now:$counter" > ${RAINSENSORQTY_STATE} &
|
||||
echo "$now:$counter" >> ${RAINSENSORQTY_STATE_HIST} &
|
||||
(( 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" &
|
||||
@@ -86,4 +109,5 @@ do
|
||||
else
|
||||
drv_rainsensorqty_writelog $f "now is $text" &
|
||||
fi
|
||||
(( before = now ))
|
||||
done
|
||||
|
||||
@@ -35,7 +35,7 @@ function drv_remote_rele_open {
|
||||
error="${error%\"}"
|
||||
error="${error#\"}"
|
||||
echo "error=$error"
|
||||
log_write "Remote rele open error: $error"
|
||||
log_write "drv_remote" "error" "Remote rele open error: $error"
|
||||
message_write "warning" "Remote rele open error: $error"
|
||||
return 1
|
||||
fi
|
||||
@@ -68,7 +68,7 @@ function drv_remote_rele_close {
|
||||
error="${error%\"}"
|
||||
error="${error#\"}"
|
||||
echo "error=$error"
|
||||
log_write "Remote rele close error: $error"
|
||||
log_write "drv_remoter" "error" "Remote rele close error: $error"
|
||||
message_write "warning" "Remote rele close error: $error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -35,7 +35,7 @@ function drv_sonoff_tasmota_http_rele_open {
|
||||
error="${error%\"}"
|
||||
error="${error#\"}"
|
||||
echo "error=$error"
|
||||
log_write "Remote rele open 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
|
||||
@@ -69,7 +69,7 @@ function drv_sonoff_tasmota_http_rele_close {
|
||||
error="${error%\"}"
|
||||
error="${error#\"}"
|
||||
echo "error=$error"
|
||||
log_write "Remote rele close 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
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,17 +25,17 @@ 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
|
||||
|
||||
@@ -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
|
||||
@@ -225,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
|
||||
|
||||
@@ -241,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))
|
||||
@@ -254,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
|
||||
|
||||
@@ -370,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
|
||||
@@ -387,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
|
||||
@@ -404,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
|
||||
@@ -421,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
|
||||
@@ -439,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
|
||||
@@ -462,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
|
||||
@@ -479,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
|
||||
@@ -559,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
|
||||
}
|
||||
|
||||
@@ -94,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"
|
||||
@@ -116,7 +116,7 @@ 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
|
||||
@@ -142,7 +142,7 @@ 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
|
||||
@@ -172,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
|
||||
@@ -186,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"
|
||||
@@ -212,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
|
||||
@@ -225,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"
|
||||
@@ -251,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
|
||||
@@ -264,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"
|
||||
@@ -288,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"
|
||||
@@ -312,7 +312,7 @@ 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"
|
||||
@@ -335,11 +335,11 @@ function drv_rain_online_get {
|
||||
|
||||
# Nessun driver definito, esegue la lettura del sensore tramite gpio del raspberry
|
||||
if [ -z "$fnc" ]; then
|
||||
log_write "Driver not found: $idx"
|
||||
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 "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"
|
||||
|
||||
@@ -94,10 +94,12 @@ function trigger_event {
|
||||
local ec=$?
|
||||
#echo "$EVENT ec=$ec" >> /tmp/piGarden.testevent
|
||||
|
||||
CURRENT_EVENT="$EVENT"
|
||||
mqtt_status &
|
||||
#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
|
||||
@@ -106,6 +108,9 @@ function trigger_event {
|
||||
|
||||
fi
|
||||
|
||||
CURRENT_EVENT="$EVENT"
|
||||
mqtt_status &
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
function check_rain_online {
|
||||
|
||||
if [ "$WEATHER_SERVICE" == "none" ]; then
|
||||
log_write "check_rain_online - online service is disable"
|
||||
log_write "rain" "warning" "check_rain_online - online service is disabled"
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -17,7 +17,7 @@ function check_rain_online {
|
||||
|
||||
if [[ $local_epoch =~ ^-?[0-9]+$ ]]; then
|
||||
if [ $local_epoch -eq 0 ]; then
|
||||
log_write "check_rain_online - failed read online data"
|
||||
log_write "rain" "error" "check_rain_online - failed read online data"
|
||||
else
|
||||
if [ $local_epoch -gt 0 ]; then
|
||||
current_state_rain_online='rain'
|
||||
@@ -27,14 +27,14 @@ function check_rain_online {
|
||||
fi
|
||||
weather=$(cat "$STATUS_DIR/last_weather_online" | $JQ -M ".weather")
|
||||
|
||||
log_write "check_rain_online - weather=$weather, local_epoch=$local_epoch"
|
||||
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 "check_rain_online - failed read online data"
|
||||
log_write "rain" "error" "check_rain_online - failed read online data"
|
||||
fi
|
||||
|
||||
trigger_event "check_rain_online_after" "$current_state_rain_online" "$weather"
|
||||
@@ -57,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"
|
||||
@@ -69,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`
|
||||
|
||||
@@ -102,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
|
||||
@@ -110,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
249
include/sensor.include.sh
Normal 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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +61,7 @@ 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
|
||||
|
||||
@@ -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,6 +240,27 @@ 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"
|
||||
;;
|
||||
|
||||
279
piGarden.sh
279
piGarden.sh
@@ -10,7 +10,7 @@
|
||||
#
|
||||
function initialize {
|
||||
|
||||
log_write "Run initialize"
|
||||
log_write "general" "info" "Run initialize"
|
||||
|
||||
unlock
|
||||
|
||||
@@ -50,13 +50,13 @@ function initialize {
|
||||
# Inizializza il sensore di rilevamento pioggia
|
||||
if [ -n "$RAIN_GPIO" ]; then
|
||||
drv_rain_sensor_init "$RAIN_GPIO"
|
||||
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"
|
||||
|
||||
}
|
||||
|
||||
@@ -88,35 +88,48 @@ function ev_open {
|
||||
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
|
||||
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 "Solenoid '$1' not open for rain (online check)"
|
||||
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
|
||||
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 "Solenoid '$1' not open for rain (sensor check)"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
local state=1
|
||||
@@ -126,7 +139,7 @@ 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
|
||||
@@ -150,7 +163,7 @@ function ev_open {
|
||||
|
||||
ev_set_state $EVNUM $state
|
||||
|
||||
log_write "Solenoid '$1' open"
|
||||
log_write "irrigate" "info" "Solenoid '$1' open"
|
||||
message_write "success" "Solenoid open"
|
||||
|
||||
trigger_event "ev_open_after" "$1" "$2"
|
||||
@@ -223,9 +236,6 @@ function ev_open_in {
|
||||
|
||||
trigger_event "ev_open_in_after" "$3" "$4" "$cron_start" "$cron_stop"
|
||||
|
||||
#echo $cron_start
|
||||
#echo $cron_stop
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -262,7 +272,7 @@ function ev_close {
|
||||
|
||||
ev_set_state $EVNUM 0
|
||||
|
||||
log_write "Solenoid '$1' close"
|
||||
log_write "irrigate" "info" "Solenoid '$1' close"
|
||||
message_write "success" "Solenoid close"
|
||||
|
||||
trigger_event "ev_close_after" "$1"
|
||||
@@ -288,7 +298,9 @@ function supply_negative {
|
||||
|
||||
#
|
||||
# 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
|
||||
@@ -299,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
|
||||
|
||||
}
|
||||
|
||||
#
|
||||
@@ -368,7 +406,7 @@ 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
|
||||
@@ -388,7 +426,7 @@ 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
|
||||
@@ -475,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
|
||||
|
||||
@@ -498,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=""
|
||||
@@ -513,6 +552,7 @@ 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\"}"
|
||||
|
||||
@@ -531,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
|
||||
@@ -543,10 +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`
|
||||
@@ -555,15 +602,7 @@ function json_status {
|
||||
if [[ ! -z "$last_weather_online" ]]; then
|
||||
json_last_weather_online=$last_weather_online
|
||||
fi
|
||||
#if [ -f "$LAST_INFO_FILE.$current_pid" ]; then
|
||||
# last_info=`cat "$LAST_INFO_FILE.$current_pid"`
|
||||
#fi
|
||||
#if [ -f "$LAST_WARNING_FILE.$current_pid" ]; then
|
||||
# last_warning=`cat "$LAST_WARNING_FILE.$current_pid"`
|
||||
#fi
|
||||
#if [ -f "$LAST_SUCCESS_FILE.$current_pid" ]; then
|
||||
# last_success=`cat "$LAST_SUCCESS_FILE.$current_pid"`
|
||||
#fi
|
||||
|
||||
last_info="$MESSAGE_INFO"
|
||||
last_warning="$MESSAGE_WARNING"
|
||||
last_success="$MESSAGE_SUCCESS"
|
||||
@@ -607,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=""
|
||||
@@ -640,8 +697,9 @@ function json_status {
|
||||
fi
|
||||
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_timestamp,$json_event,$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"
|
||||
|
||||
@@ -683,10 +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 "\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 "\t$NAME_SCRIPT check_rain_online check rain from http://api.wunderground.com/"
|
||||
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"
|
||||
@@ -715,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"
|
||||
}
|
||||
@@ -753,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
|
||||
@@ -807,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
|
||||
|
||||
@@ -835,6 +910,31 @@ function exec_reboot {
|
||||
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
|
||||
@@ -932,8 +1032,8 @@ function debug2 {
|
||||
}
|
||||
|
||||
VERSION=0
|
||||
SUB_VERSION=5
|
||||
RELEASE_VERSION=11
|
||||
SUB_VERSION=6
|
||||
RELEASE_VERSION=5
|
||||
|
||||
DIR_SCRIPT=`dirname $0`
|
||||
NAME_SCRIPT=${0##*/}
|
||||
@@ -959,11 +1059,9 @@ 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=""
|
||||
@@ -971,8 +1069,14 @@ 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"
|
||||
fi
|
||||
@@ -987,6 +1091,11 @@ 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
|
||||
max_age_lock_file=11
|
||||
@@ -1038,6 +1147,26 @@ 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
|
||||
;;
|
||||
@@ -1046,6 +1175,22 @@ case "$1" in
|
||||
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
|
||||
;;
|
||||
@@ -1077,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)
|
||||
@@ -1159,6 +1304,10 @@ case "$1" in
|
||||
get_cron_close)
|
||||
get_cron_close $2
|
||||
;;
|
||||
|
||||
cmd_pigardensched)
|
||||
cmd_pigardensched $2 $3 $4 $5
|
||||
;;
|
||||
|
||||
reboot)
|
||||
exec_reboot
|
||||
|
||||
Reference in New Issue
Block a user