> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pictor.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Saber por que a câmera não abre

> O veredito de uma câmera, elo por elo — e o que o operador faz com cada resposta.

Todo VMS diz **se** a câmera está online. Esta rota diz **por que** ela não está,
e qual é a próxima ação — sem abrir chamado e sem esperar alguém olhar.

```bash theme={null}
curl -s -H "Authorization: Bearer $PICTOR_KEY" \
  "$PICTOR_API/api/devices/CAM-001/diagnostics"
```

## A resposta, em três partes

```json theme={null}
{
  "device_id": "CAM-001",
  "checked_at": "2026-09-03T16:40:12Z",
  "summary": {
    "verdict": "fail",
    "headline": "Falta o usuário e a senha desta câmera.",
    "next_action": "Informe usuário e senha em Configuração › Conexão."
  },
  "media_route": {
    "kind": "edge_mesh_pull",
    "label": "Pelo servidor de borda instalado no local",
    "transcode": false
  },
  "transports": { "webrtc": "available", "hls": "available" },
  "checks": [ /* … a cadeia inteira … */ ]
}
```

`summary` é o que você mostra na tela. `headline` e `next_action` são escritos
para o operador ler — **mostre-os como vieram, não traduza**. `media_route.label`
diz por onde o vídeo chega, em termos do mundo dele.

## A regra que faz isso valer: a primeira falha é a causa

`checks` vem na ordem da cadeia real de falha. Então:

<Warning>
  **A primeira entrada com `status: "fail"` é a causa raiz. Todas as seguintes são
  sintoma.** Mostrar as sete linhas vermelhas de uma vez faz o operador perseguir o
  sintoma mais visível em vez da causa.
</Warning>

```js theme={null}
function causaRaiz(diagnostico) {
  // Exatamente o que o `summary` já fez — reproduzido aqui para quem precisa
  // do objeto do check (evidência, remediação) e não só do texto.
  return diagnostico.checks.find((c) => c.status === 'fail')
      ?? diagnostico.checks.find((c) => c.status === 'warn')
      ?? null;
}
```

## Os elos, na ordem em que falham

| # | `id`                     | Rótulo                            | O que ele prova                                              |
| - | ------------------------ | --------------------------------- | ------------------------------------------------------------ |
| 1 | `cadastro`               | Dados da câmera                   | os campos que aquele tipo de conexão exige estão preenchidos |
| 2 | `credencial_plataforma`  | Licença do fabricante             | a conta do fabricante, quando a câmera depende de uma        |
| 3 | `conexao_fabricante`     | Conexão com o fabricante          | o serviço do fabricante está respondendo                     |
| 4 | `credencial_dispositivo` | Usuário e senha do equipamento    | o aparelho aceitou a conta que temos                         |
| 5 | `codigo_verificacao`     | Código de verificação do aparelho | o código que libera a imagem naquele modelo                  |
| 6 | `rota_de_midia`          | Caminho do vídeo                  | há um caminho resolvido até a imagem                         |
| 7 | `alcance`                | Contato com a câmera              | a câmera respondeu de verdade                                |
| 8 | `recepcao_de_video`      | Recepção de vídeo                 | está chegando imagem agora                                   |
| 9 | `ultimo_frame`           | Última imagem                     | há quanto tempo chegou o último quadro                       |

Cada entrada traz `id`, `label`, `status`, `detail`, `remediation` e, quando
existe, `evidence`.

`status` é um de: **`ok`** · **`warn`** (funciona, com ressalva) · **`fail`**
(está quebrado aqui) · **`skip`** (não se aplica a esta câmera — uma câmera que
envia o vídeo por conta própria não tem usuário e senha para conferir).

<Tip>
  `evidence` é material de suporte, não texto de tela. Guarde no seu log, mande
  junto quando abrir chamado, e **não** desenhe para o operador.
</Tip>

## Os vereditos, e o que fazer com cada um

| `verdict`  | O que significa                  | O que o seu sistema faz                               |
| ---------- | -------------------------------- | ----------------------------------------------------- |
| `ok`       | há imagem                        | nada                                                  |
| `degraded` | funciona, com um ajuste pendente | registra; não acorda ninguém de madrugada             |
| `fail`     | não abre                         | abre a ocorrência com `headline` e `next_action`      |
| `unknown`  | não deu para determinar          | tenta de novo mais tarde — **não** reporte como queda |

<Warning>
  `unknown` **não é** "câmera caída". Ele quer dizer que nenhum elo conseguiu
  concluir nada — tratar isso como falha gera alarme falso, e alarme falso é o que
  faz o operador parar de olhar para os alarmes.
</Warning>

### Imagem chegando ganha do resto

Se o elo `recepcao_de_video` está `ok`, o pior veredito possível é `degraded` —
nunca `fail`. Há o que ajustar, não há o que socorrer.

É uma regra que existe por experiência: a mesma tela já mostrou o texto "Falha"
por cima de um vídeo que estava rodando, e quem lê acredita no texto vermelho,
não no vídeo que está vendo.

## O custo, e como não estourar o teto

<Warning>
  `probe=true` (o padrão) **disca na câmera de verdade**. É diagnóstico sob
  demanda — para quando alguém vai olhar —, não vigília.
</Warning>

Não construa a sua vigilância varrendo esta rota. A conta não fecha: 40
condomínios × 25 câmeras = 1.000 chamadas por volta, cada uma abrindo uma sessão
no equipamento.

O desenho certo tem dois níveis:

<Steps>
  <Step title="Varra a frota com UMA chamada por conta">
    ```bash theme={null}
    curl -s -H "Authorization: Bearer $PICTOR_KEY" \
      "$PICTOR_API/api/devices?transmitting=no&limit=500"
    ```

    `transmitting` é o eixo de IMAGEM: `no` = já chegou quadro desta câmera
    alguma vez, mas não dentro da janela. É a lista de "quem está sem imagem
    agora", pela conta inteira, numa requisição.

    Confira `transmitting_witness_fresh` no envelope: em `false`, a medição está
    parada e "0 sem imagem" significa **não deu para medir**.
  </Step>

  <Step title="Diagnostique só as que apareceram">
    ```bash theme={null}
    curl -s -H "Authorization: Bearer $PICTOR_KEY" \
      "$PICTOR_API/api/devices/CAM-001/diagnostics"
    ```

    Agora sim com `probe=true`: são poucas, e alguém vai agir sobre o resultado.
  </Step>
</Steps>

Precisa do estado de muitas câmeras sem discar em nenhuma? `probe=false`
devolve só o que já se sabe, sem tocar a rede:

```bash theme={null}
curl -s -H "Authorization: Bearer $PICTOR_KEY" \
  "$PICTOR_API/api/devices/CAM-001/diagnostics?probe=false"
```

## Da falha à ocorrência, inteiro

```python theme={null}
import httpx

def verificar_condominio(chave: str, base: str) -> list[dict]:
    """Uma chamada para achar quem está sem imagem; diagnóstico só nessas."""
    cabecalho = {"Authorization": f"Bearer {chave}"}
    with httpx.Client(base_url=base, headers=cabecalho, timeout=30) as http:
        frota = http.get("/api/devices", params={"transmitting": "no", "limit": 500})
        frota.raise_for_status()
        corpo = frota.json()

        # Sem a testemunha viva, "nenhuma sem imagem" não é uma afirmação.
        if not corpo.get("transmitting_witness_fresh", True):
            return []

        ocorrencias = []
        for camera in corpo["items"]:
            d = http.get(f"/api/devices/{camera['device_id']}/diagnostics")
            if d.status_code != 200:
                continue
            diag = d.json()
            if diag["summary"]["verdict"] not in ("fail", "degraded"):
                continue
            causa = next((c for c in diag["checks"] if c["status"] == "fail"), None)
            ocorrencias.append({
                "camera": camera["device_id"],
                "gravidade": diag["summary"]["verdict"],
                "titulo": diag["summary"]["headline"],
                "acao": diag["summary"]["next_action"],
                "elo": causa["id"] if causa else None,
                # Material de suporte — vai para o log, não para a tela.
                "evidencia": causa.get("evidence") if causa else None,
            })
        return ocorrencias
```

## O que ele não faz

Diagnóstico é leitura: ele não conserta, não reinicia e não muda cadastro. A
`next_action` aponta para onde a correção acontece — e é uma pessoa que a faz.
