Validé le 13 avril 2026. Document de travail pour la réalisation.
Toutes les données sensibles sont conservées uniquement sur le téléphone. Le propriétaire peut, s’il le souhaite, en faire transiter une partie par le serveur — avec un avertissement explicite.
| Données | Par défaut | En option via le serveur |
|---|---|---|
| Coordonnées (lat, lon) | Uniquement sur le téléphone | Peut être partagé avec les invités (avec avertissement) |
| Empreinte Wi-Fi (BSSID[]) | Uniquement sur le téléphone | Jamais transmise |
| URL et secret du webhook | Uniquement sur le téléphone | Peut être stocké sur le serveur (avec avertissement) |
| Rayon, temps, niveau de confirmation | Via le serveur | — |
À l’ajout d’un objet, le propriétaire choisit le type de liaison. La fenêtre de réglages de ce type s’ouvre ensuite.
| Icon | Nom | Comment ça marche | Feedback | Example |
|---|---|---|---|---|
| 📞 | Call | Un appel depuis le téléphone du propriétaire | Aucun (l’appel est parti ou non) | Une barrière, un interphone |
| 🌐 | URL (webhook) | Une requête HTTP vers l’adresse de l’appareil, depuis le téléphone ou depuis le serveur Entrixy | Oui — une réponse JSON avec un statut | Une serrure connectée avec API, Tasmota, Shelly Cloud |
| 📟 | Contrôleur internet (WS) | Une commande par WebSocket vers un contrôleur connecté | Oui — une réponse par le WebSocket | ESP32 / ESP8266 maison, Shelly Plus 1, NodeMCU |
| 📶 | Contrôleur BLE | Un canal Bluetooth direct entre téléphone et contrôleur, sans internet | Oui — une réponse par BLE (notify) | Un contrôleur sur piles à un portail sans Wi-Fi |
Le configurateur de micrologiciel pour les deux derniers types : /controller/ (BLE et Internet, dans le navigateur).
Pour l’utilisateur, tous les types se ressemblent : une carte, un bouton Appeler, un statut. Seul le mécanisme d’acheminement diffère.
La fenêtre de réglages du webhook propose un sélecteur, avec les avantages et inconvénients écrits à l’écran :
| Mode | Où sont l’URL et le secret | Le serveur connaît-il l’URL ? |
|---|---|---|
| Depuis le téléphone | Prefs sur le téléphone du propriétaire | No |
| Depuis le serveur | La table numbers dans la base de données | Yes |
Sécurité : une signature HMAC. L’appareil et Entrixy partagent 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)"
}
L’appareil vérifie que la signature est valide, que l’horodatage a moins de 30 secondes et que le nonce n’est pas répété. Si tout va bien, il agit et répond :
{"status": "ok", "message": "Door opened"}
or
{"status": "error", "message": "Lock jammed"}
Le statut est affiché à l’utilisateur dans l’application.
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
Dans les deux modes, le journal sur le téléphone du propriétaire et de l’invité consigne qui, quand, quel objet et avec quel statut.
Le contrôleur — ESP32, ESP32-S3, ESP32-C3 et ESP8266 sont pris en charge — n’a pas d’IP publique. Il maintient une connexion WebSocket sortante vers le serveur Entrixy, exactement comme le téléphone du propriétaire.
Le micrologiciel et le configurateur vivent à /esp/socket/. Sources : ESP32, ESP8266.
// 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"}
Le serveur relaie le statut aux clients — propriétaire et invité — par leurs connexions WebSocket.
device_key + device_secretwss://entrixy.com/wsLa carte de l’objet montre l’état de connexion de l’appareil, en ligne ou hors ligne, comme les invités voient l’état du propriétaire.
Un contrôleur sur ESP32, S3, C3, C6 ou H2 n’a besoin ni de Wi-Fi ni de serveur pour déclencher. Il dort profondément, se réveille une fois par seconde pendant environ 200 ms et émet une annonce BLE avec un compteur signé. Quand un téléphone détenant une clé valide approche, Android — ou l’application si elle est au premier plan — capte l’annonce, se connecte en GATT, envoie une commande fire avec un nonce à usage unique, et le contrôleur ferme le relais le temps d’une brève impulsion. Pour les motorisations bistables et les serrures, un mode sépare ouvrir et fermer avec suivi de position, où ouvert/fermé voyage sous forme d’octet chiffré dans l’annonce.
Le serveur Entrixy ne participe pas à ce canal. Il n’est nécessaire qu’à la création d’une clé invité : le propriétaire signe un GuestToken et le transmet à l’invité par le canal chiffré de bout en bout. Ensuite l’invité travaille hors ligne.
La spécification complète du protocole, la disposition des bits et les vecteurs de test se trouvent à entrixy.com/ble. Le modèle de menaces figure dans un document séparé.
On peut équiper la même motorisation en BLE et en WebSocket — ils apparaissent comme deux objets distincts dans l’application. Le BLE pour ouvrir de près sans internet, le WebSocket pour les moments où vous êtes loin.
entrixy-pair)Le micrologiciel et le configurateur vivent à /esp/ble/. Source : /esp32-example/.
Sur la carte de l’objet : gris signifie invisible dans les ondes, trop loin ou éteint ; orange, visible mais l’automatisme n’a pas déclenché ; vert, déclenché ou en train de déclencher ; gris à contour vert, déclenché récemment et l’on peut toucher pour rouvrir.
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 un troisième rôle WebSocket s’ajoute : device (aux côtés de host et guest).
Le GPS et le Wi-Fi fonctionnent indépendamment. Si les deux sont configurés, c’est la condition remplie en premier qui déclenche. La pause commune entre deux déclenchements est de 20 secondes.
Mode 1 : l’image Wi-Fi (empreinte)
Mode 2 : connecté à un réseau
WifiManager.getScanResults() — des résultats en cache sans nouveau balayage ; nous l’appelons à chaque mise à jour GPS sans coûtNetworkCallback, sans aucun balayage
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
}
Le propriétaire décide si une confirmation est requise. La façon de confirmer l’identité relève du client, parmi ce que propose l’appareil.
| Level | Ce que voit l’utilisateur | Example |
|---|---|---|
| Automatic | Rien — l’objet s’ouvre tout seul | Barrières, portails |
| Confirm | Une notification dans le volet : Ouvrir [objet] ? → une pression | Un garage, un portillon, une protection contre les faux déclenchements |
| Confirmer l’identité | Biométrie, code PIN du téléphone ou code PIN de l’application | Une porte d’entrée |
Confirmer n’est pas une étape gaspillée. La notification apparaît d’elle-même dans le volet au bon moment. Pas besoin de chercher l’application, de déverrouiller, de trouver l’objet. Une pression et la porte est ouverte.
BiometricPromptKeyguardManagerLe propriétaire définit Heures autorisées : 07:00–23:00 . Hors de cette fenêtre, la condition est ignorée en silence. Un intervalle de nuit fonctionne aussi (22:00–06:00).
| Setting | Propriétaire (le sien) | Propriétaire → invités | Invité |
|---|---|---|---|
| Type d’objet | Chooses | Transmet | Receives |
| Coordinates | Les fixe | En option (avec avertissement) | Les fixe ou les reçoit |
| Rayon GPS | Le fixe | Transmet | Receives |
| Empreinte Wi-Fi | Scans | Never | Scanne lui-même |
| Correspondances Wi-Fi minimales | Le fixe | Transmet | Receives |
| Fenêtre horaire | Le fixe | Transmet | Receives |
| Confirmation | Le fixe | Transmet | Receives |
| GPS activé/désactivé | yes | — | yes |
| Wi-Fi activé/désactivé | yes | — | yes |
La première étape est le choix du type de liaison :
┌────────────────────────────┐ │ Connection type │ │ │ │ 📞 Call │ │ Opening with a phone │ │ call │ │ │ │ 🌐 URL │ │ Sending a command to │ │ a device address │ │ │ │ 📟 Device │ │ Connected through Entrixy │ │ (ESP32, Arduino) │ └────────────────────────────┘
Après le choix, la fenêtre de réglages de ce type s’ouvre.
┌──────────────────────────────────────────┐ │ Corner barrier 📶 3/5 312m │ │ Mine • ● │ ├──────────────────────────────────────────┤ │ [Edit] [Icon] │ │ ───────────────────────── │ │ [📍 Geo] [📶 Wi-Fi] │ └──────────────────────────────────────────┘
◉ Par l’image Wi-Fi
○ Par la connexion à un réseau
En dessous : la fenêtre horaire et la confirmation, communes avec Géo
| Trigger | Indicator |
|---|---|
| GPS | "312 m" |
| Empreinte Wi-Fi | 📶 barres (0–4 selon les correspondances) |
| Connexion Wi-Fi | 📶 vert ou gris |
| Webhook/Device | ● vert (en ligne) ou gris (hors ligne) |
Offres internes. Elles ne sont pas montrées à l’utilisateur, mais les limites s’appliquent.
| Offre par défaut | Limit |
|---|---|
| Objets (numbers) | 10 |
| Invités (user_keys) | 10 |
En cas de dépassement, un message doux : Le nombre d’objets est limité. Contactez le support pour l’augmenter.
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;
-- 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 — renseignés seulement si le propriétaire a activé Partager les coordonnées avec les invités .
webhook_url / webhook_secret — renseignés uniquement en mode serveur. En mode téléphone, ils vivent dans les Prefs.
Le propriétaire attribue une icône personnalisée à un objet et l’invité doit la voir. Mais l’icône n’est pas stockée sur le serveur — le serveur joue les facteurs : il livre et oublie.
À sa définition, l’icône est aussitôt réduite à 64×64 PNG (environ 3–5 Ko) et conservée localement dans les Prefs ou dans un fichier.
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
| Situation | Behaviour |
|---|---|
| Le propriétaire est hors ligne | L’invité voit l’icône par défaut jusqu’à ce que le propriétaire revienne en ligne. |
| 50 invités demandent en même temps | 50 × 5 Ko = 250 Ko par le WebSocket — négligeable. |
| Le propriétaire change l’icône | has_avatar_hash change et les invités la redemandent |
| Le propriétaire supprime l’icône | has_avatar : false et les invités affichent l’icône par défaut |
| Subscribers | En ligne simultanément (~15 %) | Connexions WebSocket | Server |
|---|---|---|---|
| 1 000 | ~150 | ~150 | Easy |
| 10 000 | ~1 500 | ~1 500 | Easy |
| 50 000 | ~7 500 | ~7 500 | un worker à la limite |
| 100 000 | ~15 000 | ~15 000 | 2–3 workers |
Workerman sur un seul worker : 5 000–10 000 connexions simultanées, d’environ 20 Ko chacune.
est MySQL, pas les WebSockets. Mais 250 000 insertions par jour — 50 000 abonnés × 5 appels — ne sont rien pour MySQL sur SSD.
Entrixy — architecture finale v2, validée le 13 avril 2026