# Voz → Calendario Nextcloud — Blueprint Completo **Versión:** 1.0 **Fecha:** 2026-07-03 **Autor:** Álvaro **n8n:** https://n8n.crewinghunters.com --- ## Visión General Grabas un audio en el teléfono → se lo mandas al bot de Telegram → la IA local transcribe y entiende el evento → aparece en tu calendario de Nextcloud → te recuerda 30 minutos antes por Telegram. ``` 📱 Audio (teléfono) │ ▼ Telegram ┌─────────────────────────────────────────────────┐ │ n8n (Raspberry Pi 4 — Colombia) │ │ │ │ 1. Recibe audio de Telegram │ │ 2. Descarga el archivo .ogg/.mp3 │ │ 3. Llama a Whisper API (tu PC via Tailscale) │ │ 4. Llama a Ollama (tu PC via Tailscale) │ │ 5. Crea evento en Nextcloud Calendar (CalDAV) │ │ 6. Confirma por Telegram │ │ 7. Programa recordatorio 30min antes │ └─────────────────────────────────────────────────┘ │ ▼ Tailscale VPN (túnel privado gratis) ┌─────────────────────────────────────────────────┐ │ Tu PC (Windows) │ │ ├── Ollama → AsistentePersonal / qwen2.5:14b │ │ └── Whisper API → faster-whisper (Python) │ └─────────────────────────────────────────────────┘ ``` --- ## Stack Tecnológico | Componente | Herramienta | Dónde corre | |-----------|------------|------------| | Automatización | n8n | RPi4 Colombia | | Bot mensajería | Telegram Bot | Cloud (Telegram) | | Transcripción voz | faster-whisper | Tu PC (Windows) | | LLM extracción evento | Ollama / qwen2.5:14b | Tu PC (Windows) | | Calendario | Nextcloud CalDAV | RPi4 Colombia | | Túnel privado | Tailscale | PC + RPi4 | | API transcripción | FastAPI (Python) | Tu PC (Windows) | --- ## PASO 1 — Tailscale (conectar PC con RPi4) Tailscale crea una red privada entre tus dispositivos. Una vez instalado, tu PC tendrá una IP tipo `100.x.x.x` accesible desde el RPi4 sin abrir puertos ni router. ### En tu PC (Windows) 1. Descarga e instala Tailscale: https://tailscale.com/download/windows 2. Inicia sesión con Google o email 3. Anota tu IP Tailscale: aparece en el icono de la bandeja del sistema - Ejemplo: `100.64.1.10` ### En el RPi4 (via SSH desde Colombia) ```bash # Conectar al RPi4 ssh usuario@n8n.crewinghunters.com # Instalar Tailscale en RPi4 curl -fsSL https://tailscale.com/install.sh | sh # Iniciar y autenticar (con la MISMA cuenta que en el PC) sudo tailscale up # Verificar que ve tu PC tailscale ping 100.64.1.10 # ← IP de tu PC Tailscale ``` ✅ Si el ping responde, están conectados. El RPi4 puede hablar con tu PC. --- ## PASO 2 — Whisper API en tu PC Instalamos faster-whisper y levantamos una API HTTP que n8n puede llamar. ### Instalar faster-whisper ```bash # En tu PC (PowerShell) pip install faster-whisper fastapi uvicorn python-multipart ``` ### Crear el servidor API Guarda este archivo en: `C:\whisper-api\server.py` ```python from fastapi import FastAPI, UploadFile, File from faster_whisper import WhisperModel import tempfile, os app = FastAPI() # Carga el modelo una vez al iniciar (medium es bueno para español) # Opciones: "tiny", "base", "small", "medium", "large-v3" model = WhisperModel("medium", device="cpu", compute_type="int8") @app.post("/transcribe") async def transcribe(file: UploadFile = File(...)): # Guardar audio temporalmente suffix = os.path.splitext(file.filename)[1] or ".ogg" with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp: tmp.write(await file.read()) tmp_path = tmp.name # Transcribir segments, info = model.transcribe(tmp_path, language="es") text = " ".join([seg.text for seg in segments]).strip() os.unlink(tmp_path) return {"text": text, "language": info.language} @app.get("/health") def health(): return {"status": "ok"} ``` ### Script para iniciar la API (guarda como `C:\whisper-api\start.bat`) ```bat @echo off cd C:\whisper-api uvicorn server:app --host 0.0.0.0 --port 8765 ``` ### Iniciar automáticamente con Windows 1. Presiona `Win + R` → escribe `shell:startup` 2. Crea un acceso directo a `start.bat` en esa carpeta 3. La API estará disponible en: `http://100.64.1.10:8765` (via Tailscale) ### Verificar que funciona (desde RPi4) ```bash # Desde el RPi4 via SSH curl http://100.64.1.10:8765/health # Debe responder: {"status":"ok"} ``` --- ## PASO 3 — Exponer Ollama via Tailscale Por defecto Ollama solo escucha en localhost. Hay que permitir que escuche en todas las interfaces. ### En tu PC (PowerShell como Administrador) ```powershell # Agregar variable de entorno para Ollama [System.Environment]::SetEnvironmentVariable("OLLAMA_HOST", "0.0.0.0:11434", "Machine") # Reiniciar Ollama (busca en la bandeja del sistema → clic derecho → Quit, luego reabre) ``` ### Verificar desde RPi4 ```bash curl http://100.64.1.10:11434/api/tags # Debe listar tus modelos ``` --- ## PASO 4 — Bot de Telegram ### Crear el bot 1. Abre Telegram → busca `@BotFather` 2. Escribe `/newbot` 3. Nombre del bot: `Mi Calendario Personal` (o el que quieras) 4. Username: `mi_calendario_alvaro_bot` (debe terminar en `_bot`) 5. BotFather te da un **token** — guárdalo: `7123456789:AAF...` ### Obtener tu Chat ID 1. Escribe cualquier mensaje a tu nuevo bot 2. Abre en el navegador (reemplaza TOKEN): ``` https://api.telegram.org/botTOKEN/getUpdates ``` 3. Busca `"id"` dentro de `"chat"` — ese número es tu **Chat ID** - Ejemplo: `123456789` --- ## PASO 5 — Nextcloud Calendar (CalDAV) n8n se conecta al calendario de Nextcloud via CalDAV. ### Obtener la URL CalDAV En Nextcloud → Calendario → ⚙️ (engranaje junto al calendario) → "Copiar enlace privado" La URL tiene esta forma: ``` https://tu-nextcloud.com/remote.php/dav/calendars/USUARIO/personal/ ``` ### Credenciales - Usuario: tu usuario de Nextcloud - Contraseña: genera una **contraseña de aplicación** en Nextcloud: Configuración → Seguridad → Dispositivos y sesiones → "Crear nueva contraseña de aplicación" - Nombre: `n8n-calendario` --- ## PASO 6 — Organizar Carpetas en n8n Antes de crear el workflow, organiza las carpetas: 1. Abre https://n8n.crewinghunters.com 2. En el panel izquierdo → "Workflows" → "Add folder" 3. Crea estas carpetas: ``` 📁 Prisa Yachts ← mueve el bot de WhatsApp aquí 📁 Personal - Productividad 📁 AIS Navigator 📁 Pruebas / Sandbox ``` 4. El nuevo workflow irá en `📁 Personal - Productividad` --- ## PASO 7 — Workflow n8n Completo Nombre del workflow: `🎙️ Voz → Calendario Nextcloud` Carpeta: `Personal - Productividad` ### Nodos del workflow (en orden): ``` [Telegram Trigger] │ ▼ [IF: ¿es audio?] │ sí ▼ [HTTP: Descargar audio de Telegram] │ ▼ [HTTP: Whisper API → transcripción] │ ▼ [HTTP: Ollama → extraer evento] │ ▼ [Code: parsear JSON respuesta Ollama] │ ▼ [HTTP: Crear evento CalDAV en Nextcloud] │ ▼ [Telegram: Confirmar al usuario] │ ▼ [Wait: esperar hasta 30min antes del evento] │ ▼ [Telegram: Enviar recordatorio] ``` --- ### Nodo 1 — Telegram Trigger ``` Tipo: Telegram Trigger Credential: (crear nueva → Token del bot) Updates: message ``` ### Nodo 2 — IF: ¿Es audio? ``` Tipo: IF Condición: {{ $json.message.voice }} existe O {{ $json.message.audio }} existe ``` ### Nodo 3 — HTTP: Descargar audio de Telegram ``` Tipo: HTTP Request Método: GET URL: https://api.telegram.org/bot{{ $env.TELEGRAM_TOKEN }}/getFile Parámetros: file_id: {{ $json.message.voice.file_id }} ``` Luego un segundo HTTP Request para descargar el archivo: ``` URL: https://api.telegram.org/file/bot{{ $env.TELEGRAM_TOKEN }}/{{ $json.result.file_path }} Respuesta: archivo binario ``` ### Nodo 4 — HTTP: Whisper API (transcripción) ``` Tipo: HTTP Request Método: POST URL: http://100.64.1.10:8765/transcribe ← IP Tailscale de tu PC Body: form-data file: [archivo de audio del nodo anterior] ``` Resultado esperado: ```json { "text": "Reunión con Juan el viernes a las diez de la mañana en el puerto" } ``` ### Nodo 5 — HTTP: Ollama (extraer evento) ``` Tipo: HTTP Request Método: POST URL: http://100.64.1.10:11434/api/generate Body JSON: { "model": "qwen2.5:14b", "stream": false, "prompt": "Extrae el evento de esta frase y devuelve SOLO JSON válido sin explicaciones:\n\nFrase: '{{ $json.text }}'\n\nFecha actual: {{ $now.format('YYYY-MM-DD') }}, {{ $now.format('dddd') }}\n\nDevuelve exactamente este formato:\n{\"titulo\": \"...\", \"fecha\": \"YYYY-MM-DD\", \"hora_inicio\": \"HH:MM\", \"hora_fin\": \"HH:MM\", \"lugar\": \"...\", \"descripcion\": \"...\"}\n\nSi no hay hora fin, suma 1 hora a la hora inicio. Si no hay lugar, pon vacío." } ``` ### Nodo 6 — Code: Parsear respuesta Ollama ```javascript // Extraer el JSON de la respuesta de Ollama const responseText = $input.item.json.response; // Buscar el JSON dentro del texto (por si Ollama añade texto extra) const jsonMatch = responseText.match(/\{[\s\S]*\}/); if (!jsonMatch) throw new Error("Ollama no devolvió JSON válido: " + responseText); const evento = JSON.parse(jsonMatch[0]); // Construir fechas iCal (formato: 20260704T100000) const fechaInicio = evento.fecha.replace(/-/g, '') + 'T' + evento.hora_inicio.replace(':', '') + '00'; const fechaFin = evento.fecha.replace(/-/g, '') + 'T' + evento.hora_fin.replace(':', '') + '00'; // UID único para el evento const uid = 'n8n-' + Date.now() + '@crewinghunters.com'; // Formato iCalendar (CalDAV) const ical = `BEGIN:VCALENDAR VERSION:2.0 PRODID:-//n8n//Calendario Voz//ES BEGIN:VEVENT UID:${uid} DTSTART:${fechaInicio} DTEND:${fechaFin} SUMMARY:${evento.titulo} LOCATION:${evento.lugar || ''} DESCRIPTION:${evento.descripcion || 'Creado por voz via n8n'} END:VEVENT END:VCALENDAR`; return { json: { ...evento, ical, uid, fechaInicio, fechaFin, // Para el recordatorio: fecha ISO completa fechaRecordatorio: new Date(evento.fecha + 'T' + evento.hora_inicio + ':00').toISOString() } }; ``` ### Nodo 7 — HTTP: Crear evento en Nextcloud (CalDAV) ``` Tipo: HTTP Request Método: PUT URL: https://tu-nextcloud.com/remote.php/dav/calendars/USUARIO/personal/{{ $json.uid }}.ics Autenticación: Basic Auth Usuario: tu_usuario_nextcloud Password: contraseña_de_aplicacion Headers: Content-Type: text/calendar; charset=utf-8 Body: {{ $json.ical }} ``` ### Nodo 8 — Telegram: Confirmar ``` Tipo: Telegram Operación: Send Message Chat ID: {{ $env.TELEGRAM_CHAT_ID }} Texto: ✅ *Evento creado en tu calendario* 📅 *{{ $json.titulo }}* 🗓 {{ $json.fecha }} 🕐 {{ $json.hora_inicio }} – {{ $json.hora_fin }} 📍 {{ $json.lugar || 'Sin ubicación' }} _Te recordaré 30 minutos antes_ ⏰ ``` ### Nodo 9 — Wait (hasta 30min antes del evento) ``` Tipo: Wait Modo: Until specified time Fecha/Hora: {{ $json.fechaRecordatorio }} Offset: -30 minutos ``` ### Nodo 10 — Telegram: Recordatorio ``` Tipo: Telegram Operación: Send Message Chat ID: {{ $env.TELEGRAM_CHAT_ID }} Texto: ⏰ *Recordatorio — en 30 minutos:* 📅 *{{ $json.titulo }}* 🕐 {{ $json.hora_inicio }} 📍 {{ $json.lugar || '' }} ``` --- ## PASO 8 — Variables de Entorno en n8n En n8n → Settings → Variables → Agregar: | Variable | Valor | |---------|-------| | `TELEGRAM_TOKEN` | `7123456789:AAF...` (token del bot) | | `TELEGRAM_CHAT_ID` | `123456789` (tu chat ID) | | `WHISPER_URL` | `http://100.64.1.10:8765` | | `OLLAMA_URL` | `http://100.64.1.10:11434` | | `NEXTCLOUD_URL` | `https://tu-nextcloud.com` | | `NEXTCLOUD_USER` | `tu_usuario` | | `NEXTCLOUD_PASS` | `contraseña_de_aplicacion` | --- ## PASO 9 — Prueba Completa 1. Graba un audio en el teléfono diciendo: > *"Reunión con Federico el próximo martes a las tres de la tarde en el puerto de Miami"* 2. Envía el audio al bot de Telegram 3. En ~10-15 segundos deberías recibir: ``` ✅ Evento creado en tu calendario 📅 Reunión con Federico 🗓 2026-07-07 🕐 15:00 – 16:00 📍 Puerto de Miami ``` 4. Verifica en Nextcloud Calendar que el evento aparece 5. Espera el recordatorio 30 min antes ✅ --- ## Checklist de Instalación ### En tu PC (Windows) - [ ] Instalar Tailscale → anotar IP (`100.x.x.x`) - [ ] `pip install faster-whisper fastapi uvicorn python-multipart` - [ ] Crear `C:\whisper-api\server.py` (código arriba) - [ ] Crear `C:\whisper-api\start.bat` - [ ] Agregar `start.bat` al inicio de Windows - [ ] Iniciar la API y verificar: `curl http://localhost:8765/health` - [ ] Configurar Ollama para escuchar en `0.0.0.0`: variable `OLLAMA_HOST` ### En el RPi4 (via SSH) - [ ] `curl -fsSL https://tailscale.com/install.sh | sh` - [ ] `sudo tailscale up` → autenticar con la misma cuenta - [ ] Verificar ping a PC: `tailscale ping 100.x.x.x` - [ ] Verificar Whisper: `curl http://100.x.x.x:8765/health` - [ ] Verificar Ollama: `curl http://100.x.x.x:11434/api/tags` ### En Telegram - [ ] Crear bot con @BotFather → guardar token - [ ] Obtener tu Chat ID - [ ] Escribir un mensaje al bot para activarlo ### En Nextcloud - [ ] Copiar URL CalDAV del calendario - [ ] Crear contraseña de aplicación para n8n ### En n8n - [ ] Crear carpetas: Prisa Yachts / Personal / AIS Navigator / Sandbox - [ ] Crear variables de entorno (8 variables) - [ ] Crear workflow `🎙️ Voz → Calendario Nextcloud` - [ ] Agregar los 10 nodos en orden - [ ] Activar el workflow - [ ] Prueba con audio real --- ## Solución de Problemas | Problema | Causa probable | Solución | |---------|---------------|---------| | n8n no llega a Whisper | Tailscale no conectado | `sudo tailscale up` en RPi4 | | Whisper tarda mucho | Modelo "medium" pesado en CPU | Cambiar a "small" en server.py | | Ollama no responde | OLLAMA_HOST no configurado | Reiniciar Ollama tras la variable | | Evento en fecha incorrecta | Ollama interpretó mal el día | Ajustar el prompt con más contexto | | CalDAV 401 Unauthorized | Contraseña de app incorrecta | Crear nueva contraseña en Nextcloud | | Audio no se procesa | Formato no soportado | Telegram envía .ogg — faster-whisper lo soporta | --- ## Mejoras Futuras (Fase 2) - [ ] **Cancelar eventos por voz**: "Cancela la reunión del martes" → n8n borra el evento - [ ] **Consultar agenda**: "¿Qué tengo mañana?" → n8n lee el CalDAV y responde - [ ] **Recordatorio personalizable**: "Recuérdame 1 hora antes" - [ ] **Múltiples calendarios**: personal, trabajo, AIS Navigator - [ ] **Texto además de voz**: si escribes (no mandas audio), también funciona --- *Blueprint generado: 2026-07-03 | AR Electronics / Álvaro*