Entrixy v2 — arquitectura final

Acordado el 13 de abril de 2026. Documento de trabajo para la implementación.

Contenido
  1. Privacidad: el servidor no sabe nada
  2. Cuatro tipos de objeto: llamada, URL, dispositivo (WS), controlador BLE
  3. El webhook (URL) en detalle
  4. El dispositivo (ESP32 / ESP8266, WebSocket) en detalle
  5. El controlador BLE (ESP32) en detalle
  6. Disparadores: GPS y Wi-Fi
  7. Tres niveles de confirmación
  8. Propietario frente a invitado: el reparto de los ajustes
  9. La interfaz de la aplicación
  10. Límites del plan
  11. Iconos de objeto para invitados
  12. Previsión de carga
  13. Estructura de la base de datos
  14. Plan de implementación

1. Privacidad: el servidor no sabe nada (por defecto)

La promesa que hacemos: No conocemos ni los teléfonos de nuestros usuarios ni sus coordenadas

Todos los datos sensibles se guardan solo en el teléfono. Parte de ellos el propietario puede pasarlos por el servidor si quiere, con un aviso explícito.

DatosPor defectoOpcionalmente por el servidor
Coordenadas (lat, lon)Solo en el teléfonoSe puede compartir con invitados (con aviso)
Huella Wi-Fi (BSSID[])Solo en el teléfonoNo se transmite nunca
URL y secreto del webhookSolo en el teléfonoSe puede guardar en el servidor (con aviso)
Radio, tiempo, nivel de confirmaciónPor el servidor
Una huella Wi-Fi equivale a unas coordenadas. A partir de un conjunto de BSSID, los servicios te localizan con 20–50 m de precisión. La imagen Wi-Fi no pasa por el servidor bajo ninguna circunstancia.

2. Cuatro tipos de objeto

Al añadir un objeto, el propietario elige el tipo de conexión. Después se abre la ventana de ajustes de ese tipo.

IconNombreCómo funcionaFeedbackExample
📞CallUna llamada desde el teléfono del propietarioNinguno (la llamada salió o no)Una barrera, un portero automático
🌐URL (webhook)Una petición HTTP a la dirección del dispositivo, desde el teléfono o desde el servidor de EntrixySí: una respuesta JSON con estadoUna cerradura inteligente con API, Tasmota, Shelly Cloud
📟Controlador de internet (WS)Una orden por WebSocket a un controlador conectadoSí: una respuesta por el WebSocketESP32 / ESP8266 caseros, Shelly Plus 1, NodeMCU
📶Controlador BLEUn canal Bluetooth directo entre teléfono y controlador, sin internetSí: una respuesta por BLE (notify)Un controlador a pilas en un portón sin Wi-Fi

El configurador de firmware para los dos últimos tipos: /controller/ (BLE e Internet, en el navegador).

Para el usuario todos los tipos se ven igual: una tarjeta, un botón de Llamar, un estado. Solo cambia el mecanismo de entrega.

3. El webhook (URL) en detalle

Dos modos de envío

La ventana de ajustes del webhook ofrece un conmutador, con los pros y los contras escritos en pantalla:

◉ Desde el teléfono del propietario (por defecto)
La petición sale de tu teléfono. La dirección y el secreto del dispositivo se quedan contigo: el servidor de Entrixy nunca los conoce.
A favor: máxima privacidad
En contra: tu teléfono tiene que estar conectado
○ Desde el servidor de Entrixy
La petición la envía nuestro servidor. Funciona incluso con tu teléfono desconectado.
A favor: funciona sin el teléfono del propietario
En contra: la dirección del dispositivo se guarda en el servidor

Dónde viven los datos

ModeDónde están la URL y el secreto¿Conoce el servidor la URL?
Desde el teléfonoPrefs en el teléfono del propietarioNo
Desde el servidorLa tabla numbers en la base de datosYes

Protocolo de la petición

Seguridad: una firma HMAC. El dispositivo y Entrixy comparten un webhook_secret.

POST https://device-url.com/open
Content-Type: application/json

{
  "action": "open",
  "object_id": 42,
  "timestamp": 1713020000,
  "nonce": "a1b2c3",
  "signature": "hmac-sha256(secret, timestamp + nonce + action)"
}

El dispositivo comprueba que la firma es válida, que la marca de tiempo tiene menos de 30 segundos y que el nonce no se repite. Si todo va bien, actúa y responde:

{"status": "ok", "message": "Door opened"}
or
{"status": "error", "message": "Lock jammed"}

El estado se muestra al usuario en la aplicación.

Flujo de datos (modo teléfono)

The guest presses Call
  → the server receives the request
  → sends it to the owner over the WebSocket: {type: "do_webhook", ...}
  → the owner's phone makes the HTTP call to the device
  → receives the JSON response
  → sends it to the server: {type: "webhook_result", status, message}
  → the server passes it to the guest
  → both see the status in the log

Logging

En ambos modos el registro en el teléfono del propietario y del invitado anota quién, cuándo, qué objeto y con qué estado.

4. El dispositivo (ESP32 / ESP8266, WebSocket) en detalle

Cómo funciona

El controlador —se admiten ESP32, ESP32-S3, ESP32-C3 y ESP8266— no tiene IP pública. Mantiene una conexión WebSocket saliente al servidor de Entrixy, igual que el teléfono del propietario.

La firmware y el configurador viven en /esp/socket/. Fuentes: ESP32, ESP8266.

El protocolo WebSocket del dispositivo

// Device → server: connect
{"type": "device_hello", "device_key": "xyz789", "device_secret": "..."}

// Server → device: OK
{"type": "device_ok"}

// Server → device: command
{"type": "device_command", "action": "open", "command_id": "abc123"}

// Device → server: result
{"type": "device_response", "command_id": "abc123", "status": "ok", "message": "Opened"}

El servidor transmite el estado a los clientes —propietario e invitado— por sus conexiones WebSocket.

Registrar un dispositivo

  1. En la aplicación el propietario elige Añadir objeto → tipo Dispositivo
  2. Se generan device_key + device_secret
  3. Se muestra un código QR con los datos de la firmware
  4. El usuario graba el ESP32 o el ESP8266 (véase el configurator) y el dispositivo se conecta a wss://entrixy.com/ws

Indication

En la tarjeta del objeto se ve el estado de conexión del dispositivo, en línea o desconectado, igual que los invitados ven el estado del propietario.

4a. El controlador BLE (ESP32) en detalle

Cómo funciona

Un controlador sobre ESP32, S3, C3, C6 o H2 no necesita ni Wi-Fi ni servidor para dispararse. Duerme profundamente, despierta una vez por segundo unos 200 ms y emite un anuncio BLE con un contador firmado. Cuando se acerca un teléfono con una clave válida, Android —o la aplicación, si está en primer plano— capta el anuncio, se conecta por GATT, envía una orden fire con un nonce de un solo uso y el controlador cierra el relé con un pulso breve. Para motores biestables y cerraduras hay un modo con abrir y cerrar por separado y seguimiento de la posición, donde abierto/cerrado viaja como un byte cifrado dentro del anuncio.

El servidor de Entrixy no participa en este canal. Solo hace falta al crear una clave de invitado: el propietario firma un GuestToken y se lo pasa al invitado por el canal cifrado de extremo a extremo. Después el invitado trabaja sin conexión.

El protocolo BLE en breve

La especificación completa del protocolo, la disposición de bits y los vectores de prueba están en entrixy.com/ble. El modelo de amenazas está en un documento aparte.

Cuándo el BLE gana al WebSocket

Cuándo el WebSocket gana al BLE

Puedes poner BLE y WebSocket en el mismo motor: en la aplicación aparecen como dos objetos distintos. BLE para abrir de cerca sin internet; WebSocket para cuando estás lejos.

Registrar un dispositivo

  1. En la aplicación el propietario elige Añadir objeto → tipo Controlador BLE
  2. La aplicación explora el aire en busca de dispositivos en modo de emparejamiento (con el nombre entrixy-pair)
  3. Elegir un dispositivo lanza un intercambio ECDH por GATT; el secreto compartido se guarda en las Prefs del teléfono y en la NVS del controlador
  4. Ni código QR ni pulsaciones de botón: un dispositivo nuevo entra solo en modo de emparejamiento en cuanto se carga el programa

La firmware y el configurador viven en /esp/ble/. Fuente: /esp32-example/.

Indication

En la tarjeta del objeto: gris significa que no se ve en el aire, por lejanía o por estar apagado; naranja, que se ve pero la automatización no ha disparado; verde, que disparó o está disparando; gris con borde verde, que disparó hace poco y se puede tocar para volver a abrir.

Database

CREATE TABLE devices (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  host_id INT UNSIGNED NOT NULL,
  device_key VARCHAR(64) NOT NULL UNIQUE,
  secret_hash VARCHAR(64) NOT NULL,
  label VARCHAR(128) DEFAULT '',
  last_seen DATETIME NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

In server.php se añade un tercer rol de WebSocket: device (junto a host y guest).

5. Disparadores: GPS y Wi-Fi

El GPS y el Wi-Fi funcionan por separado. Con ambos configurados, dispara la condición que se cumpla primero. La pausa común entre disparos es de 20 segundos.

GPS: tal como está hoy

Wi-Fi: dos modos

Modo 1: la imagen Wi-Fi (huella)

Modo 2: conectado a una red

Consumo de energía

Almacenamiento (Prefs, nunca sale del teléfono)

wifi_trigger_<actionKey> = {
  "mode": "fingerprint" | "connect",
  "bssids": ["AA:BB:CC:DD:EE:FF", ...],
  "min_match": 5,
  "connect_bssid": "AA:BB:CC:DD:EE:FF",
  "connect_ssid": "TP-Link_5G",
  "enabled": true
}

6. Tres niveles de confirmación

El propietario decide si hace falta confirmación. Cómo se confirma la identidad lo decide el cliente, con lo que ofrezca el dispositivo.

LevelQué ve el usuarioExample
AutomaticNada: el objeto se abre soloBarreras, portones
ConfirmUna notificación en la cortinilla: ¿Abrir [objeto]? → un toqueUn garaje, un portillo, protección contra disparos falsos
Confirmar la identidadBiometría, el PIN del teléfono o el PIN de la aplicaciónUna puerta de entrada

Confirmar no es un paso desperdiciado. La notificación aparece sola en la cortinilla en el momento justo. Sin buscar la aplicación, sin desbloquear, sin encontrar el objeto. Un toque y la puerta está abierta.

Prioridad de los métodos de confirmación de identidad

  1. Biometría, si hay sensor → BiometricPrompt
  2. El PIN o la contraseña del teléfono, cuando no hay biometría → KeyguardManager
  3. El PIN propio de la aplicación, en un dispositivo sin bloqueo → nuestro propio diálogo

La ventana horaria: un factor adicional

El propietario fija Horas permitidas: 07:00–23:00. Fuera de esa ventana la condición se ignora en silencio. Un intervalo nocturno también funciona (22:00–06:00).

Tres factores de protección independientes

  1. WHERE — GPS, una huella Wi-Fi o una conexión
  2. WHO — automático, confirmación o biometría
  3. WHEN — la ventana horaria
Una puerta de entrada: huella Wi-Fi más confirmación de identidad más 07:00–23:00 — más sólido que la mayoría de las cerraduras inteligentes.

7. Propietario frente a invitado: el reparto de los ajustes

SettingPropietario (lo suyo)Propietario → invitadosInvitado
Tipo de objetoChoosesLo transmiteReceives
CoordinatesLos fijaOpcional (con aviso)Los fija o los recibe
Radio del GPSLo fijaLo transmiteReceives
Huella Wi-FiScansNeverEscanea él mismo
Coincidencias mínimas de Wi-FiLo fijaLo transmiteReceives
Ventana horariaLo fijaLo transmiteReceives
ConfirmaciónLo fijaLo transmiteReceives
GPS activado/desactivadoyesyes
Wi-Fi activado/desactivadoyesyes

8. La interfaz de la aplicación

Añadir un objeto (SettingsScreen)

El primer paso es elegir el tipo de conexión:

┌────────────────────────────┐
│  Connection type           │
│                            │
│  📞  Call                  │
│  Opening with a phone      │
│  call                      │
│                            │
│  🌐  URL                   │
│  Sending a command to      │
│  a device address          │
│                            │
│  📟  Device                │
│  Connected through Entrixy │
│  (ESP32, Arduino)          │
└────────────────────────────┘

Tras la elección se abre la ventana de ajustes de ese tipo.

La tarjeta de objeto desplegada (ActionCard)

┌──────────────────────────────────────────┐
│  Corner barrier             📶 3/5  312m │
│  Mine • ●                                │
├──────────────────────────────────────────┤
│  [Edit]  [Icon]                          │
│  ─────────────────────────               │
│  [📍 Geo]   [📶 Wi-Fi]                   │
└──────────────────────────────────────────┘

La ventana 📍 Geo

La ventana 📶 Wi-Fi

◉ Por la imagen Wi-Fi

○ Por la conexión a una red

Debajo: la ventana horaria y la confirmación, compartidas con Geo

La ventana del webhook (🌐 URL)

◉ Desde el teléfono (por defecto)
La URL se guarda solo en tu teléfono. El servidor de Entrixy nunca la conoce.
A favor: máxima privacidad
En contra: tu teléfono tiene que estar conectado
○ Desde el servidor de Entrixy
La petición la envía el servidor. Funciona sin tu teléfono.
A favor: funciona siempre
En contra: la dirección del dispositivo se guarda en el servidor

Indicación en la tarjeta plegada

TriggerIndicator
GPS"312 m"
Huella Wi-Fi📶 barras (0–4 según coincidencias)
Conexión Wi-Fi📶 verde o gris
Webhook/Device● verde (en línea) o gris (desconectado)

9. Límites del plan

Planes internos. No se muestran al usuario, pero los límites se aplican.

Plan por defectoLimit
Objetos (numbers)10
Invitados (user_keys)10

Al superarlo, un mensaje suave: El número de objetos es limitado. Contacta con soporte para ampliarlo.

CREATE TABLE plans (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(64) NOT NULL,
  max_numbers INT NOT NULL DEFAULT 10,
  max_keys INT NOT NULL DEFAULT 10
);
INSERT INTO plans (name) VALUES ('default');

ALTER TABLE hosts ADD COLUMN plan_id INT UNSIGNED DEFAULT 1;

10. Base de datos: el conjunto completo de cambios

-- Plans
CREATE TABLE plans (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(64) NOT NULL,
  max_numbers INT NOT NULL DEFAULT 10,
  max_keys INT NOT NULL DEFAULT 10
);
INSERT INTO plans (name) VALUES ('default');

ALTER TABLE hosts ADD COLUMN plan_id INT UNSIGNED DEFAULT 1;

-- Object type and settings
ALTER TABLE numbers
  ADD COLUMN type ENUM('call','webhook','device') DEFAULT 'call',
  ADD COLUMN radius INT DEFAULT 80,
  ADD COLUMN time_from TIME NULL,
  ADD COLUMN time_to TIME NULL,
  ADD COLUMN security_level ENUM('auto','confirm','identity') DEFAULT 'auto',
  ADD COLUMN geo_available TINYINT(1) DEFAULT 1,
  ADD COLUMN wifi_available TINYINT(1) DEFAULT 1,
  ADD COLUMN wifi_min_match INT DEFAULT 5,
  ADD COLUMN share_lat DOUBLE NULL,
  ADD COLUMN share_lon DOUBLE NULL,
  ADD COLUMN webhook_url VARCHAR(512) NULL,
  ADD COLUMN webhook_secret VARCHAR(64) NULL,
  ADD COLUMN webhook_mode ENUM('phone','server') DEFAULT 'phone',
  ADD COLUMN device_id INT UNSIGNED NULL;

-- Devices (ESP32)
CREATE TABLE devices (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  host_id INT UNSIGNED NOT NULL,
  device_key VARCHAR(64) NOT NULL UNIQUE,
  secret_hash VARCHAR(64) NOT NULL,
  label VARCHAR(128) DEFAULT '',
  last_seen DATETIME NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

share_lat / share_lon — se rellenan solo si el propietario ha activado Compartir coordenadas con los invitados.

webhook_url / webhook_secret — se rellenan solo en modo servidor. En modo teléfono viven en Prefs.

11. Iconos de objeto para invitados

El propietario pone un icono propio a un objeto y el invitado debe verlo. Pero el icono no se guarda en el servidor — el servidor hace de cartero: entrega y olvida.

Preparación del lado del propietario

Al ponerlo, el icono se reduce enseguida a 64×64 PNG (unos 3–5 KB) y se guarda localmente en Prefs o en un archivo.

Cómo viaja

1. The guest connects
   → guest_ok carries has_avatar: true for objects that have an icon

2. The guest client checks: has_avatar=true but no icon locally?
   → Yes → it asks the server

3. The server has no icon
   → it asks the owner over the WebSocket: {type: "avatar_request", number_id: 42}

4. The owner's phone
   → reads the local file
   → sends: {type: "avatar_data", number_id: 42, data: "base64..."}

5. The server passes it to the guest
   → {type: "avatar_data", number_id: 42, data: "base64..."}

6. The guest stores it locally and never asks again

7. The server stored nothing — the data merely passed through

Casos límite

SituationBehaviour
El propietario está desconectadoEl invitado ve el icono por defecto hasta que el propietario se conecte.
50 invitados preguntan a la vez50 × 5 KB = 250 KB por el WebSocket: insignificante.
El propietario cambia el iconohas_avatar_hash cambia y los invitados vuelven a pedirlo
El propietario borra el iconohas_avatar: false y los invitados muestran el icono por defecto
La privacidad se mantiene. El icono de una barrera no son datos sensibles, y aun así no se posa en el servidor: pasa de tránsito por el WebSocket y nunca se escribe en disco.

12. Previsión de carga

Un propietario activo genera

Un invitado activo

Scaling

SubscribersEn línea a la vez (~15 %)Conexiones WebSocketServer
1 000~150~150Easy
10 000~1 500~1 500Easy
50 000~7 500~7 500un trabajador al límite
100 000~15 000~15 0002–3 trabajadores

Workerman en un solo trabajador: 5000–10 000 conexiones simultáneas, de unos 20 KB cada una.

Webhook y dispositivo

El cuello de botella

es MySQL, no los WebSockets. Pero 250 000 inserciones al día —50 mil suscriptores × 5 llamadas— no son nada para MySQL sobre SSD.

Conclusion: до 50 000 абонентов текущий сервер справится без изменений. После — увеличиваем worker->y añadir índices.

13. Plan de implementación

  1. Database — planes, las nuevas columnas en numbers y hosts, la tabla devices
  2. Límites en el servidor — comprobaciones en number_add y key_create
  3. Tipo de objeto — elegir el tipo al añadir, con diálogos distintos
  4. API — host_sync y guest_ok con los nuevos campos de perfil
  5. Webhook — el protocolo HMAC, dos modos de envío, el estado de la respuesta
  6. WS del dispositivo — device_hello, device_command y device_response en server.php
  7. El disparador Wi-Fi — huella y conexión
  8. Ventana horaria — time_from/time_to
  9. Confirmación — tres niveles: automático, confirmar, confirmar la identidad
  10. Icons — entrega de tránsito por el WebSocket, comprimida a 64×64
  11. UI — los botones de Geo y Wi-Fi, los diálogos, los indicadores, el selector de tipo
  12. Compartir coordenadas — una opción con aviso
  13. Compilar y publicar
PWA: El disparador Wi-Fi no está disponible: los navegadores no tienen API de Wi-Fi. El webhook y el dispositivo funcionan por la PWA: botón → servidor → dispositivo. La ventana horaria y la confirmación solo existen en la aplicación nativa.
Compatibilidad hacia atrás: casi no hay clientes antiguos. Hacemos lo que resulte cómodo y, si hace falta, forzamos la actualización con version_check.

Entrixy — arquitectura final v2, acordada el 13 de abril de 2026