---
name: beltzac-home-integration
description: "Complete home integration — LG TV (55SM8600PSA), Google Nest Mini, and Raspberry Pi media server. Covers UPnP/DLNA, SSAP WebSocket API, webOS Developer Mode, Google Cast, and all MCP tools. Use when managing media playback across devices, debugging LG TV integration, or extending the home automation system."
---

# Beltzac Home Integration — LG TV + Google Nest Mini + Pi

## 📡 Network Map

```
Internet
    │
    ├── Tailscale (tail903f71.ts.net)
    │   ├── raspberrypi (100.79.33.79) — Pi 3 server
    │   ├── beltzac-g15 (100.126.172.45) — Windows workstation
    │   └── moto-g54 (100.79.77.119) — Android phone
    │
    └── LAN 192.168.18.0/24 (LIGGA2811 WiFi)
        ├── 192.168.18.1 — Gateway
        ├── 192.168.18.7 — Google Nest Mini (Bedroom speaker)
        ├── 192.168.18.80 — Thataflix Theme (unknown)
        ├── 192.168.18.206 — Pi 3 (wlan0)
        ├── 192.168.18.243 — Pi 3 (eth0, cable)
        └── 192.168.18.248 — LG 55SM8600PSA TV
```

---

## 📺 LG TV 55SM8600PSA

### Specs & Discovery
| Property | Value |
|---|---|
| Model | LG 55SM8600PSA (2019, webOS 4.x) |
| IP | `192.168.18.248` |
| Ethernet MAC | `A8:23:FE:C0:55:E5` |
| WiFi MAC | `2C:2B:F9:46:40:DA` |
| DLNA Name | `TV` (LG WebOSTV DMRplus) |
| UDN (MediaRenderer) | `uuid:386e610b-6468-cfb0-0f2c-0903e8c8a0bd` |
| UDN (LG TV Service) | `uuid:5bcfc12a-e761-4ca2-800e-8c6456926c81` |

### Open Ports (when TV is ON)

| Port | Service | Protocol | Notes |
|---|---|---|---|
| 3000 | SSAP WebSocket | WebSocket | Device pairing + limited API |
| 3001 | (empty) | raw socket | Unknown purpose |
| 9080 | NRDP API | HTTP | Generic `status=ok` endpoint (seems dummy) |
| 9922 | Developer Mode Key Server | SSH (OpenSSH 6.1) | Key registration for full SSH access |

**Dynamic UPnP ports** (change on each TV boot):
- MediaRenderer: port varies (e.g., 1275, 1239)
- LG TV Service: port varies (e.g., 1863)
- UPnP Discovery: port varies (e.g., 1141, 1500, 1667)

### UPnP/DLNA Details

**MediaRenderer control URL** (AVTransport):
```
http://192.168.18.248:{PORT}/AVTransport/{UDN}/control.xml
```
Example (from a session): `http://192.168.18.248:1239/AVTransport/386e610b-6468-cfb0-0f2c-0903e8c8a0bd/control.xml`

**Supported media formats** (from GetProtocolInfo):
- `audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM`
- `audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM`
- `audio/mpeg:DLNA.ORG_PN=MP3`
- `image/jpeg:DLNA.ORG_PN=JPEG_SM`

**AVTransport actions**:
| Action | Status | Notes |
|---|---|---|
| `SetAVTransportURI` | ✅ Works | Sends URL to TV |
| `Play` | ❌ Error 501 | Blocked by LG DRM (Action Failed) |
| `Stop` | ✅ Works | |
| `Pause` | ✅ Works | |
| `GetTransportInfo` | ✅ Works | Returns state (STOPPED/PLAYING/PAUSED) |
| `GetMediaInfo` | ✅ Works | Returns current URI |
| `GetDeviceCapabilities` | ✅ Works | (Returns empty PlayMedia — locked) |

**RenderingControl actions** (Require pairing, error 606):
- `SetVolume` / `GetVolume` → Error 606 (Action not authorized)
- `SetMute` / `GetMute` → Error 606

**ConnectionManager actions**:
- `GetProtocolInfo` ✅
- `GetCurrentConnectionIDs` ✅
- `GetCurrentConnectionInfo` ✅

---

## 🔌 SSAP WebSocket API (Port 3000)

### Connection
```python
import socket, time, json, struct

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.18.248", 3000))
handshake = "GET / HTTP/1.1\r\nHost: 192.168.18.248\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: x3JJHMbLi1EzLkh9GBhXCw==\r\nSec-WebSocket-Version: 13\r\n\r\n"
s.send(handshake.encode())
s.recv(4096)
```

### Registration (Pairing)
```json
// Send:
{"type":"register","payload":{"client-key":"","name":"pi-agent","pairingType":"PROMPT"}}

// Response (prompt shown on TV):
{"type":"response","payload":{"pairingType":"PROMPT","returnValue":true}}

// Response (user accepted):
{"type":"registered","payload":{"client-key":"e800642aa4bed152fd270b6d77cfbcff"}}
```

**Saved client keys:**
| Key | Date | Notes |
|---|---|---|
| `89c53a2e2b1793fa786b557e33dfb50f` | 2026-07-11 | First pairing (basic) |
| `e800642aa4bed152fd270b6d77cfbcff` | 2026-07-11 | Re-pair (still basic) |

⚠️ Both keys only grant **Level 0** permissions (system info only). Media control returns 401.

### Available Services (via `ssap://api/getServiceList`)
```
api, audio, config, externalpq, media.controls, media.viewer, 
pairing, settings, system, system.launcher, system.notifications, 
timer, tv, user, webapp
```

### Command Reference
| URI | Permission | Status |
|---|---|---|
| `ssap://api/getServiceList` | ✅ Public | Returns service list |
| `ssap://system/getSystemInfo` | ✅ Public | Returns system info |
| `ssap://media.controls/play` | ❌ 401 | Needs LG Connect app |
| `ssap://media.controls/pause` | ❌ 401 | |
| `ssap://media.controls/stop` | ❌ 401 | |
| `ssap://media.viewer/open` | ❌ 401 | |
| `ssap://media.viewer/openPhotoVideo` | ❌ 404 | Method not found on this model |
| `ssap://audio/getVolume` | ❌ 401 | |
| `ssap://audio/setVolume` | ❌ 401 | |
| `ssap://system.launcher/launch` | ❌ 401 | |
| `ssap://system.launcher/open` | ❌ 401 | |
| `ssap://system.notifications/createToast` | ❌ 401 | |
| `ssap://tv/getCurrentChannel` | ❌ 401 | |
| `ssap://tv/getExternalInputList` | ❌ 401 | |
| `ssap://tv/switchInput` | ❌ 401 | |
| `ssap://settings/getSystemSettings` | ❌ 401 | |

---

## 🔐 webOS Developer Mode

### Correct SSH Connection Method

**Crítico**: O Key Server NÃO usa password auth. Usa chave RSA baixada via HTTP e descriptografada localmente.

```bash
# 1. Baixar chave privada criptografada (porta 9991)
curl -f http://192.168.18.248:9991/webos_rsa -o webos_rsa

# 2. Descriptografar (senha = passphrase do app Developer Mode)
ssh-keygen -p -P '86DEB3' -N '' -f webos_rsa    # Remove passphrase
# OU usar com passphrase (requer ssh-agent)

# 3. Conectar como prisoner (NÃO root!)
ssh -i webos_rsa \
  -o IdentitiesOnly=yes \
  -o HostKeyAlgorithms=+ssh-rsa \
  -o PubkeyAcceptedAlgorithms=+ssh-rsa \
  -p 9922 \
  prisoner@192.168.18.248
```

### Dados da Conexao
| Propriedade | Valor |
|---|---|
| Usuario | `prisoner` (nao `root`!) |
| Porta SSH | 9922 |
| Porta HTTP Key Server | 9991 |
| Tipo de auth | RSA public key |
| Passphrase | descriptografa chave localmente (86DEB3) |
| Chave local | `~/.ssh/lgtv/webos_rsa_nopass` (sem passphrase, pronta pra uso) |
| SO TV | Linux aarch64, kernel 4.4.84, webOS 4.x |

### Comando Rapido (Termux)
```bash
ssh -i ~/.ssh/lgtv/webos_rsa_nopass \
  -o IdentitiesOnly=yes \
  -o HostKeyAlgorithms=+ssh-rsa \
  -o PubkeyAcceptedAlgorithms=+ssh-rsa \
  -p 9922 prisoner@192.168.18.248
```

### Luna Bus (Public - acessivel sem root)

O usuario `prisoner` tem acesso ao **Luna public bus** via socket Unix `/var/run/luna-service2/com.palm.hub`. A ferramenta `luna-send-pub` envia comandos.

**Metodos disponiveis (public bus):**

| Servico | Metodo | Status | Descricao |
|---|---|---|---|
| `com.webos.applicationManager/launch` | `{"id":"APP_ID"}` | ✅ | Lancar qualquer app |
| `com.webos.applicationManager/close` | `{"id":"APP_ID"}` | ❌ | Denied method call |
| `com.webos.applicationManager/listApps` | `{}` | ❌ | Denied method call |
| `com.webos.applicationManager/getForegroundAppInfo` | `{}` | ❌ | Denied method call |
| Servicos de sistema (audio, tv, system) | varios | ❌ | Nao disponiveis no barramento publico |

**App IDs conhecidos:**
| App | ID |
|---|---|
| YouTube | `youtube.leanback.v4` |
| Netflix | `netflix` |
| Browser | `com.webos.app.browser` |
| Live TV | `com.webos.app.livetv` |
| HDMI 1 | `com.webos.app.hdmi1` |
| HDMI 2 | `com.webos.app.hdmi2` |
| Settings | `com.palm.app.settings` |
| App Store | `com.webos.app.discovery` |
| Photos & Video | `com.webos.app.photovideo` |
| Music | `com.webos.app.music` |
| SmartShare (DLNA) | `com.webos.app.smartshare` |
| Prime Video | `com.amazon.aiv` (provar) |
| Disney+ | `com.disney.disneyplus` (provar) |

**Exemplos:**
```bash
# Lancar YouTube
ssh -i ~/.ssh/lgtv/webos_rsa_nopass -o IdentitiesOnly=yes \
  -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa \
  -p 9922 prisoner@192.168.18.248 \
  "/usr/bin/luna-send-pub -i -n 1 luna://com.webos.applicationManager/launch '{\"id\":\"youtube.leanback.v4\"}'"

# Lancar Live TV (TV aberta)
ssh ... "/usr/bin/luna-send-pub -i -n 1 luna://com.webos.applicationManager/launch '{\"id\":\"com.webos.app.livetv\"}'"
```

### 🔓 Proximos Passos (Full Control)

Para obter controle total (volume, input switching, media playback), precisamos:
1. **Acessar o private Luna bus** — apps instalados tem grupo `luna` e acesso ao barramento privado
2. **Instalar um IPK customizado** — app empacotado com acesso ao barramento privado, expoe API HTTP
3. **Escalar privilegios** — explorar capabilities (CAP_SYS_PTRACE, CAP_DAC_OVERRIDE, CAP_SYS_ADMIN)

Ferramentas disponiveis no jail:
- `gdb`, `gdbserver` — debugging de processos
- `strace` — syscall tracing
- `ApplicationInstallerUtility` — instalacao de IPKs (a testar)
- Capabilities: `0x3fffffffff` (quase root — CAP_SYS_PTRACE, CAP_SYS_ADMIN, CAP_DAC_OVERRIDE, etc.)

---

## 🔊 Google Nest Mini (Bedroom Speaker)

### Discovery
| Property | Value |
|---|---|
| Friendly Name | `Bedroom speaker` |
| Model | Google Nest Mini |
| IP | `192.168.18.7` |
| Chromecast Port | 8009 |
| Cast Type | `audio` |
| UUID | `40a12539-461d-9f59-7f8c-1947b914049d` |

### MCP Tools (on Pi MCP server)
All tools via `http://localhost:8789` (MCP server)

| Tool | Description |
|---|---|
| `googlecast_discover` | 🔍 Scan for Google Cast devices on LAN |
| `googlecast_status` | 📊 Device status (volume, app, idle) |
| `googlecast_tts` | 🗣️ Text-to-speech (gTTS → MP3 → cast) |
| `googlecast_volume` | 🔊 Set volume (0.0 to 1.0) |
| `googlecast_play_url` | 🔗 Cast any audio URL |
| `googlecast_play_jellyfin` | 🎵 Search Jellyfin + cast music |
| `googlecast_play_shuffle` | 🎲 Random song from Jellyfin |
| `googlecast_stop` | ⏹️ Stop playback |
| `googlecast_cleanup` | 🧹 Clean tmp TTS files |

### TTS Audio Flow
```
User command → MCP Server → pi-tts.sh
  ├─► gTTS (Google TTS, needs internet) → MP3
  ├─► Save to /var/www/pi-server/tts/{uuid}.mp3
  └─► pychromecast → cast URL → Google Nest Mini
```
Auto-cleanup after 60s.

### Jellyfin Music Flow
```
googlecast_play_jellyfin("query")
  ├─► Jellyfin API: /Search/Hints?searchTerm=...&mediaTypes=Audio
  ├─► Build stream URL: http://192.168.18.243:8096/Audio/{id}/stream?audioCodec=aac
  └─► pychromecast → PLAYING on Bedroom speaker
```

### Jellyfin Streaming URL Format
```
Audio: http://192.168.18.243:8096/Audio/{itemId}/stream?api_key={key}&deviceId=pi-agent&audioCodec=aac
Video: http://192.168.18.243:8096/Video/{itemId}/stream?api_key={key}&deviceId=pi-agent&audioCodec=aac
Via Caddy: http://192.168.18.243/jf/Audio/{itemId}/stream?api_key={key}&deviceId=pi-agent&audioCodec=aac
```

---

## 🖥️ Pi Server (Raspberry Pi 3)

### Caddy Proxy (port 80)
```
/jf/* → localhost:8096 (Jellyfin)
/api/* → localhost:7777 (Dashboard)
/nova/* → localhost:8787 (Nova API)
/mcp/* → localhost:8789 (MCP Server)
/* → file_server /var/www/pi-server/
```

### MCP Server (port 8789)
```
50 tools total across:
- torrents (qBittorrent)
- media (Jellyfin)
- system (Pi stats)
- files (file management)
- subtitles
- googlecast (Google Home)
- lgtv (LG TV)
```

---

## 🎯 Current Integration Status

### Working ✅
| Integration | Method |
|---|---|
| **Google Nest Mini TTS** | `googlecast_tts` |
| **Google Nest Mini music** | `googlecast_play_jellyfin`, `googlecast_play_shuffle` |
| **LG TV SSH access (prisoner)** | RSA key via HTTP :9991, descriptografada com passphrase (2026-07-11) |
| **LG TV app launch** | `luna-send-pub` → `applicationManager/launch` (YouTube, Netflix, LiveTV, etc.) |
| **LG TV URL push** | `lgtv_send_url`, `lgtv_play` (user presses Play on remote) |
| **LG TV status** | `lgtv_status`, `lgtv_ping` |
| **LG TV WebSocket paired** | Client keys saved (SSAP Level 0) |
| **Jellyfin DLNA Server** | TV can browse/play via SmartShare app |
| **Caddy proxy** | `/jf/*` → Jellyfin |

### Partially Working 🟡
| Integration | Issue |
|---|---|
| **LG TV volume control** | Bloqueado por DRM/capabilities — precisa acesso ao private bus |
| **LG TV input switching** | So via app launch (hdmi1/hdmi2/livetv) |

### Not Working ❌
| Integration | Issue |
|---|---|
| **Full webOS API (input switch, app list, mute)** | Bloqueado no barramento PUBLICO — requer barramento privado |
| **Media launch via SSAP** | 401 insufficient permissions (Level 0 apenas) |
| **DLNA Play automatico** | Bloqueado por DRM (error 501) |
| **Root SSH** | Nao disponivel sem root na TV |

---

## 🤖 PiAgent — HTTP Bridge (instalado na TV)

Agente Node.js rodando na TV como `prisoner` que expoe API HTTP na porta **9999**. Usa `luna-send-pub` para acessar o barramento publico Luna.

### Deploy
```bash
# Copiar e iniciar o agente na TV
~/com.pi.agent/deploy_agent.sh start

# Verificar status
~/com.pi.agent/deploy_agent.sh status

# Parar
~/com.pi.agent/deploy_agent.sh stop
```

### Endpoints (TV interna: 127.0.0.1:9999)

| Metodo | Endpoint | Descricao | Status |
|---|---|---|---|
| GET | `/status` | Health check | ✅ |
| GET | `/audio/volume` | Volume atual (0-100) | ✅ |
| POST | `/audio/setVolume` | Definir volume `{"volume": N}` | ✅ |
| GET | `/volume/up` | Volume +5 | ✅ |
| GET | `/volume/down` | Volume -5 | ✅ |
| GET | `/apps` | Lista de apps conhecidos | ✅ |
| GET | `/launch/{app}` | Lancar app (youtube, netflix, hdmi1, livetv, etc.) | ✅ |
| POST | `/app/launch` | Lancar app `{"id": "APP_ID"}` | ✅ |
| POST | `/settings/get` | Config do sistema `{"keys":["localeInfo"]}` | ✅ |
| POST | `/config/get` | Configs `{"configNames":[...]}` | ✅ |
| POST | `/call/SERVICE/METHOD` | Chamada Luna generica | ✅ |

### Exemplos
```bash
# Volume
curl -s http://TV_IP:9999/audio/volume
curl -s -X POST http://TV_IP:9999/audio/setVolume -d '{"volume":30}'
curl -s http://TV_IP:9999/volume/up

# Apps
curl -s http://TV_IP:9999/launch/youtube
curl -s http://TV_IP:9999/launch/netflix
curl -s http://TV_IP:9999/launch/hdmi1
curl -s http://TV_IP:9999/launch/livetv

# Chamada direta Luna
curl -s -X POST http://TV_IP:9999/call/com.webos.audio/getVolume -d '{}'
```

### Acessando externamente (SSH tunnel do Pi/phone)
```bash
# Criar tunel SSH
ssh -i ~/.ssh/lgtv/webos_rsa_nopass \
  -o IdentitiesOnly=yes -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa \
  -N -L 19999:127.0.0.1:9999 -p 9922 prisoner@192.168.18.248

# Agora acessar via localhost:19999
curl http://127.0.0.1:19999/audio/volume
```

### Limitacoes (barramento publico)
- `app/list`, `app/close`, `app/foreground` — bloqueados
- `audio/setMute`, `audio/getStatus` — bloqueados
- `tv/switchInput`, `tv/getCurrentChannel` — bloqueados
- Para acesso total: instalar IPK com permissoes de barramento privado (requer assinatura ou root)

## 📦 Dependencies (on Pi)

### Python Packages
```
pychromecast>=14.0.0    # Google Cast control
gtts>=2.5.0             # Text-to-speech generation
pywebostv>=0.8.9        # LG webOS pairing (limited use)
upnpclient>=2.0.3       # UPnP/DLNA control (lxml issue on Pi)
websocket-client        # WebSocket (fallback)
paramiko>=2.12.0        # SSH client (for Key Server)
pexpect>=4.9.0          # Interactive SSH (limited use)
sshpass (apt)           # SSH password auth (limited use)
```

### Node.js Packages
```
@webosose/ares-cli (npm)  # LG webOS CLI tools
```

### System Packages
```
caddy                    # Reverse proxy (port 80)
jellyfin                 # Media server (port 8096)
qbittorrent-nox          # Torrent client (port 9090)
```

### Scripts
```
/home/pi/pi-tts.sh       # TTS generation + Google Cast
/home/pi/pi-mcp-server/  # MCP server (Python + 50 tools)
```

---

## 🔧 Quick Commands Reference

### Google Nest Mini
```bash
# Discover
curl -s -X POST http://localhost:8789 -H "Content-Type: application/json" \
  -d '{"type":"call_tool","name":"googlecast_discover","arguments":{}}'

# Play music
curl -s -X POST http://localhost:8789 -H "Content-Type: application/json" \
  -d '{"type":"call_tool","name":"googlecast_play_jellyfin","arguments":{"query":"enter sandman"}}'

# TTS
curl -s -X POST http://localhost:8789 -H "Content-Type: application/json" \
  -d '{"type":"call_tool","name":"googlecast_tts","arguments":{"message":"cafe pronto"}}'

# Shuffle
curl -s -X POST http://localhost:8789 -H "Content-Type: application/json" \
  -d '{"type":"call_tool","name":"googlecast_play_shuffle","arguments":{"artist":"metallica"}}'
```

### LG TV
```bash
# Check if TV is on
curl -s -X POST http://localhost:8789 -H "Content-Type: application/json" \
  -d '{"type":"call_tool","name":"lgtv_ping","arguments":{}}'

# Send media URL (press Play on remote)
curl -s -X POST http://localhost:8789 -H "Content-Type: application/json" \
  -d '{"type":"call_tool","name":"lgtv_play","arguments":{"query":"cowboys from hell"}}'

# Check status
curl -s -X POST http://localhost:8789 -H "Content-Type: application/json" \
  -d '{"type":"call_tool","name":"lgtv_status","arguments":{}}'
```

### Raw UPnP (via Python)
```python
AV_CTRL = "http://192.168.18.248:{PORT}/AVTransport/{UDN}/control.xml"

# Set URI
soap(AV_CTRL, "AVTransport", "SetAVTransportURI",
    "<InstanceID>0</InstanceID><CurrentURI>{URL}</CurrentURI><CurrentURIMetaData></CurrentURIMetaData>")

# Get state
soap(AV_CTRL, "AVTransport", "GetTransportInfo", "<InstanceID>0</InstanceID>")
```

---

## 📝 Notes & Gotchas

1. **LG TV port 1275 vs dynamic ports**: The UPnP MediaRenderer port changes on each TV boot. Find it via SSDP discovery.
2. **SSAP WebSocket limitations**: This TV model only grants Level 0 API access. Full control requires the official LG Connect app or Developer Mode + ares-cli.
3. **Key Server SSH**: Usar `prisoner` (nao `root`) com chave RSA baixada de `http://IP:9991/webos_rsa`. A passphrase (`86DEB3`) descriptografa a chave localmente — nunca enviada ao TV. Ver secao "webOS Developer Mode" acima.
4. **Google Cast content type**: Use `audio/aac` with Jellyfin's `&audioCodec=aac` transcoding parameter for best compatibility.
5. **Caddy proxy**: Required `/jf/*` rule for the LG TV to access Jellyfin via port 80 (the TV has issues with non-standard ports).
6. **SSD space**: 100% full (237GB). TTS and media operations that need temp space use `/var/www/pi-server/tts/` (on SD card, ~6GB free).

---

## 🔐 Credentials & Keys

### WiFi
```
SSID: LIGGA2811
PSK: 32551632
Gateway: 192.168.18.1
Pi (cable): 192.168.18.243
Pi (WiFi): 192.168.18.206
```

### Tailscale
```
Tailnet: tail903f71.ts.net
Pi: 100.79.33.79 / raspberrypi.tail903f71.ts.net
Phone: 100.79.77.119 / moto-g54-5g.tail903f71.ts.net
Workstation: 100.126.172.45 / beltzac-g15.tail903f71.ts.net
```

### Google Nest Mini
```
Nome: Bedroom speaker
IP: 192.168.18.7
Chromecast Port: 8009
Model: Google Nest Mini
UUID: 40a12539-461d-9f59-7f8c-1947b914049d
TTS funciona: ✅ (gTTS + pychromecast)
Música do Jellyfin: ✅
```

### LG TV 55SM8600PSA
```
IP: 192.168.18.248
Developer Mode: ✅ Instalado e logado (LG Account)
Key Server: ✅ Ativado (porta 9991 HTTP + 9922 SSH)
Pass phrase atual: 86DEB3 (descriptografa chave RSA, NAO enviar ao TV!)
SSH: ✅ FUNCIONANDO (2026-07-11)
  Usuario: prisoner (NAO root)
  Auth: chave RSA baixada via HTTP :9991
  Comando: ssh -i ~/.ssh/lgtv/webos_rsa_nopass -o IdentitiesOnly=yes -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa -p 9922 prisoner@192.168.18.248
Luna Bus: ✅ Barramento PUBLICO (lanca apps, nao controla volume/input)
  App launch: luna-send-pub luna://com.webos.applicationManager/launch '{"id":"APP_ID"}'
  Apps: youtube.leanback.v4, netflix, com.webos.app.livetv, com.webos.app.hdmi1/hdmi2
Chave local (sem passphrase): ~/.ssh/lgtv/webos_rsa_nopass
Chave local (com passphrase): ~/.ssh/lgtv/webos_rsa
```

### SSAP WebSocket Client Keys (porta 3000)
```
Chave 1: 89c53a2e2b1793fa786b557e33dfb50f (basica)
Chave 2: e800642aa4bed152fd270b6d77cfbcff (basica)
Nivel: 0 - apenas system.getSystemInfo e api.getServiceList
```

### MCP Server (Pi, porta 8789)
```
URL: http://localhost:8789 (localhost no Pi)
    http://192.168.18.243:8789 (LAN)
    http://raspberrypi.tail903f71.ts.net:8789 (Tailscale)
Tools: 50 total (Google Cast + LG TV + torrents + media + system + files)
```

### Jellyfin (porta 8096)
```
API Key: 74b84707d61440548eda974d7da4c60c
URL LAN: http://192.168.18.243:8096
URL Caddy: http://192.168.18.243/jf/
Usuario: beltzac / 1234
```

### qBittorrent (porta 9090)
```
API Key: qbt_COgHDWVXVh1bxrixGqaf7tUCewgH
URL: http://192.168.18.243:9090
WebUI Login: admin / beltzac
```

### FileBrowser (porta 8080)
```
URL: http://192.168.18.243:8080
Login: beltzac / beltzaC12345
Root: /mnt
```

### Caddy (porta 80)
```
Dashboard: http://raspberrypi.tail903f71.ts.net:80
```
