132 lines
6.3 KiB
Markdown
132 lines
6.3 KiB
Markdown
# ABox Manager 架构
|
||
|
||
## 总体结构
|
||
|
||
ABox Manager 是单 Activity 的 Kotlin/Compose Android 应用。业务能力通过纯 Kotlin 领域端口表达,Android 模块负责持久化、系统集成、依赖注入和 UI,厂商 SDK 被限制在独立防腐层中。
|
||
|
||
```text
|
||
abox-mgr/
|
||
├── domain/ # 领域模型、端口、用例与执行器
|
||
├── absdk-bridge/ # ABSDK 0630 防腐层
|
||
├── runtime/simulation/ # 本地体验生产模拟运行时
|
||
├── testing/fakes/ # 测试专用 Fake
|
||
└── android/
|
||
├── core/
|
||
│ ├── designsystem/ # Compose 主题、组件和设计令牌
|
||
│ ├── preferences/ # 界面偏好 DataStore
|
||
│ ├── devicecatalog/ # ABox 本地设备目录与 Provider 路由
|
||
│ └── session/ # Profile、会话偏好和加密凭据
|
||
├── feature/
|
||
│ ├── infrared/ # 遥控器、按键、码库与学习流程
|
||
│ └── automation/ # 情景、自动化、通知和活动记录
|
||
└── app/ # 应用组合根、导航和设备页面
|
||
```
|
||
|
||
依赖遵循以下方向:
|
||
|
||
```text
|
||
android/app ──> android/core/*, android/feature/*, domain
|
||
android/app ──> absdk-bridge
|
||
android/app ──> runtime/simulation
|
||
android/feature/* ──> domain
|
||
android/core/* ──> domain
|
||
absdk-bridge ──> domain
|
||
runtime/simulation ──> domain
|
||
testing/fakes ──> domain
|
||
domain ──> Kotlin 与 Coroutines
|
||
```
|
||
|
||
`domain` 不依赖 Android、Compose、Room、DataStore 或 ABSDK。UI 不直接调用厂商 SDK。
|
||
|
||
## Provider 与会话
|
||
|
||
应用支持 `ABOX` 和本地体验两种 Provider。`ProviderProfileId` 是所有会话、目录、遥控器、情景、自动化状态和活动记录的数据隔离键。
|
||
|
||
- 任一时刻只有一个活动 Provider;
|
||
- 切换 Provider 前结束当前会话;
|
||
- ABox 会话由进程内唯一 `AbsdkBridge` 维护;
|
||
- 本地体验由 `runtime/simulation` 建立无凭据会话;
|
||
- 自动登录凭据使用 Android Keystore 的 AES-256-GCM 加密,profile id 作为 AAD;
|
||
- ABSDK 没有 logout 和 Token setter,退出只清理 App/Bridge 本地会话,重新登录覆盖 SDK Token。
|
||
|
||
## 领域端口
|
||
|
||
主要公共端口包括:
|
||
|
||
- `SessionGateway`、`LocalSessionGateway`;
|
||
- `ProviderProfileStore`、`CredentialStore`;
|
||
- `DeviceCatalog`;
|
||
- `SocketGateway`、`TemperatureHumidityGateway`、`InfraredGateway`;
|
||
- `VirtualRemoteRepository`、`InfraredKeyRepository`;
|
||
- `SceneRepository`、`SceneExecutor`、`NotificationGateway`;
|
||
- `ActivityRepository`。
|
||
|
||
所有设备与会话操作返回 `DomainResult<T>`。错误通过 `DomainError` 分类,必要时保留脱敏的厂商错误码;`ABRet`、原始 `Map` 和厂商对象不得越过 Bridge。
|
||
|
||
## 设备目录与默认内容
|
||
|
||
ABSDK 0630 没有设备发现或列表接口。ABox 设备目录由 `android/core/devicecatalog` 使用 Room 按 profile 持久化,设备操作最终使用 `DeviceName` 调用网关。
|
||
|
||
ABox profile 首次初始化时,如果某一设备类型尚不存在,App 补充以下默认记录:
|
||
|
||
- `智能插座`;
|
||
- `温湿度传感器`;
|
||
- `红外控制器`。
|
||
|
||
初始化按设备类型判断,不迁移或重命名已有记录。用户修改设备名后,名称必须与网关实际设备名一致。
|
||
|
||
同一初始化流程会为 `红外控制器` 添加两个未学习的自定义遥控器:自定义电视遥控器和自定义面板遥控器。
|
||
|
||
## 设备操作
|
||
|
||
`android/app` 中的路由网关根据活动 Provider 分发操作:
|
||
|
||
- ABox 调用共享 `AbsdkBridge`;
|
||
- 本地体验调用确定性的模拟实现。
|
||
|
||
插座控制不会把期望值直接当成真实状态。命令成功后必须回读;回读失败时 UI 保持“等待确认”或错误状态。温湿度允许单字段缺失,只有两个字段都不存在时才进入无数据状态。设备离线通过领域错误显式传递到 UI。
|
||
|
||
## 红外
|
||
|
||
`android/feature/infrared` 使用独立 Room 数据库保存虚拟遥控器、按钮布局、学习状态和硬件槽位信息。
|
||
|
||
- 预置遥控器从内置码库下载,不提供学习入口;
|
||
- 自定义遥控器通过硬件学习建立按键;
|
||
- 按键状态区分未学习、学习中、失败和可发送;
|
||
- 学习任务由应用级协调器执行,页面离开后可继续;
|
||
- 进程中断遗留的学习状态在恢复时保守标记为失败;
|
||
- 开源码库来源和转换约束见 `docs/third-party/flipper-irdb.md`。
|
||
|
||
## 情景、自动化与活动记录
|
||
|
||
`android/feature/automation` 使用 Room 保存情景文档、触发器匹配状态、定时 occurrence claim 和活动记录。
|
||
|
||
手动指令直接执行。自动指令由 WorkManager 调度和 `AutomationEngine` 评估:
|
||
|
||
1. 收集相关温湿度与插座状态快照;
|
||
2. 对每个启用的自动指令评估多个触发器;
|
||
3. 状态型触发器只在不匹配到匹配的转换上产生新触发;
|
||
4. 时间触发器通过 occurrence key 去重;
|
||
5. 任一新触发出现后,再要求全部条件满足;
|
||
6. 按情景配置执行动作并记录结果。
|
||
|
||
触发器支持时间、温度、湿度和插座状态;条件支持温度、湿度、插座状态和时间范围;动作支持插座、虚拟遥控器、红外按键、延迟和通知。
|
||
|
||
活动记录按 profile 保存设备命令、状态确认、传感器刷新、红外发送/学习、情景及动作结果。记录只保存领域层允许的信息,不保存凭据、原始响应或红外码值。
|
||
|
||
## 界面偏好
|
||
|
||
`android/core/preferences` 使用 DataStore 保存主题模式、动态颜色、强调色、色彩风格、色彩规范、界面缩放和触觉反馈偏好。当前生产界面保持 Material 渲染;未采用的渲染器值保留兼容数据,但不作为正式功能承诺。预测性返回默认关闭,且不向用户显示设置入口。
|
||
|
||
## ABSDK 边界
|
||
|
||
`absdk-bridge` 是厂商 JAR 的唯一生产接入点:
|
||
|
||
- 全部同步 SDK 调用转移到后台并串行执行;
|
||
- 进程内共享同一个 SDK/Bridge 实例;
|
||
- 不调用语义未确认的 `setHostInfo`;
|
||
- 使用 `@CleartextAbsdkTransport` 显式标记明文传输风险;
|
||
- App 仅因厂商 JAR 的历史行为允许明文流量,不把该策略扩展到其他网络功能。
|
||
|
||
协议事实、端点、签名边界和待确认事项见 `docs/protocol/absdk-0630.md`。
|