Skip to main content
Glama
README.md
# tv-quarto

Transforma uma TV em **terceira tela do PC** (tela estendida, não espelhamento) pela rede, com um botão na área de trabalho e um **servidor MCP** para o Claude controlar tudo por linguagem natural.

> *Turn a TV into an extended (not mirrored) 3rd monitor of a Windows PC over the LAN, with a one-click desktop app and an MCP server so Claude can drive it.*

```
 PC Windows                                   Box Android (UniTV)          TV (Hisense VIDAA)
┌──────────────────────────────┐   rede     ┌────────────────────┐ HDMI ┌──────────────────┐
│ Virtual Display Driver       │  (HEVC)    │ Moonlight          │─────▶│ entrada HDMI     │
│  └ monitor virtual (3ª tela) │──────────▶ │  decodifica vídeo  │      │                  │
│ Sunshine (encoder da GPU)    │            └────────▲───────────┘      └────────▲─────────┘
│ app.py / server.py (MCP) ────┼──── ADB (rede) ─────┘                           │
│                         ─────┼──── MQTT (vidaa-control): liga, entrada, volume ┘
└──────────────────────────────┘
```

- O monitor virtual **só existe durante a transmissão**: o Sunshine o liga quando a sessão começa e o desliga quando ela termina. Fora disso o Windows continua com as telas normais.
- O **áudio fica no PC** (fone/caixas). Ele não é enviado para a TV.
- Resoluções 1080p, 1440p e 4K, que se trocam pelo app.

## Hardware testado

| Peça | Modelo usado | Observação |
|---|---|---|
| PC | Windows 11, GPU AMD RX 6750 XT | Qualquer GPU com encoder suportado pelo Sunshine (NVENC, AMF, QuickSync) |
| Box | UniTV V10 (Allwinner H616, Android 10, 1 GB) | Qualquer Android com Moonlight. As partes de saída HDMI são específicas da Allwinner |
| TV | Hisense com VIDAA | Liga, desliga e troca de entrada via MQTT. Sem Hisense, o resto funciona e você troca a entrada no controle |

## Pré-requisitos

- Windows 10/11 com `winget` e Python 3.10+.
- O PC e o box na mesma rede local. **Cabo no PC é recomendado**; no box, cabo ou Wi-Fi de 5 GHz.
- No box: **Opções do desenvolvedor** ativadas, com **Depuração USB** e **Depuração ADB pela rede**. Em geral: Configurações → Sobre → clique 7× em "Versão".

## Instalação

### 1. Programas no PC

```powershell
winget install LizardByte.Sunshine
winget install VirtualDrivers.Virtual-Display-Driver
winget install Google.PlatformTools
```

Abra `https://localhost:47990` e crie o usuário do Sunshine.

### 2. Projeto

```powershell
git clone https://github.com/<seu-usuario>/tv-quarto.git
cd tv-quarto
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
copy config.example.json config.json
```

Edite o `config.json`:

| Campo | O que é |
|---|---|
| `unitv_addr` | IP do box + `:5555` (porta do ADB) |
| `hisense_host` | IP da TV |
| `hisense_unitv_source` | Entrada da TV onde o box está ligado (`hdmi1`…`hdmi4`) |
| `hisense_mac` | MAC da TV. Pode deixar `null`: ele é lido da tabela ARP |
| `sunshine_pc_uuid` | ID do PC no Sunshine. Pode deixar `null`: ele é lido de `sunshine_state.json` |
| `adb_path` | Caminho do `adb.exe`. Pode deixar `null`: ele é procurado no PATH e na pasta do winget |
| `stream_profile` | `1080p`, `1440p` ou `4k`. O app atualiza este campo |

### 3. Monitor virtual + Sunshine

Num **PowerShell como administrador**:

```powershell
powershell -ExecutionPolicy Bypass -File scripts\setup-pc.ps1
```

O script instala o driver do monitor virtual e configura o `sunshine.conf`. Ele mantém suas outras configurações e faz backup em `sunshine.conf.bak`. Também desliga o envio de áudio, aponta o Sunshine para o monitor virtual e o liga e desliga junto com a sessão. Pode rodar de novo sem problema.

### 4. Moonlight no box

```powershell
adb connect <ip-do-box>:5555        # aceite "Sempre permitir deste computador" na TV
```

Baixe o APK do [Moonlight Android](https://github.com/moonlight-stream/moonlight-android/releases) e instale com `adb install`. Depois:

1. Abra o Moonlight, clique no PC e digite o PIN mostrado na aba **PIN** do painel do Sunshine.
2. Em **Configurações → Mudar configurações do HEVC**, escolha **Prefira HEVC**.
   O Moonlight não confia no decodificador HEVC da Allwinner e cai para H.264, que nesse chip dá tela verde em 4K e borrões em 1080p.

### 5. Parear a TV Hisense

```powershell
.venv\Scripts\python pair_hisense.py
```

A TV mostra um PIN de 4 dígitos, válido por cerca de 1 minuto. Digite-o no terminal. Se o PIN não aparecer, rode de novo: às vezes ele só aparece na segunda tentativa.

### 6. Atalho na área de trabalho

```powershell
powershell -ExecutionPolicy Bypass -File scripts\create-shortcut.ps1
```

## Uso

### App da área de trabalho

O atalho **TV do quarto** abre uma janela com:
- **Ligar na TV / Desligar terceira tela**: liga a TV, troca a entrada, acorda o box, abre a sessão no Moonlight e ajusta a resolução do monitor virtual. Desligar encerra a sessão, e o monitor virtual some.
- **Resolução**: 1080p, 1440p ou 4K. Se estiver transmitindo, ele reinicia a transmissão com a nova resolução (cerca de 30 a 40 s).

A tela da TV fica à direita da principal. Arraste janelas para lá.

### MCP (Claude Code)

Copie `.mcp.example.json` para `.mcp.json`, ajuste os caminhos e abra o Claude Code nesta pasta. Exemplos: *"liga a TV como terceira tela"*, *"coloca em 1440p"*, *"abaixa o volume da TV para 10"*, *"tira um print da TV"*.

| Ferramenta | Função |
|---|---|
| `start_stream` / `stop_stream` | Liga e desliga a terceira tela |
| `set_resolution` | `1080p`, `1440p` ou `4k` |
| `status` | Estado da TV, do box, da transmissão e do monitor virtual |
| `unitv_screenshot` | Print do que o box está mostrando |
| `unitv_keys`, `unitv_type_text`, `unitv_list_apps`, `unitv_launch_app` | Controle do box |
| `hisense_power`, `hisense_keys`, `hisense_volume`, `hisense_sources`, `hisense_set_source`, `hisense_launch_app` | Controle da TV |
| `hisense_pair_start` / `hisense_pair_finish` | Pareamento da TV |

## Resoluções e desempenho

Medido com uma animação de 64 fps no monitor virtual, contando os quadros exibidos pelo box (`dumpsys SurfaceFlinger --latency`):

| Perfil | Monitor virtual | Saída HDMI do box | Quadros na TV |
|---|---|---|---|
| 1080p | 1920×1080 @ 60 | 1080p60 | **60 fps** |
| 1440p | 2560×1440 @ 60 | 2160p60 (o box amplia) | ~50 fps |
| 4K | 3840×2160 @ 60 | 2160p60 | ~43 fps |

Nos perfis acima de 1080p, o limite é o decodificador do H616. Um box mais forte (Chromecast 4K, Fire TV 4K Max, Shield) deve manter 60 fps.

## Detalhes técnicos

- **A resolução do monitor virtual não segue o cliente.** As opções `dd_*` do Sunshine rodam *antes* do `global_prep_cmd` ligar o monitor e falham com `DevicePrepFailed`. Por isso o `server.py` define o modo com `ChangeDisplaySettingsExW` logo depois que a sessão começa.
- **4K60 escondido no box Allwinner.** O menu do UniTV só lista 4K até 30 Hz, mas o modo 34 (2160p60) é suportado. O `server.py` grava o modo direto no serviço `display_output` (`com.softwinner.IDisplayOutputManager`), com os códigos de binder tirados do `DisplayDaemon.jar`:

  | Código | Operação |
  |---|---|
  | 4 | ler o modo atual |
  | 5 | gravar um modo |
  | 11 | verificar se um modo é suportado |

  ```
  adb shell service call display_output 5 i32 0 i32 34
  ```

  O AUTO do menu e o `setprop persist.disp.*` não servem: o driver volta ao modo gravado no boot.
- **Fechar a sessão é diferente de desconectar.** Sair do stream com "Voltar" deixa a sessão aberta no PC, e o monitor virtual continua lá. O `stop_stream` usa "Sair da Sessão" no Moonlight.
- **Automação do Moonlight.** É feita pela interface, com `uiautomator`. Os textos procurados estão em português (pt-BR) com alternativa em inglês. Em outro idioma, ajuste `server.py`.
- **`vidaa-control` escreve no stdout**, o que corromperia o canal stdio do MCP. Por isso todas as chamadas passam por `quiet()`.

## Segurança e avisos

- **ADB pela rede** dá controle total do box a qualquer um na sua rede local. Use apenas em rede de confiança.
- **`vidaa-control`** é um projeto não oficial: usa o certificado do app VIDAA e um protocolo obtido por engenharia reversa. Uma atualização de firmware da TV pode quebrar o controle.
- Os comandos de binder e os modos HDMI foram testados em um UniTV V10 (Allwinner H616). Em outro aparelho, confirme antes com o código 11 (verificar suporte).
- Use por sua conta e risco.

## Créditos

[Sunshine](https://github.com/LizardByte/Sunshine) · [Moonlight](https://github.com/moonlight-stream/moonlight-android) · [Virtual Display Driver](https://github.com/VirtualDrivers/Virtual-Display-Driver) · [vidaa-control](https://github.com/tombabolewski/vidaa-control) · [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk)

## Licença

[MIT](LICENSE)