From 5d232cd8302041e5e7f97badc7bb85746eb86976 Mon Sep 17 00:00:00 2001 From: minguezsanzjuanjose Date: Sun, 2 Aug 2026 22:49:59 +0200 Subject: [PATCH] superguardado: comando limpieza (limpia procesos atascados de la Pi) + limpiar.py --- .obsidian/app.json | 1 + .obsidian/appearance.json | 1 + .obsidian/core-plugins.json | 33 +++++++ .obsidian/graph.json | 22 +++++ .obsidian/workspace.json | 189 ++++++++++++++++++++++++++++++++++++ vigilancia/limpiar.py | 82 ++++++++++++++++ vigilancia/pi_bot.py | 12 ++- 7 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 .obsidian/app.json create mode 100644 .obsidian/appearance.json create mode 100644 .obsidian/core-plugins.json create mode 100644 .obsidian/graph.json create mode 100644 .obsidian/workspace.json create mode 100644 vigilancia/limpiar.py diff --git a/.obsidian/app.json b/.obsidian/app.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.obsidian/app.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.obsidian/appearance.json b/.obsidian/appearance.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.obsidian/appearance.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.obsidian/core-plugins.json b/.obsidian/core-plugins.json new file mode 100644 index 0000000..639b90d --- /dev/null +++ b/.obsidian/core-plugins.json @@ -0,0 +1,33 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "footnotes": false, + "properties": true, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": true, + "bases": true, + "webviewer": false +} \ No newline at end of file diff --git a/.obsidian/graph.json b/.obsidian/graph.json new file mode 100644 index 0000000..e21a18d --- /dev/null +++ b/.obsidian/graph.json @@ -0,0 +1,22 @@ +{ + "collapse-filter": true, + "search": "", + "showTags": false, + "showAttachments": false, + "hideUnresolved": false, + "showOrphans": true, + "collapse-color-groups": true, + "colorGroups": [], + "collapse-display": true, + "showArrow": false, + "textFadeMultiplier": 0, + "nodeSizeMultiplier": 1, + "lineSizeMultiplier": 1, + "collapse-forces": true, + "centerStrength": 0.518713248970312, + "repelStrength": 10, + "linkStrength": 1, + "linkDistance": 250, + "scale": 1, + "close": false +} \ No newline at end of file diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json new file mode 100644 index 0000000..af1cec4 --- /dev/null +++ b/.obsidian/workspace.json @@ -0,0 +1,189 @@ +{ + "main": { + "id": "926bba7b6257cb83", + "type": "split", + "children": [ + { + "id": "d8f7c226097458c7", + "type": "tabs", + "children": [ + { + "id": "72506070fed1a695", + "type": "leaf", + "state": { + "type": "graph", + "state": {}, + "icon": "lucide-git-fork", + "title": "Vista gráfica" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "84a364de2999912b", + "type": "split", + "children": [ + { + "id": "8bab02b5105eeb5b", + "type": "tabs", + "children": [ + { + "id": "487dc72c4c2ad06f", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-folder-closed", + "title": "Explorador de archivos" + } + }, + { + "id": "245580e4caa54b56", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Buscar" + } + }, + { + "id": "e78680f9daec255d", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": { + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-bookmark", + "title": "Marcadores" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "2a664eeb01bb6a0e", + "type": "split", + "children": [ + { + "id": "7d195b329221cd02", + "type": "tabs", + "children": [ + { + "id": "1ebc95b7268f14a7", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Enlaces entrantes" + } + }, + { + "id": "a3196bc4461f5957", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Enlaces salientes" + } + }, + { + "id": "464b357dcc7110d9", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "Etiquetas" + } + }, + { + "id": "fce335174b5a3979", + "type": "leaf", + "state": { + "type": "all-properties", + "state": { + "sortOrder": "frequency", + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-archive", + "title": "Todas las propiedades" + } + }, + { + "id": "139ea07527216a59", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "Esquema" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Abrir selector rápido": false, + "graph:Abrir vista gráfica": false, + "canvas:Crear nuevo lienzo": false, + "daily-notes:Abrir la nota de hoy": false, + "templates:Insertar plantilla": false, + "command-palette:Abrir paleta de comandos": false, + "bases:Crear nueva base": false + } + }, + "active": "72506070fed1a695", + "lastOpenFiles": [ + "vigilancia/__pycache__/limpiar.cpython-313.pyc", + "vigilancia/limpiar.py" + ] +} \ No newline at end of file diff --git a/vigilancia/limpiar.py b/vigilancia/limpiar.py new file mode 100644 index 0000000..7c6f4b5 --- /dev/null +++ b/vigilancia/limpiar.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Limpieza de procesos de la Pi: mata procesos zombie/atascados, reinicia servicios si es necesario.""" +import subprocess +import os +import sys +import time + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import config + +def sh(cmd): + try: + return subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=15).stdout.strip() + except Exception: + return "" + +def limpiar(): + informe = [] + + # 1. Procesos Python atascados (CPU > 50% durante >60s) + ps = sh("ps aux | grep python3 | grep -v grep") + for linea in ps.splitlines(): + if not linea: continue + parts = linea.split() + try: + cpu = float(parts[2]) + pid = parts[1] + cmd = " ".join(parts[10:]) + if cpu > 50 and "minipc" in cmd: + informe.append(f"Matando proceso atascado PID {pid} ({cpu}% CPU): {cmd[:50]}") + sh(f"kill -9 {pid}") + except (IndexError, ValueError): + pass + + # 2. Zombie processes + zombies = sh("ps aux | awk '$8==\"Z\" {print $2}'") + for z in zombies.splitlines(): + if z.strip(): + informe.append(f"Zombie PID {z.strip()} limpiado") + sh(f"kill -9 {z.strip()}") + + # 3. Procesos python viejos (>2h, sin ser servicios principales) + ps2 = sh("ps aux | grep python3 | grep -v grep") + for linea in ps2.splitlines(): + if not linea: continue + parts = linea.split() + try: + pid = parts[1] + starttime = parts[8] + cmd = " ".join(parts[10:]) + if "tv_api" not in cmd and "pi_bot" not in cmd and "http.server" not in cmd: + if "python" in cmd.lower() or "vigilancia" in cmd.lower(): + informe.append(f"Proceso residual PID {pid}: {cmd[:60]}") + except (IndexError, ValueError): + pass + + # 4. Limpiar cache del sistema + sh("echo 'raspberry' | sudo -S sync") + sh("echo 'raspberry' | sudo -S sh -c 'echo 3 > /proc/sys/vm/drop_caches'") + informe.append("Caché del sistema limpiado") + + # 5. Verificar servicios + for svc in ["tv-api", "pi-bot"]: + st = sh(f"systemctl is-active {svc}") + if st != "active": + informe.append(f"⚠️ Servicio {svc} no activo: {st}") + sh(f"echo 'raspberry' | sudo -S systemctl restart {svc}") + + # 6. Estado final + temp = sh("vcgencmd measure_temp") + load = sh("cat /proc/loadavg | cut -d' ' -f1") + mem = sh("free -h | awk 'NR==2{print $2\" total, \"$7\" libre\"}'") + + if not informe: + informe.append("✅ Todo limpio, sin procesos atascados") + + informe.append(f"\nEstado: temp={temp} | load={load} | mem={mem}") + return "\n".join(informe) + + +if __name__ == "__main__": + print(limpiar()) diff --git a/vigilancia/pi_bot.py b/vigilancia/pi_bot.py index 93bab01..da427f0 100644 --- a/vigilancia/pi_bot.py +++ b/vigilancia/pi_bot.py @@ -137,6 +137,15 @@ def procesar(texto, chat_id): log(f"panorama: {r.stdout.strip()[-200:]}") except Exception as e: log(f"panorama error: {e}") + elif t in ("limpieza", "/limpieza", "limpiar", "/limpiar"): + enviar("🧹 Ejecutando limpieza...", chat_id) + try: + from limpiar import limpiar + resultado = limpiar() + enviar(f"
{resultado}
", chat_id) + except Exception as e: + log(f"limpieza error: {e}") + enviar(f"❌ Error: {e}", chat_id) elif t.startswith("youtube ") or t.startswith("/youtube "): url = texto.split(" ", 1)[1].strip() if not url: @@ -151,7 +160,8 @@ def procesar(texto, chat_id): "tv <mensaje> — mensaje rápido a la tele\n" "alarma <mensaje> — secuencia completa (sirena x3)\n" "youtube <URL> — reproduce YouTube y apaga al terminar\n" - "panorama — fotos de las cámaras", + "panorama — fotos de las cámaras\n" + "limpieza — limpia procesos atascados de la Pi", chat_id)