From da563be55d4c33ef85e849aac7f29251347baad1 Mon Sep 17 00:00:00 2001 From: "jp.av.dev" Date: Wed, 20 Apr 2022 15:56:56 -0400 Subject: [PATCH] =?UTF-8?q?Reestructuraci=C3=B3n=20-=20separaci=C3=B3n=20d?= =?UTF-8?q?e=20la=20vista?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + README.md | 106 +++++----- scripts/README.md | 44 +++- scripts/caldera.py | 422 ++++++++++++++------------------------ scripts/caldera.sh | 4 +- scripts/script_python.png | Bin 24746 -> 30869 bytes scripts/vista_term.py | 398 +++++++++++++++++++++++++++++++++++ 7 files changed, 649 insertions(+), 328 deletions(-) create mode 100644 scripts/vista_term.py 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 eccbe0ad26ba34f15dc96486294edb8254457c8e..6214ab9ad94b2d69db8414dc95785255218f427c 100644 GIT binary patch literal 30869 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 I$r%U#ANl9;UjP6A literal 24746 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 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) +