Interfaz » History » Version 3
cristobal hernandez, 12/18/2025 01:25 AM
| 1 | 1 | cristobal hernandez | h1. Servidor |
|---|---|---|---|
| 2 | 3 | cristobal hernandez | <small> |
| 3 | 3 | cristobal hernandez | <pre><code class="python"> |
| 4 | 1 | cristobal hernandez | import time |
| 5 | 1 | cristobal hernandez | import glob |
| 6 | 1 | cristobal hernandez | import json |
| 7 | 1 | cristobal hernandez | import requests |
| 8 | 1 | cristobal hernandez | import os |
| 9 | 1 | cristobal hernandez | import random |
| 10 | 1 | cristobal hernandez | from datetime import datetime |
| 11 | 1 | cristobal hernandez | |
| 12 | 1 | cristobal hernandez | import grovepi |
| 13 | 1 | cristobal hernandez | from grovepi import * |
| 14 | 1 | cristobal hernandez | from grove_rgb_lcd import * |
| 15 | 1 | cristobal hernandez | from gpiozero import AngularServo |
| 16 | 1 | cristobal hernandez | |
| 17 | 1 | cristobal hernandez | import firebase_admin |
| 18 | 1 | cristobal hernandez | from firebase_admin import credentials, firestore, messaging |
| 19 | 1 | cristobal hernandez | |
| 20 | 1 | cristobal hernandez | |
| 21 | 1 | cristobal hernandez | # Archivos Locales |
| 22 | 1 | cristobal hernandez | FILE_ID = "savenemo_id.txt" |
| 23 | 1 | cristobal hernandez | FILE_CONFIG_LOCAL = "config.json" |
| 24 | 1 | cristobal hernandez | FILE_CREDENTIALS = "serviceAccountKey.json" |
| 25 | 1 | cristobal hernandez | |
| 26 | 1 | cristobal hernandez | # Configuracion Sensor Temperatura |
| 27 | 1 | cristobal hernandez | SENSOR_TEMP_ID = "28-00000053d2ea" |
| 28 | 1 | cristobal hernandez | SENSOR_TEMP_PATH = f"/sys/bus/w1/devices/{SENSOR_TEMP_ID}/w1_slave" |
| 29 | 1 | cristobal hernandez | |
| 30 | 1 | cristobal hernandez | # Puertos GrovePi |
| 31 | 1 | cristobal hernandez | PORT_ULTRASONIC = 8 # D8 |
| 32 | 1 | cristobal hernandez | PORT_RELAY_CAL = 3 # D3 (Calefactor) |
| 33 | 1 | cristobal hernandez | PORT_PH = 0 # A0 |
| 34 | 1 | cristobal hernandez | PORT_SENSOR_LUZ = 1 # A1 |
| 35 | 1 | cristobal hernandez | SERVO_PIN = 18 # Pin GPIO (Servomotor) |
| 36 | 1 | cristobal hernandez | |
| 37 | 1 | cristobal hernandez | # Variables Globales de Hardware |
| 38 | 1 | cristobal hernandez | alimentador_servo = None |
| 39 | 1 | cristobal hernandez | |
| 40 | 1 | cristobal hernandez | # Configuracion Inicial por Defecto |
| 41 | 1 | cristobal hernandez | DEFAULT_CONFIG = { |
| 42 | 1 | cristobal hernandez | "nombre_pez": "Esperando App...", |
| 43 | 1 | cristobal hernandez | "temp_min": 24.0, "temp_max": 28.0, |
| 44 | 1 | cristobal hernandez | "ph_min": 6.5, "ph_max": 7.5, |
| 45 | 1 | cristobal hernandez | "nivel_luz": 50, "nivel_agua": 80, |
| 46 | 1 | cristobal hernandez | "horarios_comida": [], |
| 47 | 1 | cristobal hernandez | "sistema_alimentacion_on": True |
| 48 | 1 | cristobal hernandez | } |
| 49 | 1 | cristobal hernandez | |
| 50 | 1 | cristobal hernandez | # Estado del Sistema |
| 51 | 1 | cristobal hernandez | config_actual = DEFAULT_CONFIG.copy() |
| 52 | 1 | cristobal hernandez | nemo_id = "" |
| 53 | 1 | cristobal hernandez | db = None |
| 54 | 1 | cristobal hernandez | firebase_activo = False |
| 55 | 1 | cristobal hernandez | ultimo_minuto_alimentacion = "" |
| 56 | 1 | cristobal hernandez | ya_se_envio_notificacion = False |
| 57 | 1 | cristobal hernandez | |
| 58 | 1 | cristobal hernandez | # Funciones BD |
| 59 | 1 | cristobal hernandez | |
| 60 | 1 | cristobal hernandez | def obtener_id_unico(): |
| 61 | 1 | cristobal hernandez | """Lee o genera un ID unico para identificar esta pecera.""" |
| 62 | 1 | cristobal hernandez | if os.path.exists(FILE_ID): |
| 63 | 1 | cristobal hernandez | with open(FILE_ID, 'r') as f: |
| 64 | 1 | cristobal hernandez | return f.read().strip() |
| 65 | 1 | cristobal hernandez | else: |
| 66 | 1 | cristobal hernandez | nuevo_id = f"NEMO-{random.randint(1000, 9999)}" |
| 67 | 1 | cristobal hernandez | with open(FILE_ID, 'w') as f: |
| 68 | 1 | cristobal hernandez | f.write(nuevo_id) |
| 69 | 1 | cristobal hernandez | return nuevo_id |
| 70 | 1 | cristobal hernandez | |
| 71 | 1 | cristobal hernandez | def verificar_internet(): |
| 72 | 1 | cristobal hernandez | try: |
| 73 | 1 | cristobal hernandez | requests.get('https://www.google.com', timeout=3) |
| 74 | 1 | cristobal hernandez | return True |
| 75 | 1 | cristobal hernandez | except: return False |
| 76 | 1 | cristobal hernandez | |
| 77 | 1 | cristobal hernandez | def esperar_internet(): |
| 78 | 1 | cristobal hernandez | print("Buscando conexion a internet...") |
| 79 | 1 | cristobal hernandez | while not verificar_internet(): |
| 80 | 1 | cristobal hernandez | time.sleep(3) |
| 81 | 1 | cristobal hernandez | print("Conexion establecida.") |
| 82 | 1 | cristobal hernandez | |
| 83 | 1 | cristobal hernandez | def iniciar_firebase(): |
| 84 | 1 | cristobal hernandez | global db, firebase_activo |
| 85 | 1 | cristobal hernandez | try: |
| 86 | 1 | cristobal hernandez | if os.path.exists(FILE_CREDENTIALS): |
| 87 | 1 | cristobal hernandez | cred = credentials.Certificate(FILE_CREDENTIALS) |
| 88 | 1 | cristobal hernandez | if not firebase_admin._apps: |
| 89 | 1 | cristobal hernandez | firebase_admin.initialize_app(cred) |
| 90 | 1 | cristobal hernandez | db = firestore.client() |
| 91 | 1 | cristobal hernandez | firebase_activo = True |
| 92 | 1 | cristobal hernandez | print("Firebase conectado exitosamente.") |
| 93 | 1 | cristobal hernandez | else: |
| 94 | 1 | cristobal hernandez | print("Falta archivo serviceAccountKey.json") |
| 95 | 1 | cristobal hernandez | except Exception as e: |
| 96 | 1 | cristobal hernandez | print(f"Error Firebase: {e}") |
| 97 | 1 | cristobal hernandez | firebase_activo = False |
| 98 | 1 | cristobal hernandez | |
| 99 | 1 | cristobal hernandez | def sincronizar_configuracion(): |
| 100 | 1 | cristobal hernandez | global config_actual |
| 101 | 1 | cristobal hernandez | if not firebase_activo: return |
| 102 | 1 | cristobal hernandez | try: |
| 103 | 1 | cristobal hernandez | doc = db.collection('acuarios').document(nemo_id).collection('data').document('config').get() |
| 104 | 1 | cristobal hernandez | if doc.exists: |
| 105 | 1 | cristobal hernandez | datos = doc.to_dict() |
| 106 | 1 | cristobal hernandez | if datos != config_actual: # Solo actualizar si hay cambios |
| 107 | 1 | cristobal hernandez | print("Configuracion actualizada desde la nube.") |
| 108 | 1 | cristobal hernandez | config_actual.update(datos) |
| 109 | 1 | cristobal hernandez | with open(FILE_CONFIG_LOCAL, 'w') as f: json.dump(config_actual, f) |
| 110 | 1 | cristobal hernandez | except: pass |
| 111 | 1 | cristobal hernandez | |
| 112 | 1 | cristobal hernandez | def subir_estado(temp, ph, luz, agua, alertas): |
| 113 | 1 | cristobal hernandez | """Sube los datos de sensores a Firestore.""" |
| 114 | 1 | cristobal hernandez | if not firebase_activo: return |
| 115 | 1 | cristobal hernandez | try: |
| 116 | 1 | cristobal hernandez | db.collection('acuarios').document(nemo_id).collection('data').document('estado').set({ |
| 117 | 1 | cristobal hernandez | 'temp_actual': temp, 'ph_actual': ph, 'luz_actual': luz, 'agua_nivel': agua, |
| 118 | 1 | cristobal hernandez | 'alertas': alertas, 'ultima_actualizacion': datetime.now().isoformat() |
| 119 | 1 | cristobal hernandez | }) |
| 120 | 1 | cristobal hernandez | except: pass |
| 121 | 1 | cristobal hernandez | |
| 122 | 1 | cristobal hernandez | def enviar_notificacion_push(titulo, mensaje): |
| 123 | 1 | cristobal hernandez | if not firebase_activo: return |
| 124 | 1 | cristobal hernandez | try: |
| 125 | 1 | cristobal hernandez | tokens_ref = db.collection('acuarios').document(nemo_id).collection('data').document('tokens') |
| 126 | 1 | cristobal hernandez | doc = tokens_ref.get() |
| 127 | 1 | cristobal hernandez | token = doc.to_dict().get('token_celular') if doc.exists else None |
| 128 | 1 | cristobal hernandez | |
| 129 | 1 | cristobal hernandez | if token: |
| 130 | 1 | cristobal hernandez | msg = messaging.Message( |
| 131 | 1 | cristobal hernandez | notification=messaging.Notification(title=titulo, body=mensaje), |
| 132 | 1 | cristobal hernandez | token=token, |
| 133 | 1 | cristobal hernandez | ) |
| 134 | 1 | cristobal hernandez | messaging.send(msg) |
| 135 | 1 | cristobal hernandez | print(f"Push enviado: {titulo}") |
| 136 | 1 | cristobal hernandez | except Exception as e: |
| 137 | 1 | cristobal hernandez | print(f"Error push: {e}") |
| 138 | 1 | cristobal hernandez | |
| 139 | 1 | cristobal hernandez | def guardar_historial_evento(titulo, mensaje): |
| 140 | 1 | cristobal hernandez | if not firebase_activo: return |
| 141 | 1 | cristobal hernandez | try: |
| 142 | 1 | cristobal hernandez | db.collection('acuarios').document(nemo_id).collection('historial').add({ |
| 143 | 1 | cristobal hernandez | 'titulo': titulo, |
| 144 | 1 | cristobal hernandez | 'mensaje': mensaje, |
| 145 | 1 | cristobal hernandez | 'fecha': datetime.now().strftime("%Y-%m-%d %H:%M:%S"), |
| 146 | 1 | cristobal hernandez | 'timestamp': datetime.now().timestamp() |
| 147 | 1 | cristobal hernandez | }) |
| 148 | 1 | cristobal hernandez | except: pass |
| 149 | 1 | cristobal hernandez | |
| 150 | 1 | cristobal hernandez | # Hardware |
| 151 | 1 | cristobal hernandez | |
| 152 | 1 | cristobal hernandez | def inicializar_hardware(): |
| 153 | 1 | cristobal hernandez | global alimentador_servo |
| 154 | 1 | cristobal hernandez | try: |
| 155 | 1 | cristobal hernandez | # Relay |
| 156 | 1 | cristobal hernandez | pinMode(PORT_RELAY_CAL, "OUTPUT") |
| 157 | 1 | cristobal hernandez | digitalWrite(PORT_RELAY_CAL, 0) |
| 158 | 1 | cristobal hernandez | |
| 159 | 1 | cristobal hernandez | # LCD |
| 160 | 1 | cristobal hernandez | try: |
| 161 | 1 | cristobal hernandez | setText("INICIANDO\nSISTEMA...") |
| 162 | 1 | cristobal hernandez | setRGB(0, 0, 255) # Azul |
| 163 | 1 | cristobal hernandez | except: print("LCD no detectado (continuando...)") |
| 164 | 1 | cristobal hernandez | |
| 165 | 1 | cristobal hernandez | # Servo Motor |
| 166 | 1 | cristobal hernandez | alimentador_servo = AngularServo(SERVO_PIN, min_angle=0, max_angle=180, |
| 167 | 1 | cristobal hernandez | min_pulse_width=0.0006, max_pulse_width=0.0024) |
| 168 | 1 | cristobal hernandez | print("Hardware inicializado correctamente.") |
| 169 | 1 | cristobal hernandez | except Exception as e: |
| 170 | 1 | cristobal hernandez | print(f"Error Hardware: {e}") |
| 171 | 1 | cristobal hernandez | alimentador_servo = None |
| 172 | 1 | cristobal hernandez | |
| 173 | 1 | cristobal hernandez | # Sensores |
| 174 | 1 | cristobal hernandez | def leer_temperatura(): |
| 175 | 1 | cristobal hernandez | try: |
| 176 | 1 | cristobal hernandez | if not os.path.exists(SENSOR_TEMP_PATH): |
| 177 | 1 | cristobal hernandez | print("Sensor de temperatura no encontrado (Cable desconectado?)") |
| 178 | 1 | cristobal hernandez | return 25.0 |
| 179 | 1 | cristobal hernandez | |
| 180 | 1 | cristobal hernandez | with open(SENSOR_TEMP_PATH, 'r') as f: |
| 181 | 1 | cristobal hernandez | lineas = f.readlines() |
| 182 | 1 | cristobal hernandez | |
| 183 | 1 | cristobal hernandez | if lineas[0].strip()[-3:] == 'YES': |
| 184 | 1 | cristobal hernandez | posicion_t = lineas[1].find('t=') |
| 185 | 1 | cristobal hernandez | if posicion_t != -1: |
| 186 | 1 | cristobal hernandez | temp_string = lineas[1][posicion_t+2:] |
| 187 | 1 | cristobal hernandez | temp_c = float(temp_string) / 1000.0 |
| 188 | 1 | cristobal hernandez | return round(temp_c, 2) |
| 189 | 1 | cristobal hernandez | except Exception as e: |
| 190 | 1 | cristobal hernandez | print(f"Error leyendo temp: {e}") |
| 191 | 1 | cristobal hernandez | |
| 192 | 1 | cristobal hernandez | return 25.0 |
| 193 | 1 | cristobal hernandez | |
| 194 | 1 | cristobal hernandez | def leer_ph(): |
| 195 | 1 | cristobal hernandez | VOLTAJE_NEUTRO = 2.50 |
| 196 | 1 | cristobal hernandez | FACTOR_CONVERSION = 4.17 |
| 197 | 1 | cristobal hernandez | |
| 198 | 1 | cristobal hernandez | try: |
| 199 | 1 | cristobal hernandez | valor_raw = grovepi.analogRead(PORT_PH) |
| 200 | 1 | cristobal hernandez | voltaje = float(valor_raw) * 5.0 / 1023 |
| 201 | 1 | cristobal hernandez | ph_actual = 7.0 + ((voltaje - VOLTAJE_NEUTRO) * FACTOR_CONVERSION) |
| 202 | 1 | cristobal hernandez | return round(ph_actual, 1) |
| 203 | 1 | cristobal hernandez | except: return 7.0 |
| 204 | 1 | cristobal hernandez | |
| 205 | 1 | cristobal hernandez | def leer_nivel_agua(): |
| 206 | 1 | cristobal hernandez | try: |
| 207 | 1 | cristobal hernandez | altura_total = 40 |
| 208 | 1 | cristobal hernandez | dist = ultrasonicRead(PORT_ULTRASONIC) |
| 209 | 1 | cristobal hernandez | if dist > altura_total: dist = 40 |
| 210 | 1 | cristobal hernandez | porc = int(100 - ((dist / altura_total) * 100)) |
| 211 | 1 | cristobal hernandez | return max(0, min(100, porc)) |
| 212 | 1 | cristobal hernandez | except: return 80 |
| 213 | 1 | cristobal hernandez | |
| 214 | 1 | cristobal hernandez | def leer_luz(): |
| 215 | 1 | cristobal hernandez | try: |
| 216 | 1 | cristobal hernandez | lectura = analogRead(PORT_SENSOR_LUZ) |
| 217 | 1 | cristobal hernandez | return int((lectura / 800.0) * 100) |
| 218 | 1 | cristobal hernandez | except: return 50 |
| 219 | 1 | cristobal hernandez | |
| 220 | 1 | cristobal hernandez | # Actuadores |
| 221 | 1 | cristobal hernandez | |
| 222 | 1 | cristobal hernandez | def gestionar_calefactor(temp_actual): |
| 223 | 1 | cristobal hernandez | target = float(config_actual.get('temp_min', 24.0)) |
| 224 | 1 | cristobal hernandez | if temp_actual < target: |
| 225 | 1 | cristobal hernandez | digitalWrite(PORT_RELAY_CAL, 1) # Encender |
| 226 | 1 | cristobal hernandez | return True |
| 227 | 1 | cristobal hernandez | elif temp_actual > target + 0.5: |
| 228 | 1 | cristobal hernandez | digitalWrite(PORT_RELAY_CAL, 0) # Apagar |
| 229 | 1 | cristobal hernandez | return False |
| 230 | 1 | cristobal hernandez | return False |
| 231 | 1 | cristobal hernandez | |
| 232 | 1 | cristobal hernandez | def mover_servo_alimentar(): |
| 233 | 1 | cristobal hernandez | if alimentador_servo is None: return False |
| 234 | 1 | cristobal hernandez | try: |
| 235 | 1 | cristobal hernandez | print("Dispensando comida...") |
| 236 | 1 | cristobal hernandez | alimentador_servo.angle = 90 |
| 237 | 1 | cristobal hernandez | time.sleep(0.8) |
| 238 | 1 | cristobal hernandez | alimentador_servo.angle = 110 |
| 239 | 1 | cristobal hernandez | time.sleep(0.3) |
| 240 | 1 | cristobal hernandez | alimentador_servo.angle = 90 |
| 241 | 1 | cristobal hernandez | time.sleep(0.3) |
| 242 | 1 | cristobal hernandez | alimentador_servo.angle = 0 |
| 243 | 1 | cristobal hernandez | time.sleep(1) |
| 244 | 1 | cristobal hernandez | alimentador_servo.value = None |
| 245 | 1 | cristobal hernandez | return True |
| 246 | 1 | cristobal hernandez | except Exception as e: |
| 247 | 1 | cristobal hernandez | print(f"Error Servo: {e}") |
| 248 | 1 | cristobal hernandez | return False |
| 249 | 1 | cristobal hernandez | |
| 250 | 1 | cristobal hernandez | def gestionar_comida(): |
| 251 | 1 | cristobal hernandez | global ultimo_minuto_alimentacion |
| 252 | 1 | cristobal hernandez | if not config_actual.get('sistema_alimentacion_on', True): |
| 253 | 1 | cristobal hernandez | return False |
| 254 | 1 | cristobal hernandez | |
| 255 | 1 | cristobal hernandez | hora_actual = datetime.now().strftime("%H:%M") |
| 256 | 1 | cristobal hernandez | horarios = config_actual.get("horarios_comida", []) |
| 257 | 1 | cristobal hernandez | |
| 258 | 1 | cristobal hernandez | if (hora_actual in horarios) and (hora_actual != ultimo_minuto_alimentacion): |
| 259 | 1 | cristobal hernandez | print(f"Intentando alimentar ({hora_actual})...") |
| 260 | 1 | cristobal hernandez | exito = mover_servo_alimentar() |
| 261 | 1 | cristobal hernandez | if exito: |
| 262 | 1 | cristobal hernandez | ultimo_minuto_alimentacion = hora_actual |
| 263 | 1 | cristobal hernandez | # EMOJI REMOVIDO AQUI |
| 264 | 1 | cristobal hernandez | enviar_notificacion_push("Hora de comer!", f"Tu pez ha sido alimentado a las {hora_actual}") |
| 265 | 1 | cristobal hernandez | guardar_historial_evento("Comida Servida", "Dispensador automatico activado con exito.") |
| 266 | 1 | cristobal hernandez | return True |
| 267 | 1 | cristobal hernandez | return False |
| 268 | 1 | cristobal hernandez | |
| 269 | 1 | cristobal hernandez | def actualizar_lcd(nemo_id, temp, ph, luz, agua, calefactor_on, comida_servida): |
| 270 | 1 | cristobal hernandez | try: |
| 271 | 1 | cristobal hernandez | if comida_servida: |
| 272 | 1 | cristobal hernandez | setText(" DISPENSANDO\n COMIDA...") |
| 273 | 1 | cristobal hernandez | time.sleep(3) |
| 274 | 1 | cristobal hernandez | return |
| 275 | 1 | cristobal hernandez | |
| 276 | 1 | cristobal hernandez | if calefactor_on: setRGB(255, 60, 0) # Naranja |
| 277 | 1 | cristobal hernandez | else: setRGB(0, 255, 0) # Verde |
| 278 | 1 | cristobal hernandez | |
| 279 | 1 | cristobal hernandez | setText(f"ID:{nemo_id}\nT:{temp}C pH:{ph}") |
| 280 | 1 | cristobal hernandez | time.sleep(4) |
| 281 | 1 | cristobal hernandez | setText(f"ID:{nemo_id}\nLuz:{luz}% Agua:{agua}%") |
| 282 | 1 | cristobal hernandez | time.sleep(4) |
| 283 | 1 | cristobal hernandez | except: pass |
| 284 | 1 | cristobal hernandez | |
| 285 | 1 | cristobal hernandez | def main(): |
| 286 | 1 | cristobal hernandez | global nemo_id, ya_se_envio_notificacion |
| 287 | 1 | cristobal hernandez | |
| 288 | 1 | cristobal hernandez | print("\n--- INICIANDO SAVENEMO ---") |
| 289 | 1 | cristobal hernandez | |
| 290 | 1 | cristobal hernandez | inicializar_hardware() |
| 291 | 1 | cristobal hernandez | nemo_id = obtener_id_unico() |
| 292 | 1 | cristobal hernandez | print(f"ID DISPOSITIVO: {nemo_id}") |
| 293 | 1 | cristobal hernandez | |
| 294 | 1 | cristobal hernandez | esperar_internet() |
| 295 | 1 | cristobal hernandez | iniciar_firebase() |
| 296 | 1 | cristobal hernandez | |
| 297 | 1 | cristobal hernandez | print("Loop principal iniciado.\n") |
| 298 | 1 | cristobal hernandez | |
| 299 | 1 | cristobal hernandez | while True: |
| 300 | 1 | cristobal hernandez | try: |
| 301 | 1 | cristobal hernandez | sincronizar_configuracion() |
| 302 | 1 | cristobal hernandez | |
| 303 | 1 | cristobal hernandez | temp = leer_temperatura() |
| 304 | 1 | cristobal hernandez | ph = leer_ph() |
| 305 | 1 | cristobal hernandez | agua = leer_nivel_agua() |
| 306 | 1 | cristobal hernandez | luz = leer_luz() |
| 307 | 1 | cristobal hernandez | |
| 308 | 1 | cristobal hernandez | alertas = [] |
| 309 | 1 | cristobal hernandez | cfg = config_actual |
| 310 | 1 | cristobal hernandez | if temp < float(cfg.get('temp_min', 20)): alertas.append("Temp Baja") |
| 311 | 1 | cristobal hernandez | if temp > float(cfg.get('temp_max', 35)): alertas.append("Temp Alta") |
| 312 | 1 | cristobal hernandez | if agua < int(cfg.get('nivel_agua', 80)): alertas.append("Nivel Bajo") |
| 313 | 1 | cristobal hernandez | if ph < int(cfg.get('ph_min', 80)): alertas.append("PH Bajo") |
| 314 | 1 | cristobal hernandez | if ph > int(cfg.get('ph_max', 80)): alertas.append("PH Alto") |
| 315 | 1 | cristobal hernandez | |
| 316 | 1 | cristobal hernandez | calefactor_on = gestionar_calefactor(temp) |
| 317 | 1 | cristobal hernandez | comida_servida = gestionar_comida() |
| 318 | 1 | cristobal hernandez | |
| 319 | 1 | cristobal hernandez | if alertas: |
| 320 | 1 | cristobal hernandez | if not ya_se_envio_notificacion: |
| 321 | 1 | cristobal hernandez | print(f"Alertas: {alertas}") |
| 322 | 1 | cristobal hernandez | # EMOJI REMOVIDO AQUI |
| 323 | 1 | cristobal hernandez | enviar_notificacion_push("ALERTA ACUARIO ", f"Atencion: {', '.join(alertas)}") |
| 324 | 1 | cristobal hernandez | guardar_historial_evento("Alerta Detectada", ', '.join(alertas)) |
| 325 | 1 | cristobal hernandez | ya_se_envio_notificacion = True |
| 326 | 1 | cristobal hernandez | else: |
| 327 | 1 | cristobal hernandez | if ya_se_envio_notificacion: ya_se_envio_notificacion = False |
| 328 | 1 | cristobal hernandez | |
| 329 | 1 | cristobal hernandez | subir_estado(temp, ph, luz, agua, alertas) |
| 330 | 1 | cristobal hernandez | |
| 331 | 1 | cristobal hernandez | cal_status = 'ON' if calefactor_on else 'OFF' |
| 332 | 1 | cristobal hernandez | |
| 333 | 1 | cristobal hernandez | # Solo para ver por terminal |
| 334 | 1 | cristobal hernandez | print(f"[{datetime.now().strftime('%H:%M')}] T:{temp}C pH:{ph} L:{luz}% A:{agua}% Cal:{cal_status}") |
| 335 | 1 | cristobal hernandez | |
| 336 | 1 | cristobal hernandez | actualizar_lcd(nemo_id, temp, ph, luz, agua, calefactor_on, comida_servida) |
| 337 | 1 | cristobal hernandez | |
| 338 | 1 | cristobal hernandez | except KeyboardInterrupt: |
| 339 | 1 | cristobal hernandez | print("\nApagando sistema...") |
| 340 | 1 | cristobal hernandez | try: |
| 341 | 1 | cristobal hernandez | digitalWrite(PORT_RELAY_CAL, 0) |
| 342 | 1 | cristobal hernandez | setRGB(0, 0, 0) |
| 343 | 1 | cristobal hernandez | setText("") |
| 344 | 1 | cristobal hernandez | if alimentador_servo: alimentador_servo.detach() |
| 345 | 1 | cristobal hernandez | except: pass |
| 346 | 1 | cristobal hernandez | break |
| 347 | 1 | cristobal hernandez | except Exception as e: |
| 348 | 1 | cristobal hernandez | print(f"Error en Loop: {e}") |
| 349 | 1 | cristobal hernandez | time.sleep(5) |
| 350 | 1 | cristobal hernandez | |
| 351 | 1 | cristobal hernandez | if __name__ == "__main__": |
| 352 | 1 | cristobal hernandez | main() |
| 353 | 1 | cristobal hernandez | |
| 354 | 1 | cristobal hernandez | </code></pre> |
| 355 | 3 | cristobal hernandez | </small> |