@condorinst/mobile-bridge 0.20.0 → 0.22.0
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/AGENTS.md +109 -42
- package/CHANGELOG.md +99 -0
- package/README.md +80 -14
- package/contract/bridge-contract.json +59 -14
- package/dist/cli.cjs +933 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +932 -3
- package/dist/cli.js.map +1 -1
- package/dist/dev-harness.global.js +2110 -0
- package/dist/dev.cjs +1662 -0
- package/dist/dev.cjs.map +1 -0
- package/dist/dev.d.cts +683 -0
- package/dist/dev.d.ts +683 -0
- package/dist/dev.js +1611 -0
- package/dist/dev.js.map +1 -0
- package/dist/index.cjs +96 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +236 -14
- package/dist/index.d.ts +236 -14
- package/dist/index.js +94 -3
- package/dist/index.js.map +1 -1
- package/docs/agent-guide.md +75 -24
- package/docs/bridge-contract.md +121 -20
- package/docs/client-guide.md +42 -29
- package/docs/device-testing.md +114 -0
- package/docs/pilot-onboarding.md +31 -13
- package/docs/troubleshooting.md +79 -18
- package/examples/minimal-home/AGENTS.md +9 -6
- package/examples/minimal-home/README.md +10 -5
- package/examples/minimal-home/build.mjs +2 -1
- package/examples/minimal-home/index.html +2 -0
- package/examples/minimal-home/package.json +3 -2
- package/examples/minimal-home/src/main.ts +151 -73
- package/llms-full.txt +2086 -0
- package/llms.txt +15 -13
- package/package.json +9 -3
package/dist/index.d.cts
CHANGED
|
@@ -225,23 +225,126 @@ interface HandshakeOptions {
|
|
|
225
225
|
interface PingPongPayload {
|
|
226
226
|
data: 'ping' | 'pong';
|
|
227
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* One linked device, as configured on the server. Mirrors
|
|
230
|
+
* `DeviceConfig.toMap()` in `condor_core`
|
|
231
|
+
* (`domain/entities/device_config_entity.dart`).
|
|
232
|
+
*
|
|
233
|
+
* `deviceId` is the server id and is unique. `serialNo` is printed on the
|
|
234
|
+
* device and **can repeat**: an ActLumus and an ActLumus Plus may share one.
|
|
235
|
+
*/
|
|
236
|
+
interface DeviceConfigItem {
|
|
237
|
+
/** Server id. Unique — the join key across the three arrays. */
|
|
238
|
+
deviceId: number;
|
|
239
|
+
/** Printed serial number. Not unique: an ActLumus and a Plus may share one. */
|
|
240
|
+
serialNo: number;
|
|
241
|
+
/** Where the device is worn. `null` when not set. */
|
|
242
|
+
deviceBodyPart: 'wrist' | 'necklace' | null;
|
|
243
|
+
/**
|
|
244
|
+
* Device model id. Absent when the server did not send one, which means an
|
|
245
|
+
* ActLumus (gen1); the Plus always carries it. Never a display name.
|
|
246
|
+
*/
|
|
247
|
+
modelId?: 'actlumus' | 'actlumus_plus' | (string & {});
|
|
248
|
+
/** BLE advertising period, in minutes, set by the Condor web system. */
|
|
249
|
+
bleConnectionPeriod: number;
|
|
250
|
+
autosync: boolean;
|
|
251
|
+
eraseDeviceMemory: boolean;
|
|
252
|
+
autoEraseDeviceMemory: boolean;
|
|
253
|
+
autoEraseDeviceMemoryThreshold: number;
|
|
254
|
+
readSensorsMemory: boolean;
|
|
255
|
+
startDevice: boolean;
|
|
256
|
+
readFullMemory: boolean;
|
|
257
|
+
allowFirmwareUpdate: boolean;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* One failed sync attempt. Mirrors `DeviceSyncError.toMap()`
|
|
261
|
+
* (`domain/entities/device_sync_error_entity.dart`).
|
|
262
|
+
*/
|
|
263
|
+
interface DeviceSyncErrorItem {
|
|
264
|
+
serialNo: number;
|
|
265
|
+
/** Epoch milliseconds. */
|
|
266
|
+
attemptDate: number;
|
|
267
|
+
/** Already localized by the app. Show it; do not branch on it. */
|
|
268
|
+
message: string;
|
|
269
|
+
/** Stable identifier to branch on. `null` for legacy errors. */
|
|
270
|
+
messageCode: string | null;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Last known state of one device, as read over BLE. Mirrors
|
|
274
|
+
* `DeviceTelemetry.toMap()` (`domain/entities/device_telemetry_entity.dart`).
|
|
275
|
+
*
|
|
276
|
+
* Every reading is `null` until the device has synced at least once.
|
|
277
|
+
*/
|
|
278
|
+
interface DeviceTelemetryItem {
|
|
279
|
+
serialNo: number;
|
|
280
|
+
/**
|
|
281
|
+
* Server id. **Omitted** on legacy data the app could not attribute, so join
|
|
282
|
+
* by `deviceId` when present and fall back to `serialNo` only when that
|
|
283
|
+
* `serialNo` is unique in `devicesConfig`.
|
|
284
|
+
*/
|
|
285
|
+
deviceId?: number;
|
|
286
|
+
/**
|
|
287
|
+
* Last successful sync, epoch milliseconds. The misspelling (`updata`) is the
|
|
288
|
+
* real field name — do not "fix" it.
|
|
289
|
+
*/
|
|
290
|
+
updataDateTime: number | null;
|
|
291
|
+
/** Display name reported by the device. Not in `devicesConfig`. */
|
|
292
|
+
deviceName: string | null;
|
|
293
|
+
/** Firmware version. */
|
|
294
|
+
deviceVersion: string | null;
|
|
295
|
+
hardwareVersion: string | null;
|
|
296
|
+
/** Device clock at the last sync, epoch milliseconds. */
|
|
297
|
+
deviceDateTime: number | null;
|
|
298
|
+
/** Data memory used, percent 0–100. */
|
|
299
|
+
memoryDataUsage: number | null;
|
|
300
|
+
/** System memory used, percent 0–100. */
|
|
301
|
+
memorySystemUsage: number | null;
|
|
302
|
+
/** Battery charge, percent 0–100. */
|
|
303
|
+
batteryLevel: number | null;
|
|
304
|
+
/** Battery voltage, volts. */
|
|
305
|
+
batteryVoltage: number | null;
|
|
306
|
+
isCharging: boolean | null;
|
|
307
|
+
isOnDock: boolean | null;
|
|
308
|
+
/** Sync failures of this device. Empty when there are none. */
|
|
309
|
+
syncErrors: DeviceSyncErrorItem[];
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Per-device toggles of the user. Mirrors `DeviceUserConfig.toMap()`
|
|
313
|
+
* (`domain/entities/device_user_config_entity.dart`).
|
|
314
|
+
*/
|
|
315
|
+
interface DeviceUserConfigItem {
|
|
316
|
+
serialNo: number;
|
|
317
|
+
/** Server id. Omitted on legacy data — see `DeviceTelemetryItem.deviceId`. */
|
|
318
|
+
deviceId?: number;
|
|
319
|
+
syncTime: boolean;
|
|
320
|
+
realTime: boolean;
|
|
321
|
+
deleteMemory: boolean;
|
|
322
|
+
readAllMemory: boolean;
|
|
323
|
+
readAllSystemLog: boolean;
|
|
324
|
+
}
|
|
228
325
|
/**
|
|
229
326
|
* Snapshot of the user's device store. Mirrors `UserDevices.toMap()` in
|
|
230
327
|
* `condor_core`.
|
|
231
328
|
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
329
|
+
* **Joining the three arrays.** `devicesConfig` lists the devices; the other
|
|
330
|
+
* two describe them. Join on `deviceId`. When an item of `devicesTelemetry` or
|
|
331
|
+
* `devicesUserConfig` has no `deviceId` (legacy data), fall back to `serialNo`
|
|
332
|
+
* — but only when that `serialNo` appears once in `devicesConfig`. A device may
|
|
333
|
+
* have no telemetry yet: render it with empty readings, never drop it.
|
|
334
|
+
*
|
|
335
|
+
* **It is not guaranteed to answer.** Use `BridgeClient.requestUserDevices()`
|
|
336
|
+
* instead of `send('userDevice')`; see `docs/troubleshooting.md`.
|
|
235
337
|
*
|
|
236
338
|
* The app enforces no device cap. A commercial rule currently limits a user to
|
|
237
339
|
* two linked devices, but that can change with no signal in this contract — read
|
|
238
340
|
* the length of the array rather than hard-coding it.
|
|
239
341
|
*/
|
|
240
342
|
interface UserDevicePayload {
|
|
343
|
+
/** Epoch milliseconds of the snapshot. */
|
|
241
344
|
lastUpdate: number;
|
|
242
|
-
devicesConfig:
|
|
243
|
-
devicesTelemetry:
|
|
244
|
-
devicesUserConfig:
|
|
345
|
+
devicesConfig: DeviceConfigItem[];
|
|
346
|
+
devicesTelemetry: DeviceTelemetryItem[];
|
|
347
|
+
devicesUserConfig: DeviceUserConfigItem[];
|
|
245
348
|
}
|
|
246
349
|
/**
|
|
247
350
|
* Raw light-sensor channels, under the names the backend uses in
|
|
@@ -444,6 +547,63 @@ interface SensorLogQueryPayload {
|
|
|
444
547
|
/** Exclusive, `YYYY-MM-DD HH:MM:SS`, in device-local time. */
|
|
445
548
|
endTime?: string;
|
|
446
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* One sensor log, passed through from the backend `get_log` contract.
|
|
552
|
+
*
|
|
553
|
+
* **Cadence.** The device writes one log per period, which is programmable per
|
|
554
|
+
* device (default 60 s). Never assume one per minute: size pagination from the
|
|
555
|
+
* gaps between `log_time` values you actually receive.
|
|
556
|
+
*
|
|
557
|
+
* `null` on a channel means the hardware does not have it, never zero.
|
|
558
|
+
*/
|
|
559
|
+
interface SensorLogItem {
|
|
560
|
+
/** Stable id of the log. */
|
|
561
|
+
id: number;
|
|
562
|
+
/** `'YYYY-MM-DD HH:MM:SS'` in **device-local time**, not UTC. */
|
|
563
|
+
log_time: string;
|
|
564
|
+
log_type: number;
|
|
565
|
+
body_part: 'wrist' | 'necklace' | null;
|
|
566
|
+
/** Degrees Celsius, already converted by the backend. */
|
|
567
|
+
temperature: number | null;
|
|
568
|
+
/** Photopic illuminance, lux. */
|
|
569
|
+
lux_photopic: number | null;
|
|
570
|
+
/** Melanopic illuminance (melanopic EDI), lux. */
|
|
571
|
+
lux_melanopic: number | null;
|
|
572
|
+
company_id?: number;
|
|
573
|
+
user_id?: number;
|
|
574
|
+
device_id?: number;
|
|
575
|
+
orientation?: number | null;
|
|
576
|
+
light_sensor_f1?: number | null;
|
|
577
|
+
light_sensor_f2?: number | null;
|
|
578
|
+
light_sensor_f3?: number | null;
|
|
579
|
+
light_sensor_f4?: number | null;
|
|
580
|
+
light_sensor_f5?: number | null;
|
|
581
|
+
light_sensor_f6?: number | null;
|
|
582
|
+
light_sensor_f7?: number | null;
|
|
583
|
+
light_sensor_f8?: number | null;
|
|
584
|
+
light_sensor_clear?: number | null;
|
|
585
|
+
light_sensor_nir?: number | null;
|
|
586
|
+
/** ActLumus Plus only (`null` on gen1). Approximates CIE X; not an exact CIE value. */
|
|
587
|
+
light_sensor_x?: number | null;
|
|
588
|
+
/** ActLumus Plus only (`null` on gen1). */
|
|
589
|
+
light_sensor_y?: number | null;
|
|
590
|
+
/** ActLumus Plus only (`null` on gen1). */
|
|
591
|
+
light_sensor_z?: number | null;
|
|
592
|
+
/** ActLumus Plus only (`null` on gen1). Reserved by the firmware; do not interpret. */
|
|
593
|
+
light_sensor_fd?: number | null;
|
|
594
|
+
act_data_pim?: number | null;
|
|
595
|
+
act_data_tat?: number | null;
|
|
596
|
+
act_data_zcm?: number | null;
|
|
597
|
+
off_wrist?: number | null;
|
|
598
|
+
cap_sensor_1?: number | null;
|
|
599
|
+
cap_sensor_2?: number | null;
|
|
600
|
+
event?: number | null;
|
|
601
|
+
state?: number | null;
|
|
602
|
+
from_service?: boolean;
|
|
603
|
+
/** Batch markers of the upload, not per-log timestamps. Do not plot them. */
|
|
604
|
+
device_log_time?: string | null;
|
|
605
|
+
app_log_time?: string | null;
|
|
606
|
+
}
|
|
447
607
|
/**
|
|
448
608
|
* Reply to the `sensorLog` command.
|
|
449
609
|
*
|
|
@@ -464,7 +624,8 @@ interface SensorLogQueryPayload {
|
|
|
464
624
|
interface SensorLogPayload {
|
|
465
625
|
requestId: string;
|
|
466
626
|
success: boolean;
|
|
467
|
-
|
|
627
|
+
/** Chronological ascending. Without a cursor or range: the most recent page. */
|
|
628
|
+
logs?: SensorLogItem[];
|
|
468
629
|
startingAfter?: string | null;
|
|
469
630
|
endingBefore?: string | null;
|
|
470
631
|
hasMore?: boolean;
|
|
@@ -735,7 +896,9 @@ declare const Bridge: CondorBridge;
|
|
|
735
896
|
* deletes them before your bundle runs.
|
|
736
897
|
*
|
|
737
898
|
* It deliberately does **not** expose `installDevPanel`, `mockEmit`, `isMock`
|
|
738
|
-
* or `getMockState`: those belong to the shell (`BridgeShell`).
|
|
899
|
+
* or `getMockState`: those belong to the shell (`BridgeShell`). To run a bundle
|
|
900
|
+
* in a browser, use the dev harness (`npx condor-mobile-bridge dev`, or
|
|
901
|
+
* `createDevHarness` from `@condorinst/mobile-bridge/dev`).
|
|
739
902
|
*
|
|
740
903
|
* The module also answers the shell's `verify-sandbox` probe — the iframe
|
|
741
904
|
* attesting whether it can still see the native globals. If any is reachable,
|
|
@@ -747,14 +910,14 @@ interface BridgeClientHandshakeAck {
|
|
|
747
910
|
shellVersion: string;
|
|
748
911
|
/**
|
|
749
912
|
* Schema version of the **native** side, relayed by the shell. `null` when
|
|
750
|
-
* the native side has not answered,
|
|
751
|
-
* the shell is older than 0.6.0.
|
|
913
|
+
* the native side has not answered, or the shell is older than 0.6.0.
|
|
752
914
|
*
|
|
753
915
|
* **This number is the only capability signal a client gets.** There is no
|
|
754
916
|
* version negotiation, and a sandboxed client reaches the native side by no
|
|
755
917
|
* other path. For example, the raw light channels (`light_sensor_*` in
|
|
756
918
|
* `deviceLightSensors`) exist from `0.16.0`. Always keep a working branch
|
|
757
|
-
* for `null
|
|
919
|
+
* for `null`. The dev harness reports `0.17.0` unless a scenario says
|
|
920
|
+
* otherwise.
|
|
758
921
|
*/
|
|
759
922
|
nativeVersion: string | null;
|
|
760
923
|
/**
|
|
@@ -815,13 +978,62 @@ interface CondorBridgeClient {
|
|
|
815
978
|
* native enum is dropped in silence.
|
|
816
979
|
*/
|
|
817
980
|
requestPermission(permission: string): void;
|
|
981
|
+
/**
|
|
982
|
+
* Resolves when the app's **background service** answers a `ping`.
|
|
983
|
+
*
|
|
984
|
+
* Commands that need device data (`userDevice`, `ping`) travel to a service
|
|
985
|
+
* the app starts after your bundle is already running. A command sent before
|
|
986
|
+
* the service is up is **lost without a reply** — the handshake, `systemStatus`
|
|
987
|
+
* and `language` work earlier, which makes the bridge look ready when it is
|
|
988
|
+
* not. This helper resends `ping` with backoff (1.5 s, 3 s, 6 s, then every
|
|
989
|
+
* 10 s) until a `pingPong` arrives.
|
|
990
|
+
*
|
|
991
|
+
* Never rejects: resolves `{ status: 'timeout' }` after `timeoutMs`
|
|
992
|
+
* (default 30 s).
|
|
993
|
+
*/
|
|
994
|
+
waitForService(opts?: {
|
|
995
|
+
timeoutMs?: number;
|
|
996
|
+
}): Promise<{
|
|
997
|
+
status: 'ready' | 'timeout';
|
|
998
|
+
}>;
|
|
999
|
+
/**
|
|
1000
|
+
* Requests the user's devices and **keeps asking** until they arrive. Use
|
|
1001
|
+
* this instead of `send('userDevice')`, which may never be answered:
|
|
1002
|
+
*
|
|
1003
|
+
* - on a cold start the first request is lost while the background service
|
|
1004
|
+
* comes up;
|
|
1005
|
+
* - an account with no linked device gets no reply at all.
|
|
1006
|
+
*
|
|
1007
|
+
* Resends with backoff (1.5 s, 3 s, 6 s, then every 10 s) and on every
|
|
1008
|
+
* `systemStatus` while pending. Never rejects. Resolves with
|
|
1009
|
+
* `{ status: 'ok', devices }`, or `{ status: 'timeout', hasDevices }` after
|
|
1010
|
+
* `timeoutMs` (default 30 s), where `hasDevices` is the last
|
|
1011
|
+
* `systemStatus.hasDevices` seen (`null` if none). `false` there **suggests**
|
|
1012
|
+
* an account with no device — show an empty state with a retry button — but
|
|
1013
|
+
* the app also reports `false` when it could not reach the server.
|
|
1014
|
+
*
|
|
1015
|
+
* `getFromServer: true` asks the app to refresh from the server; leave it
|
|
1016
|
+
* `false` (default) at boot, where the cached list is enough.
|
|
1017
|
+
*/
|
|
1018
|
+
requestUserDevices(opts?: {
|
|
1019
|
+
timeoutMs?: number;
|
|
1020
|
+
getFromServer?: boolean;
|
|
1021
|
+
}): Promise<{
|
|
1022
|
+
status: 'ok';
|
|
1023
|
+
devices: UserDevicePayload;
|
|
1024
|
+
} | {
|
|
1025
|
+
status: 'timeout';
|
|
1026
|
+
hasDevices: boolean | null;
|
|
1027
|
+
}>;
|
|
818
1028
|
/**
|
|
819
1029
|
* Handshakes with the shell. Resolves with
|
|
820
1030
|
* `{ shellVersion, nativeVersion, accepted }` — read `nativeVersion` to learn
|
|
821
1031
|
* what this device can do, and ignore `accepted`, which is always `true`.
|
|
822
1032
|
*
|
|
823
|
-
* Rejects after 5 s if the shell does not answer
|
|
824
|
-
*
|
|
1033
|
+
* Rejects after 5 s if the shell does not answer, and **at once** when the
|
|
1034
|
+
* page is not inside the Condor shell (opened directly in a browser) — run
|
|
1035
|
+
* `npx condor-mobile-bridge dev` instead. Only one handshake may be in flight
|
|
1036
|
+
* at a time.
|
|
825
1037
|
*/
|
|
826
1038
|
handshake(opts: {
|
|
827
1039
|
clientVersion: string;
|
|
@@ -1096,4 +1308,14 @@ declare class BridgeShell {
|
|
|
1096
1308
|
private postToClient;
|
|
1097
1309
|
}
|
|
1098
1310
|
|
|
1099
|
-
|
|
1311
|
+
/**
|
|
1312
|
+
* `versionAtLeast(ack.nativeVersion, '0.17.0')` — the capability check every
|
|
1313
|
+
* client needs. Compares the three numeric segments; `null` (an unknown native
|
|
1314
|
+
* side) is never at least anything, so it always takes the baseline branch.
|
|
1315
|
+
*
|
|
1316
|
+
* Do not compare version strings directly: `'0.9.0' > '0.16.0'` is `true` as
|
|
1317
|
+
* strings and false as versions.
|
|
1318
|
+
*/
|
|
1319
|
+
declare function versionAtLeast(version: string | null | undefined, min: string): boolean;
|
|
1320
|
+
|
|
1321
|
+
export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, type ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConfigItem, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type DeviceSyncErrorItem, type DeviceTelemetryItem, type DeviceUserConfigItem, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, NATIVE_CHANNEL_NAME, type NavigationBackPayload, type NavigationStatePayload, type OutgoingStamp, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogItem, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, type ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload, versionAtLeast };
|
package/dist/index.d.ts
CHANGED
|
@@ -225,23 +225,126 @@ interface HandshakeOptions {
|
|
|
225
225
|
interface PingPongPayload {
|
|
226
226
|
data: 'ping' | 'pong';
|
|
227
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* One linked device, as configured on the server. Mirrors
|
|
230
|
+
* `DeviceConfig.toMap()` in `condor_core`
|
|
231
|
+
* (`domain/entities/device_config_entity.dart`).
|
|
232
|
+
*
|
|
233
|
+
* `deviceId` is the server id and is unique. `serialNo` is printed on the
|
|
234
|
+
* device and **can repeat**: an ActLumus and an ActLumus Plus may share one.
|
|
235
|
+
*/
|
|
236
|
+
interface DeviceConfigItem {
|
|
237
|
+
/** Server id. Unique — the join key across the three arrays. */
|
|
238
|
+
deviceId: number;
|
|
239
|
+
/** Printed serial number. Not unique: an ActLumus and a Plus may share one. */
|
|
240
|
+
serialNo: number;
|
|
241
|
+
/** Where the device is worn. `null` when not set. */
|
|
242
|
+
deviceBodyPart: 'wrist' | 'necklace' | null;
|
|
243
|
+
/**
|
|
244
|
+
* Device model id. Absent when the server did not send one, which means an
|
|
245
|
+
* ActLumus (gen1); the Plus always carries it. Never a display name.
|
|
246
|
+
*/
|
|
247
|
+
modelId?: 'actlumus' | 'actlumus_plus' | (string & {});
|
|
248
|
+
/** BLE advertising period, in minutes, set by the Condor web system. */
|
|
249
|
+
bleConnectionPeriod: number;
|
|
250
|
+
autosync: boolean;
|
|
251
|
+
eraseDeviceMemory: boolean;
|
|
252
|
+
autoEraseDeviceMemory: boolean;
|
|
253
|
+
autoEraseDeviceMemoryThreshold: number;
|
|
254
|
+
readSensorsMemory: boolean;
|
|
255
|
+
startDevice: boolean;
|
|
256
|
+
readFullMemory: boolean;
|
|
257
|
+
allowFirmwareUpdate: boolean;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* One failed sync attempt. Mirrors `DeviceSyncError.toMap()`
|
|
261
|
+
* (`domain/entities/device_sync_error_entity.dart`).
|
|
262
|
+
*/
|
|
263
|
+
interface DeviceSyncErrorItem {
|
|
264
|
+
serialNo: number;
|
|
265
|
+
/** Epoch milliseconds. */
|
|
266
|
+
attemptDate: number;
|
|
267
|
+
/** Already localized by the app. Show it; do not branch on it. */
|
|
268
|
+
message: string;
|
|
269
|
+
/** Stable identifier to branch on. `null` for legacy errors. */
|
|
270
|
+
messageCode: string | null;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Last known state of one device, as read over BLE. Mirrors
|
|
274
|
+
* `DeviceTelemetry.toMap()` (`domain/entities/device_telemetry_entity.dart`).
|
|
275
|
+
*
|
|
276
|
+
* Every reading is `null` until the device has synced at least once.
|
|
277
|
+
*/
|
|
278
|
+
interface DeviceTelemetryItem {
|
|
279
|
+
serialNo: number;
|
|
280
|
+
/**
|
|
281
|
+
* Server id. **Omitted** on legacy data the app could not attribute, so join
|
|
282
|
+
* by `deviceId` when present and fall back to `serialNo` only when that
|
|
283
|
+
* `serialNo` is unique in `devicesConfig`.
|
|
284
|
+
*/
|
|
285
|
+
deviceId?: number;
|
|
286
|
+
/**
|
|
287
|
+
* Last successful sync, epoch milliseconds. The misspelling (`updata`) is the
|
|
288
|
+
* real field name — do not "fix" it.
|
|
289
|
+
*/
|
|
290
|
+
updataDateTime: number | null;
|
|
291
|
+
/** Display name reported by the device. Not in `devicesConfig`. */
|
|
292
|
+
deviceName: string | null;
|
|
293
|
+
/** Firmware version. */
|
|
294
|
+
deviceVersion: string | null;
|
|
295
|
+
hardwareVersion: string | null;
|
|
296
|
+
/** Device clock at the last sync, epoch milliseconds. */
|
|
297
|
+
deviceDateTime: number | null;
|
|
298
|
+
/** Data memory used, percent 0–100. */
|
|
299
|
+
memoryDataUsage: number | null;
|
|
300
|
+
/** System memory used, percent 0–100. */
|
|
301
|
+
memorySystemUsage: number | null;
|
|
302
|
+
/** Battery charge, percent 0–100. */
|
|
303
|
+
batteryLevel: number | null;
|
|
304
|
+
/** Battery voltage, volts. */
|
|
305
|
+
batteryVoltage: number | null;
|
|
306
|
+
isCharging: boolean | null;
|
|
307
|
+
isOnDock: boolean | null;
|
|
308
|
+
/** Sync failures of this device. Empty when there are none. */
|
|
309
|
+
syncErrors: DeviceSyncErrorItem[];
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Per-device toggles of the user. Mirrors `DeviceUserConfig.toMap()`
|
|
313
|
+
* (`domain/entities/device_user_config_entity.dart`).
|
|
314
|
+
*/
|
|
315
|
+
interface DeviceUserConfigItem {
|
|
316
|
+
serialNo: number;
|
|
317
|
+
/** Server id. Omitted on legacy data — see `DeviceTelemetryItem.deviceId`. */
|
|
318
|
+
deviceId?: number;
|
|
319
|
+
syncTime: boolean;
|
|
320
|
+
realTime: boolean;
|
|
321
|
+
deleteMemory: boolean;
|
|
322
|
+
readAllMemory: boolean;
|
|
323
|
+
readAllSystemLog: boolean;
|
|
324
|
+
}
|
|
228
325
|
/**
|
|
229
326
|
* Snapshot of the user's device store. Mirrors `UserDevices.toMap()` in
|
|
230
327
|
* `condor_core`.
|
|
231
328
|
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
329
|
+
* **Joining the three arrays.** `devicesConfig` lists the devices; the other
|
|
330
|
+
* two describe them. Join on `deviceId`. When an item of `devicesTelemetry` or
|
|
331
|
+
* `devicesUserConfig` has no `deviceId` (legacy data), fall back to `serialNo`
|
|
332
|
+
* — but only when that `serialNo` appears once in `devicesConfig`. A device may
|
|
333
|
+
* have no telemetry yet: render it with empty readings, never drop it.
|
|
334
|
+
*
|
|
335
|
+
* **It is not guaranteed to answer.** Use `BridgeClient.requestUserDevices()`
|
|
336
|
+
* instead of `send('userDevice')`; see `docs/troubleshooting.md`.
|
|
235
337
|
*
|
|
236
338
|
* The app enforces no device cap. A commercial rule currently limits a user to
|
|
237
339
|
* two linked devices, but that can change with no signal in this contract — read
|
|
238
340
|
* the length of the array rather than hard-coding it.
|
|
239
341
|
*/
|
|
240
342
|
interface UserDevicePayload {
|
|
343
|
+
/** Epoch milliseconds of the snapshot. */
|
|
241
344
|
lastUpdate: number;
|
|
242
|
-
devicesConfig:
|
|
243
|
-
devicesTelemetry:
|
|
244
|
-
devicesUserConfig:
|
|
345
|
+
devicesConfig: DeviceConfigItem[];
|
|
346
|
+
devicesTelemetry: DeviceTelemetryItem[];
|
|
347
|
+
devicesUserConfig: DeviceUserConfigItem[];
|
|
245
348
|
}
|
|
246
349
|
/**
|
|
247
350
|
* Raw light-sensor channels, under the names the backend uses in
|
|
@@ -444,6 +547,63 @@ interface SensorLogQueryPayload {
|
|
|
444
547
|
/** Exclusive, `YYYY-MM-DD HH:MM:SS`, in device-local time. */
|
|
445
548
|
endTime?: string;
|
|
446
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* One sensor log, passed through from the backend `get_log` contract.
|
|
552
|
+
*
|
|
553
|
+
* **Cadence.** The device writes one log per period, which is programmable per
|
|
554
|
+
* device (default 60 s). Never assume one per minute: size pagination from the
|
|
555
|
+
* gaps between `log_time` values you actually receive.
|
|
556
|
+
*
|
|
557
|
+
* `null` on a channel means the hardware does not have it, never zero.
|
|
558
|
+
*/
|
|
559
|
+
interface SensorLogItem {
|
|
560
|
+
/** Stable id of the log. */
|
|
561
|
+
id: number;
|
|
562
|
+
/** `'YYYY-MM-DD HH:MM:SS'` in **device-local time**, not UTC. */
|
|
563
|
+
log_time: string;
|
|
564
|
+
log_type: number;
|
|
565
|
+
body_part: 'wrist' | 'necklace' | null;
|
|
566
|
+
/** Degrees Celsius, already converted by the backend. */
|
|
567
|
+
temperature: number | null;
|
|
568
|
+
/** Photopic illuminance, lux. */
|
|
569
|
+
lux_photopic: number | null;
|
|
570
|
+
/** Melanopic illuminance (melanopic EDI), lux. */
|
|
571
|
+
lux_melanopic: number | null;
|
|
572
|
+
company_id?: number;
|
|
573
|
+
user_id?: number;
|
|
574
|
+
device_id?: number;
|
|
575
|
+
orientation?: number | null;
|
|
576
|
+
light_sensor_f1?: number | null;
|
|
577
|
+
light_sensor_f2?: number | null;
|
|
578
|
+
light_sensor_f3?: number | null;
|
|
579
|
+
light_sensor_f4?: number | null;
|
|
580
|
+
light_sensor_f5?: number | null;
|
|
581
|
+
light_sensor_f6?: number | null;
|
|
582
|
+
light_sensor_f7?: number | null;
|
|
583
|
+
light_sensor_f8?: number | null;
|
|
584
|
+
light_sensor_clear?: number | null;
|
|
585
|
+
light_sensor_nir?: number | null;
|
|
586
|
+
/** ActLumus Plus only (`null` on gen1). Approximates CIE X; not an exact CIE value. */
|
|
587
|
+
light_sensor_x?: number | null;
|
|
588
|
+
/** ActLumus Plus only (`null` on gen1). */
|
|
589
|
+
light_sensor_y?: number | null;
|
|
590
|
+
/** ActLumus Plus only (`null` on gen1). */
|
|
591
|
+
light_sensor_z?: number | null;
|
|
592
|
+
/** ActLumus Plus only (`null` on gen1). Reserved by the firmware; do not interpret. */
|
|
593
|
+
light_sensor_fd?: number | null;
|
|
594
|
+
act_data_pim?: number | null;
|
|
595
|
+
act_data_tat?: number | null;
|
|
596
|
+
act_data_zcm?: number | null;
|
|
597
|
+
off_wrist?: number | null;
|
|
598
|
+
cap_sensor_1?: number | null;
|
|
599
|
+
cap_sensor_2?: number | null;
|
|
600
|
+
event?: number | null;
|
|
601
|
+
state?: number | null;
|
|
602
|
+
from_service?: boolean;
|
|
603
|
+
/** Batch markers of the upload, not per-log timestamps. Do not plot them. */
|
|
604
|
+
device_log_time?: string | null;
|
|
605
|
+
app_log_time?: string | null;
|
|
606
|
+
}
|
|
447
607
|
/**
|
|
448
608
|
* Reply to the `sensorLog` command.
|
|
449
609
|
*
|
|
@@ -464,7 +624,8 @@ interface SensorLogQueryPayload {
|
|
|
464
624
|
interface SensorLogPayload {
|
|
465
625
|
requestId: string;
|
|
466
626
|
success: boolean;
|
|
467
|
-
|
|
627
|
+
/** Chronological ascending. Without a cursor or range: the most recent page. */
|
|
628
|
+
logs?: SensorLogItem[];
|
|
468
629
|
startingAfter?: string | null;
|
|
469
630
|
endingBefore?: string | null;
|
|
470
631
|
hasMore?: boolean;
|
|
@@ -735,7 +896,9 @@ declare const Bridge: CondorBridge;
|
|
|
735
896
|
* deletes them before your bundle runs.
|
|
736
897
|
*
|
|
737
898
|
* It deliberately does **not** expose `installDevPanel`, `mockEmit`, `isMock`
|
|
738
|
-
* or `getMockState`: those belong to the shell (`BridgeShell`).
|
|
899
|
+
* or `getMockState`: those belong to the shell (`BridgeShell`). To run a bundle
|
|
900
|
+
* in a browser, use the dev harness (`npx condor-mobile-bridge dev`, or
|
|
901
|
+
* `createDevHarness` from `@condorinst/mobile-bridge/dev`).
|
|
739
902
|
*
|
|
740
903
|
* The module also answers the shell's `verify-sandbox` probe — the iframe
|
|
741
904
|
* attesting whether it can still see the native globals. If any is reachable,
|
|
@@ -747,14 +910,14 @@ interface BridgeClientHandshakeAck {
|
|
|
747
910
|
shellVersion: string;
|
|
748
911
|
/**
|
|
749
912
|
* Schema version of the **native** side, relayed by the shell. `null` when
|
|
750
|
-
* the native side has not answered,
|
|
751
|
-
* the shell is older than 0.6.0.
|
|
913
|
+
* the native side has not answered, or the shell is older than 0.6.0.
|
|
752
914
|
*
|
|
753
915
|
* **This number is the only capability signal a client gets.** There is no
|
|
754
916
|
* version negotiation, and a sandboxed client reaches the native side by no
|
|
755
917
|
* other path. For example, the raw light channels (`light_sensor_*` in
|
|
756
918
|
* `deviceLightSensors`) exist from `0.16.0`. Always keep a working branch
|
|
757
|
-
* for `null
|
|
919
|
+
* for `null`. The dev harness reports `0.17.0` unless a scenario says
|
|
920
|
+
* otherwise.
|
|
758
921
|
*/
|
|
759
922
|
nativeVersion: string | null;
|
|
760
923
|
/**
|
|
@@ -815,13 +978,62 @@ interface CondorBridgeClient {
|
|
|
815
978
|
* native enum is dropped in silence.
|
|
816
979
|
*/
|
|
817
980
|
requestPermission(permission: string): void;
|
|
981
|
+
/**
|
|
982
|
+
* Resolves when the app's **background service** answers a `ping`.
|
|
983
|
+
*
|
|
984
|
+
* Commands that need device data (`userDevice`, `ping`) travel to a service
|
|
985
|
+
* the app starts after your bundle is already running. A command sent before
|
|
986
|
+
* the service is up is **lost without a reply** — the handshake, `systemStatus`
|
|
987
|
+
* and `language` work earlier, which makes the bridge look ready when it is
|
|
988
|
+
* not. This helper resends `ping` with backoff (1.5 s, 3 s, 6 s, then every
|
|
989
|
+
* 10 s) until a `pingPong` arrives.
|
|
990
|
+
*
|
|
991
|
+
* Never rejects: resolves `{ status: 'timeout' }` after `timeoutMs`
|
|
992
|
+
* (default 30 s).
|
|
993
|
+
*/
|
|
994
|
+
waitForService(opts?: {
|
|
995
|
+
timeoutMs?: number;
|
|
996
|
+
}): Promise<{
|
|
997
|
+
status: 'ready' | 'timeout';
|
|
998
|
+
}>;
|
|
999
|
+
/**
|
|
1000
|
+
* Requests the user's devices and **keeps asking** until they arrive. Use
|
|
1001
|
+
* this instead of `send('userDevice')`, which may never be answered:
|
|
1002
|
+
*
|
|
1003
|
+
* - on a cold start the first request is lost while the background service
|
|
1004
|
+
* comes up;
|
|
1005
|
+
* - an account with no linked device gets no reply at all.
|
|
1006
|
+
*
|
|
1007
|
+
* Resends with backoff (1.5 s, 3 s, 6 s, then every 10 s) and on every
|
|
1008
|
+
* `systemStatus` while pending. Never rejects. Resolves with
|
|
1009
|
+
* `{ status: 'ok', devices }`, or `{ status: 'timeout', hasDevices }` after
|
|
1010
|
+
* `timeoutMs` (default 30 s), where `hasDevices` is the last
|
|
1011
|
+
* `systemStatus.hasDevices` seen (`null` if none). `false` there **suggests**
|
|
1012
|
+
* an account with no device — show an empty state with a retry button — but
|
|
1013
|
+
* the app also reports `false` when it could not reach the server.
|
|
1014
|
+
*
|
|
1015
|
+
* `getFromServer: true` asks the app to refresh from the server; leave it
|
|
1016
|
+
* `false` (default) at boot, where the cached list is enough.
|
|
1017
|
+
*/
|
|
1018
|
+
requestUserDevices(opts?: {
|
|
1019
|
+
timeoutMs?: number;
|
|
1020
|
+
getFromServer?: boolean;
|
|
1021
|
+
}): Promise<{
|
|
1022
|
+
status: 'ok';
|
|
1023
|
+
devices: UserDevicePayload;
|
|
1024
|
+
} | {
|
|
1025
|
+
status: 'timeout';
|
|
1026
|
+
hasDevices: boolean | null;
|
|
1027
|
+
}>;
|
|
818
1028
|
/**
|
|
819
1029
|
* Handshakes with the shell. Resolves with
|
|
820
1030
|
* `{ shellVersion, nativeVersion, accepted }` — read `nativeVersion` to learn
|
|
821
1031
|
* what this device can do, and ignore `accepted`, which is always `true`.
|
|
822
1032
|
*
|
|
823
|
-
* Rejects after 5 s if the shell does not answer
|
|
824
|
-
*
|
|
1033
|
+
* Rejects after 5 s if the shell does not answer, and **at once** when the
|
|
1034
|
+
* page is not inside the Condor shell (opened directly in a browser) — run
|
|
1035
|
+
* `npx condor-mobile-bridge dev` instead. Only one handshake may be in flight
|
|
1036
|
+
* at a time.
|
|
825
1037
|
*/
|
|
826
1038
|
handshake(opts: {
|
|
827
1039
|
clientVersion: string;
|
|
@@ -1096,4 +1308,14 @@ declare class BridgeShell {
|
|
|
1096
1308
|
private postToClient;
|
|
1097
1309
|
}
|
|
1098
1310
|
|
|
1099
|
-
|
|
1311
|
+
/**
|
|
1312
|
+
* `versionAtLeast(ack.nativeVersion, '0.17.0')` — the capability check every
|
|
1313
|
+
* client needs. Compares the three numeric segments; `null` (an unknown native
|
|
1314
|
+
* side) is never at least anything, so it always takes the baseline branch.
|
|
1315
|
+
*
|
|
1316
|
+
* Do not compare version strings directly: `'0.9.0' > '0.16.0'` is `true` as
|
|
1317
|
+
* strings and false as versions.
|
|
1318
|
+
*/
|
|
1319
|
+
declare function versionAtLeast(version: string | null | undefined, min: string): boolean;
|
|
1320
|
+
|
|
1321
|
+
export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, type ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConfigItem, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type DeviceSyncErrorItem, type DeviceTelemetryItem, type DeviceUserConfigItem, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, NATIVE_CHANNEL_NAME, type NavigationBackPayload, type NavigationStatePayload, type OutgoingStamp, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogItem, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, type ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload, versionAtLeast };
|