# Monitoreo v1

Público: `wss://transbook.mx/homecore/ws`. Local: `ws://127.0.0.1:8080/ws`.
JSON texto, máximo 4096 bytes y `protocol: 1` en todos los mensajes.
No utiliza cookies ni sesiones del navegador; conexiones con Origin de
navegador se rechazan, salvo el valor exacto `file://` que envía por defecto
arduinoWebSockets. Este encabezado nunca concede acceso: UID/token siguen
siendo obligatorios. No hay comandos físicos implementados.

## Autenticación (primer mensaje dentro de 5 segundos)

```json
{"protocol":1,"type":"device.auth","device_uid":"HC_ISSUED_BY_APP","token":"ISSUED_BY_APP","firmware":"esp32-monitor-1"}
```

El UID real tiene prefijo `HC-` más 32 caracteres hexadecimales. El token
contiene 64 caracteres hexadecimales (32 bytes aleatorios). Respuesta:

```json
{"protocol":1,"type":"device.auth.result","success":true,"heartbeat_seconds":20,"server_time":"2026-09-11T19:00:00.000Z"}
```

Un UID solo puede tener una conexión. Se rechazan duplicados (4409).
Identidad y hogar derivan de la autenticación, nunca de la telemetría.
Una clave rota o un dispositivo pausado invalidan escrituras de inmediato
por comprobación transaccional; el socket se cierra en el próximo barrido,
normalmente dentro de 5 segundos.

## Heartbeat y lectura

```json
{"protocol":1,"type":"device.heartbeat"}
```

```json
{"protocol":1,"type":"telemetry.publish","message_id":"boot-random-1","source":"simulated","readings":{"temperature":24.6,"humidity":55}}
```

`source`: `sensor` o `simulated`. Solo se aceptan ambos valores numéricos:
temperatura de -40 a 85 °C y humedad de 0 a 100 %. No se admiten canales
adicionales ni datos no finitos. `message_id`: 1–64 caracteres alfanuméricos,
guion o guion bajo. La hora de recepción del servidor es autoritativa;
no hay cola de muestras offline. El firmware descarta el pendiente al perder
conexión para no presentar una muestra antigua como recién medida.

Después de persistir:

```json
{"protocol":1,"type":"telemetry.ack","message_id":"boot-random-1","stored":true,"duplicate":false}
```

Repetir `message_id` del mismo dispositivo no duplica ni modifica la primera
muestra. El firmware reintenta dentro de la misma conexión al faltar ACK.
La deduplicación dura mientras se conserve esa muestra (retención 30 días).

## Presencia y fallos

Heartbeat/telemetría cada 20 segundos. Tras 60 segundos sin mensajes válidos,
se cierra el socket y la app muestra desconectado. Se envían pings de
transporte, pero un pong no sustituye el heartbeat de aplicación.
Lecturas mayores a 90 segundos se marcan antiguas aunque exista conexión.
Cerrar normalmente limpia la conexión; reiniciar el servicio limpia las
sesiones de transporte anteriores. Un bloqueo MariaDB evita dos procesos
simultáneos para la misma base.

Límites: 100 sockets, 10 mensajes/segundo/socket y cola máxima de 8. JSON
malformado, binarios, identidad ajena o tipos no soportados cierran la
conexión. Los errores no contienen claves ni detalles internos de SQL.
La ESP32 reconecta con espera incremental y valida certificado y reloj.
