Entrixy v2 —— 最终架构

于 2026 年 4 月 13 日定案。供实现使用的工作文档。

目录
  1. 隐私:服务器一无所知
  2. 四种对象类型:呼叫、URL、设备(WS)、BLE 控制器
  3. Webhook(URL)详解
  4. 设备(ESP32 / ESP8266,WebSocket)详解
  5. BLE 控制器(ESP32)详解
  6. 触发器:GPS 与 Wi-Fi
  7. 三级确认
  8. 主人与访客——设置的分工
  9. 应用界面
  10. 套餐限额
  11. 给访客看的对象图标
  12. 负载预测
  13. 数据库结构
  14. 实施计划

1. 隐私:服务器一无所知(默认)

我们的承诺: “我们既不知道用户的电话号码,也不知道他们的坐标”

所有敏感数据只保存 在手机上。主人可以选择让其中一部分经过服务器——并会收到明确警告。

数据默认可选:经服务器
坐标(lat, lon)仅在手机上可以分享给访客(带警告)
Wi-Fi 指纹(BSSID[])仅在手机上从不传输
Webhook 地址与密钥仅在手机上可以存放在服务器上(带警告)
半径、时间、确认层级经服务器
Wi-Fi 指纹等同于坐标。 仅凭一组 BSSID,相关服务就能把你定位到 20–50 米以内。Wi-Fi 画面在任何情况下都不经过服务器。

2. 四种对象类型

添加对象时,主人先选连接类型。随后打开该类型的设置窗口。

Icon名称工作原理FeedbackExample
📞Call从主人手机发起的电话呼叫没有(电话要么拨出,要么没有)道闸、对讲门禁
🌐URL(Webhook)向设备地址发出的 HTTP 请求,可来自手机或 Entrixy 服务器有——带状态的 JSON 响应带 API 的智能锁、Tasmota、Shelly Cloud
📟联网控制器(WS)通过 WebSocket 发给已连接控制器的命令有——通过 WebSocket 返回的应答自制 ESP32 / ESP8266、Shelly Plus 1、NodeMCU
📶BLE 控制器手机与控制器之间的蓝牙直连通道,无需互联网有——通过 BLE 返回的应答(notify)装在没有 Wi-Fi 的门口、用电池供电的控制器

后两种类型的固件配置器: /controller/ (BLE 与“联网”,在浏览器中)。

在用户看来,各种类型长得都一样:一张卡片、一个“呼叫”按钮、一个状态。不同的只是投递机制。

3. Webhook(URL)详解

两种发送模式

Webhook 设置窗口提供一个切换项,利弊直接写在屏幕上:

◉ 从主人的手机发出(默认)
请求从你的手机发出。设备的地址和密钥留在你这里——Entrixy 服务器永远不会知道。
优点:隐私最大化
缺点:手机必须在线
○ 从 Entrixy 服务器发出
由我们的服务器发送请求。即使你的手机离线也照样工作。
优点:不依赖主人的手机
缺点:设备地址存放在服务器上

数据存在哪里

Mode地址与密钥在哪里服务器知道地址吗?
从手机主人手机上的 PrefsNo
从服务器numbers 数据库中的Yes

请求协议

安全性:HMAC 签名。设备与 Entrixy 共享一个 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)"
}

设备会核验签名有效、时间戳不超过 30 秒、nonce 不是重复值。一切正常就执行并回复:

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

状态会在应用中显示给用户。

数据流(手机模式)

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

两种模式下,主人和访客手机上的日志都会记下:谁、何时、哪个对象、返回了什么状态。

4. 设备(ESP32 / ESP8266,WebSocket)详解

工作原理

控制器——支持 ESP32、ESP32-S3、ESP32-C3 与 ESP8266——没有公网 IP。它与 Entrixy 服务器保持一条出站 WebSocket 连接,跟主人的手机一样。

固件与配置器位于 /esp/socket/。源码: ESP32, ESP8266.

设备的 WebSocket 协议

// 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"}

服务器通过各自的 WebSocket 连接把状态转发给客户端——主人与访客。

注册设备

  1. 主人在应用中选择“添加对象”→ 类型“设备”
  2. 于是生成 device_key + device_secret
  3. 显示带固件参数的二维码
  4. 用户为 ESP32 或 ESP8266 刷入程序(见 configurator),设备随即连接到 wss://entrixy.com/ws

Indication

对象卡片会显示设备的连接状态,在线或离线,就像访客看到主人的状态一样。

4a. BLE 控制器(ESP32)详解

工作原理

基于 ESP32、S3、C3、C6 或 H2 的控制器,触发时既不需要 Wi-Fi 也不需要服务器。它处于深度睡眠,每秒醒来约 200 毫秒,广播一条带签名计数器的 BLE 通告。当持有有效密钥的手机靠近,Android——或前台运行的应用——捕获该通告,通过 GATT 连接,发送带一次性 nonce 的 fire 命令,控制器便以短脉冲闭合继电器。对于双稳态驱动和门锁,另有一种“开”“关”分离并跟踪位置的模式,其中“开/关”以加密字节随通告一同发送。

Entrixy 服务器不参与这条通道。 只有在创建访客密钥时才需要它:主人签署一个 GuestToken,经端到端加密通道交给访客。此后访客即可离线工作。

BLE 协议简述

完整的协议规范、位布局和测试向量见 entrixy.com/ble。威胁模型见 单独的文档.

什么时候 BLE 胜过 WebSocket

什么时候 WebSocket 胜过 BLE

同一台驱动装置可以同时装 BLE 与 WebSocket——在应用里显示为两个独立对象。BLE 用于近处无网开启,WebSocket 用于你人在远方的时候。

注册设备

  1. 主人在应用中选择“添加对象”→ 类型“BLE 控制器”
  2. 应用扫描空口,寻找处于配对模式的设备(名称为 entrixy-pair)
  3. 选定设备后通过 GATT 进行 ECDH 交换;共享密钥保存在手机的 Prefs 和控制器的 NVS 中
  4. 既不用二维码也不用按按钮——程序装好后,新设备自己就进入配对模式

固件与配置器位于 /esp/ble/。源码: /esp32-example/.

Indication

在对象卡片上:灰色表示空口不可见,太远或已关闭;橙色表示可见但自动化尚未触发;绿色表示已触发或正在触发;灰色带绿边表示刚刚触发过,点一下可以再开一次。

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 新增第三种 WebSocket 角色: device (与 hostguest).

5. 触发器:GPS 与 Wi-Fi

GPS 与 Wi-Fi 各自独立工作。两者都配置时,谁先满足谁触发。两次触发之间的公共冷却为 20 秒。

GPS——目前的样子

Wi-Fi——两种模式

模式 1:Wi-Fi 画面(指纹)

模式 2:已连接到网络

能耗

存储(Prefs,绝不离开手机)

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. 三级确认

由主人决定是否需要确认。 具体如何确认身份由客户端决定,取决于设备提供了什么。

Level用户看到什么Example
Automatic什么都不用做——对象自行开启道闸、大门
Confirm通知栏里的提醒:“打开[对象]?”→ 点一下车库、小门,以及防误触发
确认身份生物识别、手机 PIN 或应用 PIN入户门

“确认”并不是多余的一步。 通知会在恰当的时刻自己出现在通知栏。不用找应用、不用解锁、不用翻出对象。点一下,门就开了。

身份确认方式的优先级

  1. 有传感器时用生物识别 → BiometricPrompt
  2. 没有生物识别时用手机的 PIN 或密码 → KeyguardManager
  3. 设备未设锁时用应用自带的 PIN → 我们自己的对话框

时间窗口——额外的一重因素

主人设定“允许时段:07:00–23:00”。窗口之外,条件会被静默忽略。跨夜区间同样有效(22:00–06:00)。

三重相互独立的防护因素

  1. WHERE ——GPS、Wi-Fi 指纹或已连接网络
  2. WHO ——自动、确认或生物识别
  3. WHEN ——时间窗口
入户门: Wi-Fi 指纹加身份确认加 07:00–23:00 ——比多数“智能锁”都更结实。

7. 主人与访客——设置的分工

Setting主人(自己的)主人 → 访客访客
对象类型Chooses传递Receives
Coordinates由其设定可选(带警告)由其设定或接收
GPS 半径由其设定传递Receives
Wi-Fi 指纹ScansNever自行扫描
Wi-Fi 最少匹配数由其设定传递Receives
时间窗口由其设定传递Receives
确认由其设定传递Receives
GPS 开/关yesyes
Wi-Fi 开/关yesyes

8. 应用界面

添加对象(SettingsScreen)

第一步是选择连接类型:

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

选定之后,打开该类型的设置窗口。

展开的对象卡片(ActionCard)

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

📍“定位”窗口

📶 Wi-Fi 窗口

◉ 按 Wi-Fi 画面

○ 按连接到网络

下方:时间窗口与确认,与“定位”共用

Webhook 窗口(🌐 URL)

◉ 从手机发出(默认)
地址只保存在你的手机上。Entrixy 服务器永远不会知道。
优点:隐私最大化
缺点:手机必须在线
○ 从 Entrixy 服务器发出
由服务器发送请求。不需要你的手机也能用。
优点:始终可用
缺点:设备地址存放在服务器上

折叠卡片上的指示

TriggerIndicator
GPS"312 m"
Wi-Fi 指纹📶 信号格(按匹配数 0–4)
Wi-Fi 连接📶 绿色或灰色
Webhook/Device● 绿色(在线)或灰色(离线)

9. 套餐限制

内部套餐。不向用户展示,但限制照样生效。

默认套餐Limit
对象(numbers)10
访客(user_keys)10

超出时给出温和的提示:“对象数量有限。如需提高,请联系支持。”

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. 数据库——全部改动

-- 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 ——只有当主人启用了“与访客分享坐标”时才填写。

webhook_url / webhook_secret ——仅在服务器模式下填写。手机模式下它们存放在 Prefs 里。

11. 给访客看的对象图标

主人给对象设置了自定义图标,访客应当能看到。但是 图标并不存放在服务器上 ——服务器只当邮差:送到即忘。

主人侧的准备

设置时图标会立刻缩小为 64×64 PNG (约 3–5 KB),并本地存于 Prefs 或文件中。

它如何传输

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

边界情况

SituationBehaviour
主人离线在主人上线之前,访客看到的是默认图标。
50 位访客同时索取50 × 5 KB = 250 KB 走 WebSocket——微不足道。
主人更换图标has_avatar_hash 变化,访客会重新索取
主人删除图标has_avatar: false,访客显示默认图标
隐私得以保全。 道闸的图标并非敏感数据,可即便如此它也不在服务器上落脚:只是过境般经过 WebSocket,从不写入磁盘。

12. 负载预测

一位活跃主人会产生

一位活跃访客

Scaling

Subscribers同时在线(约 15%)WebSocket 连接数Server
1 000~150~150Easy
10 000~1 500~1 500Easy
50 000~7 500~7 500单个工作进程已到极限
100 000~15 000~15 0002–3 个工作进程

单个工作进程上的 Workerman:5000–10000 条并发连接,每条约 20 KB。

Webhook 与设备

瓶颈

在 MySQL,而不在 WebSocket。可每天 25 万次插入——5 万订户 × 5 次呼叫——对 SSD 上的 MySQL 来说不值一提。

Conclusion: до 50 000 абонентов текущий сервер справится без изменений. После — увеличиваем worker->,并添加索引。

13. 实施计划

  1. Database ——套餐、numbers 与 hosts 的新列、devices 表
  2. 服务端限制 ——在 number_add 与 key_create 中做校验
  3. 对象类型 ——添加时选择类型,配以不同对话框
  4. API ——带新档案字段的 host_sync 与 guest_ok
  5. Webhook ——HMAC 协议、两种发送模式、响应状态
  6. 设备 WS ——server.php 中的 device_hello、device_command 与 device_response
  7. Wi-Fi 触发器 ——指纹与连接
  8. 时间窗口 ——time_from/time_to
  9. 确认 ——三个层级:自动、确认、确认身份
  10. Icons ——经 WebSocket 过境投递,压缩到 64×64
  11. UI ——“定位”与 Wi-Fi 按钮、对话框、指示器、类型选择器
  12. 分享坐标 ——一个带警告的选项
  13. 构建与发布
PWA: Wi-Fi 触发器不可用——浏览器没有 Wi-Fi API。Webhook 与“设备”通过 PWA 工作:按钮 → 服务器 → 设备。时间窗口与确认只存在于原生应用中。
向后兼容: 几乎没有旧客户端。怎么方便怎么做,必要时通过 version_check 强制更新。

Entrixy —— 最终架构 v2,2026 年 4 月 13 日定案