diff --git a/.gitignore b/.gitignore index ef240af..7a3fa4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ +__pycache__/ caldera_esp/confidencial.h +config.cfg +TODO.txt diff --git a/README.md b/README.md index f583fa1..1aa8e5d 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,82 @@ # Automatizacion de Caldera - - Enciende o apaga el termo según configuración. Admite hasta dos - horarios de funcionamiento al día, ajustables en modo 3. - - El funcionamiento autonomo es la opción por defecto (modo 1). - - Funcionamiento manual espera instrucción para realizar acción de - encendido o apagado (modo 5). - - Hora y fecha ajustables en el modo 2 - - Posiciones del servo al encender o apagar el termo (modo 4). - Son dos posiciones, ya que el servo realiza movimiento repetitivo - con para asegurar el accionamiento mecánico del interruptor del termo. - - El modo 6 permite mover libremente el servo (desde 16 hasta 144). - Útil para probar ajustes. +***Control de encendido para caldera electrica.*** +Enciende o apaga el termo según configuración. Admite hasta dos horarios +de funcionamiento al día, ajustables en modo 3. +El funcionamiento autonomo es la opción por defecto (modo 1). +Funcionamiento manual espera instrucción para realizar acción de encendido +o apagado (modo 5). +Hora y fecha ajustables en el modo 2. -Hardware utilizado: -- ATmega328p (ArduinoNano old bootloader) -- RTC-DS3231 -- Servo -- esp8266 (ESP01) +Posiciones del servo al encender o apagar el termo (modo 4). Son dos posiciones, +ya que el servo realiza movimiento repetitivo para asegurar el accionamiento +mecánico del interruptor del termo. -Arduino funciona de forma autonoma, no requiere del módulo wifi. -El ESP-01 funciona como interface web entre el usuario y el puerto serie de arduino. +El modo 6 permite mover libremente el servo (desde 16 hasta 144). Útil para probar ajustes. -caldera.sh: utilidad para enviar controlar caldera por terminal. +### Hardware utilizado: +- [ATmega328p](https://en.wikipedia.org/wiki/Arduino_Nano) (ArduinoNano old bootloader) +- [Real time clock](https://en.wikipedia.org/wiki/Real-time_clock) (RTC-DS3231) +- [Servo](https://es.wikipedia.org/wiki/Servomotor) +- [esp8266](https://en.wikipedia.org/wiki/ESP8266) (ESP01) +- [buck converter](https://en.wikipedia.org/wiki/Buck_converter) + +**Arduino** funciona de forma autonoma, no requiere del módulo wifi. +**ESP-01** funciona como interface web basica entre el usuario y el puerto serie de arduino. + +### Herramientas de control (scripts) +- [caldera.sh](.scripts/caldera.sh) (bash) +- [caldera.py](.scripts/caldera.py) (python) ----- -## Control de horario de encendido caldera electrica +## Arduino +Control de horario de encendido caldera electrica. Trabaja de forma autonoma y/o comandado por puerto serie (**115200 baudio**). * 3 Modos de Operación - Automatizado (1, por defecto) - Manual (5) - Libre (6) -* 3 Modos de Configuracion - - Configuracion de fecha y hora (2) - - Configuracion horas de encendido y apagado (3) - - Configuracion de posiciones de encendido y apagado (4) +* 3 Modos de Configuración + - Configuración fecha y hora (2) + - Configuración horas de encendido y apagado (3) + - Configuración de posiciones de encendido y apagado (4) Arduino envia cada segundo los valores actuales de las variables de control al puerto serie. ``` - ej. - 1,0,5,7,16,18,120,90,45,62,1,42,24,14,7,2021 + ej. + 1,0,5,7,16,18,120,90,45,62,1,42,24,14,7,2021 - 1 > modo, - | 0 > estado termo (O = apagado, 1 = Encendido) - | | 5 > hora de encendido - | | | 7 > hora de apagado - | | | | 16 > 2da hora de encendido (opcional) - | | | | | 18 > 2da hora de apgado (opcional) - | | | | | | 120 > 1ra posicion encendido - | | | | | | | 90 > 2ra posicion encendido - | | | | | | | | 45 > 1ra posicion apagado - | | | | | | | | | 62 > 2ra posicion apagado - | | | | | | | | | | 1 > hora en RTC - | | | | | | | | | | | 42 > minutos en RTC - | | | | | | | | | | | | 24 > segundos en RTC - | | | | | | | | | | | | | 14 > dia en RTC - | | | | | | | | | | | | | | 7 > mes en RTC - | | | | | | | | | | | | | | | 2021 > año en RTC - 1,0,5,7,16,18,120,90,45,62,1,42,24,14,7,2021 + 1 ______________________________________________ modo, + | 0 ____________________________________________ estado termo (O = apagado, 1 = Encendido) + | | 5 __________________________________________ hora de encendido + | | | 7 ________________________________________ hora de apagado + | | | | 16 _____________________________________ 2da hora de encendido (opcional) + | | | | | 18 __________________________________ 2da hora de apagado (opcional) + | | | | | | 120 ______________________________ 1ra posicion encendido + | | | | | | | 90 ___________________________ 2ra posicion encendido + | | | | | | | | 45 ________________________ 1ra posicion apagado + | | | | | | | | | 62 _____________________ 2ra posicion apagado + | | | | | | | | | | 1 ___________________ hora en RTC + | | | | | | | | | | | 42 ________________ minutos en RTC + | | | | | | | | | | | | 24 _____________ segundos en RTC + | | | | | | | | | | | | | 14 __________ dia en RTC + | | | | | | | | | | | | | | 7 ________ mes en RTC + | | | | | | | | | | | | | | | 2021 ___ año en RTC + 1,0,5,7,16,18,120,90,45,62,1,42,24,14,7,2021 ``` ----- ## ESP01 Establece comunicación serial con arduino (**115200 baudio**). -Interface web sencilla para consulta y configuraciones +Interface web básica para consulta y configuraciones. Archivo de configuracion ***confidencial.h*** ```c @@ -105,7 +105,8 @@ const char* password = "password"; Scripts para enviar peticiones web al ESP. -### caldera.sh +### Bash +[caldera.sh](./scripts/caldera.sh) ``` ============================== @@ -134,6 +135,7 @@ const char* password = "password"; ``` -### caldera.py +### Python +[caldera.py](./scripts/caldera.py) ![python_script](./scripts/script_python.png) diff --git a/scripts/README.md b/scripts/README.md index 7d3f13b..da0bd27 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,7 +1,25 @@ -## Utilidades +# Utilidades Scripts para enviar peticiones web al ESP. -### caldera.sh +## Bash +``` +─ 📂️ scripts + ├── 📄️ caldera.sh + └── 📄️ config.cfg +``` + +Archivo de configuración +***./config.cfg*** +```cfg +[esp01] +URL = +``` +Dependencias +- curl +- grep +- cut + +Correr: ` ./caldera.sh ` ``` ============================== @@ -29,7 +47,27 @@ Ingresa Opcion : ``` +---- -### caldera.py +## Python +``` +─ 📂️ scripts + ├── 📄️ caldera.py + ├── 📄️ config.cfg + └── 📄️ vista_term.py +``` + +Archivo de configuración +***./config.cfg*** +```cfg +[esp01] +URL = +``` + +Dependencias: +- requests +- colorama + +Correr: ` ./caldera.py ` o ` python caldera.py ` ![python_script](./script_python.png) diff --git a/scripts/caldera.py b/scripts/caldera.py index 9e420e5..0846abe 100755 --- a/scripts/caldera.py +++ b/scripts/caldera.py @@ -1,296 +1,176 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 + """ - Script de control Caldera autmatizada Arduino+ESP01 + Script de control Caldera automatizada [Arduino]+[ESP01] """ import os import sys from time import sleep +import configparser as cfg +import vista_term as vt + +try: + parser = cfg.ConfigParser() + parser.read('config.cfg') + ESP01 = parser.get('esp01', 'URL') +except cfg.Error as ex: + print('Error al leer archivo de configuración') + sys.exit() + try: import requests - from colorama import Fore, Back, Style except ModuleNotFoundError as ex: print("Debes instalar los modulos necesarios\n") print(ex) sleep(2) sys.exit() -ESP01 = "http://" clear = lambda: os.system('clear') if os.name == 'posix' else os.system('cls') def enviar_consulta(consulta): + """Retorna la respuesta a la petición GET *consulta* + + :consulta: str ( consulta creada por consultas() ) + :returns: str ( respuesta del GET request ) + """ resp = requests.get(consulta) return resp.text -def consultas(modo, *args): - orden = '?' - for indx, arg in enumerate(args): - orden += str(indx+1)+'='+str(arg)+'&' - resp = enviar_consulta(ESP01+modo+orden[:-1]) +def consultas(modo, id_modo='', datos=['']): + """Crea la petición GET según si los datos son list o str + Retorna la respuesta de enviar_consulta() + + :modo: str (sub-menu) + :id_modo: str (id modo de operacion) + :datos: list[str] | str + :returns: str (respuesta del GET request) + """ + if id_modo != '': + modo = modo + '?1=' + id_modo + orden = '' + cont = 2 + if type(datos) is list: + if len(datos) == 1: + orden = '' + else: + for dato in datos: + if dato != '': + orden += '&' + str(cont) + '=' + str(dato) + cont += 1 + else: + orden += '&' + str(cont) + '=' + str(datos) + resp = enviar_consulta(ESP01 + modo + orden) return resp -def pantallas(pantalla, vals): - if pantalla == '': - if vals[0] == '\n1': - vals[0] = ' AUTO ' - elif vals[0] == '\n5': - vals[0] = ' MANUAL ' - elif vals[0] == '\n6': - vals[0] = ' LIBRE ' - else: - vals[0] = '¡¡¡ERROR!!!' - for i in range(10,15): - if int(vals[i]) < 10: - vals[i] = '0'+vals[i] - for i in range(2,6): - if int(vals[i]) < 10: - vals[i] = ' '+vals[i] - clear() - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - if vals[1] == '1': - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" ESTADO TERMO "+Style.RESET_ALL+ - Fore.GREEN+Back.BLUE+Style.BRIGHT+f" ENCENDIDO " + Style.RESET_ALL) - elif vals[1] == '0': - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" ESTADO TERMO "+Style.RESET_ALL+ - Fore.RED+Back.BLUE+Style.BRIGHT+f" APAGADO " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" MODO "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f"{vals[0]}" + Style.RESET_ALL) - print(Fore.LIGHTGREEN_EX+Back.LIGHTBLACK_EX+ - f" {vals[10]}:{vals[11]}:{vals[12]} {vals[13]}/{vals[14]}/{vals[15]} "+ - Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" ON-1 "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[2]} " + Style.RESET_ALL, end='') - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" OFF-1 "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[3]} " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" ON-2 "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[4]} " + Style.RESET_ALL, end='') - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" OFF-2 "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[5]} " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Menu de Opciones "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" 1.- Modo Autonomo "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" 2.- Ajustar hora y fecha "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" 3.- Ajustar temporizador "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" 4.- Calibrar servo "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" 5.- Modo Manual "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" 6.- Modo Libre "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" 0.- Salir "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" ⏎.- Actualizar "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Ingresa una opción "+Style.RESET_ALL, - end='\b\b\b\b\b\b') - elif pantalla == '1': - print(Fore.MAGENTA+Back.BLUE+Style.BRIGHT+vals+Style.RESET_ALL) - elif pantalla == '2': - for i in range(0,5): - if int(vals[i]) < 10 and vals[i][0:1] != '0' : - vals[i] = '0'+vals[i] - clear() - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Valores actuales en RTC "+Style.RESET_ALL) - print(Fore.LIGHTGREEN_EX+Back.LIGHTBLACK_EX+ - f" Hora : {vals[0]}:{vals[1]}:{vals[2]} "+Style.RESET_ALL) - print(Fore.LIGHTGREEN_EX+Back.LIGHTBLACK_EX+ - f" Fecha : {vals[3]}/{vals[4]}/{vals[5]} "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Ajustando fecha y hora "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - elif pantalla == '3': - for i in range(0,4): - if int(vals[i]) < 10 and vals[i][0:1] != ' ': - vals[i] = ' '+vals[i] - clear() - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Horario Temporizador "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" ON-1 "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[0]} " + Style.RESET_ALL, end='') - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" OFF-1 "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[1]} " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" ON-2 "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[2]} " + Style.RESET_ALL, end='') - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" OFF-2 "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[3]} " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Ingresa nuevo horario "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - elif pantalla == '4': - for i in range(0,4): - if int(vals[i]) < 100: - vals[i] = ' '+vals[i] - clear() - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Configuracion Actual "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" 1 Posición ON "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[0]} " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" 2 Posición ON "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[1]} " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" 1 Posición OFF "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[2]} " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" 2 Posición OFF "+Style.RESET_ALL+ - Fore.LIGHTRED_EX+Back.BLUE+Style.BRIGHT+f" {vals[3]} " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Ingresa nuevos valores "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - elif pantalla == '5': - clear() - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Accionamiento Manual "+Style.RESET_ALL) - if vals[0] == '1': - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" ESTADO TERMO "+Style.RESET_ALL+ - Fore.GREEN+Back.BLUE+Style.BRIGHT+f" ENCENDIDO " + Style.RESET_ALL) - elif vals[0] == '0': - print(Fore.BLUE+Back.LIGHTWHITE_EX+Style.DIM+" ESTADO TERMO "+Style.RESET_ALL+ - Fore.RED+Back.BLUE+Style.BRIGHT+f" APAGADO " + Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Encender o Apagar "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - elif pantalla == '6': - clear() - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Movimiento Libre "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+"==========================="+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" Ingresa posición (16..164)"+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) -def solicita_dato(valid, mensaje): - invalido = True - while invalido: +intentos=0 + +def datos_a_lista() -> list[str] : + """Retorna el str recibido de consultas() como una lista, valida el largo + de la lista, añade 0 o ' ' a valores, según sea necesario para el correcto + formateo del texto a mostrar. + + :returns: list[str] ( len(list) = 16 ) + """ + datos = [] + global intentos + while True: try: - dato = input(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+mensaje+Style.RESET_ALL) - dato = int(dato) - if valid[0] < dato < valid[1]: - invalido = False - return dato - raise ValueError - except ValueError: - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+ - "Ingresa un número válido "+Style.RESET_ALL) - -def solicita_dato_str(valid, mensaje): - invalido = True - while invalido: - try: - dato = input(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+mensaje+Style.RESET_ALL) - if dato in valid: - invalido = False - return dato - raise ValueError - except ValueError: - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+ - " Debes ingresar \'on\' u \'off\' "+Style.RESET_ALL) - -intentos = 0 -while True: - #res[1] = 'ENCENDIDO' if res[1] == '1' else 'APAGADO' if '0' else '' - res = [] - try: - clear() - resp = consultas('') - for var in resp.split(','): - res.append(var) - assert len(res) == 16 - pantallas('', res) - opc = input(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+":"+Style.RESET_ALL) - if opc == '0': - sys.exit() - elif opc == '1': - resp = consultas('/auto') - print(Fore.MAGENTA+Back.BLUE+Style.BRIGHT+resp+Style.RESET_ALL) - sleep(8) + resp = consultas('') + for var in resp.split(','): + datos.append(var) + assert len(datos) == 16 + for i in range(10,15): + if int(datos[i]) < 10: + datos[i] = '0'+datos[i] + for i in range(2,6): + if int(datos[i]) < 10: + datos[i] = ' '+datos[i] intentos = 0 - elif opc == '2': - pantallas('2',[res[10], res[11], res[12], res[13], res[14], res[15]]) - resp = consultas('/sethora', opc, solicita_dato((0,32),' DIA : '), - solicita_dato((0,13),' MES : '), - solicita_dato((2020,2100),' AÑO : '), - solicita_dato((-1,24),' HORA : '), - solicita_dato((-1,60),' MINUTO : '), - solicita_dato((-1,60),' SEGUNDO : ')) - print(Fore.MAGENTA+Back.BLUE+Style.BRIGHT+resp+Style.RESET_ALL) - sleep(8) - elif opc == '3': - pantallas('3',[res[2], res[3], res[4], res[5]]) - resp = consultas('/horasAcc', opc, - solicita_dato((-1,24),' 1er Encendido : '), - solicita_dato((-1,24),' 1er Apagado : '), - solicita_dato((-1,24),' 2do Ecendido (opc.): '), - solicita_dato((-1,24),' 2do Apagado (opc.): ')) - print(Fore.MAGENTA+Back.BLUE+Style.BRIGHT+resp+Style.RESET_ALL) - sleep(8) - elif opc == '4': - pantallas('4',[res[6], res[7], res[8], res[9]]) - resp = consultas('/setservo', opc, - solicita_dato((15,165),' 1ra Posición Encendido:'), - solicita_dato((15,165),' 2ra Posición Encendido:'), - solicita_dato((15,165),' 1ra Posición Apagado :'), - solicita_dato((15,165),' 2da Posición Apagado :')) - print(Fore.MAGENTA+Back.BLUE+Style.BRIGHT+resp+Style.RESET_ALL) - sleep(8) - elif opc == '5': - pantallas('5',[res[1]]) - resp = consultas('/accion', opc, solicita_dato_str(('on','off'), - ' Ingresa \'on\' u \'off\': ')) - print(Fore.MAGENTA+Back.BLUE+Style.BRIGHT+resp+Style.RESET_ALL) - sleep(8) - elif opc == '6': - pantallas('6',[res[1]]) - resp = consultas('/setlibre', opc, solicita_dato((15,165),' Ingresa posición :')) - print(Fore.MAGENTA+Back.BLUE+Style.BRIGHT+resp+Style.RESET_ALL) - sleep(8) - elif opc == '': - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+ - Style.RESET_ALL) - print(Fore.RED+Back.YELLOW+Style.BRIGHT+" Actualizando... "+ - Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+ - Style.RESET_ALL) - sleep(1.5) - else: - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+ - Style.RESET_ALL) - print(Fore.RED+Back.YELLOW+Style.BRIGHT+" Opción incorrecta "+ - Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+ - Style.RESET_ALL) - sleep(1.5) - except KeyboardInterrupt: - sys.exit() - except AssertionError: - clear() - intentos += 1 - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.RED+Back.YELLOW+Style.BRIGHT+" Arduino no disponible "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+f" Intentando nuevamente ({intentos+1}) "+ - Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - intentos += 1 - sleep(2) - except ConnectionError: - clear() - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+f" Intentando conexión ({intentos+1}) "+ - Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - intentos += 1 - sleep(2) - except OSError: - clear() - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+f" Intentando conexión ({intentos+1}) "+ - Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - intentos += 1 - sleep(2) - if intentos > 4: - print(Fore.RED+Back.YELLOW+Style.BRIGHT+" Imposible conectar con "+Style.RESET_ALL) - print(Fore.RED+Back.YELLOW+Style.BRIGHT+" ESP, o Arduino, verfica "+Style.RESET_ALL) - print(Fore.RED+Back.YELLOW+Style.BRIGHT+" IP, conexión o estado de "+Style.RESET_ALL) - print(Fore.RED+Back.YELLOW+Style.BRIGHT+ " de los dispositivos. "+Style.RESET_ALL) - print(Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM+" "+Style.RESET_ALL) - sys.exit() + return datos + except KeyboardInterrupt: + sys.exit() + except AssertionError: + vt.resp_conex('assert', intentos) + intentos += 1 + sleep(2) + except ConnectionError: + vt.resp_conex('conexion', intentos) + intentos += 1 + sleep(2) + except OSError: + vt.resp_conex('oserror', intentos) + intentos += 1 + sleep(2) + if intentos > 4: + vt.resp_conex() + sys.exit() + +def main(): + global intentos + while True: + # Lista con valores de los datos recibidos + vals = datos_a_lista() + clear() + vt.pant_principal(vals[0], vals[1], vals[10:16], vals[2:6]) + modo_user = vt.entrada_usuario() + match modo_user: + case 's': + sys.exit() + case '1': + #vt.pant_test_valores(vals) + vt.respuesta_config(consultas('/auto')) + intentos = 0 + sleep(3) + case'2': + vt.pant_config_fecha(vals[10:16]) + resp_user = vt.entrada_usuario('/sethora') + vt.respuesta_config(consultas('/sethora', modo_user, resp_user)) + intentos = 0 + sleep(5) + case '3': + vt.pant_termporizador(vals[2:6]) + resp_user = vt.entrada_usuario('/horasAcc') + vt.respuesta_config(consultas('/horasAcc', modo_user, resp_user)) + intentos = 0 + sleep(5) + case '4': + pos_servo = [] + for val in vals[6:10]: + if int(val) < 100: + pos_servo.append(' '+val) + else: + pos_servo.append(val) + vt.pant_posic_servo(pos_servo) + resp_user = vt.entrada_usuario('/setservo') + vt.respuesta_config(consultas('/setservo', modo_user, resp_user)) + intentos = 0 + sleep(5) + case '5': + vt.pant_accion_manual(vals[1]) + resp_user = vt.entrada_usuario('/accion') + vt.respuesta_config(consultas('/accion', modo_user, resp_user)) + intentos = 0 + sleep(5) + case '6': + vt.pant_servo_manual() + resp_user = vt.entrada_usuario('/setlibre') + vt.respuesta_config(consultas('/setlibre', modo_user, resp_user)) + intentos = 0 + sleep(5) + case '': + vt.respuesta_info() + sleep(1.5) + case 'v': + vt.respuesta_info('volver') + intentos = 0 + sleep(1.5) + case _: + modo_user = vt.entrada_usuario() + vt.respuesta_info('error') + sleep(1.5) + + +if __name__ == "__main__": + main() diff --git a/scripts/caldera.sh b/scripts/caldera.sh index ed35053..b21b7e7 100755 --- a/scripts/caldera.sh +++ b/scripts/caldera.sh @@ -1,6 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash -IP_CALDERA="" +IP_CALDERA=$(grep URL config.cfg | cut -d'=' -f2) estado_caldera() { RESP=$(curl -s ${IP_CALDERA}) diff --git a/scripts/script_python.png b/scripts/script_python.png index eccbe0a..6214ab9 100644 Binary files a/scripts/script_python.png and b/scripts/script_python.png differ diff --git a/scripts/vista_term.py b/scripts/vista_term.py new file mode 100644 index 0000000..8ba0b1c --- /dev/null +++ b/scripts/vista_term.py @@ -0,0 +1,398 @@ +""" + Gestión de pantallas (dialogos, opcs, menus, etc) y entradas de usuario. +""" +import os +import sys +from time import sleep +from caldera import main +try: + from colorama import Fore, Back, Style +except ModuleNotFoundError as ex: + print("Debes instalar los modulos necesarios\n") + print(ex) + sleep(2) + sys.exit() + +clear = lambda: os.system('clear') if os.name == 'posix' else os.system('cls') + +# Colores +col_rst = Style.RESET_ALL +col_menu = Fore.BLACK+Back.LIGHTBLACK_EX+Style.DIM +col_head = Fore.BLUE+Back.LIGHTWHITE_EX+Style.NORMAL +col_headGrn = Fore.GREEN+Back.MAGENTA+Style.BRIGHT +col_headRed = Fore.RED+Back.BLUE+Style.DIM +col_headLred = Fore.LIGHTRED_EX+Back.BLUE+Style.DIM +col_headBBlk = Fore.BLACK+Back.MAGENTA+Style.BRIGHT +col_headBlk = Fore.BLACK+Back.MAGENTA+Style.NORMAL +col_date = Fore.LIGHTGREEN_EX+Back.LIGHTBLACK_EX +col_error = Fore.RED+Back.YELLOW+Style.NORMAL +col_errorB = Fore.RED+Back.YELLOW+Style.BRIGHT +col_input = Fore.BLACK+Back.LIGHTBLACK_EX+Style.DIM +col_excep = Fore.BLUE+Back.LIGHTBLACK_EX+Style.DIM +col_resp = Fore.BLACK+Back.MAGENTA+Style.BRIGHT + +# Dialogos +diag_bar = '===========================' +diag_spc = ' ' +diag_state = [ diag_bar, ' ESTADO TERMO ', ' ENCENDIDO ', ' APAGADO ', ' - - - - ' ] +diag_modo = [ ' MODO ', ' AUTO ', ' MANUAL ', + ' LIBRE ', ' ¡ERROR! ' ] +diag_hrsfun = [ ' ON-1 ', ' OFF-1 ', ' ON-2 ', ' OFF-2 ', diag_bar ] +diag_opcs = [ diag_bar, ' Menu de Opciones ', diag_spc, + ' 1) Modo Autonomo ', ' 2) Ajustar hora y fecha ', + ' 3) Ajustar temporizador ', ' 4) Calibrar servo ', + ' 5) Modo Manual ', ' 6) Modo Libre ', + ' v) Volver(en sub-menus) ', ' s) Salir ', + ' ⏎) Actualizar ', diag_spc, + ' Ingresa una opción \b\b\b\b\b\b:' ] + +diag_setdate = [ diag_bar,' Valores actuales en RTC ', ' Hora : ', ' Fecha : ', + diag_bar, diag_spc, ' Ajustando fecha y hora ', diag_spc, + ' DIA : \b\b\b\b\b\b\b', + ' MES : \b\b\b\b\b\b\b', + ' AÑO : \b\b\b\b\b\b\b', + ' HORA : \b\b\b\b\b\b\b', + ' MINUTO : \b\b\b\b\b\b\b', + ' SEGUNDO : \b\b\b\b\b\b\b' ] + +diag_error = [ ' Ingresa un número válido! ', ' Debes ingresar \'on\' u \'off\' ', + ' Entrada no válida! ', ' Entrada incorrecta! ' ] + +diag_settimer = [ diag_bar,' Horario Temporizador ', ' ON-1 ', ' OFF-1 ', + ' ON-2 ',' OFF-2 ', diag_bar, diag_spc, ' Ingresa nuevo horario ', + diag_spc, ' 1er Encendido : ', ' 1er Apagado : ', + ' 2do Ecendido (opc.): ', ' 2do Apagado (opc.): ' ] + +diag_posservo = [ diag_bar,' Configuracion Actual ', ' 1 Posición ON ', + ' 2 Posición ON ', ' 1 Posición OFF ',' 2 Posición OFF ', + diag_bar, diag_spc,' Ingresa nuevos valores ', diag_spc, + ' 1ra posic. encendido : \b\b\b\b', + ' 2da posic. encendido : \b\b\b\b', + ' 1ra posic. apagado : \b\b\b\b', + ' 2da posic. apagado : \b\b\b\b' ] + +diag_accman = [ diag_bar, ' Accionamiento Manual ', diag_state[1:5], + diag_bar, diag_spc, ' Encender o Apagar ? ', diag_spc, + ' Ingresa \'on\' u \'off\': \b\b\b\b' ] + +diag_servman = [ diag_bar, ' Movimiento Libre ', diag_bar, diag_spc, + 'Posiciónes validas (16-164)', diag_spc , + ' Ingresa posición : \b\b\b\b\b' ] + +diag_excepcn = [ diag_spc, ' Error en lectura de datos ', ' Arduino no disponible ', + ' Error al solicitar datos ', f' Intentando conexión (', ') ', + ' Imposible conectar con ', ' ESP o Arduino, verifica ', + ' IP, conexión o estado de ', ' de los dispositivos. ' ] + +diag_volver = [ diag_spc, ' Actualizando... ', + ' Volviendo... ', ' Seleción incorrecta ' ] + +# Valores para validación +# (min, max) dia - mes - año - hora - minuto - segundo +valid_dates = [(0,32),(0,13),(2020,2100),(-1,24),(-1,60),(-1,60)] +valid_opcs = ['1','2','3','4','5','6',''] +valid_hrs_timer = (-1,24) +valid_posicion = (15,168) +valid_manual = ('on', 'off') + + +def resp_conex(excepcion=None, intentos=0): + """Imprime en terminal dialogos de error de conexión + + :excepcion: str (tipo de error) + :intentos: int (nro. de intentos de conexión) + """ + intentos += 1 + dialogos = diag_excepcn + match excepcion: + case 'assert': + clear() + print(col_menu + dialogos[0] + col_rst) + print(col_error + dialogos[1] + col_rst) + print(col_menu + dialogos[4] + str(intentos) + dialogos[5] + col_rst) + print(col_menu + dialogos[0] + col_rst) + case 'conexion': + clear() + print(col_menu + dialogos[0] + col_rst) + print(col_error + dialogos[2] + col_rst) + print(col_menu + dialogos[4] + str(intentos) + dialogos[5] + col_rst) + print(col_menu + dialogos[0] + col_rst) + case 'oserror': + clear() + print(col_menu + dialogos[0] + col_rst) + print(col_error + dialogos[3] + col_rst) + print(col_menu + dialogos[4] + str(intentos) + dialogos[5] + col_rst) + print(col_menu + dialogos[0] + col_rst) + case _: + clear() + print(col_menu + dialogos[0] + col_rst) + for dialogo in dialogos[6:10]: + print(col_errorB + dialogo + col_rst) + print(col_menu + dialogos[0] + col_rst) + + +def pant_servo_manual(): + """Imprime en terminal dialogos de configuración manual del servo + """ + clear() + dialogos = diag_servman[:-1] + for dialogo in dialogos: + print(col_menu+dialogo+col_rst) + + +def pant_accion_manual(estado): + """Imprime en terminal dialogos de accionamiento manual del servo + + :estado: str ('0' o '1') + """ + clear() + dialogos = diag_accman[0:7] + for ind, dialogo in enumerate(dialogos): + match ind: + case 2: + print(col_head+dialogo[0]+col_rst, end='') + match estado: + case '1': + print(col_headGrn+dialogo[1]+col_rst) + case '0': + print(col_headBlk+dialogo[2]+col_rst) + case _: + print(col_headBlk+dialogo[3]+col_rst) + ind+=3 + case _: + print(col_menu+dialogo+col_rst) + + +def pant_posic_servo(pos_servo): + """Imprime en terminal posiciones de trabajo del servo + + :pos_servo: list[str] + """ + clear() + dialogos = diag_posservo[0:10] + for ind, dialogo in enumerate(dialogos): + match ind: + case 2: + print(f"{col_head}{dialogo}{col_headGrn} {pos_servo[0]} {col_rst}") + case 3: + print(f"{col_head}{dialogo}{col_headGrn} {pos_servo[1]} {col_rst}") + case 4: + print(f"{col_head}{dialogo}{col_headGrn} {pos_servo[2]} {col_rst}") + case 5: + print(f"{col_head}{dialogo}{col_headGrn} {pos_servo[3]} {col_rst}") + case _: + print(col_menu + dialogo + col_rst) + + +def pant_termporizador(hrs_timer): + """Imprime en terminal horas de trabajo del servo + + :hrs_timer: list[str] + """ + clear() + dialogos = diag_settimer[0:10] + for ind, dialogo in enumerate(dialogos): + match ind: + case 2: + #ind+=1 + print(f"{col_head}{dialogo}{col_headGrn} {hrs_timer[0]} {col_rst}", end='') + case 3: + print(f"{col_head}{dialogo}{col_headGrn} {hrs_timer[1]} {col_rst}") + case 4: + print(f"{col_head}{dialogo}{col_headGrn} {hrs_timer[2]} {col_rst}", end='') + case 5: + print(f"{col_head}{dialogo}{col_headGrn} {hrs_timer[3]} {col_rst}") + case _: + print(col_menu + dialogo + col_rst) + + +def pant_config_fecha(tiempo): + """Imprime hora y fecha en terminal, segun ESP. + + :tiempo: list[str] [hora, minuto, segundo, dia, mes, año] + """ + clear() + dialogos = diag_setdate + for ind, dialogo in enumerate(dialogos): + match ind: + case 2: + print(f"{col_date}{dialogo}{tiempo[0]}:{tiempo[1]}:{tiempo[2]} {col_rst}") + case 3: + print(f"{col_date}{dialogo}{tiempo[3]}/{tiempo[4]}/{tiempo[5]} {col_rst}") + case 8: + break + case _: + print(col_menu + dialogo + col_rst) + + +def pant_principal(modo, estado, tiempo, hrs_timer): + """Imprime menu principal en terminal. + + :modo: str (ej. '\\n') + :estado: str estado del termo ('0','1') + :tiempo: List[str] hora y fecha + :hrs_timer: List[str] horario de accionamiento + """ + clear() + # Estado + dialogos = diag_state + print(col_menu+dialogos[0]+col_rst) + print(col_head+dialogos[1]+col_rst, end='') + match estado: + case '1': + print(col_headGrn+dialogos[2]+col_rst) + case '0': + print(col_headBlk+dialogos[3]+col_rst) + case _: + print(col_headBlk+dialogos[4]+col_rst) + + # Modo de operación + dialogos = diag_modo + print(col_head+dialogos[0]+col_rst, end='') + match modo: + case '\n1': + print(col_headGrn+dialogos[1]+col_rst) + case '\n5': + print(col_headBlk+dialogos[2]+col_rst) + case '\n6': + print(col_headBlk+dialogos[3]+col_rst) + case _: + print(col_headRed+dialogos[4]+col_rst) + + # Hora y Fecha + print(col_date, end='') + print(f" {tiempo[0]}:{tiempo[1]}:{tiempo[2]}", end='') + print(f" {tiempo[3]}/{tiempo[4]}/{tiempo[5]} "+col_rst) + + # Horas de funcionamiento + dialogos = diag_hrsfun + print(col_head + dialogos[0] + col_rst, end='') + print(col_headBBlk + ' ' + hrs_timer[0] + ' ' + col_rst, end='') + print(col_head + dialogos[1]+col_rst, end='') + print(col_headBBlk + ' ' + hrs_timer[1] + ' ' + col_rst) + print(col_head + dialogos[2] + col_rst, end='') + print(col_headBBlk + ' ' + hrs_timer[2] + ' ' + col_rst, end='') + print(col_head + dialogos[3]+col_rst, end='') + print(col_headBBlk + ' ' + hrs_timer[3] + ' ' + col_rst) + + # Opciones del menu principal + dialogos = diag_opcs[:-1] + for indx, dialogo in enumerate(dialogos): + if indx != (len(dialogos)-1): + print(col_menu+dialogo+col_rst) + else: + print(col_menu+dialogo+col_rst) + + +def pant_test_valores(valores): + """Función de prueba, imprime en terminal valores de lista. + + :valores: Lista de strings [ str, str, ...] + """ + for val in valores: + print(Fore.MAGENTA+Back.BLACK+Style.BRIGHT+val+Style.RESET_ALL) + + +def valida_entrada(valid, mensaje, tipo_dato='') -> list[str] | str: + """Solicita entrada de usuario, imprime dialogo en terminal y + valida según tupla *valid*. + + :valid: tuple (límites o alternativas válidas) + :mensaje: str (mensaje para solicitar entrada de usuario) + """ + invalido = True + err_msg = '' + while invalido: + entrada_usuario = input(col_input+mensaje+Style.RESET_ALL) + if entrada_usuario == 's': + sys.exit() + #return 's' + elif entrada_usuario == 'v': + main() + #return 'v' + match tipo_dato: + case '': + try: + entrada_usuario = int(entrada_usuario) + if valid[0] < entrada_usuario < valid[1]: + invalido = False + return str(entrada_usuario) + else: + raise ValueError + except ValueError: + err_msg = diag_error[0] + print(col_error + err_msg + Style.RESET_ALL) + case 'str': + try: + if entrada_usuario in valid: + invalido = False + return entrada_usuario + else: + err_msg = diag_error[3] + raise ValueError + except ValueError: + err_msg = diag_error[3] + print(col_error + err_msg + Style.RESET_ALL) + case _: + assert entrada_usuario in ['','str'] + err_msg = diag_error[2] + print(col_error + err_msg + Style.RESET_ALL) + + +def entrada_usuario(caso=None) -> list[str] | str : + """Retorna la(s) respuesta(s) del usuario, segun caso (submenu) + + :caso: str (submenu) + """ + match caso: + case '/sethora': + user_ins = [] + i = 0 + for preg in diag_setdate[8:14]: + user_ins.append(valida_entrada(valid_dates[i], preg)) + i += 1 + return user_ins + case '/horasAcc': + user_ins = [] + for preg in diag_settimer[10:14]: + user_ins.append(valida_entrada(valid_hrs_timer, preg)) + return user_ins + case '/setservo': + user_ins = [] + for preg in diag_posservo[10:14]: + user_ins += valida_entrada(valid_posicion, preg) + return user_ins + case '/accion': + return valida_entrada(valid_manual ,diag_accman[-1],'str') + case '/setlibre': + return valida_entrada(valid_posicion ,diag_servman[-1]) + case _: + mensaje = ' Ingresa una opción \b\b\b\b\b\b:' + return valida_entrada(valid_opcs, mensaje, 'str') + + +def respuesta_config(respuesta): + """Imprime el argumento formateado en el terminal bajo el contexto de + respuesta del ESP a alguna configuración. + + :respuesta: str (mensaje de respuesta) + """ + print(col_resp + respuesta + col_rst) + +def respuesta_info(caso='foo'): + """Imprime en terminal bajo el contexto de respuesta informativa o + advertencia del ESP a alguna solicitud. + + :caso: str (tipo de mensaje) + """ + match caso: + case 'error': + print(col_error + diag_volver[3] + col_rst) + sleep(1) + case 'volver': + print(col_error + diag_volver[2] + col_rst) + sleep(1) + case _: + print(col_error + diag_volver[1] + col_rst) + sleep(1) +