diff --git a/CHANGELOG_INFRA.md b/CHANGELOG_INFRA.md index b08431f..a56d806 100644 --- a/CHANGELOG_INFRA.md +++ b/CHANGELOG_INFRA.md @@ -9,9 +9,24 @@ - **2026-10-03-perfil-juanjo-instaladas-dependencias-python-21-paquetes-en-** [scripts] Perfil juanjo: instaladas dependencias Python (21 paquetes) en C:\Python314 (aplicado en: sobremesa) - **2026-10-03-sobremesa-juanjo-instaladas-17-apps-de-usuario-que-estaban-e** [scripts] Sobremesa juanjo: instaladas 17 apps de usuario que estaban en juanm (aplicado en: sobremesa) - **2026-10-03-migracion-cuenta-juanjo-rutas-juanm-dinamicas-os-path-expand** [scripts] Migracion cuenta juanjo: rutas juanm -> dinamicas (os.path.expanduser / \C:\Users\juanjo / USERPROFILE) (aplicado en: sobremesa) +- **2026-10-03-monitor-de-estado-telegram-correo-jira-en-segundo-plano** [servicios] Monitor de estado (Telegram + correo + Jira) en segundo plano (aplicado en: portatil) +- **2026-10-03-acceso-remoto-al-sobremesa-openssh-server-tailscale-ssh-powe** [servicios] Acceso remoto al sobremesa: OpenSSH Server + Tailscale (ssh -> PowerShell) (aplicado en: portatil) +- **2026-10-03-voicemonkey-sustituido-por-home-assistant-alexa** [servicios] VoiceMonkey sustituido por Home Assistant (Alexa) (aplicado en: portatil) +- **2026-10-03-omniroute-3-8-48-3-8-51-fix-catalogo-live-antigravity** [omniroute] OmniRoute 3.8.48 -> 3.8.51: fix catalogo live antigravity (aplicado en: portatil) +- **2026-10-03-enrutado-de-voz-de-avisos-carolina-juanjo-abuelo-voicestudio** [scripts] Enrutado de voz de avisos: Carolina/Juanjo/Abuelo (VoiceStudio) (aplicado en: portatil) +- **2026-10-03-voces-de-aviso-cambiadas-a-nativas-en-castellano-carolina-f4** [scripts] Voces de aviso cambiadas a nativas en castellano (Carolina f40515ca / Abuelo e0ecda0a) (aplicado en: portatil) +- **2026-10-04-protocolo-de-encendido-remoto-del-sobremesa-powershell-por-s** [servicios] Protocolo de encendido remoto del sobremesa + PowerShell por SSH (aplicado en: portatil) ## Historial +### 2026-10-04 · Protocolo de encendido remoto del sobremesa + PowerShell por SSH +- **id**: `2026-10-04-protocolo-de-encendido-remoto-del-sobremesa-powershell-por-s` +- **Equipo**: portatil +- **Categoria**: servicios +- **Transferible**: si +- **Aplicado en**: portatil +- **Detalle**: docs/PROTOCOLO_ENCENDIDO_REMOTO_SSH.md + scripts/encender_sobremesa.py (check Tailscale -> enciende regleta via Alexa/HA -> espera 3min -> check -> WoL best-effort -> espera 3min -> informe con voz Carolina) + scripts/ssh_sobremesa.ps1 (abre PowerShell en juanjo@100.75.115.29). Validado: regleta encendio el PC y SSH funciona. Red doble NAT Zyxel(.1)->Asus(.50); se llega por Tailscale. + ### 2026-10-03 · SuperWhisper Enter: AHK F13 boton auxiliar Logitech + tarea programada - **id**: `2026-10-03-superwhisper-enter-ahk-f13-boton-auxiliar-logitech-tarea-pro` - **Equipo**: portatil @@ -99,6 +114,21 @@ - **Transferible**: no - **Aplicado en**: sobremesa - **Detalle**: Colgaba 15s por ruta C:\Users\juanm hardcodeada tras migrar a juanjo; ahora usa ~/Documents/Github/contabo, ConnectTimeout 8s y sigue aunque el VPS no responda. +### 2026-10-03 · Enrutado de voz de avisos: Carolina/Juanjo/Abuelo (VoiceStudio) +- **id**: `2026-10-03-enrutado-de-voz-de-avisos-carolina-juanjo-abuelo-voicestudio` +- **Equipo**: portatil +- **Categoria**: scripts +- **Transferible**: si +- **Aplicado en**: portatil +- **Detalle**: Nuevo INFRAESTRUCTURA\scripts\voz_mensaje.py: genera+reproduce avisos con VoiceStudio (Carolina 10466680 por defecto, Juanjo 4f18d2df biblioteca, Abuelo 9f83c527 critico), en castellano. biblioteca\scripts\aviso_sonoro.py reescrito como wrapper. Requiere VoiceStudio local. + +### 2026-10-03 · Voces de aviso cambiadas a nativas en castellano (Carolina f40515ca / Abuelo e0ecda0a) +- **id**: `2026-10-03-voces-de-aviso-cambiadas-a-nativas-en-castellano-carolina-f4` +- **Equipo**: portatil +- **Categoria**: scripts +- **Transferible**: si +- **Aplicado en**: portatil +- **Detalle**: voz_mensaje.py actualizado: Carolina=f40515ca (ml_spanish_companion) por defecto, Abuelo=e0ecda0a (ml_spanish_explainer) critico, Juanjo=4f18d2df biblioteca. Antes usaba arquetipos ingleses (leian castellano con acento ingles). AGENTS Regla 35 actualizada. ### 2026-10-02 · superguardado.py: protocolo guardado total automatico portatil+sobremesa - **id**: `2026-10-02-superguardado-py-protocolo-guardado-total-automatico-portati` diff --git a/changelog_infra.json b/changelog_infra.json index 65941c5..811bd28 100644 --- a/changelog_infra.json +++ b/changelog_infra.json @@ -351,6 +351,42 @@ "sobremesa" ], "detalle": "Colgaba 15s por ruta C:\\Users\\juanm hardcodeada tras migrar a juanjo; ahora usa ~/Documents/Github/contabo, ConnectTimeout 8s y sigue aunque el VPS no responda." + }, + { + "id": "2026-10-03-enrutado-de-voz-de-avisos-carolina-juanjo-abuelo-voicestudio", + "fecha": "2026-10-03", + "equipo": "portatil", + "categoria": "scripts", + "titulo": "Enrutado de voz de avisos: Carolina/Juanjo/Abuelo (VoiceStudio)", + "transferible": true, + "aplicado_en": [ + "portatil" + ], + "detalle": "Nuevo INFRAESTRUCTURA\\scripts\\voz_mensaje.py: genera+reproduce avisos con VoiceStudio (Carolina 10466680 por defecto, Juanjo 4f18d2df biblioteca, Abuelo 9f83c527 critico), en castellano. biblioteca\\scripts\\aviso_sonoro.py reescrito como wrapper. Requiere VoiceStudio local." + }, + { + "id": "2026-10-03-voces-de-aviso-cambiadas-a-nativas-en-castellano-carolina-f4", + "fecha": "2026-10-03", + "equipo": "portatil", + "categoria": "scripts", + "titulo": "Voces de aviso cambiadas a nativas en castellano (Carolina f40515ca / Abuelo e0ecda0a)", + "transferible": true, + "aplicado_en": [ + "portatil" + ], + "detalle": "voz_mensaje.py actualizado: Carolina=f40515ca (ml_spanish_companion) por defecto, Abuelo=e0ecda0a (ml_spanish_explainer) critico, Juanjo=4f18d2df biblioteca. Antes usaba arquetipos ingleses (leian castellano con acento ingles). AGENTS Regla 35 actualizada." + }, + { + "id": "2026-10-04-protocolo-de-encendido-remoto-del-sobremesa-powershell-por-s", + "fecha": "2026-10-04", + "equipo": "portatil", + "categoria": "servicios", + "titulo": "Protocolo de encendido remoto del sobremesa + PowerShell por SSH", + "transferible": true, + "aplicado_en": [ + "portatil" + ], + "detalle": "docs/PROTOCOLO_ENCENDIDO_REMOTO_SSH.md + scripts/encender_sobremesa.py (check Tailscale -> enciende regleta via Alexa/HA -> espera 3min -> check -> WoL best-effort -> espera 3min -> informe con voz Carolina) + scripts/ssh_sobremesa.ps1 (abre PowerShell en juanjo@100.75.115.29). Validado: regleta encendio el PC y SSH funciona. Red doble NAT Zyxel(.1)->Asus(.50); se llega por Tailscale." } ] } diff --git a/docs/PROTOCOLO_ENCENDIDO_REMOTO_SSH.md b/docs/PROTOCOLO_ENCENDIDO_REMOTO_SSH.md new file mode 100644 index 0000000..2a2c7e2 --- /dev/null +++ b/docs/PROTOCOLO_ENCENDIDO_REMOTO_SSH.md @@ -0,0 +1,54 @@ +# Protocolo — Encendido remoto del sobremesa + PowerShell por SSH + +Fecha: 03-oct-2026. Objetivo: **encender el sobremesa estando apagado y conectarse a su PowerShell** desde el portátil (o iPad). + +## Datos +| Elemento | Valor | +|----------|-------| +| Nombre Tailscale | `desktop-m8se61g` | +| IP Tailscale | `100.75.115.29` | +| IP LAN | `192.168.1.239` (Realtek 2.5GbE, MAC `74:56:3C:CB:0E:CE`) | +| Usuario SSH | `juanjo` (administrador, cuenta local) | +| Regleta (Tapo) | `192.168.50.224` · comando Alexa: **"enciende regleta"** | +| Red | Doble NAT: Zyxel (`.1`) → Asus (`.50`). El portátil está en `.50` y llega al sobremesa por **Tailscale** | + +## Protocolo (paso a paso) + +**0. Estado de la regleta** (opcional, si hay acceso al Tapo): `device_on` vía PyP100 (`getDeviceInfo`). + +**1. Comprobar si el PC está apagado** +```powershell +& "$env:ProgramFiles\Tailscale\tailscale.exe" status | findstr desktop-m8se61g +``` +`offline` = apagado. (Desde la red `.1` también vale `ping 192.168.1.239`.) + +**2. Si está apagado → encender la regleta** (comando Alexa vía Home Assistant) +```powershell +python C:\Users\juanm\Documents\GitHub\biblioteca_negocio_prolongo\scripts\ha_alexa.py command "enciende regleta" +``` + +**3. Esperar 3 minutos.** + +**4. Volver a comprobar el estado.** Si está encendido → **listo**. + +**5. Si sigue apagado → intentar Wake-on-LAN** (magic packet a la MAC `74:56:3C:CB:0E:CE`). +> Nota: desde el portátil (`.50`) el magic packet **no llega** a `.1` por el doble NAT. Solo funcionaría desde un equipo en la red `.1`. El método fiable es la **regleta** (+ BIOS `Restore on AC Power Loss = Power On`). + +**6. Esperar 3 minutos, comprobar e informar.** + +**7. Conectar a PowerShell:** +```powershell +ssh juanjo@100.75.115.29 +``` +(o `scripts\ssh_sobremesa.ps1`). + +## Scripts +- `scripts\encender_sobremesa.py` — automatiza el protocolo (check → regleta → 3 min → check → WoL → 3 min → informe + aviso de voz Carolina). +- `scripts\ssh_sobremesa.ps1` — abre una sesión **PowerShell** interactiva en el sobremesa por SSH. +- `scripts\ha_alexa.py` (repo de negocio) — `announce` / `command` a Alexa vía Home Assistant. + +## Notas +- **Fast Startup** activado → WoL desde apagado poco fiable. +- `sshd` y Tailscale arrancan como **servicios** (no requieren que haya usuario logueado). +- Los avisos de fin de comprobación se hacen con la **voz Carolina** (`voz_mensaje.py --tipo default`). +- Si el SSH al VPS (`185.187.169.109`) deja de responder desde un equipo, puede ser **fail2ban** por reintentos; esperar o desbloquear la IP en el VPS. diff --git a/scripts/encender_sobremesa.py b/scripts/encender_sobremesa.py new file mode 100644 index 0000000..a3c3215 --- /dev/null +++ b/scripts/encender_sobremesa.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""encender_sobremesa.py — Protocolo de encendido remoto del sobremesa. + +Pasos (protocolo acordado): + 0. Estado de la regleta (best-effort, si hay acceso al Tapo). + 1. Comprobar si el PC esta apagado (Tailscale). + 2. Si esta apagado -> encender la regleta con un comando a Alexa (via Home Assistant). + 3. Esperar 3 min. + 4. Volver a comprobar. Si esta encendido -> fin. + 5. Si sigue apagado -> intentar Wake-on-LAN (best-effort). + 6. Esperar 3 min, comprobar e informar. + +Al terminar cada comprobacion emite un aviso de voz (Carolina) para que el usuario +pueda hacer otras cosas mientras tanto. + +Uso: + python scripts/encender_sobremesa.py [--espera 180] [--no-voz] +""" + +from __future__ import annotations + +import argparse +import glob +import os +import re +import subprocess +import sys +import time +from pathlib import Path + +_SCRIPTS_DIR = Path(__file__).resolve().parent +if str(_SCRIPTS_DIR) not in sys.path: + sys.path.insert(0, str(_SCRIPTS_DIR)) +import infra_paths # noqa: E402 + +TS_EXE = os.path.join(os.environ.get("ProgramFiles", r"C:\Program Files"), "Tailscale", "tailscale.exe") +NODO = "desktop-m8se61g" +TAILSCALE_IP = "100.75.115.29" +MAC = "74:56:3C:CB:0E:CE" +COMANDO_REGLETA = "enciende regleta" + + +# --------------------------------------------------------------- estado del PC + +def pc_encendido() -> bool | None: + """True/False segun Tailscale. None si no se puede determinar.""" + try: + out = subprocess.run([TS_EXE, "status"], capture_output=True, text=True, timeout=20).stdout + except Exception: + return None + for linea in out.splitlines(): + if NODO in linea: + return "offline" not in linea + return None + + +# --------------------------------------------------------------- regleta (Tapo) + +def estado_regleta() -> bool | None: + """Best-effort: True/False si se puede consultar el Tapo; None si no.""" + try: + from PyP100 import PyP100 as PP + except Exception: + return None + # credenciales hardcodeadas en un script existente del repo de negocio + repo = infra_paths.biblioteca_repo() + creds = None + for f in glob.glob(str(repo / "scripts" / "protocolo_apagado*.py")) + glob.glob(str(_SCRIPTS_DIR / "protocolo_apagado*.py")): + try: + s = open(f, encoding="utf-8", errors="replace").read() + except Exception: + continue + m = re.search(r"P100\(\s*'([^']+)'\s*,\s*'([^']+)'\s*,\s*'([^']+)'\s*\)", s) + if m: + creds = m.groups() + break + if not creds: + return None + try: + d = PP.P100(*creds) + d.handshake() + d.login() + return bool(d.getDeviceInfo().get("result", {}).get("device_on")) + except Exception: + return None + + +# --------------------------------------------------------------- Alexa / HA + +def encender_regleta() -> bool: + """Envia 'enciende regleta' a Alexa vía Home Assistant.""" + infra_paths.bootstrap_biblioteca_paths() + try: + from ha_alexa import command + + return command(COMANDO_REGLETA) + except Exception as e: + print(f"[encender_sobremesa] Error llamando a HA: {e}", file=sys.stderr) + return False + + +# --------------------------------------------------------------- WoL + +def wol() -> None: + """Magic packet (best-effort). Desde .50 no cruza a .1; se intenta igualmente.""" + import socket + + paquete = b"\xff" * 6 + bytes.fromhex(MAC.replace(":", "")) * 16 + for destino in (f"{TAILSCALE_IP.split('.')[0]}.255", "255.255.255.255", "192.168.1.255", "192.168.50.255"): + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + s.sendto(paquete, (destino, 9)) + s.close() + print(f" magic packet -> {destino}:9") + except Exception as e: + print(f" magic packet -> {destino}:9 ERROR {e}") + + +# --------------------------------------------------------------- voz + +def aviso(texto: str, voz: bool = True) -> None: + print(f"[aviso] {texto}") + if not voz: + return + try: + subprocess.run([sys.executable, str(_SCRIPTS_DIR / "voz_mensaje.py"), texto, "--tipo", "default"], + check=False, timeout=120) + except Exception: + pass + + +# --------------------------------------------------------------- protocolo + +def main() -> int: + ap = argparse.ArgumentParser(description="Encendido remoto del sobremesa.") + ap.add_argument("--espera", type=int, default=180, help="Segundos de espera entre comprobaciones (defecto 180).") + ap.add_argument("--no-voz", action="store_true", help="Sin avisos de voz.") + args = ap.parse_args() + voz = not args.no_voz + + reg = estado_regleta() + print(f"Regleta: {'ENCENDIDA' if reg else 'APAGADA' if reg is False else 'desconocido'}") + + st = pc_encendido() + print(f"PC: {'encendido' if st else 'apagado' if st is False else 'desconocido'}") + if st: + aviso("El sobremesa ya estaba encendido.", voz) + return 0 + + print(f"-> Encendiendo regleta (Alexa: '{COMANDO_REGLETA}')... {encender_regleta()}") + print(f"-> Esperando {args.espera}s...") + time.sleep(args.espera) + + if pc_encendido(): + aviso("El sobremesa se ha encendido correctamente con la regleta.", voz) + return 0 + + print("-> Sigue apagado; intentando Wake-on-LAN (best-effort)...") + wol() + print(f"-> Esperando {args.espera}s...") + time.sleep(args.espera) + + if pc_encendido(): + aviso("El sobremesa se ha encendido tras el Wake on LAN.", voz) + return 0 + + aviso("No se ha podido encender el sobremesa ni con la regleta ni con Wake on LAN.", voz) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/ssh_sobremesa.ps1 b/scripts/ssh_sobremesa.ps1 new file mode 100644 index 0000000..f3015f8 --- /dev/null +++ b/scripts/ssh_sobremesa.ps1 @@ -0,0 +1,21 @@ +<# +.SYNOPSIS + Abre una sesion PowerShell remota en el sobremesa por SSH (sobre Tailscale). + +.DESCRIPTION + Se conecta a `juanjo@100.75.115.29` (sobremesa, DESKTOP-M8SE61G) y cae en su + PowerShell. Requiere que el sobremesa este encendido (ver + `scripts/encender_sobremesa.py`) y Tailscale activo en ambos equipos. + +.EXAMPLE + powershell -ExecutionPolicy Bypass -File scripts\ssh_sobremesa.ps1 +#> +param( + [string]$Maquina = "100.75.115.29", + [string]$Usuario = "juanjo" +) + +$ErrorActionPreference = 'Stop' +Write-Host "Conectando a PowerShell del sobremesa ($Usuario@$Maquina)..." -ForegroundColor Cyan +Write-Host "Para salir: escribe 'exit'." -ForegroundColor DarkGray +ssh "$Usuario@$Maquina" diff --git a/scripts/voz_mensaje.py b/scripts/voz_mensaje.py index 148a35a..f733365 100644 --- a/scripts/voz_mensaje.py +++ b/scripts/voz_mensaje.py @@ -4,9 +4,11 @@ Genera y reproduce un mensaje hablado con VoiceStudio (http://127.0.0.1:3900) eligiendo el perfil de voz segun el TIPO de mensaje: - default -> Carolina (10466680) Mensajes trascendentes de OpenCode (por defecto) + default -> Carolina (f40515ca) Mensajes trascendentes de OpenCode (por defecto) biblioteca -> Juanjo (4f18d2df) Mensajes de la biblioteca Prolongo - critico -> Abuelo (9f83c527) Mensajes importantes / criticos + critico -> Abuelo (e0ecda0a) Mensajes importantes / criticos + +Todas las voces son NATIVAS EN CASTELLANO (language=es). Perfiles en VoiceStudio. Uso: python voz_mensaje.py "texto a decir" [--tipo default|biblioteca|critico] @@ -28,9 +30,9 @@ BASE = os.environ.get("VOICESTUDIO_BASE", "http://127.0.0.1:3900") # tipo -> (profile_id, nombre) PROFILES = { - "default": ("10466680", "Carolina"), + "default": ("f40515ca", "Carolina"), "biblioteca": ("4f18d2df", "Juanjo"), - "critico": ("9f83c527", "Abuelo"), + "critico": ("e0ecda0a", "Abuelo"), }