@ziztechnology/dial-library 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +432 -1
- package/dist/index.d.mts +263 -6
- package/dist/index.mjs +1729 -1
- package/package.json +11 -6
package/README.md
CHANGED
|
@@ -1 +1,432 @@
|
|
|
1
|
-
# Toooony
|
|
1
|
+
# Toooony 表盘 SDK
|
|
2
|
+
|
|
3
|
+
`@ziztechnology/dial-library` 为 Toooony 表盘提供统一的设备信息和行车状态 API。你可以用它读取传感器,也可以直接订阅经过平滑、确认和回退处理后的行车状态。
|
|
4
|
+
|
|
5
|
+
> [!NOTE]
|
|
6
|
+
> SDK 最低支持 Toooony Runtime `v1.5.20`。行车状态传感器 Bridge 仅向审核通过的 `PACKAGED_H5` 开放。第三方表盘应使用本 SDK,不要直接依赖 Runtime 注入到 `window` 上的内部方法。
|
|
7
|
+
|
|
8
|
+
## 安装
|
|
9
|
+
|
|
10
|
+
使用 npm、pnpm 或 yarn 安装:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @ziztechnology/dial-library
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @ziztechnology/dial-library
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
yarn add @ziztechnology/dial-library
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
SDK 是 ESM 包,并自带 TypeScript 类型。
|
|
25
|
+
|
|
26
|
+
## 快速开始
|
|
27
|
+
|
|
28
|
+
下面的例子会监听行车状态,并把当前状态显示在页面上:
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<p id="driving-status">正在读取行车状态…</p>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { CAR_RUNNING_LABELS, createDrivingStatusController } from '@ziztechnology/dial-library';
|
|
36
|
+
|
|
37
|
+
const statusElement = document.querySelector('#driving-status');
|
|
38
|
+
if (!statusElement) throw new Error('找不到 #driving-status');
|
|
39
|
+
|
|
40
|
+
const controller = createDrivingStatusController();
|
|
41
|
+
|
|
42
|
+
// 控制器默认从 STOPPED 开始。subscribe 只在稳定状态改变后触发。
|
|
43
|
+
statusElement.textContent = CAR_RUNNING_LABELS[controller.getSnapshot().status];
|
|
44
|
+
|
|
45
|
+
const unsubscribe = controller.subscribe((event) => {
|
|
46
|
+
statusElement.textContent = CAR_RUNNING_LABELS[event.status];
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
controller.start();
|
|
50
|
+
|
|
51
|
+
// 页面不再使用控制器时释放资源。
|
|
52
|
+
const destroy = () => {
|
|
53
|
+
unsubscribe();
|
|
54
|
+
controller.destroy();
|
|
55
|
+
};
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`start()` 启动传感器轮询;`destroy()` 停止轮询、取消未完成的读取并移除监听器。`start()`、`stop()` 和 `destroy()` 都可以重复调用。
|
|
59
|
+
|
|
60
|
+
控制器默认会跟随 Runtime 和页面生命周期自动暂停、恢复,因此大多数表盘不需要额外监听 `visibilitychange`。
|
|
61
|
+
|
|
62
|
+
## 常量
|
|
63
|
+
|
|
64
|
+
### 行车状态
|
|
65
|
+
|
|
66
|
+
`CarRunningStatus` 一共有八种取值。状态字符串是稳定协议,可以用于判断逻辑或作为素材配置的键:
|
|
67
|
+
|
|
68
|
+
| 常量值 | `CAR_RUNNING_LABELS` 中文名称 | 默认优先级 |
|
|
69
|
+
| -------------------- | ----------------------------- | ---------: |
|
|
70
|
+
| `STOPPED` | 静止 | 0 |
|
|
71
|
+
| `STEADY_DRIVING` | 匀速行驶 | 0 |
|
|
72
|
+
| `ACCELERATION` | 加速 | 1 |
|
|
73
|
+
| `BRAKING` | 刹车 | 2 |
|
|
74
|
+
| `LEFT_TURN` | 左转 | 3 |
|
|
75
|
+
| `RIGHT_TURN` | 右转 | 3 |
|
|
76
|
+
| `RAPID_ACCELERATION` | 急加速 | 4 |
|
|
77
|
+
| `SUDDEN_BRAKING` | 急刹车 | 5 |
|
|
78
|
+
|
|
79
|
+
相关导出:
|
|
80
|
+
|
|
81
|
+
- `CAR_RUNNING_STATUSES`:按协议顺序保存全部八种状态,适合遍历。
|
|
82
|
+
- `CAR_RUNNING_LABELS`:状态到中文名称的映射。
|
|
83
|
+
- `CAR_RUNNING_STATUS_PRIORITY`:状态优先级。高优先级动作可以抢占正在展示的低优先级状态。
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { CAR_RUNNING_LABELS, CAR_RUNNING_STATUSES, type CarRunningStatus } from '@ziztechnology/dial-library';
|
|
87
|
+
|
|
88
|
+
const renderStatus = (status: CarRunningStatus) => {
|
|
89
|
+
console.log(status, CAR_RUNNING_LABELS[status]);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
CAR_RUNNING_STATUSES.forEach(renderStatus);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 电池状态
|
|
96
|
+
|
|
97
|
+
统一传感器信息中的电池字段使用以下字符串联合类型:
|
|
98
|
+
|
|
99
|
+
- `BatteryStatus`:`CHARGING`、`DISCHARGING`、`FULL`、`NOT_CHARGING`、`UNKNOWN`。
|
|
100
|
+
- `BatteryPlugged`:`AC`、`USB`、`WIRELESS`、`DOCK`、`NONE`。
|
|
101
|
+
- `BatteryHealth`:`GOOD`、`OVERHEAT`、`DEAD`、`OVER_VOLTAGE`、`UNSPECIFIED_FAILURE`、`COLD`、`UNKNOWN`。
|
|
102
|
+
- `BATTERY_HEALTH_LABELS`:`BatteryHealth` 到中文名称的映射。
|
|
103
|
+
|
|
104
|
+
## 统一的传感器
|
|
105
|
+
|
|
106
|
+
`unifiedSensorInfo()` 一次返回设备当前的统一快照。快照包含陀螺仪、加速度、电池、温度、Wi-Fi、方向和磁场等信息:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { unifiedSensorInfo } from '@ziztechnology/dial-library';
|
|
110
|
+
|
|
111
|
+
const info = await unifiedSensorInfo();
|
|
112
|
+
|
|
113
|
+
if (info.temperature.available) {
|
|
114
|
+
console.log(`电池温度:${info.temperature.value}${info.temperature.unit}`);
|
|
115
|
+
} else {
|
|
116
|
+
console.log(`温度不可用:${info.temperature.unavailableReason}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (info.linearAcceleration.available) {
|
|
120
|
+
const { x, y, z } = info.linearAcceleration.value;
|
|
121
|
+
console.log('线性加速度:', { x, y, z }, info.linearAcceleration.unit);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
每个指标都使用 `available` 作为区分字段:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
type SensorMetric<T> =
|
|
129
|
+
| {
|
|
130
|
+
available: true;
|
|
131
|
+
value: T;
|
|
132
|
+
unit: string | null;
|
|
133
|
+
source: string;
|
|
134
|
+
sampledAtMs: number;
|
|
135
|
+
unavailableReason: null;
|
|
136
|
+
}
|
|
137
|
+
| {
|
|
138
|
+
available: false;
|
|
139
|
+
value: null;
|
|
140
|
+
unit: string | null;
|
|
141
|
+
source: string;
|
|
142
|
+
sampledAtMs: number | null;
|
|
143
|
+
unavailableReason: UnifiedSensorUnavailableReason | null;
|
|
144
|
+
};
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
先判断 `available`,TypeScript 才会把 `value` 收窄到真实类型。不要用 `value === null` 猜测设备是否支持某项指标。
|
|
148
|
+
|
|
149
|
+
常见的 `unavailableReason` 有:
|
|
150
|
+
|
|
151
|
+
| 原因 | 含义 |
|
|
152
|
+
| ---------------------- | -------------------------- |
|
|
153
|
+
| `SENSOR_MISSING` | 设备没有对应传感器 |
|
|
154
|
+
| `NO_SAMPLE` | 传感器存在,但暂时没有样本 |
|
|
155
|
+
| `PLATFORM_UNAVAILABLE` | 当前平台不提供该能力 |
|
|
156
|
+
| `PERMISSION_DENIED` | 没有读取权限 |
|
|
157
|
+
| `NOT_CONNECTED` | 对应设备或网络未连接 |
|
|
158
|
+
|
|
159
|
+
`capturedAtMs` 是快照时间,具体指标的 `sampledAtMs` 是采样时间。行车状态识别默认拒绝未来样本和超过 1 秒的旧样本。
|
|
160
|
+
|
|
161
|
+
### 可用字段
|
|
162
|
+
|
|
163
|
+
| 字段 | 值 | 单位 |
|
|
164
|
+
| -------------------- | -------------------------------------- | -------- |
|
|
165
|
+
| `gyroscope` | `{ x, y, z }` | `rad/s` |
|
|
166
|
+
| `accelerometer` | `{ x, y, z }` | `m/s²` |
|
|
167
|
+
| `linearAcceleration` | `{ x, y, z }` | `m/s²` |
|
|
168
|
+
| `gravity` | `{ x, y, z }` | `m/s²` |
|
|
169
|
+
| `magneticField` | `{ x, y, z }` | `µT` |
|
|
170
|
+
| `orientation` | `{ azimuth, pitch, roll }` | `degree` |
|
|
171
|
+
| `motionIntensity` | 数值;旧 Runtime 中可能不存在 | `m/s²` |
|
|
172
|
+
| `temperature` | 数值 | `°C` |
|
|
173
|
+
| `wifiSsid` | 字符串 | 无 |
|
|
174
|
+
| `batteryCapacity` | `{ levelPercent, chargeCounterUah }` | 无 |
|
|
175
|
+
| `battery` | 电池是否存在、充电状态、健康度和电压等 | 无 |
|
|
176
|
+
|
|
177
|
+
如果只需要根据单次快照做无状态判断,可以使用纯函数 `classifyDrivingSnapshot()`:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { classifyDrivingSnapshot, unifiedSensorInfo } from '@ziztechnology/dial-library';
|
|
181
|
+
|
|
182
|
+
const status = classifyDrivingSnapshot(await unifiedSensorInfo());
|
|
183
|
+
|
|
184
|
+
if (status === null) {
|
|
185
|
+
console.log('传感器数据不可用、非法或已经过期');
|
|
186
|
+
} else {
|
|
187
|
+
console.log('当前瞬时状态:', status);
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`checkRunningStatus()` 是同一函数的兼容别名。单次分类不会去抖,也不会保存上一次状态;它使用 Runtime 的 `motionIntensity >= 0.4 m/s²` 判断瞬时行驶。面向 UI 时,通常应该使用下一节带 accelerometer 滑窗的状态控制器。
|
|
192
|
+
|
|
193
|
+
设备的固定安装方向为:
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
+X = 车身右侧
|
|
197
|
+
+Y = 上方
|
|
198
|
+
-Z = 车辆前进方向
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
因此纵向加速度取 `-linearAcceleration.z`,横向加速度取 `linearAcceleration.x`,偏航角速度取 `gyroscope.y`。
|
|
202
|
+
|
|
203
|
+
## 行车状态基本使用
|
|
204
|
+
|
|
205
|
+
### 订阅稳定状态
|
|
206
|
+
|
|
207
|
+
`createDrivingStatusController()` 负责持续读取传感器,并处理瞬时噪声。推荐让页面只响应它发出的 `status_changed`:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { CAR_RUNNING_LABELS, createDrivingStatusController } from '@ziztechnology/dial-library';
|
|
211
|
+
|
|
212
|
+
const controller = createDrivingStatusController({
|
|
213
|
+
initialStatus: 'STOPPED',
|
|
214
|
+
pollIntervalMs: 200,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const unsubscribe = controller.subscribe((event) => {
|
|
218
|
+
console.log(`${CAR_RUNNING_LABELS[event.previousStatus]} -> ${CAR_RUNNING_LABELS[event.status]}`, event.reason);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
controller.start();
|
|
222
|
+
|
|
223
|
+
// 临时停止;之后可以再次 start()。
|
|
224
|
+
controller.stop();
|
|
225
|
+
|
|
226
|
+
// 永久释放;destroy() 后不能重新启动。
|
|
227
|
+
unsubscribe();
|
|
228
|
+
controller.destroy();
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
控制器默认每 200ms 读取一次,并保证上一次读取结束后才开始下一次。它会对普通动作平滑、要求候选状态连续出现、设置状态最短展示时间,并允许急刹车等高优先级状态抢占。候选状态不会触发 UI,只有提交后的稳定状态才会通知订阅者。
|
|
232
|
+
|
|
233
|
+
静止和匀速使用 accelerometer 的 1 秒三轴波动窗口判断。窗口按 `sampledAtMs` 去重,至少积累 5 个有效样本;总体标准差大于 `0.4 m/s²` 时进入 `STEADY_DRIVING`,小于 `0.25 m/s²` 时进入 `STOPPED`,中间区间保持原基础状态。滑窗预热不影响加速、刹车或转弯识别。accelerometer 不可用或过期时,整次行车快照进入不可用流程。
|
|
234
|
+
|
|
235
|
+
### 读取当前状态
|
|
236
|
+
|
|
237
|
+
订阅只通知后续变化。初次渲染或调试时,可以读取快照:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
const snapshot = controller.getSnapshot();
|
|
241
|
+
|
|
242
|
+
console.log(snapshot.status); // 已提交状态
|
|
243
|
+
console.log(snapshot.candidate); // 正在确认的候选状态,可能为 null
|
|
244
|
+
console.log(snapshot.candidateConfirmationCount);
|
|
245
|
+
console.log(snapshot.lifecycle); // stopped、running 或 destroyed
|
|
246
|
+
console.log(snapshot.tuningVersion); // driving-status-v1
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 调整识别参数
|
|
250
|
+
|
|
251
|
+
默认参数集中在 `DEFAULT_DRIVING_STATUS_TUNING`。只覆盖需要调整的字段即可:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
const controller = createDrivingStatusController({
|
|
255
|
+
tuning: {
|
|
256
|
+
turnYawThreshold: 0.6,
|
|
257
|
+
motionWindowMs: 1_200,
|
|
258
|
+
motionMinimumSamples: 6,
|
|
259
|
+
drivingMotionThreshold: 0.45,
|
|
260
|
+
stoppedMotionThreshold: 0.28,
|
|
261
|
+
confirmationSamples: {
|
|
262
|
+
LEFT_TURN: 3,
|
|
263
|
+
RIGHT_TURN: 3,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`drivingMotionThreshold` 必须大于 `stoppedMotionThreshold`,形成进入行驶和退出行驶的迟滞区间。`motionMinimumSamples` 必须是正整数,其余窗口和阈值必须是正有限数值。
|
|
270
|
+
|
|
271
|
+
固定安装的惯性传感器无法严格区分“完全静止”和“理想匀速直线运动”。默认算法使用三轴加速度波动和陀螺仪扰动近似判断,修改阈值后应在真机上路测。
|
|
272
|
+
|
|
273
|
+
### 驱动行车表情
|
|
274
|
+
|
|
275
|
+
Runtime 可以为八种状态注入图片、视频、Live Photo、TGS 或 Emoji 配置。`readDrivingExpressionsConfig()` 会读取并校验这份配置,播放器负责切换素材:
|
|
276
|
+
|
|
277
|
+
```html
|
|
278
|
+
<div id="expression"></div>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import {
|
|
283
|
+
createDrivingExpressionPlayer,
|
|
284
|
+
createDrivingStatusController,
|
|
285
|
+
readDrivingExpressionsConfig,
|
|
286
|
+
} from '@ziztechnology/dial-library';
|
|
287
|
+
|
|
288
|
+
const container = document.querySelector<HTMLElement>('#expression');
|
|
289
|
+
if (!container) throw new Error('找不到 #expression');
|
|
290
|
+
|
|
291
|
+
const config = readDrivingExpressionsConfig();
|
|
292
|
+
if (!config) throw new Error('Runtime 未提供有效的行车表情配置');
|
|
293
|
+
|
|
294
|
+
const player = createDrivingExpressionPlayer(container, { config });
|
|
295
|
+
const controller = createDrivingStatusController();
|
|
296
|
+
|
|
297
|
+
await player.show(controller.getSnapshot().status);
|
|
298
|
+
|
|
299
|
+
const unsubscribe = controller.subscribe((event) => {
|
|
300
|
+
void player.show(event.status);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
controller.start();
|
|
304
|
+
|
|
305
|
+
const destroy = () => {
|
|
306
|
+
unsubscribe();
|
|
307
|
+
controller.destroy();
|
|
308
|
+
player.destroy();
|
|
309
|
+
};
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
播放器同一时间只保留一个活动动态素材。它会复用视频元素,并在状态离开时停止播放;页面暂停或隐藏时,播放器与控制器都会自动暂停。
|
|
313
|
+
|
|
314
|
+
## 行车状态的回退
|
|
315
|
+
|
|
316
|
+
行车状态有两类彼此独立的回退:传感器不可用时的**状态回退**,以及表情素材失败时的**媒体回退**。
|
|
317
|
+
|
|
318
|
+
### 传感器不可用时回退到静止
|
|
319
|
+
|
|
320
|
+
控制器遇到一次读取失败、非法快照或过期样本时,会暂时保持最后一个已提交状态,避免页面闪烁。连续不可用达到 3 秒后,控制器才提交 `STOPPED`:
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
const controller = createDrivingStatusController({
|
|
324
|
+
unavailableFallbackMs: 3_000,
|
|
325
|
+
onDiagnostic(event) {
|
|
326
|
+
if (event.type === 'fallback_to_stopped') {
|
|
327
|
+
console.warn(`传感器已连续不可用 ${event.unavailableForMs}ms`);
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
controller.subscribe((event) => {
|
|
333
|
+
if (event.reason === 'unavailable_fallback') {
|
|
334
|
+
console.log('已回退到 STOPPED');
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
传感器恢复后,控制器会清除不可用状态并重新确认候选,不会立即把单个样本提交给 UI。`unavailableFallbackMs` 必须是大于 0 的有限数值。
|
|
340
|
+
|
|
341
|
+
### 素材失败时使用显式回退
|
|
342
|
+
|
|
343
|
+
SDK 提供 `OFFICIAL_DRIVING_EXPRESSION_FALLBACKS`,对应的默认 Emoji 为:
|
|
344
|
+
|
|
345
|
+
| 状态 | 回退内容 |
|
|
346
|
+
| -------------------- | -------- |
|
|
347
|
+
| `STOPPED` | 😐 |
|
|
348
|
+
| `STEADY_DRIVING` | 😊 |
|
|
349
|
+
| `ACCELERATION` | 😄 |
|
|
350
|
+
| `RAPID_ACCELERATION` | 😲 |
|
|
351
|
+
| `BRAKING` | 😣 |
|
|
352
|
+
| `SUDDEN_BRAKING` | 😱 |
|
|
353
|
+
| `LEFT_TURN` | 🤨 |
|
|
354
|
+
| `RIGHT_TURN` | 🤨 |
|
|
355
|
+
|
|
356
|
+
官方母版必须显式把它传给播放器:
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { OFFICIAL_DRIVING_EXPRESSION_FALLBACKS, createDrivingExpressionPlayer } from '@ziztechnology/dial-library';
|
|
360
|
+
|
|
361
|
+
const player = createDrivingExpressionPlayer(container, {
|
|
362
|
+
config,
|
|
363
|
+
fallbacks: OFFICIAL_DRIVING_EXPRESSION_FALLBACKS,
|
|
364
|
+
onDiagnostic(event) {
|
|
365
|
+
if (event.type === 'media_fallback_applied') {
|
|
366
|
+
console.warn(`${event.status} 已使用 ${event.kind} 回退素材`);
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
回退是**选择加入**的:第三方不传 `fallbacks`,SDK 就不会自动使用官方 Emoji。素材缺失、URL 非法、网络失败、超时或解码失败时,播放器会进入错误状态并发送 `media_load_failed` 诊断事件。如果你对你开发的表盘的样式一致性比较高,你可以编写自己的回退。
|
|
373
|
+
|
|
374
|
+
也可以只为部分状态提供自己的回退:
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
const player = createDrivingExpressionPlayer(container, {
|
|
378
|
+
config,
|
|
379
|
+
fallbacks: {
|
|
380
|
+
STOPPED: { kind: 'emoji', text: '😐' },
|
|
381
|
+
STEADY_DRIVING: { kind: 'emoji', text: '😊' },
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
播放器默认等待素材 5 秒,失败后按 15 秒退避重试。可以通过 `loadTimeoutMs` 和 `retryBackoffMs` 调整。Live Photo 的 motion 加载失败时也会应用该状态的显式回退,而不会一直停留在 cover。
|
|
387
|
+
|
|
388
|
+
### 配置本身无效时
|
|
389
|
+
|
|
390
|
+
`readDrivingExpressionsConfig()` 优先读取 Runtime 注入的 `__TOOOONY_DRIVING_EXPRESSIONS__`,并兼容旧的 `__TOOOONY_FACE_CONFIG__` 路径。配置不存在或无效时返回 `null`,不会替你补齐默认值:
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
const config = readDrivingExpressionsConfig();
|
|
394
|
+
|
|
395
|
+
if (!config) {
|
|
396
|
+
// 由表盘决定:显示静态占位、隐藏组件或终止初始化。
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
schema v1 要求 `states` 恰好包含全部八种状态。支持的单个状态素材为:
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
type StructuredDrivingExpressionMedia =
|
|
404
|
+
| { kind: 'emoji'; text: string }
|
|
405
|
+
| { kind: 'image'; mediaAssetId?: number; url: string; mimeType?: string }
|
|
406
|
+
| {
|
|
407
|
+
kind: 'video';
|
|
408
|
+
mediaAssetId?: number;
|
|
409
|
+
url: string;
|
|
410
|
+
coverUrl?: string;
|
|
411
|
+
mimeType?: string;
|
|
412
|
+
}
|
|
413
|
+
| {
|
|
414
|
+
kind: 'live_photo';
|
|
415
|
+
mediaAssetId?: number;
|
|
416
|
+
coverUrl: string;
|
|
417
|
+
motionUrl: string;
|
|
418
|
+
}
|
|
419
|
+
| { kind: 'tgs'; mediaAssetId?: number; url: string; coverUrl?: string };
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
媒体 URL 必须是长期有效的公网 HTTPS 地址。解析器会拒绝 `http:`、`data:`、`blob:`、localhost、内网或保留地址,以及带常见临时签名参数的 URL。服务端仍应根据 `mediaAssetId` 和用户身份重建可信 URL;前端校验不能替代服务端所有权和域名校验。
|
|
423
|
+
|
|
424
|
+
旧版表盘曾使用裸图片 URL。只有迁移旧配置时,才应显式开启兼容:
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
import { parseDrivingExpressionsConfig } from '@ziztechnology/dial-library';
|
|
428
|
+
|
|
429
|
+
const config = parseDrivingExpressionsConfig(rawConfig, {
|
|
430
|
+
allowLegacyImageUrls: true,
|
|
431
|
+
});
|
|
432
|
+
```
|