@camstack/addon-pipeline 1.2.630 → 1.2.632
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/CHANGELOG.md +16 -1
- package/README.md +39 -20
- package/dist/audio-analyzer/index.js +3 -3
- package/dist/audio-analyzer/index.mjs +3 -3
- package/dist/{custom-model-catalog-qwNT0qE-.mjs → custom-model-catalog-B2w26VBG.mjs} +2 -2
- package/dist/{custom-model-catalog-CsHQljey.js → custom-model-catalog-DN4xBVQE.js} +2 -2
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-M09KjuSx.js → dist-CLo3QHtG.js} +1574 -934
- package/dist/{dist-C56NTN3j.mjs → dist-DWkdwtle.mjs} +1574 -934
- package/dist/{model-catalogs-BmZHxpaD.js → model-catalogs-DIviCtiQ.js} +1 -1
- package/dist/{model-catalogs-vPj_kgtX.mjs → model-catalogs-rs7okKyp.mjs} +1 -1
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-CdsOgsyN.mjs → node-CAJ1Dal-.mjs} +1 -1
- package/dist/{node-C0CzsJ0N.js → node-CPqDCFas.js} +1 -1
- package/dist/pipeline-runner/index.js +3 -3
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-BURe-pwd.js → process-memory-BrKzYxim.js} +1 -1
- package/dist/{process-memory-DAhr_IbO.mjs → process-memory-DJWthb6k.mjs} +1 -1
- package/dist/recorder/index.js +3 -2
- package/dist/recorder/index.mjs +3 -2
- package/dist/{segment-demux-js-B391lmQZ.js → segment-demux-js-Bt5bxfyh.js} +1 -1
- package/dist/{segment-demux-js-CobIINvd.mjs → segment-demux-js-CZdgio0n.mjs} +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Bxan5418.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CjjdxZUq.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DZmxw-we.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CFflpMdb.mjs +26 -0
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-CmFod5rw.mjs → hostInit-MWvZi8Nq.mjs} +3 -3
- package/dist/stream-broker/index.js +3 -3
- package/dist/stream-broker/index.mjs +3 -3
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-inxALjZf.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-wSzIkKI-.mjs +0 -26
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
//#region ../types/dist/event-category-
|
|
2
|
+
//#region ../types/dist/event-category-DmZDPDf_.mjs
|
|
3
3
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4
4
|
EventCategory["SystemBoot"] = "system.boot";
|
|
5
5
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -626,7 +626,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
626
626
|
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
627
627
|
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
628
628
|
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
629
|
-
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
629
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
|
|
630
630
|
*/
|
|
631
631
|
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
632
632
|
/**
|
|
@@ -764,6 +764,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
764
764
|
*/
|
|
765
765
|
EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
|
|
766
766
|
/**
|
|
767
|
+
* An action run started or ended. Emitted by the `actions` surface.
|
|
768
|
+
* Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
|
|
769
|
+
*/
|
|
770
|
+
EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
|
|
771
|
+
/**
|
|
767
772
|
* Progress update from a running model conversion job.
|
|
768
773
|
* Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
|
|
769
774
|
* Emitted by `addon-model-studio` on the converting node.
|
|
@@ -5575,7 +5580,7 @@ var ZodIssueCode = {
|
|
|
5575
5580
|
var ZodFirstPartyTypeKind;
|
|
5576
5581
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5577
5582
|
//#endregion
|
|
5578
|
-
//#region ../types/dist/sleep
|
|
5583
|
+
//#region ../types/dist/sleep-Crlmu_tO.mjs
|
|
5579
5584
|
/**
|
|
5580
5585
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5581
5586
|
* window to float samples (D455).
|
|
@@ -9034,6 +9039,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
|
|
|
9034
9039
|
/** Every capability name, so addon code can check a name without the schema graph. */
|
|
9035
9040
|
var CAP_NAMES = Object.freeze([
|
|
9036
9041
|
"accessories",
|
|
9042
|
+
"action-source",
|
|
9043
|
+
"actions",
|
|
9037
9044
|
"addon-pages",
|
|
9038
9045
|
"addon-pages-source",
|
|
9039
9046
|
"addon-routes",
|
|
@@ -9222,11 +9229,11 @@ var CAP_NAMES = Object.freeze([
|
|
|
9222
9229
|
Object.freeze({
|
|
9223
9230
|
"air-quality-sensor": [
|
|
9224
9231
|
{
|
|
9225
|
-
"path": "
|
|
9232
|
+
"path": "co2",
|
|
9226
9233
|
"valueType": "number"
|
|
9227
9234
|
},
|
|
9228
9235
|
{
|
|
9229
|
-
"path": "
|
|
9236
|
+
"path": "voc",
|
|
9230
9237
|
"valueType": "number"
|
|
9231
9238
|
},
|
|
9232
9239
|
{
|
|
@@ -9276,7 +9283,7 @@ Object.freeze({
|
|
|
9276
9283
|
}
|
|
9277
9284
|
],
|
|
9278
9285
|
"ambient-light-sensor": [{
|
|
9279
|
-
"path": "
|
|
9286
|
+
"path": "illuminance",
|
|
9280
9287
|
"valueType": "number"
|
|
9281
9288
|
}],
|
|
9282
9289
|
"audio-metrics": [
|
|
@@ -9337,7 +9344,7 @@ Object.freeze({
|
|
|
9337
9344
|
],
|
|
9338
9345
|
"battery": [
|
|
9339
9346
|
{
|
|
9340
|
-
"path": "
|
|
9347
|
+
"path": "level",
|
|
9341
9348
|
"valueType": "number"
|
|
9342
9349
|
},
|
|
9343
9350
|
{
|
|
@@ -9359,7 +9366,7 @@ Object.freeze({
|
|
|
9359
9366
|
"valueType": "number"
|
|
9360
9367
|
},
|
|
9361
9368
|
{
|
|
9362
|
-
"path": "
|
|
9369
|
+
"path": "voltage",
|
|
9363
9370
|
"valueType": "number"
|
|
9364
9371
|
},
|
|
9365
9372
|
{
|
|
@@ -9380,7 +9387,7 @@ Object.freeze({
|
|
|
9380
9387
|
"valueType": "boolean"
|
|
9381
9388
|
}],
|
|
9382
9389
|
"brightness": [{
|
|
9383
|
-
"path": "
|
|
9390
|
+
"path": "level",
|
|
9384
9391
|
"valueType": "number"
|
|
9385
9392
|
}],
|
|
9386
9393
|
"broker": [{
|
|
@@ -9581,7 +9588,7 @@ Object.freeze({
|
|
|
9581
9588
|
"valueType": "number"
|
|
9582
9589
|
},
|
|
9583
9590
|
{
|
|
9584
|
-
"path": "
|
|
9591
|
+
"path": "colorTemperature",
|
|
9585
9592
|
"valueType": "number"
|
|
9586
9593
|
}
|
|
9587
9594
|
],
|
|
@@ -9760,15 +9767,15 @@ Object.freeze({
|
|
|
9760
9767
|
"valueType": "string"
|
|
9761
9768
|
},
|
|
9762
9769
|
{
|
|
9763
|
-
"path": "
|
|
9770
|
+
"path": "remaining",
|
|
9764
9771
|
"valueType": "number"
|
|
9765
9772
|
},
|
|
9766
9773
|
{
|
|
9767
|
-
"path": "
|
|
9774
|
+
"path": "delayedStart",
|
|
9768
9775
|
"valueType": "number"
|
|
9769
9776
|
},
|
|
9770
9777
|
{
|
|
9771
|
-
"path": "
|
|
9778
|
+
"path": "progress",
|
|
9772
9779
|
"valueType": "number"
|
|
9773
9780
|
},
|
|
9774
9781
|
{
|
|
@@ -9882,7 +9889,7 @@ Object.freeze({
|
|
|
9882
9889
|
],
|
|
9883
9890
|
"fan-control": [
|
|
9884
9891
|
{
|
|
9885
|
-
"path": "
|
|
9892
|
+
"path": "speed",
|
|
9886
9893
|
"valueType": "number"
|
|
9887
9894
|
},
|
|
9888
9895
|
{
|
|
@@ -9982,7 +9989,7 @@ Object.freeze({
|
|
|
9982
9989
|
}
|
|
9983
9990
|
],
|
|
9984
9991
|
"humidity-sensor": [{
|
|
9985
|
-
"path": "
|
|
9992
|
+
"path": "humidity",
|
|
9986
9993
|
"valueType": "number"
|
|
9987
9994
|
}],
|
|
9988
9995
|
"image": [{
|
|
@@ -10092,7 +10099,7 @@ Object.freeze({
|
|
|
10092
10099
|
"valueType": "number"
|
|
10093
10100
|
},
|
|
10094
10101
|
{
|
|
10095
|
-
"path": "
|
|
10102
|
+
"path": "progress",
|
|
10096
10103
|
"valueType": "number"
|
|
10097
10104
|
},
|
|
10098
10105
|
{
|
|
@@ -10171,11 +10178,11 @@ Object.freeze({
|
|
|
10171
10178
|
"valueType": "string"
|
|
10172
10179
|
},
|
|
10173
10180
|
{
|
|
10174
|
-
"path": "
|
|
10181
|
+
"path": "position",
|
|
10175
10182
|
"valueType": "number"
|
|
10176
10183
|
},
|
|
10177
10184
|
{
|
|
10178
|
-
"path": "
|
|
10185
|
+
"path": "duration",
|
|
10179
10186
|
"valueType": "number"
|
|
10180
10187
|
},
|
|
10181
10188
|
{
|
|
@@ -10277,11 +10284,11 @@ Object.freeze({
|
|
|
10277
10284
|
]
|
|
10278
10285
|
},
|
|
10279
10286
|
{
|
|
10280
|
-
"path": "
|
|
10287
|
+
"path": "signal",
|
|
10281
10288
|
"valueType": "number"
|
|
10282
10289
|
},
|
|
10283
10290
|
{
|
|
10284
|
-
"path": "
|
|
10291
|
+
"path": "rssi",
|
|
10285
10292
|
"valueType": "number"
|
|
10286
10293
|
},
|
|
10287
10294
|
{
|
|
@@ -10336,6 +10343,18 @@ Object.freeze({
|
|
|
10336
10343
|
"path": "foodLevel",
|
|
10337
10344
|
"valueType": "number"
|
|
10338
10345
|
},
|
|
10346
|
+
{
|
|
10347
|
+
"path": "bowlFood",
|
|
10348
|
+
"valueType": "number"
|
|
10349
|
+
},
|
|
10350
|
+
{
|
|
10351
|
+
"path": "eating",
|
|
10352
|
+
"valueType": "boolean"
|
|
10353
|
+
},
|
|
10354
|
+
{
|
|
10355
|
+
"path": "supportsMarkRefilled",
|
|
10356
|
+
"valueType": "boolean"
|
|
10357
|
+
},
|
|
10339
10358
|
{
|
|
10340
10359
|
"path": "food1",
|
|
10341
10360
|
"valueType": "number"
|
|
@@ -10400,23 +10419,23 @@ Object.freeze({
|
|
|
10400
10419
|
],
|
|
10401
10420
|
"power-meter": [
|
|
10402
10421
|
{
|
|
10403
|
-
"path": "
|
|
10422
|
+
"path": "power",
|
|
10404
10423
|
"valueType": "number"
|
|
10405
10424
|
},
|
|
10406
10425
|
{
|
|
10407
|
-
"path": "
|
|
10426
|
+
"path": "energy",
|
|
10408
10427
|
"valueType": "number"
|
|
10409
10428
|
},
|
|
10410
10429
|
{
|
|
10411
|
-
"path": "
|
|
10430
|
+
"path": "voltage",
|
|
10412
10431
|
"valueType": "number"
|
|
10413
10432
|
},
|
|
10414
10433
|
{
|
|
10415
|
-
"path": "
|
|
10434
|
+
"path": "current",
|
|
10416
10435
|
"valueType": "number"
|
|
10417
10436
|
},
|
|
10418
10437
|
{
|
|
10419
|
-
"path": "
|
|
10438
|
+
"path": "apparentPower",
|
|
10420
10439
|
"valueType": "number"
|
|
10421
10440
|
},
|
|
10422
10441
|
{
|
|
@@ -10444,14 +10463,10 @@ Object.freeze({
|
|
|
10444
10463
|
{
|
|
10445
10464
|
"path": "gps.accuracyMeters",
|
|
10446
10465
|
"valueType": "number"
|
|
10447
|
-
},
|
|
10448
|
-
{
|
|
10449
|
-
"path": "batteryPercent",
|
|
10450
|
-
"valueType": "number"
|
|
10451
10466
|
}
|
|
10452
10467
|
],
|
|
10453
10468
|
"pressure-sensor": [{
|
|
10454
|
-
"path": "
|
|
10469
|
+
"path": "pressure",
|
|
10455
10470
|
"valueType": "number"
|
|
10456
10471
|
}],
|
|
10457
10472
|
"privacy-mask": [{
|
|
@@ -10765,7 +10780,7 @@ Object.freeze({
|
|
|
10765
10780
|
"valueType": "boolean"
|
|
10766
10781
|
}],
|
|
10767
10782
|
"temperature-sensor": [{
|
|
10768
|
-
"path": "
|
|
10783
|
+
"path": "temperature",
|
|
10769
10784
|
"valueType": "number"
|
|
10770
10785
|
}],
|
|
10771
10786
|
"update": [
|
|
@@ -10813,7 +10828,7 @@ Object.freeze({
|
|
|
10813
10828
|
"valueType": "string"
|
|
10814
10829
|
},
|
|
10815
10830
|
{
|
|
10816
|
-
"path": "
|
|
10831
|
+
"path": "progress",
|
|
10817
10832
|
"valueType": "number"
|
|
10818
10833
|
},
|
|
10819
10834
|
{
|
|
@@ -11450,7 +11465,7 @@ function errMsg(err) {
|
|
|
11450
11465
|
return String(err);
|
|
11451
11466
|
}
|
|
11452
11467
|
//#endregion
|
|
11453
|
-
//#region ../types/dist/composition-
|
|
11468
|
+
//#region ../types/dist/composition-CZImLpOI.mjs
|
|
11454
11469
|
var AUDIO_ENCODER_BY_CODEC = {
|
|
11455
11470
|
opus: "libopus",
|
|
11456
11471
|
aac: "aac",
|
|
@@ -18162,6 +18177,14 @@ var AutomationConditionLeafSchema = object({
|
|
|
18162
18177
|
array(union$1([string(), number()]))
|
|
18163
18178
|
])
|
|
18164
18179
|
});
|
|
18180
|
+
var AutomationConditionStationaryPresentSchema = object({
|
|
18181
|
+
kind: literal("stationary-present"),
|
|
18182
|
+
deviceId: number().int().nonnegative(),
|
|
18183
|
+
/** Class names; a macro class matches every raw class that maps to it. */
|
|
18184
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
18185
|
+
/** The parked object must be in at least one of these zones. */
|
|
18186
|
+
zones: array(string().min(1)).min(1).optional()
|
|
18187
|
+
});
|
|
18165
18188
|
/**
|
|
18166
18189
|
* The expression leaf, declared as a plain object rather than an intersection
|
|
18167
18190
|
* with {@link ExpressionSourceSchema}: a discriminated union has to be able to
|
|
@@ -18195,7 +18218,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
|
18195
18218
|
child: AutomationConditionSchema
|
|
18196
18219
|
}),
|
|
18197
18220
|
AutomationConditionLeafSchema,
|
|
18198
|
-
AutomationConditionExpressionSchema
|
|
18221
|
+
AutomationConditionExpressionSchema,
|
|
18222
|
+
AutomationConditionStationaryPresentSchema
|
|
18199
18223
|
]));
|
|
18200
18224
|
/**
|
|
18201
18225
|
* What starts a run.
|
|
@@ -18236,7 +18260,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
18236
18260
|
kind: literal("schedule"),
|
|
18237
18261
|
cron: string().min(1).max(120)
|
|
18238
18262
|
}),
|
|
18239
|
-
object({ kind: literal("manual") })
|
|
18263
|
+
object({ kind: literal("manual") }),
|
|
18264
|
+
object({
|
|
18265
|
+
kind: literal("detection"),
|
|
18266
|
+
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
18267
|
+
* matches every raw class that maps to it; any other name matches
|
|
18268
|
+
* exactly. */
|
|
18269
|
+
classes: array(string().min(1)).min(1),
|
|
18270
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
18271
|
+
/** The track must have visited at least one of these zones. */
|
|
18272
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
18273
|
+
/** Best-so-far confidence the track must reach. */
|
|
18274
|
+
minScore: number().min(0).max(1).optional()
|
|
18275
|
+
}),
|
|
18276
|
+
object({
|
|
18277
|
+
kind: literal("stationary"),
|
|
18278
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
18279
|
+
/** Class names; a macro class matches like the detection trigger. */
|
|
18280
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
18281
|
+
/** The parked object must be in at least one of these zones. */
|
|
18282
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
18283
|
+
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
18284
|
+
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
18285
|
+
phase: _enum(["appeared", "departed"])
|
|
18286
|
+
}),
|
|
18287
|
+
object({
|
|
18288
|
+
kind: literal("interval"),
|
|
18289
|
+
minutes: number().int().min(1).max(1440)
|
|
18290
|
+
})
|
|
18240
18291
|
]);
|
|
18241
18292
|
/**
|
|
18242
18293
|
* One action step.
|
|
@@ -18256,28 +18307,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
18256
18307
|
* is called. The runner still receives a number and is untouched; the
|
|
18257
18308
|
* resolution is the recipe's job, not the runner's.
|
|
18258
18309
|
*/
|
|
18310
|
+
var AutomationWaitStepSchema = object({
|
|
18311
|
+
kind: literal("wait"),
|
|
18312
|
+
seconds: number().min(0).max(300)
|
|
18313
|
+
});
|
|
18314
|
+
var AutomationCapStepSchema = object({
|
|
18315
|
+
kind: literal("cap"),
|
|
18316
|
+
/**
|
|
18317
|
+
* One device, several same-family devices (locks / climate family / …),
|
|
18318
|
+
* or `{ $var }` for the device that triggered the run. An array is
|
|
18319
|
+
* fan-out at codegen — each id gets the same cap.method call.
|
|
18320
|
+
*/
|
|
18321
|
+
deviceId: union$1([
|
|
18322
|
+
number().int(),
|
|
18323
|
+
array(number().int()).min(1),
|
|
18324
|
+
object({ $var: string().min(1) })
|
|
18325
|
+
]),
|
|
18326
|
+
cap: string().min(1),
|
|
18327
|
+
method: string().min(1),
|
|
18328
|
+
/** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
18329
|
+
* SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
|
|
18330
|
+
* the expression leaf. */
|
|
18331
|
+
args: record(string(), unknown()).optional()
|
|
18332
|
+
});
|
|
18333
|
+
/**
|
|
18334
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
18335
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
18336
|
+
*/
|
|
18337
|
+
var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
|
|
18259
18338
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
18339
|
+
AutomationWaitStepSchema,
|
|
18340
|
+
AutomationCapStepSchema,
|
|
18260
18341
|
object({
|
|
18261
|
-
kind: literal("
|
|
18262
|
-
|
|
18263
|
-
|
|
18264
|
-
|
|
18265
|
-
|
|
18266
|
-
|
|
18267
|
-
|
|
18268
|
-
|
|
18269
|
-
|
|
18270
|
-
*/
|
|
18271
|
-
deviceId: union$1([
|
|
18272
|
-
number().int(),
|
|
18273
|
-
array(number().int()).min(1),
|
|
18274
|
-
object({ $var: string().min(1) })
|
|
18275
|
-
]),
|
|
18276
|
-
cap: string().min(1),
|
|
18277
|
-
method: string().min(1),
|
|
18278
|
-
/** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
|
|
18279
|
-
* evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
|
|
18280
|
-
args: record(string(), unknown()).optional()
|
|
18342
|
+
kind: literal("action"),
|
|
18343
|
+
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
18344
|
+
actionId: string().min(1),
|
|
18345
|
+
/** Values for the action's declared variables; the others take their default. */
|
|
18346
|
+
variables: record(string(), union$1([
|
|
18347
|
+
string(),
|
|
18348
|
+
number(),
|
|
18349
|
+
boolean()
|
|
18350
|
+
])).optional()
|
|
18281
18351
|
}),
|
|
18282
18352
|
object({
|
|
18283
18353
|
kind: literal("code"),
|
|
@@ -18286,74 +18356,743 @@ var AutomationActionSchema = discriminatedUnion("kind", [
|
|
|
18286
18356
|
code: string().min(1).max(2e4)
|
|
18287
18357
|
})
|
|
18288
18358
|
]);
|
|
18359
|
+
/**
|
|
18360
|
+
* How a trigger that arrives while the automation is busy (or cooling down) is
|
|
18361
|
+
* handled. One of the automation's execution modes, never a property of its
|
|
18362
|
+
* actions. Absent means `single`.
|
|
18363
|
+
*
|
|
18364
|
+
* - `single` — drop the trigger while a run is in progress (traced + logged).
|
|
18365
|
+
* - `restart` — abort the run in progress (at its next step boundary), start fresh.
|
|
18366
|
+
* - `queued` — queue up to `max` triggers, drop the rest (traced).
|
|
18367
|
+
* - `parallel` — up to `max` runs at once, drop the rest (traced).
|
|
18368
|
+
* - `cooldown` — `single`, and no new run until `minutes` after the last run
|
|
18369
|
+
* STARTED; persisted across a restart. A manual trigger bypasses only this
|
|
18370
|
+
* wait, never the single/queued limits.
|
|
18371
|
+
*/
|
|
18372
|
+
var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
18373
|
+
object({ mode: literal("single") }),
|
|
18374
|
+
object({ mode: literal("restart") }),
|
|
18375
|
+
object({
|
|
18376
|
+
mode: literal("queued"),
|
|
18377
|
+
max: number().int().min(1).max(10)
|
|
18378
|
+
}),
|
|
18379
|
+
object({
|
|
18380
|
+
mode: literal("parallel"),
|
|
18381
|
+
max: number().int().min(1).max(10)
|
|
18382
|
+
}),
|
|
18383
|
+
object({
|
|
18384
|
+
mode: literal("cooldown"),
|
|
18385
|
+
minutes: number().int().min(1).max(1440)
|
|
18386
|
+
})
|
|
18387
|
+
]);
|
|
18289
18388
|
var AutomationRecipeSchema = object({
|
|
18290
18389
|
triggers: array(AutomationTriggerSchema),
|
|
18291
18390
|
conditions: AutomationConditionSchema.optional(),
|
|
18292
|
-
actions: array(AutomationActionSchema)
|
|
18391
|
+
actions: array(AutomationActionSchema),
|
|
18392
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
18393
|
+
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
18394
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
18293
18395
|
});
|
|
18396
|
+
var COCO_80_LABELS = [
|
|
18397
|
+
{
|
|
18398
|
+
id: "person",
|
|
18399
|
+
name: "Person"
|
|
18400
|
+
},
|
|
18401
|
+
{
|
|
18402
|
+
id: "bicycle",
|
|
18403
|
+
name: "Bicycle"
|
|
18404
|
+
},
|
|
18405
|
+
{
|
|
18406
|
+
id: "car",
|
|
18407
|
+
name: "Car"
|
|
18408
|
+
},
|
|
18409
|
+
{
|
|
18410
|
+
id: "motorcycle",
|
|
18411
|
+
name: "Motorcycle"
|
|
18412
|
+
},
|
|
18413
|
+
{
|
|
18414
|
+
id: "airplane",
|
|
18415
|
+
name: "Airplane"
|
|
18416
|
+
},
|
|
18417
|
+
{
|
|
18418
|
+
id: "bus",
|
|
18419
|
+
name: "Bus"
|
|
18420
|
+
},
|
|
18421
|
+
{
|
|
18422
|
+
id: "train",
|
|
18423
|
+
name: "Train"
|
|
18424
|
+
},
|
|
18425
|
+
{
|
|
18426
|
+
id: "truck",
|
|
18427
|
+
name: "Truck"
|
|
18428
|
+
},
|
|
18429
|
+
{
|
|
18430
|
+
id: "boat",
|
|
18431
|
+
name: "Boat"
|
|
18432
|
+
},
|
|
18433
|
+
{
|
|
18434
|
+
id: "traffic light",
|
|
18435
|
+
name: "Traffic Light"
|
|
18436
|
+
},
|
|
18437
|
+
{
|
|
18438
|
+
id: "fire hydrant",
|
|
18439
|
+
name: "Fire Hydrant"
|
|
18440
|
+
},
|
|
18441
|
+
{
|
|
18442
|
+
id: "stop sign",
|
|
18443
|
+
name: "Stop Sign"
|
|
18444
|
+
},
|
|
18445
|
+
{
|
|
18446
|
+
id: "parking meter",
|
|
18447
|
+
name: "Parking Meter"
|
|
18448
|
+
},
|
|
18449
|
+
{
|
|
18450
|
+
id: "bench",
|
|
18451
|
+
name: "Bench"
|
|
18452
|
+
},
|
|
18453
|
+
{
|
|
18454
|
+
id: "bird",
|
|
18455
|
+
name: "Bird"
|
|
18456
|
+
},
|
|
18457
|
+
{
|
|
18458
|
+
id: "cat",
|
|
18459
|
+
name: "Cat"
|
|
18460
|
+
},
|
|
18461
|
+
{
|
|
18462
|
+
id: "dog",
|
|
18463
|
+
name: "Dog"
|
|
18464
|
+
},
|
|
18465
|
+
{
|
|
18466
|
+
id: "horse",
|
|
18467
|
+
name: "Horse"
|
|
18468
|
+
},
|
|
18469
|
+
{
|
|
18470
|
+
id: "sheep",
|
|
18471
|
+
name: "Sheep"
|
|
18472
|
+
},
|
|
18473
|
+
{
|
|
18474
|
+
id: "cow",
|
|
18475
|
+
name: "Cow"
|
|
18476
|
+
},
|
|
18477
|
+
{
|
|
18478
|
+
id: "elephant",
|
|
18479
|
+
name: "Elephant"
|
|
18480
|
+
},
|
|
18481
|
+
{
|
|
18482
|
+
id: "bear",
|
|
18483
|
+
name: "Bear"
|
|
18484
|
+
},
|
|
18485
|
+
{
|
|
18486
|
+
id: "zebra",
|
|
18487
|
+
name: "Zebra"
|
|
18488
|
+
},
|
|
18489
|
+
{
|
|
18490
|
+
id: "giraffe",
|
|
18491
|
+
name: "Giraffe"
|
|
18492
|
+
},
|
|
18493
|
+
{
|
|
18494
|
+
id: "backpack",
|
|
18495
|
+
name: "Backpack"
|
|
18496
|
+
},
|
|
18497
|
+
{
|
|
18498
|
+
id: "umbrella",
|
|
18499
|
+
name: "Umbrella"
|
|
18500
|
+
},
|
|
18501
|
+
{
|
|
18502
|
+
id: "handbag",
|
|
18503
|
+
name: "Handbag"
|
|
18504
|
+
},
|
|
18505
|
+
{
|
|
18506
|
+
id: "tie",
|
|
18507
|
+
name: "Tie"
|
|
18508
|
+
},
|
|
18509
|
+
{
|
|
18510
|
+
id: "suitcase",
|
|
18511
|
+
name: "Suitcase"
|
|
18512
|
+
},
|
|
18513
|
+
{
|
|
18514
|
+
id: "frisbee",
|
|
18515
|
+
name: "Frisbee"
|
|
18516
|
+
},
|
|
18517
|
+
{
|
|
18518
|
+
id: "skis",
|
|
18519
|
+
name: "Skis"
|
|
18520
|
+
},
|
|
18521
|
+
{
|
|
18522
|
+
id: "snowboard",
|
|
18523
|
+
name: "Snowboard"
|
|
18524
|
+
},
|
|
18525
|
+
{
|
|
18526
|
+
id: "sports ball",
|
|
18527
|
+
name: "Sports Ball"
|
|
18528
|
+
},
|
|
18529
|
+
{
|
|
18530
|
+
id: "kite",
|
|
18531
|
+
name: "Kite"
|
|
18532
|
+
},
|
|
18533
|
+
{
|
|
18534
|
+
id: "baseball bat",
|
|
18535
|
+
name: "Baseball Bat"
|
|
18536
|
+
},
|
|
18537
|
+
{
|
|
18538
|
+
id: "baseball glove",
|
|
18539
|
+
name: "Baseball Glove"
|
|
18540
|
+
},
|
|
18541
|
+
{
|
|
18542
|
+
id: "skateboard",
|
|
18543
|
+
name: "Skateboard"
|
|
18544
|
+
},
|
|
18545
|
+
{
|
|
18546
|
+
id: "surfboard",
|
|
18547
|
+
name: "Surfboard"
|
|
18548
|
+
},
|
|
18549
|
+
{
|
|
18550
|
+
id: "tennis racket",
|
|
18551
|
+
name: "Tennis Racket"
|
|
18552
|
+
},
|
|
18553
|
+
{
|
|
18554
|
+
id: "bottle",
|
|
18555
|
+
name: "Bottle"
|
|
18556
|
+
},
|
|
18557
|
+
{
|
|
18558
|
+
id: "wine glass",
|
|
18559
|
+
name: "Wine Glass"
|
|
18560
|
+
},
|
|
18561
|
+
{
|
|
18562
|
+
id: "cup",
|
|
18563
|
+
name: "Cup"
|
|
18564
|
+
},
|
|
18565
|
+
{
|
|
18566
|
+
id: "fork",
|
|
18567
|
+
name: "Fork"
|
|
18568
|
+
},
|
|
18569
|
+
{
|
|
18570
|
+
id: "knife",
|
|
18571
|
+
name: "Knife"
|
|
18572
|
+
},
|
|
18573
|
+
{
|
|
18574
|
+
id: "spoon",
|
|
18575
|
+
name: "Spoon"
|
|
18576
|
+
},
|
|
18577
|
+
{
|
|
18578
|
+
id: "bowl",
|
|
18579
|
+
name: "Bowl"
|
|
18580
|
+
},
|
|
18581
|
+
{
|
|
18582
|
+
id: "banana",
|
|
18583
|
+
name: "Banana"
|
|
18584
|
+
},
|
|
18585
|
+
{
|
|
18586
|
+
id: "apple",
|
|
18587
|
+
name: "Apple"
|
|
18588
|
+
},
|
|
18589
|
+
{
|
|
18590
|
+
id: "sandwich",
|
|
18591
|
+
name: "Sandwich"
|
|
18592
|
+
},
|
|
18593
|
+
{
|
|
18594
|
+
id: "orange",
|
|
18595
|
+
name: "Orange"
|
|
18596
|
+
},
|
|
18597
|
+
{
|
|
18598
|
+
id: "broccoli",
|
|
18599
|
+
name: "Broccoli"
|
|
18600
|
+
},
|
|
18601
|
+
{
|
|
18602
|
+
id: "carrot",
|
|
18603
|
+
name: "Carrot"
|
|
18604
|
+
},
|
|
18605
|
+
{
|
|
18606
|
+
id: "hot dog",
|
|
18607
|
+
name: "Hot Dog"
|
|
18608
|
+
},
|
|
18609
|
+
{
|
|
18610
|
+
id: "pizza",
|
|
18611
|
+
name: "Pizza"
|
|
18612
|
+
},
|
|
18613
|
+
{
|
|
18614
|
+
id: "donut",
|
|
18615
|
+
name: "Donut"
|
|
18616
|
+
},
|
|
18617
|
+
{
|
|
18618
|
+
id: "cake",
|
|
18619
|
+
name: "Cake"
|
|
18620
|
+
},
|
|
18621
|
+
{
|
|
18622
|
+
id: "chair",
|
|
18623
|
+
name: "Chair"
|
|
18624
|
+
},
|
|
18625
|
+
{
|
|
18626
|
+
id: "couch",
|
|
18627
|
+
name: "Couch"
|
|
18628
|
+
},
|
|
18629
|
+
{
|
|
18630
|
+
id: "potted plant",
|
|
18631
|
+
name: "Potted Plant"
|
|
18632
|
+
},
|
|
18633
|
+
{
|
|
18634
|
+
id: "bed",
|
|
18635
|
+
name: "Bed"
|
|
18636
|
+
},
|
|
18637
|
+
{
|
|
18638
|
+
id: "dining table",
|
|
18639
|
+
name: "Dining Table"
|
|
18640
|
+
},
|
|
18641
|
+
{
|
|
18642
|
+
id: "toilet",
|
|
18643
|
+
name: "Toilet"
|
|
18644
|
+
},
|
|
18645
|
+
{
|
|
18646
|
+
id: "tv",
|
|
18647
|
+
name: "TV"
|
|
18648
|
+
},
|
|
18649
|
+
{
|
|
18650
|
+
id: "laptop",
|
|
18651
|
+
name: "Laptop"
|
|
18652
|
+
},
|
|
18653
|
+
{
|
|
18654
|
+
id: "mouse",
|
|
18655
|
+
name: "Mouse"
|
|
18656
|
+
},
|
|
18657
|
+
{
|
|
18658
|
+
id: "remote",
|
|
18659
|
+
name: "Remote"
|
|
18660
|
+
},
|
|
18661
|
+
{
|
|
18662
|
+
id: "keyboard",
|
|
18663
|
+
name: "Keyboard"
|
|
18664
|
+
},
|
|
18665
|
+
{
|
|
18666
|
+
id: "cell phone",
|
|
18667
|
+
name: "Cell Phone"
|
|
18668
|
+
},
|
|
18669
|
+
{
|
|
18670
|
+
id: "microwave",
|
|
18671
|
+
name: "Microwave"
|
|
18672
|
+
},
|
|
18673
|
+
{
|
|
18674
|
+
id: "oven",
|
|
18675
|
+
name: "Oven"
|
|
18676
|
+
},
|
|
18677
|
+
{
|
|
18678
|
+
id: "toaster",
|
|
18679
|
+
name: "Toaster"
|
|
18680
|
+
},
|
|
18681
|
+
{
|
|
18682
|
+
id: "sink",
|
|
18683
|
+
name: "Sink"
|
|
18684
|
+
},
|
|
18685
|
+
{
|
|
18686
|
+
id: "refrigerator",
|
|
18687
|
+
name: "Refrigerator"
|
|
18688
|
+
},
|
|
18689
|
+
{
|
|
18690
|
+
id: "book",
|
|
18691
|
+
name: "Book"
|
|
18692
|
+
},
|
|
18693
|
+
{
|
|
18694
|
+
id: "clock",
|
|
18695
|
+
name: "Clock"
|
|
18696
|
+
},
|
|
18697
|
+
{
|
|
18698
|
+
id: "vase",
|
|
18699
|
+
name: "Vase"
|
|
18700
|
+
},
|
|
18701
|
+
{
|
|
18702
|
+
id: "scissors",
|
|
18703
|
+
name: "Scissors"
|
|
18704
|
+
},
|
|
18705
|
+
{
|
|
18706
|
+
id: "teddy bear",
|
|
18707
|
+
name: "Teddy Bear"
|
|
18708
|
+
},
|
|
18709
|
+
{
|
|
18710
|
+
id: "hair drier",
|
|
18711
|
+
name: "Hair Drier"
|
|
18712
|
+
},
|
|
18713
|
+
{
|
|
18714
|
+
id: "toothbrush",
|
|
18715
|
+
name: "Toothbrush"
|
|
18716
|
+
}
|
|
18717
|
+
];
|
|
18718
|
+
new Set([
|
|
18719
|
+
{
|
|
18720
|
+
id: "person",
|
|
18721
|
+
name: "Person"
|
|
18722
|
+
},
|
|
18723
|
+
{
|
|
18724
|
+
id: "vehicle",
|
|
18725
|
+
name: "Vehicle"
|
|
18726
|
+
},
|
|
18727
|
+
{
|
|
18728
|
+
id: "animal",
|
|
18729
|
+
name: "Animal"
|
|
18730
|
+
},
|
|
18731
|
+
{
|
|
18732
|
+
id: "package",
|
|
18733
|
+
name: "Package"
|
|
18734
|
+
}
|
|
18735
|
+
].map((l) => l.id));
|
|
18736
|
+
var COCO_TO_MACRO = {
|
|
18737
|
+
mapping: {
|
|
18738
|
+
person: "person",
|
|
18739
|
+
bicycle: "vehicle",
|
|
18740
|
+
car: "vehicle",
|
|
18741
|
+
motorcycle: "vehicle",
|
|
18742
|
+
airplane: "vehicle",
|
|
18743
|
+
bus: "vehicle",
|
|
18744
|
+
train: "vehicle",
|
|
18745
|
+
truck: "vehicle",
|
|
18746
|
+
boat: "vehicle",
|
|
18747
|
+
bird: "animal",
|
|
18748
|
+
cat: "animal",
|
|
18749
|
+
dog: "animal",
|
|
18750
|
+
horse: "animal",
|
|
18751
|
+
sheep: "animal",
|
|
18752
|
+
cow: "animal",
|
|
18753
|
+
elephant: "animal",
|
|
18754
|
+
bear: "animal",
|
|
18755
|
+
zebra: "animal",
|
|
18756
|
+
giraffe: "animal",
|
|
18757
|
+
suitcase: "package",
|
|
18758
|
+
backpack: "package",
|
|
18759
|
+
handbag: "package"
|
|
18760
|
+
},
|
|
18761
|
+
preserveOriginal: false
|
|
18762
|
+
};
|
|
18763
|
+
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
18294
18764
|
/**
|
|
18295
|
-
*
|
|
18296
|
-
*
|
|
18765
|
+
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
18766
|
+
* so the editor can paint every block green or red with its reason.
|
|
18297
18767
|
*
|
|
18298
|
-
*
|
|
18299
|
-
*
|
|
18300
|
-
*
|
|
18301
|
-
*
|
|
18302
|
-
*
|
|
18768
|
+
* A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
|
|
18769
|
+
* every condition node (containers and leaves, including the ones a
|
|
18770
|
+
* short-circuit never reached), the run-mode verdict and every action step —
|
|
18771
|
+
* each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
|
|
18772
|
+
* for an Action run). Skipped
|
|
18773
|
+
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
18774
|
+
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
18303
18775
|
*
|
|
18304
|
-
*
|
|
18305
|
-
*
|
|
18306
|
-
* provider. Splitting the public aggregator from the raw collection
|
|
18307
|
-
* keeps both ends in codegen — there's no hand-written
|
|
18308
|
-
* `addon-pages.router.ts` wrapper anymore.
|
|
18776
|
+
* {@link AutomationStepTraceSchema} is deliberately free of automation
|
|
18777
|
+
* vocabulary so an Action run records the same step shape.
|
|
18309
18778
|
*/
|
|
18310
|
-
|
|
18311
|
-
|
|
18312
|
-
|
|
18313
|
-
|
|
18314
|
-
|
|
18315
|
-
|
|
18316
|
-
|
|
18317
|
-
|
|
18318
|
-
|
|
18779
|
+
/** A value a trace may show: JSON primitives and short lists, nothing deeper. */
|
|
18780
|
+
var TraceValueSchema = union$1([
|
|
18781
|
+
string(),
|
|
18782
|
+
number(),
|
|
18783
|
+
boolean(),
|
|
18784
|
+
_null(),
|
|
18785
|
+
array(union$1([string(), number()]))
|
|
18786
|
+
]);
|
|
18787
|
+
var AutomationRunOutcomeSchema = _enum([
|
|
18788
|
+
"ran",
|
|
18789
|
+
"skipped-trigger",
|
|
18790
|
+
"skipped-conditions",
|
|
18791
|
+
"skipped-cooldown",
|
|
18792
|
+
"skipped-overlap",
|
|
18793
|
+
"failed",
|
|
18794
|
+
"aborted"
|
|
18795
|
+
]);
|
|
18796
|
+
/** `not-evaluated` = a short-circuit never reached the node. */
|
|
18797
|
+
var TraceNodeOutcomeSchema = _enum([
|
|
18798
|
+
"pass",
|
|
18799
|
+
"fail",
|
|
18800
|
+
"not-evaluated"
|
|
18801
|
+
]);
|
|
18802
|
+
var AutomationTriggerTraceSchema = object({
|
|
18803
|
+
kind: string().min(1),
|
|
18804
|
+
outcome: _enum(["pass", "fail"]),
|
|
18805
|
+
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
18806
|
+
reason: string(),
|
|
18807
|
+
/** deviceId, className, trackId, score … — only what the trigger exposes. */
|
|
18808
|
+
vars: record(string(), TraceValueSchema)
|
|
18319
18809
|
});
|
|
18320
|
-
var
|
|
18321
|
-
|
|
18322
|
-
|
|
18323
|
-
|
|
18810
|
+
var AutomationConditionTraceNodeSchema = object({
|
|
18811
|
+
/** `conditions` for the root, `conditions.<i>` for its i-th child, … */
|
|
18812
|
+
path: string().min(1),
|
|
18813
|
+
kind: _enum([
|
|
18814
|
+
"all",
|
|
18815
|
+
"any",
|
|
18816
|
+
"not",
|
|
18817
|
+
"condition",
|
|
18818
|
+
"expression",
|
|
18819
|
+
"stationary-present"
|
|
18820
|
+
]),
|
|
18821
|
+
outcome: TraceNodeOutcomeSchema,
|
|
18822
|
+
reason: string(),
|
|
18823
|
+
deviceId: number().int().optional(),
|
|
18824
|
+
cap: string().optional(),
|
|
18825
|
+
fieldPath: string().optional(),
|
|
18826
|
+
operator: string().optional(),
|
|
18827
|
+
expected: TraceValueSchema.optional(),
|
|
18828
|
+
/** What the mirror held; `null` when it held nothing (unknown). */
|
|
18829
|
+
observed: TraceValueSchema.optional()
|
|
18830
|
+
});
|
|
18831
|
+
var AutomationRunVerdictSchema = object({
|
|
18832
|
+
mode: AutomationRunModeSchema,
|
|
18833
|
+
decision: _enum([
|
|
18834
|
+
"run",
|
|
18835
|
+
"queued",
|
|
18836
|
+
"restart",
|
|
18837
|
+
"drop",
|
|
18838
|
+
"cooldown",
|
|
18839
|
+
"bypass"
|
|
18840
|
+
]),
|
|
18841
|
+
reason: string()
|
|
18842
|
+
});
|
|
18843
|
+
/** One executed (or not-run) step — shared by automations and Action runs. */
|
|
18844
|
+
var AutomationStepTraceSchema = object({
|
|
18845
|
+
/** `actions.<i>` */
|
|
18846
|
+
path: string().min(1),
|
|
18847
|
+
kind: string().min(1),
|
|
18848
|
+
cap: string().optional(),
|
|
18849
|
+
method: string().optional(),
|
|
18850
|
+
deviceId: number().int().optional(),
|
|
18851
|
+
startedAt: number().optional(),
|
|
18852
|
+
endedAt: number().optional(),
|
|
18853
|
+
outcome: _enum([
|
|
18854
|
+
"ok",
|
|
18855
|
+
"error",
|
|
18856
|
+
"not-run"
|
|
18857
|
+
]),
|
|
18858
|
+
message: string().optional()
|
|
18859
|
+
});
|
|
18860
|
+
var AutomationRunTraceSchema = object({
|
|
18861
|
+
runId: string().min(1),
|
|
18862
|
+
startedAt: number(),
|
|
18863
|
+
endedAt: number(),
|
|
18864
|
+
trigger: AutomationTriggerTraceSchema,
|
|
18865
|
+
conditions: array(AutomationConditionTraceNodeSchema),
|
|
18866
|
+
verdict: AutomationRunVerdictSchema.optional(),
|
|
18867
|
+
steps: array(AutomationStepTraceSchema),
|
|
18868
|
+
outcome: AutomationRunOutcomeSchema,
|
|
18869
|
+
/** How many identical consecutive skips this trace stands for (>= 1). */
|
|
18870
|
+
repeat: number().int().min(1),
|
|
18871
|
+
/** When the latest of those repeats happened. */
|
|
18872
|
+
lastAt: number()
|
|
18324
18873
|
});
|
|
18325
|
-
method(_void(), array(AddonPageInfoSchema).readonly());
|
|
18326
18874
|
/**
|
|
18327
|
-
* `
|
|
18328
|
-
*
|
|
18329
|
-
|
|
18330
|
-
|
|
18331
|
-
|
|
18332
|
-
|
|
18875
|
+
* What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
|
|
18876
|
+
* `action:<actionId>`): the cooldown stamp and the trace ring.
|
|
18877
|
+
*/
|
|
18878
|
+
var AutomationStateDocSchema = object({
|
|
18879
|
+
version: literal(1),
|
|
18880
|
+
lastRunStartedAt: number().nullable(),
|
|
18881
|
+
traces: array(AutomationRunTraceSchema)
|
|
18882
|
+
});
|
|
18883
|
+
/**
|
|
18884
|
+
* Actions — named, runnable things an operator (or an automation) can start,
|
|
18885
|
+
* declared as LIST ENTRIES, never as devices.
|
|
18333
18886
|
*
|
|
18334
|
-
*
|
|
18335
|
-
*
|
|
18336
|
-
*
|
|
18887
|
+
* An entry is either **internal** (a short list of cap/wait steps kept in the
|
|
18888
|
+
* hub's `actions` record kind) or declared by an addon through the
|
|
18889
|
+
* `action-source` collection (a Home Assistant script, a scene …). The one
|
|
18890
|
+
* surface the UI and automations talk to is the `actions` cap; it keeps no
|
|
18891
|
+
* catalog of its own (D3) — every `list` asks the providers live.
|
|
18892
|
+
*
|
|
18893
|
+
* This file is the data half: the entry schema, the variables an entry may
|
|
18894
|
+
* declare, and the pure resolution of the values a run was given. It holds no
|
|
18895
|
+
* runtime.
|
|
18337
18896
|
*/
|
|
18338
|
-
|
|
18339
|
-
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18346
|
-
|
|
18347
|
-
|
|
18348
|
-
|
|
18349
|
-
|
|
18350
|
-
|
|
18351
|
-
|
|
18352
|
-
|
|
18353
|
-
|
|
18354
|
-
|
|
18355
|
-
|
|
18356
|
-
|
|
18897
|
+
/** What a variable's value is. `select` takes one of `options`. */
|
|
18898
|
+
var ActionVariableTypeSchema = _enum([
|
|
18899
|
+
"string",
|
|
18900
|
+
"number",
|
|
18901
|
+
"boolean",
|
|
18902
|
+
"select"
|
|
18903
|
+
]);
|
|
18904
|
+
var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
18905
|
+
var ActionVariableValueSchema = union$1([
|
|
18906
|
+
string(),
|
|
18907
|
+
number(),
|
|
18908
|
+
boolean()
|
|
18909
|
+
]);
|
|
18910
|
+
/**
|
|
18911
|
+
* One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
|
|
18912
|
+
* value) or `{{vars.name}}` (inside a string) in their args.
|
|
18913
|
+
*/
|
|
18914
|
+
var ActionVariableSchema = object({
|
|
18915
|
+
name: string().regex(ACTION_VARIABLE_NAME_RE),
|
|
18916
|
+
label: string().min(1).max(120).optional(),
|
|
18917
|
+
type: ActionVariableTypeSchema,
|
|
18918
|
+
required: boolean().optional(),
|
|
18919
|
+
default: ActionVariableValueSchema.optional(),
|
|
18920
|
+
/** The allowed values of a `select`. */
|
|
18921
|
+
options: array(string().min(1)).min(1).optional()
|
|
18922
|
+
}).superRefine((v, ctx) => {
|
|
18923
|
+
if (v.type === "select" && v.options === void 0) ctx.addIssue({
|
|
18924
|
+
code: "custom",
|
|
18925
|
+
message: "a select variable needs options",
|
|
18926
|
+
path: ["options"]
|
|
18927
|
+
});
|
|
18928
|
+
if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
|
|
18929
|
+
code: "custom",
|
|
18930
|
+
message: variableValueError(v, v.default) ?? "invalid default",
|
|
18931
|
+
path: ["default"]
|
|
18932
|
+
});
|
|
18933
|
+
});
|
|
18934
|
+
/** Why `value` is not a legal value for the variable, or null when it is. */
|
|
18935
|
+
function variableValueError(variable, value) {
|
|
18936
|
+
switch (variable.type) {
|
|
18937
|
+
case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
|
|
18938
|
+
case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
|
|
18939
|
+
case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
|
|
18940
|
+
case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
|
|
18941
|
+
}
|
|
18942
|
+
}
|
|
18943
|
+
var ActionSourceKindSchema = _enum(["internal", "addon"]);
|
|
18944
|
+
function isInternalActionId(actionId) {
|
|
18945
|
+
return actionId.startsWith("internal:") && actionId.length > 9;
|
|
18946
|
+
}
|
|
18947
|
+
/** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
|
|
18948
|
+
function addonIdOfActionId(actionId) {
|
|
18949
|
+
if (isInternalActionId(actionId)) return null;
|
|
18950
|
+
const at = actionId.indexOf(":");
|
|
18951
|
+
return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
|
|
18952
|
+
}
|
|
18953
|
+
var ActionStatusSchema = object({
|
|
18954
|
+
isRunning: boolean(),
|
|
18955
|
+
/** Ms epoch the latest run STARTED; null when it never ran. */
|
|
18956
|
+
lastRunAt: number().nullable(),
|
|
18957
|
+
/** The latest run's failure; null on success or when it never ran. */
|
|
18958
|
+
lastError: string().nullable()
|
|
18959
|
+
});
|
|
18960
|
+
var ActionEntrySchema = object({
|
|
18961
|
+
/** `internal:<uuid>` or `<addonId>:<localId>`. */
|
|
18962
|
+
id: string().min(3),
|
|
18963
|
+
name: string().min(1).max(120),
|
|
18964
|
+
/** The addon that declared it; `core-blocks` for an internal one. */
|
|
18965
|
+
addonId: string().min(1),
|
|
18966
|
+
source: ActionSourceKindSchema,
|
|
18967
|
+
/** An addon-declared entry is edited where it was declared. */
|
|
18968
|
+
readOnly: boolean(),
|
|
18969
|
+
/** Internal only: cap and wait steps. No action-in-action, so no cycles. */
|
|
18970
|
+
steps: array(ActionStepSchema).min(1).max(50).optional(),
|
|
18971
|
+
variables: array(ActionVariableSchema).max(20).optional(),
|
|
18972
|
+
status: ActionStatusSchema
|
|
18973
|
+
}).superRefine((entry, ctx) => {
|
|
18974
|
+
if (entry.source === "internal") {
|
|
18975
|
+
if (!isInternalActionId(entry.id)) ctx.addIssue({
|
|
18976
|
+
code: "custom",
|
|
18977
|
+
message: "an internal action id is internal:<uuid>",
|
|
18978
|
+
path: ["id"]
|
|
18979
|
+
});
|
|
18980
|
+
return;
|
|
18981
|
+
}
|
|
18982
|
+
if (entry.steps !== void 0) ctx.addIssue({
|
|
18983
|
+
code: "custom",
|
|
18984
|
+
message: "only an internal action carries steps",
|
|
18985
|
+
path: ["steps"]
|
|
18986
|
+
});
|
|
18987
|
+
if (!entry.readOnly) ctx.addIssue({
|
|
18988
|
+
code: "custom",
|
|
18989
|
+
message: "an addon action is read-only here",
|
|
18990
|
+
path: ["readOnly"]
|
|
18991
|
+
});
|
|
18992
|
+
if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
|
|
18993
|
+
code: "custom",
|
|
18994
|
+
message: "an addon action id is <addonId>:<localId>",
|
|
18995
|
+
path: ["id"]
|
|
18996
|
+
});
|
|
18997
|
+
});
|
|
18998
|
+
/** What an operator writes for an internal action. */
|
|
18999
|
+
var ActionInputSchema = object({
|
|
19000
|
+
name: string().min(1).max(120),
|
|
19001
|
+
steps: array(ActionStepSchema).min(1).max(50),
|
|
19002
|
+
variables: array(ActionVariableSchema).max(20).optional()
|
|
19003
|
+
});
|
|
19004
|
+
/** Who started a run. Absent = the operator pressed it. */
|
|
19005
|
+
var ActionRunOriginSchema = object({
|
|
19006
|
+
/** The automation block whose step started it. */
|
|
19007
|
+
blockId: string().min(1).optional() });
|
|
19008
|
+
/** `started` = the caller did not ask to wait. */
|
|
19009
|
+
var ActionRunOutcomeSchema = union$1([AutomationRunOutcomeSchema, literal("started")]);
|
|
19010
|
+
var ActionRunResultSchema = object({
|
|
19011
|
+
runId: string().min(1),
|
|
19012
|
+
outcome: ActionRunOutcomeSchema,
|
|
19013
|
+
/** Why it did not run, or what failed. */
|
|
19014
|
+
message: string().optional()
|
|
19015
|
+
});
|
|
19016
|
+
_enum([
|
|
19017
|
+
"not-found",
|
|
19018
|
+
"read-only",
|
|
19019
|
+
"invalid-variables",
|
|
19020
|
+
"invalid-steps",
|
|
19021
|
+
"not-linked",
|
|
19022
|
+
"busy"
|
|
19023
|
+
]);
|
|
19024
|
+
object({
|
|
19025
|
+
actionId: string(),
|
|
19026
|
+
runId: string(),
|
|
19027
|
+
isRunning: boolean(),
|
|
19028
|
+
/** Set when the run ended. */
|
|
19029
|
+
outcome: AutomationRunOutcomeSchema.optional(),
|
|
19030
|
+
lastRunAt: number().nullable(),
|
|
19031
|
+
lastError: string().nullable()
|
|
19032
|
+
});
|
|
19033
|
+
/**
|
|
19034
|
+
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
19035
|
+
* surface that admin-ui consumes through `useAddonPagesListPages()`.
|
|
19036
|
+
*
|
|
19037
|
+
* The provider iterates every `addon-pages-source` (collection) provider
|
|
19038
|
+
* and emits `AddonPageInfo[]` enriched with versioned `bundleUrl` strings
|
|
19039
|
+
* pointing at `/api/addon-pages/<addonId>/<bundle>?v=<mtime>`. The
|
|
19040
|
+
* filesystem `mtime` cache-buster lets the browser pick up addon
|
|
19041
|
+
* rebuilds without manual reload.
|
|
19042
|
+
*
|
|
19043
|
+
* The hub-local builtin `addon-pages-aggregator` (see
|
|
19044
|
+
* `@camstack/system/builtins/addon-pages-aggregator`) registers the
|
|
19045
|
+
* provider. Splitting the public aggregator from the raw collection
|
|
19046
|
+
* keeps both ends in codegen — there's no hand-written
|
|
19047
|
+
* `addon-pages.router.ts` wrapper anymore.
|
|
19048
|
+
*/
|
|
19049
|
+
var AddonPageDeclarationSchema$1 = object({
|
|
19050
|
+
id: string(),
|
|
19051
|
+
label: string(),
|
|
19052
|
+
icon: string(),
|
|
19053
|
+
path: string(),
|
|
19054
|
+
remoteName: string(),
|
|
19055
|
+
bundle: string(),
|
|
19056
|
+
section: string().optional(),
|
|
19057
|
+
sectionLabel: string().optional()
|
|
19058
|
+
});
|
|
19059
|
+
var AddonPageInfoSchema = object({
|
|
19060
|
+
addonId: string(),
|
|
19061
|
+
page: AddonPageDeclarationSchema$1,
|
|
19062
|
+
bundleUrl: string()
|
|
19063
|
+
});
|
|
19064
|
+
method(_void(), array(AddonPageInfoSchema).readonly());
|
|
19065
|
+
/**
|
|
19066
|
+
* `addon-pages-source` — collection cap exposing per-provider raw page
|
|
19067
|
+
* declarations. Every addon that contributes a UI page registers a
|
|
19068
|
+
* provider here. The hub-side singleton aggregator (`addon-pages` cap,
|
|
19069
|
+
* see `addon-pages.cap.ts`) walks this collection, stamps versioned
|
|
19070
|
+
* `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
|
|
19071
|
+
* that admin-ui consumes.
|
|
19072
|
+
*
|
|
19073
|
+
* The split exists because the public listing has a different output
|
|
19074
|
+
* shape than the per-provider raw declarations, and we want both ends
|
|
19075
|
+
* to flow through codegen instead of relying on a hand-written wrapper.
|
|
19076
|
+
*/
|
|
19077
|
+
var AddonPageDeclarationSchema = object({
|
|
19078
|
+
id: string(),
|
|
19079
|
+
label: string(),
|
|
19080
|
+
icon: string(),
|
|
19081
|
+
path: string(),
|
|
19082
|
+
/**
|
|
19083
|
+
* Module Federation remote name — must match the `name` field on the
|
|
19084
|
+
* page addon's `federation()` plugin config. Used by admin-ui's
|
|
19085
|
+
* `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
|
|
19086
|
+
* Conventionally `addon_<id>_page` (snake_case; MF names cannot
|
|
19087
|
+
* contain hyphens).
|
|
19088
|
+
*/
|
|
19089
|
+
remoteName: string(),
|
|
19090
|
+
/**
|
|
19091
|
+
* Bundle filename inside the addon's `dist/` dir served at
|
|
19092
|
+
* `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
|
|
19093
|
+
* is always `'remoteEntry.js'`; the value is kept on the metadata so
|
|
19094
|
+
* the static-file route can compute an mtime-based cache-buster URL
|
|
19095
|
+
* without a separate filesystem stat.
|
|
18357
19096
|
*/
|
|
18358
19097
|
bundle: string(),
|
|
18359
19098
|
/**
|
|
@@ -20002,7 +20741,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
20002
20741
|
*/
|
|
20003
20742
|
interactiveConnectPath: string().min(1).optional(),
|
|
20004
20743
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
20005
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
20744
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
20745
|
+
/**
|
|
20746
|
+
* The kind signs in through a server-held interactive session instead of
|
|
20747
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
20748
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
20749
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
20750
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
20751
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
20752
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
20753
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
20754
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
20755
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
20756
|
+
* offers it as the default path; the fields the session fills are
|
|
20757
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
20758
|
+
*/
|
|
20759
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
20006
20760
|
}))
|
|
20007
20761
|
});
|
|
20008
20762
|
var ListInputSchema = object({
|
|
@@ -21327,7 +22081,7 @@ var CompositionEventPlanSchema = object({
|
|
|
21327
22081
|
stateField: string(),
|
|
21328
22082
|
fieldKey: string()
|
|
21329
22083
|
});
|
|
21330
|
-
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.
|
|
22084
|
+
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
|
|
21331
22085
|
var CompositionProblemSchema = object({
|
|
21332
22086
|
code: _enum([
|
|
21333
22087
|
"reserved-variant",
|
|
@@ -21646,7 +22400,7 @@ var GROUP_TEMPLATES = [
|
|
|
21646
22400
|
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
21647
22401
|
role: DeviceRole.TemperatureSensor,
|
|
21648
22402
|
outputCap: "temperature-sensor",
|
|
21649
|
-
outputField: "
|
|
22403
|
+
outputField: "temperature",
|
|
21650
22404
|
quantity: "temperature",
|
|
21651
22405
|
defaultAggregate: "mean",
|
|
21652
22406
|
empty: "null",
|
|
@@ -21657,7 +22411,7 @@ var GROUP_TEMPLATES = [
|
|
|
21657
22411
|
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
21658
22412
|
role: DeviceRole.HumiditySensor,
|
|
21659
22413
|
outputCap: "humidity-sensor",
|
|
21660
|
-
outputField: "
|
|
22414
|
+
outputField: "humidity",
|
|
21661
22415
|
quantity: "humidity",
|
|
21662
22416
|
defaultAggregate: "mean",
|
|
21663
22417
|
empty: "null",
|
|
@@ -21668,7 +22422,7 @@ var GROUP_TEMPLATES = [
|
|
|
21668
22422
|
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
21669
22423
|
role: DeviceRole.PressureSensor,
|
|
21670
22424
|
outputCap: "pressure-sensor",
|
|
21671
|
-
outputField: "
|
|
22425
|
+
outputField: "pressure",
|
|
21672
22426
|
quantity: "pressure",
|
|
21673
22427
|
defaultAggregate: "mean",
|
|
21674
22428
|
empty: "null",
|
|
@@ -21679,7 +22433,7 @@ var GROUP_TEMPLATES = [
|
|
|
21679
22433
|
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
21680
22434
|
role: DeviceRole.AmbientLightSensor,
|
|
21681
22435
|
outputCap: "ambient-light-sensor",
|
|
21682
|
-
outputField: "
|
|
22436
|
+
outputField: "illuminance",
|
|
21683
22437
|
quantity: "illuminance",
|
|
21684
22438
|
defaultAggregate: "mean",
|
|
21685
22439
|
empty: "null",
|
|
@@ -21690,7 +22444,7 @@ var GROUP_TEMPLATES = [
|
|
|
21690
22444
|
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
21691
22445
|
role: DeviceRole.PowerSensor,
|
|
21692
22446
|
outputCap: "power-meter",
|
|
21693
|
-
outputField: "
|
|
22447
|
+
outputField: "power",
|
|
21694
22448
|
quantity: "power",
|
|
21695
22449
|
defaultAggregate: "sum",
|
|
21696
22450
|
empty: "omit",
|
|
@@ -21701,7 +22455,7 @@ var GROUP_TEMPLATES = [
|
|
|
21701
22455
|
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
21702
22456
|
role: DeviceRole.EnergySensor,
|
|
21703
22457
|
outputCap: "power-meter",
|
|
21704
|
-
outputField: "
|
|
22458
|
+
outputField: "energy",
|
|
21705
22459
|
quantity: "energy",
|
|
21706
22460
|
defaultAggregate: "sum",
|
|
21707
22461
|
empty: "omit",
|
|
@@ -21712,7 +22466,7 @@ var GROUP_TEMPLATES = [
|
|
|
21712
22466
|
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
21713
22467
|
role: DeviceRole.BatterySensor,
|
|
21714
22468
|
outputCap: "battery",
|
|
21715
|
-
outputField: "
|
|
22469
|
+
outputField: "level",
|
|
21716
22470
|
quantity: "percentage",
|
|
21717
22471
|
defaultAggregate: "mean",
|
|
21718
22472
|
empty: "null",
|
|
@@ -22290,7 +23044,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
22290
23044
|
kind: "mutation",
|
|
22291
23045
|
auth: "admin",
|
|
22292
23046
|
access: "view"
|
|
22293
|
-
})
|
|
23047
|
+
}), method(object({
|
|
23048
|
+
blockId: string().min(1),
|
|
23049
|
+
trace: AutomationRunTraceSchema.optional(),
|
|
23050
|
+
runStartedAt: number().optional(),
|
|
23051
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
23052
|
+
}), _void(), {
|
|
23053
|
+
kind: "mutation",
|
|
23054
|
+
auth: "admin"
|
|
23055
|
+
}), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
|
|
23056
|
+
traces: array(AutomationRunTraceSchema),
|
|
23057
|
+
lastError: string().nullable()
|
|
23058
|
+
}), { auth: "admin" });
|
|
22294
23059
|
/**
|
|
22295
23060
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
22296
23061
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
@@ -47109,7 +47874,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
47109
47874
|
});
|
|
47110
47875
|
/**
|
|
47111
47876
|
* Optional, richer endpoint shape returned by providers that expose
|
|
47112
|
-
* MORE than one ingress concurrently (Tailscale
|
|
47877
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
47113
47878
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
47114
47879
|
* the originating provider config (mode + sourcePort) so the
|
|
47115
47880
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -47146,729 +47911,362 @@ method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _v
|
|
|
47146
47911
|
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
47147
47912
|
* alternative would fork the UI per addon and cannot host the
|
|
47148
47913
|
* discovery→adopt flow.
|
|
47149
|
-
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
47150
|
-
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
47151
|
-
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
47152
|
-
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
47153
|
-
* `addonId` the generated collection router extracts from the call input.
|
|
47154
|
-
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
47155
|
-
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
47156
|
-
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
47157
|
-
* base64 fallback needed.
|
|
47158
|
-
*
|
|
47159
|
-
* TODO (deferred, closed-set change — separate decision): add
|
|
47160
|
-
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
47161
|
-
* admin "Integrations" page.
|
|
47162
|
-
*/
|
|
47163
|
-
/**
|
|
47164
|
-
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
47165
|
-
* adapter picks what it supports and the degrade engine filters the rest.
|
|
47166
|
-
*/
|
|
47167
|
-
var AttachmentMediaTypeSchema = _enum([
|
|
47168
|
-
"image",
|
|
47169
|
-
"video",
|
|
47170
|
-
"gif",
|
|
47171
|
-
"audio",
|
|
47172
|
-
"icon"
|
|
47173
|
-
]);
|
|
47174
|
-
/**
|
|
47175
|
-
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
47176
|
-
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
47177
|
-
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
47178
|
-
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
47179
|
-
*/
|
|
47180
|
-
var AttachmentSchema = object({
|
|
47181
|
-
mediaType: AttachmentMediaTypeSchema,
|
|
47182
|
-
url: string().optional(),
|
|
47183
|
-
bytes: _instanceof(Uint8Array).optional(),
|
|
47184
|
-
mime: string().optional(),
|
|
47185
|
-
name: string().optional()
|
|
47186
|
-
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
47187
|
-
var NotificationFormatSchema = _enum([
|
|
47188
|
-
"text",
|
|
47189
|
-
"markdown",
|
|
47190
|
-
"html"
|
|
47191
|
-
]);
|
|
47192
|
-
/**
|
|
47193
|
-
* The CLOSED icon vocabulary an action button may use.
|
|
47194
|
-
*
|
|
47195
|
-
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
47196
|
-
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
47197
|
-
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
47198
|
-
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
47199
|
-
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
47200
|
-
*
|
|
47201
|
-
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
47202
|
-
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
47203
|
-
* differently.
|
|
47204
|
-
*
|
|
47205
|
-
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
47206
|
-
*
|
|
47207
|
-
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
47208
|
-
* the whole set onto its own renderer's vocabulary through a
|
|
47209
|
-
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
47210
|
-
* fallback, so adding a member here fails every adapter's build until someone
|
|
47211
|
-
* decides its glyph, which is the only place that decision can be made
|
|
47212
|
-
* honestly.
|
|
47213
|
-
*
|
|
47214
|
-
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
47215
|
-
* `disarm` straight through; iOS feeds that string to
|
|
47216
|
-
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
47217
|
-
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
47218
|
-
* mapping, and "the field is documented" is not "the value renders".
|
|
47219
|
-
*
|
|
47220
|
-
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
47221
|
-
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
47222
|
-
* so an addon that emits a token the running hub does not know does not lose an
|
|
47223
|
-
* icon — its whole `send` fails Zod validation and the notification never
|
|
47224
|
-
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
47225
|
-
*/
|
|
47226
|
-
var NotificationActionIconSchema = _enum([
|
|
47227
|
-
"acknowledge",
|
|
47228
|
-
"dismiss",
|
|
47229
|
-
"silence",
|
|
47230
|
-
"snooze",
|
|
47231
|
-
"view",
|
|
47232
|
-
"play",
|
|
47233
|
-
"open",
|
|
47234
|
-
"close",
|
|
47235
|
-
"lock",
|
|
47236
|
-
"unlock",
|
|
47237
|
-
"arm",
|
|
47238
|
-
"arm-home",
|
|
47239
|
-
"arm-away",
|
|
47240
|
-
"arm-night",
|
|
47241
|
-
"disarm",
|
|
47242
|
-
"light",
|
|
47243
|
-
"alert",
|
|
47244
|
-
"camera"
|
|
47245
|
-
]);
|
|
47246
|
-
/** A single tap-through action button. */
|
|
47247
|
-
var NotificationActionSchema = object({
|
|
47248
|
-
id: string(),
|
|
47249
|
-
label: string(),
|
|
47250
|
-
url: string().optional(),
|
|
47251
|
-
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
47252
|
-
icon: NotificationActionIconSchema.optional(),
|
|
47253
|
-
/**
|
|
47254
|
-
* Renders in a warning style where the notifier supports it.
|
|
47255
|
-
*
|
|
47256
|
-
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
47257
|
-
* else — see `notification-center/action-token.ts` for what that does and
|
|
47258
|
-
* does not buy.
|
|
47259
|
-
*/
|
|
47260
|
-
destructive: boolean().optional(),
|
|
47261
|
-
/**
|
|
47262
|
-
* How the tap should REACH the url.
|
|
47263
|
-
*
|
|
47264
|
-
* `navigate` (absent, and every button authored before this field) opens it:
|
|
47265
|
-
* the phone leaves the notification and shows whatever the callback returns.
|
|
47266
|
-
* That is right for a button whose answer the operator wants to read.
|
|
47267
|
-
*
|
|
47268
|
-
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
47269
|
-
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
47270
|
-
* an answer to the notification, and being thrown into a browser tab to
|
|
47271
|
-
* confirm it costs more attention than the notification did. A backend that
|
|
47272
|
-
* cannot do a background call renders it as an ordinary link (the adapters
|
|
47273
|
-
* fall back rather than dropping the button), so this is a preference, never
|
|
47274
|
-
* a requirement.
|
|
47275
|
-
*/
|
|
47276
|
-
mode: _enum(["navigate", "background"]).optional()
|
|
47277
|
-
});
|
|
47278
|
-
/**
|
|
47279
|
-
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
47280
|
-
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
47281
|
-
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
47282
|
-
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
47283
|
-
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
47284
|
-
* `priority` for that one target.
|
|
47285
|
-
*/
|
|
47286
|
-
var NotificationSchema = object({
|
|
47287
|
-
body: string(),
|
|
47288
|
-
title: string().optional(),
|
|
47289
|
-
format: NotificationFormatSchema.default("text"),
|
|
47290
|
-
priority: number().int().min(1).max(5).default(3),
|
|
47291
|
-
level: string().optional(),
|
|
47292
|
-
attachments: array(AttachmentSchema).optional(),
|
|
47293
|
-
clickUrl: string().optional(),
|
|
47294
|
-
actions: array(NotificationActionSchema).optional(),
|
|
47295
|
-
sound: string().optional(),
|
|
47296
|
-
ttl: number().optional(),
|
|
47297
|
-
tag: string().optional(),
|
|
47298
|
-
deviceId: number().optional(),
|
|
47299
|
-
eventId: string().optional(),
|
|
47300
|
-
metadata: record(string(), unknown()).optional()
|
|
47301
|
-
});
|
|
47302
|
-
/** One declared native severity/priority level for a kind. */
|
|
47303
|
-
var TargetKindLevelSchema = object({
|
|
47304
|
-
id: string(),
|
|
47305
|
-
label: string(),
|
|
47306
|
-
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
47307
|
-
ordinal: number().int().min(1).max(5).nullable(),
|
|
47308
|
-
flags: object({
|
|
47309
|
-
critical: boolean().optional(),
|
|
47310
|
-
silent: boolean().optional(),
|
|
47311
|
-
noPush: boolean().optional()
|
|
47312
|
-
}).optional(),
|
|
47313
|
-
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
47314
|
-
requires: array(string()).optional(),
|
|
47315
|
-
description: string().optional()
|
|
47316
|
-
});
|
|
47317
|
-
/** The full capability block consulted before dispatch. */
|
|
47318
|
-
var TargetKindCapsSchema = object({
|
|
47319
|
-
attachments: object({
|
|
47320
|
-
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
47321
|
-
mode: _enum([
|
|
47322
|
-
"url",
|
|
47323
|
-
"bytes",
|
|
47324
|
-
"both"
|
|
47325
|
-
]),
|
|
47326
|
-
max: number().int().nonnegative(),
|
|
47327
|
-
maxBytes: number().int().positive().optional()
|
|
47328
|
-
}),
|
|
47329
|
-
/** Max action buttons (0 = none). */
|
|
47330
|
-
actions: number().int().nonnegative(),
|
|
47331
|
-
/**
|
|
47332
|
-
* Whether this kind renders a per-action ICON.
|
|
47333
|
-
*
|
|
47334
|
-
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
47335
|
-
* run on the addon cap path — three production failures in one day taught
|
|
47336
|
-
* this repo that once. Absent is read as false by the degrade engine, which
|
|
47337
|
-
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
47338
|
-
* assumed and dropped costs the operator's trust in the field.
|
|
47339
|
-
*/
|
|
47340
|
-
actionIcons: boolean().optional(),
|
|
47341
|
-
levels: array(TargetKindLevelSchema),
|
|
47342
|
-
format: array(NotificationFormatSchema),
|
|
47343
|
-
clickUrl: boolean(),
|
|
47344
|
-
sound: boolean(),
|
|
47345
|
-
ttl: boolean(),
|
|
47346
|
-
bodyMaxLen: number().int().positive()
|
|
47347
|
-
});
|
|
47348
|
-
/**
|
|
47349
|
-
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
47350
|
-
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
47351
|
-
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
47352
|
-
* the union is large and not meant for runtime validation here; the exported
|
|
47353
|
-
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
47354
|
-
*/
|
|
47355
|
-
var ConfigSchemaPassthrough$2 = unknown();
|
|
47356
|
-
var TargetKindSchema = object({
|
|
47357
|
-
kind: string(),
|
|
47358
|
-
label: string(),
|
|
47359
|
-
icon: string(),
|
|
47360
|
-
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
47361
|
-
addonId: string(),
|
|
47362
|
-
/**
|
|
47363
|
-
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
47364
|
-
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
47365
|
-
* when the addon bundles no icon for that kind — the client then falls back
|
|
47366
|
-
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
47367
|
-
*
|
|
47368
|
-
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
47369
|
-
* client, and a native client joins it onto its own hub base.
|
|
47370
|
-
*
|
|
47371
|
-
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
47372
|
-
* field that survived only because the runtime cap-router forwards provider
|
|
47373
|
-
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
47374
|
-
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
47375
|
-
* broken silently the moment output validation was tightened anywhere.
|
|
47376
|
-
*/
|
|
47377
|
-
iconUrl: string().optional(),
|
|
47378
|
-
/**
|
|
47379
|
-
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
47380
|
-
*
|
|
47381
|
-
* The server knows this and therefore says it, because the client cannot
|
|
47382
|
-
* safely guess: a React-Native client renders SVG and raster through two
|
|
47383
|
-
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
47384
|
-
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
47385
|
-
* placeholder glyph for every vector icon while the web build looked fine.
|
|
47386
|
-
*
|
|
47387
|
-
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
47388
|
-
* not been updated — a client that cannot determine the type should prefer
|
|
47389
|
-
* its raster path, which is the safe default for an unknown image.
|
|
47390
|
-
*/
|
|
47391
|
-
iconMediaType: string().optional(),
|
|
47392
|
-
configSchema: ConfigSchemaPassthrough$2,
|
|
47393
|
-
/**
|
|
47394
|
-
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
47395
|
-
* base URL, instance, chat, recipient). A save that changes any of them
|
|
47396
|
-
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
47397
|
-
* editor uses this to ask for the secret on exactly those edits instead of
|
|
47398
|
-
* on every edit.
|
|
47399
|
-
*
|
|
47400
|
-
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
47401
|
-
* kinds) and from a provider older than this field — an editor that finds
|
|
47402
|
-
* it absent must assume ANY edit may need the secret.
|
|
47403
|
-
*/
|
|
47404
|
-
destinationKeys: array(string()).optional(),
|
|
47405
|
-
supportsDiscovery: boolean(),
|
|
47406
|
-
caps: TargetKindCapsSchema
|
|
47407
|
-
});
|
|
47408
|
-
/**
|
|
47409
|
-
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
47410
|
-
* (return a presence marker only) when serving `listTargets` — never
|
|
47411
|
-
* round-trip a stored secret to the UI.
|
|
47412
|
-
*/
|
|
47413
|
-
var TargetSchema = object({
|
|
47414
|
-
id: string(),
|
|
47415
|
-
name: string(),
|
|
47416
|
-
kind: string(),
|
|
47417
|
-
addonId: string(),
|
|
47418
|
-
enabled: boolean(),
|
|
47419
|
-
config: record(string(), unknown())
|
|
47420
|
-
});
|
|
47421
|
-
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
47422
|
-
var DiscoveredTargetSchema = object({
|
|
47423
|
-
kind: string(),
|
|
47424
|
-
suggestedName: string(),
|
|
47425
|
-
config: record(string(), unknown())
|
|
47426
|
-
});
|
|
47427
|
-
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
47428
|
-
var RenderedAsSchema = object({
|
|
47429
|
-
level: string(),
|
|
47430
|
-
format: NotificationFormatSchema,
|
|
47431
|
-
attachmentsSent: number().int().nonnegative(),
|
|
47432
|
-
actionsSent: number().int().nonnegative(),
|
|
47433
|
-
truncated: boolean(),
|
|
47434
|
-
dropped: array(string())
|
|
47435
|
-
});
|
|
47436
|
-
var SendResultSchema = object({
|
|
47437
|
-
success: boolean(),
|
|
47438
|
-
error: string().optional(),
|
|
47439
|
-
renderedAs: RenderedAsSchema.optional()
|
|
47440
|
-
});
|
|
47441
|
-
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
47442
|
-
var TestResultSchema = SendResultSchema;
|
|
47443
|
-
/**
|
|
47444
|
-
* Pre-save credential probe against a partial (unsaved) target config.
|
|
47445
|
-
* `resolvedUrl` never includes secrets (token stripped from path).
|
|
47446
|
-
*/
|
|
47447
|
-
var ProbeTargetConfigResultSchema = object({
|
|
47448
|
-
ok: boolean(),
|
|
47449
|
-
resolvedUrl: string(),
|
|
47450
|
-
httpStatus: number().int().optional(),
|
|
47451
|
-
stateInstance: string().optional(),
|
|
47452
|
-
error: string().optional()
|
|
47453
|
-
});
|
|
47454
|
-
method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({ targetId: string() }), TargetSchema, { auth: "admin" }), method(object({
|
|
47455
|
-
kind: string(),
|
|
47456
|
-
config: record(string(), unknown()).optional(),
|
|
47457
|
-
/**
|
|
47458
|
-
* When editing / duplicating, the blank password field never round-trips
|
|
47459
|
-
* the stored token. Pass the source target id so the provider can restore
|
|
47460
|
-
* secrets server-side before dialling (same merge as upsert).
|
|
47461
|
-
*/
|
|
47462
|
-
targetId: string().optional()
|
|
47463
|
-
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
47464
|
-
kind: string(),
|
|
47465
|
-
config: record(string(), unknown()),
|
|
47466
|
-
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
47467
|
-
targetId: string().optional()
|
|
47468
|
-
}), ProbeTargetConfigResultSchema, {
|
|
47469
|
-
kind: "mutation",
|
|
47470
|
-
auth: "admin"
|
|
47471
|
-
}), method(object({
|
|
47472
|
-
targetId: string(),
|
|
47473
|
-
notification: NotificationSchema
|
|
47474
|
-
}), SendResultSchema, {
|
|
47475
|
-
kind: "mutation",
|
|
47476
|
-
auth: "admin"
|
|
47477
|
-
}), method(object({
|
|
47478
|
-
targetId: string(),
|
|
47479
|
-
sample: NotificationSchema.optional()
|
|
47480
|
-
}), TestResultSchema, {
|
|
47481
|
-
kind: "mutation",
|
|
47482
|
-
auth: "admin"
|
|
47483
|
-
}), method(object({
|
|
47484
|
-
target: TargetSchema,
|
|
47485
|
-
/**
|
|
47486
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
47487
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
47488
|
-
* password fields are restored from this source (D634 still applies if
|
|
47489
|
-
* destinations diverge without a re-typed secret).
|
|
47490
|
-
*/
|
|
47491
|
-
cloneSecretsFrom: string().optional()
|
|
47492
|
-
}), TargetSchema, {
|
|
47493
|
-
kind: "mutation",
|
|
47494
|
-
auth: "admin"
|
|
47495
|
-
}), method(object({ targetId: string() }), _void(), {
|
|
47496
|
-
kind: "mutation",
|
|
47497
|
-
auth: "admin"
|
|
47498
|
-
}), method(object({
|
|
47499
|
-
targetId: string(),
|
|
47500
|
-
enabled: boolean()
|
|
47501
|
-
}), _void(), {
|
|
47502
|
-
kind: "mutation",
|
|
47503
|
-
auth: "admin"
|
|
47504
|
-
});
|
|
47505
|
-
var COCO_80_LABELS = [
|
|
47506
|
-
{
|
|
47507
|
-
id: "person",
|
|
47508
|
-
name: "Person"
|
|
47509
|
-
},
|
|
47510
|
-
{
|
|
47511
|
-
id: "bicycle",
|
|
47512
|
-
name: "Bicycle"
|
|
47513
|
-
},
|
|
47514
|
-
{
|
|
47515
|
-
id: "car",
|
|
47516
|
-
name: "Car"
|
|
47517
|
-
},
|
|
47518
|
-
{
|
|
47519
|
-
id: "motorcycle",
|
|
47520
|
-
name: "Motorcycle"
|
|
47521
|
-
},
|
|
47522
|
-
{
|
|
47523
|
-
id: "airplane",
|
|
47524
|
-
name: "Airplane"
|
|
47525
|
-
},
|
|
47526
|
-
{
|
|
47527
|
-
id: "bus",
|
|
47528
|
-
name: "Bus"
|
|
47529
|
-
},
|
|
47530
|
-
{
|
|
47531
|
-
id: "train",
|
|
47532
|
-
name: "Train"
|
|
47533
|
-
},
|
|
47534
|
-
{
|
|
47535
|
-
id: "truck",
|
|
47536
|
-
name: "Truck"
|
|
47537
|
-
},
|
|
47538
|
-
{
|
|
47539
|
-
id: "boat",
|
|
47540
|
-
name: "Boat"
|
|
47541
|
-
},
|
|
47542
|
-
{
|
|
47543
|
-
id: "traffic light",
|
|
47544
|
-
name: "Traffic Light"
|
|
47545
|
-
},
|
|
47546
|
-
{
|
|
47547
|
-
id: "fire hydrant",
|
|
47548
|
-
name: "Fire Hydrant"
|
|
47549
|
-
},
|
|
47550
|
-
{
|
|
47551
|
-
id: "stop sign",
|
|
47552
|
-
name: "Stop Sign"
|
|
47553
|
-
},
|
|
47554
|
-
{
|
|
47555
|
-
id: "parking meter",
|
|
47556
|
-
name: "Parking Meter"
|
|
47557
|
-
},
|
|
47558
|
-
{
|
|
47559
|
-
id: "bench",
|
|
47560
|
-
name: "Bench"
|
|
47561
|
-
},
|
|
47562
|
-
{
|
|
47563
|
-
id: "bird",
|
|
47564
|
-
name: "Bird"
|
|
47565
|
-
},
|
|
47566
|
-
{
|
|
47567
|
-
id: "cat",
|
|
47568
|
-
name: "Cat"
|
|
47569
|
-
},
|
|
47570
|
-
{
|
|
47571
|
-
id: "dog",
|
|
47572
|
-
name: "Dog"
|
|
47573
|
-
},
|
|
47574
|
-
{
|
|
47575
|
-
id: "horse",
|
|
47576
|
-
name: "Horse"
|
|
47577
|
-
},
|
|
47578
|
-
{
|
|
47579
|
-
id: "sheep",
|
|
47580
|
-
name: "Sheep"
|
|
47581
|
-
},
|
|
47582
|
-
{
|
|
47583
|
-
id: "cow",
|
|
47584
|
-
name: "Cow"
|
|
47585
|
-
},
|
|
47586
|
-
{
|
|
47587
|
-
id: "elephant",
|
|
47588
|
-
name: "Elephant"
|
|
47589
|
-
},
|
|
47590
|
-
{
|
|
47591
|
-
id: "bear",
|
|
47592
|
-
name: "Bear"
|
|
47593
|
-
},
|
|
47594
|
-
{
|
|
47595
|
-
id: "zebra",
|
|
47596
|
-
name: "Zebra"
|
|
47597
|
-
},
|
|
47598
|
-
{
|
|
47599
|
-
id: "giraffe",
|
|
47600
|
-
name: "Giraffe"
|
|
47601
|
-
},
|
|
47602
|
-
{
|
|
47603
|
-
id: "backpack",
|
|
47604
|
-
name: "Backpack"
|
|
47605
|
-
},
|
|
47606
|
-
{
|
|
47607
|
-
id: "umbrella",
|
|
47608
|
-
name: "Umbrella"
|
|
47609
|
-
},
|
|
47610
|
-
{
|
|
47611
|
-
id: "handbag",
|
|
47612
|
-
name: "Handbag"
|
|
47613
|
-
},
|
|
47614
|
-
{
|
|
47615
|
-
id: "tie",
|
|
47616
|
-
name: "Tie"
|
|
47617
|
-
},
|
|
47618
|
-
{
|
|
47619
|
-
id: "suitcase",
|
|
47620
|
-
name: "Suitcase"
|
|
47621
|
-
},
|
|
47622
|
-
{
|
|
47623
|
-
id: "frisbee",
|
|
47624
|
-
name: "Frisbee"
|
|
47625
|
-
},
|
|
47626
|
-
{
|
|
47627
|
-
id: "skis",
|
|
47628
|
-
name: "Skis"
|
|
47629
|
-
},
|
|
47630
|
-
{
|
|
47631
|
-
id: "snowboard",
|
|
47632
|
-
name: "Snowboard"
|
|
47633
|
-
},
|
|
47634
|
-
{
|
|
47635
|
-
id: "sports ball",
|
|
47636
|
-
name: "Sports Ball"
|
|
47637
|
-
},
|
|
47638
|
-
{
|
|
47639
|
-
id: "kite",
|
|
47640
|
-
name: "Kite"
|
|
47641
|
-
},
|
|
47642
|
-
{
|
|
47643
|
-
id: "baseball bat",
|
|
47644
|
-
name: "Baseball Bat"
|
|
47645
|
-
},
|
|
47646
|
-
{
|
|
47647
|
-
id: "baseball glove",
|
|
47648
|
-
name: "Baseball Glove"
|
|
47649
|
-
},
|
|
47650
|
-
{
|
|
47651
|
-
id: "skateboard",
|
|
47652
|
-
name: "Skateboard"
|
|
47653
|
-
},
|
|
47654
|
-
{
|
|
47655
|
-
id: "surfboard",
|
|
47656
|
-
name: "Surfboard"
|
|
47657
|
-
},
|
|
47658
|
-
{
|
|
47659
|
-
id: "tennis racket",
|
|
47660
|
-
name: "Tennis Racket"
|
|
47661
|
-
},
|
|
47662
|
-
{
|
|
47663
|
-
id: "bottle",
|
|
47664
|
-
name: "Bottle"
|
|
47665
|
-
},
|
|
47666
|
-
{
|
|
47667
|
-
id: "wine glass",
|
|
47668
|
-
name: "Wine Glass"
|
|
47669
|
-
},
|
|
47670
|
-
{
|
|
47671
|
-
id: "cup",
|
|
47672
|
-
name: "Cup"
|
|
47673
|
-
},
|
|
47674
|
-
{
|
|
47675
|
-
id: "fork",
|
|
47676
|
-
name: "Fork"
|
|
47677
|
-
},
|
|
47678
|
-
{
|
|
47679
|
-
id: "knife",
|
|
47680
|
-
name: "Knife"
|
|
47681
|
-
},
|
|
47682
|
-
{
|
|
47683
|
-
id: "spoon",
|
|
47684
|
-
name: "Spoon"
|
|
47685
|
-
},
|
|
47686
|
-
{
|
|
47687
|
-
id: "bowl",
|
|
47688
|
-
name: "Bowl"
|
|
47689
|
-
},
|
|
47690
|
-
{
|
|
47691
|
-
id: "banana",
|
|
47692
|
-
name: "Banana"
|
|
47693
|
-
},
|
|
47694
|
-
{
|
|
47695
|
-
id: "apple",
|
|
47696
|
-
name: "Apple"
|
|
47697
|
-
},
|
|
47698
|
-
{
|
|
47699
|
-
id: "sandwich",
|
|
47700
|
-
name: "Sandwich"
|
|
47701
|
-
},
|
|
47702
|
-
{
|
|
47703
|
-
id: "orange",
|
|
47704
|
-
name: "Orange"
|
|
47705
|
-
},
|
|
47706
|
-
{
|
|
47707
|
-
id: "broccoli",
|
|
47708
|
-
name: "Broccoli"
|
|
47709
|
-
},
|
|
47710
|
-
{
|
|
47711
|
-
id: "carrot",
|
|
47712
|
-
name: "Carrot"
|
|
47713
|
-
},
|
|
47714
|
-
{
|
|
47715
|
-
id: "hot dog",
|
|
47716
|
-
name: "Hot Dog"
|
|
47717
|
-
},
|
|
47718
|
-
{
|
|
47719
|
-
id: "pizza",
|
|
47720
|
-
name: "Pizza"
|
|
47721
|
-
},
|
|
47722
|
-
{
|
|
47723
|
-
id: "donut",
|
|
47724
|
-
name: "Donut"
|
|
47725
|
-
},
|
|
47726
|
-
{
|
|
47727
|
-
id: "cake",
|
|
47728
|
-
name: "Cake"
|
|
47729
|
-
},
|
|
47730
|
-
{
|
|
47731
|
-
id: "chair",
|
|
47732
|
-
name: "Chair"
|
|
47733
|
-
},
|
|
47734
|
-
{
|
|
47735
|
-
id: "couch",
|
|
47736
|
-
name: "Couch"
|
|
47737
|
-
},
|
|
47738
|
-
{
|
|
47739
|
-
id: "potted plant",
|
|
47740
|
-
name: "Potted Plant"
|
|
47741
|
-
},
|
|
47742
|
-
{
|
|
47743
|
-
id: "bed",
|
|
47744
|
-
name: "Bed"
|
|
47745
|
-
},
|
|
47746
|
-
{
|
|
47747
|
-
id: "dining table",
|
|
47748
|
-
name: "Dining Table"
|
|
47749
|
-
},
|
|
47750
|
-
{
|
|
47751
|
-
id: "toilet",
|
|
47752
|
-
name: "Toilet"
|
|
47753
|
-
},
|
|
47754
|
-
{
|
|
47755
|
-
id: "tv",
|
|
47756
|
-
name: "TV"
|
|
47757
|
-
},
|
|
47758
|
-
{
|
|
47759
|
-
id: "laptop",
|
|
47760
|
-
name: "Laptop"
|
|
47761
|
-
},
|
|
47762
|
-
{
|
|
47763
|
-
id: "mouse",
|
|
47764
|
-
name: "Mouse"
|
|
47765
|
-
},
|
|
47766
|
-
{
|
|
47767
|
-
id: "remote",
|
|
47768
|
-
name: "Remote"
|
|
47769
|
-
},
|
|
47770
|
-
{
|
|
47771
|
-
id: "keyboard",
|
|
47772
|
-
name: "Keyboard"
|
|
47773
|
-
},
|
|
47774
|
-
{
|
|
47775
|
-
id: "cell phone",
|
|
47776
|
-
name: "Cell Phone"
|
|
47777
|
-
},
|
|
47778
|
-
{
|
|
47779
|
-
id: "microwave",
|
|
47780
|
-
name: "Microwave"
|
|
47781
|
-
},
|
|
47782
|
-
{
|
|
47783
|
-
id: "oven",
|
|
47784
|
-
name: "Oven"
|
|
47785
|
-
},
|
|
47786
|
-
{
|
|
47787
|
-
id: "toaster",
|
|
47788
|
-
name: "Toaster"
|
|
47789
|
-
},
|
|
47790
|
-
{
|
|
47791
|
-
id: "sink",
|
|
47792
|
-
name: "Sink"
|
|
47793
|
-
},
|
|
47794
|
-
{
|
|
47795
|
-
id: "refrigerator",
|
|
47796
|
-
name: "Refrigerator"
|
|
47797
|
-
},
|
|
47798
|
-
{
|
|
47799
|
-
id: "book",
|
|
47800
|
-
name: "Book"
|
|
47801
|
-
},
|
|
47802
|
-
{
|
|
47803
|
-
id: "clock",
|
|
47804
|
-
name: "Clock"
|
|
47805
|
-
},
|
|
47806
|
-
{
|
|
47807
|
-
id: "vase",
|
|
47808
|
-
name: "Vase"
|
|
47809
|
-
},
|
|
47810
|
-
{
|
|
47811
|
-
id: "scissors",
|
|
47812
|
-
name: "Scissors"
|
|
47813
|
-
},
|
|
47814
|
-
{
|
|
47815
|
-
id: "teddy bear",
|
|
47816
|
-
name: "Teddy Bear"
|
|
47817
|
-
},
|
|
47818
|
-
{
|
|
47819
|
-
id: "hair drier",
|
|
47820
|
-
name: "Hair Drier"
|
|
47821
|
-
},
|
|
47822
|
-
{
|
|
47823
|
-
id: "toothbrush",
|
|
47824
|
-
name: "Toothbrush"
|
|
47825
|
-
}
|
|
47826
|
-
];
|
|
47827
|
-
new Set([
|
|
47828
|
-
{
|
|
47829
|
-
id: "person",
|
|
47830
|
-
name: "Person"
|
|
47831
|
-
},
|
|
47832
|
-
{
|
|
47833
|
-
id: "vehicle",
|
|
47834
|
-
name: "Vehicle"
|
|
47835
|
-
},
|
|
47836
|
-
{
|
|
47837
|
-
id: "animal",
|
|
47838
|
-
name: "Animal"
|
|
47839
|
-
},
|
|
47840
|
-
{
|
|
47841
|
-
id: "package",
|
|
47842
|
-
name: "Package"
|
|
47843
|
-
}
|
|
47844
|
-
].map((l) => l.id));
|
|
47845
|
-
var COCO_TO_MACRO = {
|
|
47846
|
-
mapping: {
|
|
47847
|
-
person: "person",
|
|
47848
|
-
bicycle: "vehicle",
|
|
47849
|
-
car: "vehicle",
|
|
47850
|
-
motorcycle: "vehicle",
|
|
47851
|
-
airplane: "vehicle",
|
|
47852
|
-
bus: "vehicle",
|
|
47853
|
-
train: "vehicle",
|
|
47854
|
-
truck: "vehicle",
|
|
47855
|
-
boat: "vehicle",
|
|
47856
|
-
bird: "animal",
|
|
47857
|
-
cat: "animal",
|
|
47858
|
-
dog: "animal",
|
|
47859
|
-
horse: "animal",
|
|
47860
|
-
sheep: "animal",
|
|
47861
|
-
cow: "animal",
|
|
47862
|
-
elephant: "animal",
|
|
47863
|
-
bear: "animal",
|
|
47864
|
-
zebra: "animal",
|
|
47865
|
-
giraffe: "animal",
|
|
47866
|
-
suitcase: "package",
|
|
47867
|
-
backpack: "package",
|
|
47868
|
-
handbag: "package"
|
|
47869
|
-
},
|
|
47870
|
-
preserveOriginal: false
|
|
47871
|
-
};
|
|
47914
|
+
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
47915
|
+
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
47916
|
+
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
47917
|
+
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
47918
|
+
* `addonId` the generated collection router extracts from the call input.
|
|
47919
|
+
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
47920
|
+
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
47921
|
+
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
47922
|
+
* base64 fallback needed.
|
|
47923
|
+
*
|
|
47924
|
+
* TODO (deferred, closed-set change — separate decision): add
|
|
47925
|
+
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
47926
|
+
* admin "Integrations" page.
|
|
47927
|
+
*/
|
|
47928
|
+
/**
|
|
47929
|
+
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
47930
|
+
* adapter picks what it supports and the degrade engine filters the rest.
|
|
47931
|
+
*/
|
|
47932
|
+
var AttachmentMediaTypeSchema = _enum([
|
|
47933
|
+
"image",
|
|
47934
|
+
"video",
|
|
47935
|
+
"gif",
|
|
47936
|
+
"audio",
|
|
47937
|
+
"icon"
|
|
47938
|
+
]);
|
|
47939
|
+
/**
|
|
47940
|
+
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
47941
|
+
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
47942
|
+
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
47943
|
+
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
47944
|
+
*/
|
|
47945
|
+
var AttachmentSchema = object({
|
|
47946
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
47947
|
+
url: string().optional(),
|
|
47948
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
47949
|
+
mime: string().optional(),
|
|
47950
|
+
name: string().optional()
|
|
47951
|
+
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
47952
|
+
var NotificationFormatSchema = _enum([
|
|
47953
|
+
"text",
|
|
47954
|
+
"markdown",
|
|
47955
|
+
"html"
|
|
47956
|
+
]);
|
|
47957
|
+
/**
|
|
47958
|
+
* The CLOSED icon vocabulary an action button may use.
|
|
47959
|
+
*
|
|
47960
|
+
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
47961
|
+
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
47962
|
+
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
47963
|
+
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
47964
|
+
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
47965
|
+
*
|
|
47966
|
+
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
47967
|
+
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
47968
|
+
* differently.
|
|
47969
|
+
*
|
|
47970
|
+
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
47971
|
+
*
|
|
47972
|
+
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
47973
|
+
* the whole set onto its own renderer's vocabulary through a
|
|
47974
|
+
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
47975
|
+
* fallback, so adding a member here fails every adapter's build until someone
|
|
47976
|
+
* decides its glyph, which is the only place that decision can be made
|
|
47977
|
+
* honestly.
|
|
47978
|
+
*
|
|
47979
|
+
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
47980
|
+
* `disarm` straight through; iOS feeds that string to
|
|
47981
|
+
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
47982
|
+
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
47983
|
+
* mapping, and "the field is documented" is not "the value renders".
|
|
47984
|
+
*
|
|
47985
|
+
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
47986
|
+
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
47987
|
+
* so an addon that emits a token the running hub does not know does not lose an
|
|
47988
|
+
* icon — its whole `send` fails Zod validation and the notification never
|
|
47989
|
+
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
47990
|
+
*/
|
|
47991
|
+
var NotificationActionIconSchema = _enum([
|
|
47992
|
+
"acknowledge",
|
|
47993
|
+
"dismiss",
|
|
47994
|
+
"silence",
|
|
47995
|
+
"snooze",
|
|
47996
|
+
"view",
|
|
47997
|
+
"play",
|
|
47998
|
+
"open",
|
|
47999
|
+
"close",
|
|
48000
|
+
"lock",
|
|
48001
|
+
"unlock",
|
|
48002
|
+
"arm",
|
|
48003
|
+
"arm-home",
|
|
48004
|
+
"arm-away",
|
|
48005
|
+
"arm-night",
|
|
48006
|
+
"disarm",
|
|
48007
|
+
"light",
|
|
48008
|
+
"alert",
|
|
48009
|
+
"camera"
|
|
48010
|
+
]);
|
|
48011
|
+
/** A single tap-through action button. */
|
|
48012
|
+
var NotificationActionSchema = object({
|
|
48013
|
+
id: string(),
|
|
48014
|
+
label: string(),
|
|
48015
|
+
url: string().optional(),
|
|
48016
|
+
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
48017
|
+
icon: NotificationActionIconSchema.optional(),
|
|
48018
|
+
/**
|
|
48019
|
+
* Renders in a warning style where the notifier supports it.
|
|
48020
|
+
*
|
|
48021
|
+
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
48022
|
+
* else — see `notification-center/action-token.ts` for what that does and
|
|
48023
|
+
* does not buy.
|
|
48024
|
+
*/
|
|
48025
|
+
destructive: boolean().optional(),
|
|
48026
|
+
/**
|
|
48027
|
+
* How the tap should REACH the url.
|
|
48028
|
+
*
|
|
48029
|
+
* `navigate` (absent, and every button authored before this field) opens it:
|
|
48030
|
+
* the phone leaves the notification and shows whatever the callback returns.
|
|
48031
|
+
* That is right for a button whose answer the operator wants to read.
|
|
48032
|
+
*
|
|
48033
|
+
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
48034
|
+
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
48035
|
+
* an answer to the notification, and being thrown into a browser tab to
|
|
48036
|
+
* confirm it costs more attention than the notification did. A backend that
|
|
48037
|
+
* cannot do a background call renders it as an ordinary link (the adapters
|
|
48038
|
+
* fall back rather than dropping the button), so this is a preference, never
|
|
48039
|
+
* a requirement.
|
|
48040
|
+
*/
|
|
48041
|
+
mode: _enum(["navigate", "background"]).optional()
|
|
48042
|
+
});
|
|
48043
|
+
/**
|
|
48044
|
+
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
48045
|
+
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
48046
|
+
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
48047
|
+
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
48048
|
+
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
48049
|
+
* `priority` for that one target.
|
|
48050
|
+
*/
|
|
48051
|
+
var NotificationSchema = object({
|
|
48052
|
+
body: string(),
|
|
48053
|
+
title: string().optional(),
|
|
48054
|
+
format: NotificationFormatSchema.default("text"),
|
|
48055
|
+
priority: number().int().min(1).max(5).default(3),
|
|
48056
|
+
level: string().optional(),
|
|
48057
|
+
attachments: array(AttachmentSchema).optional(),
|
|
48058
|
+
clickUrl: string().optional(),
|
|
48059
|
+
actions: array(NotificationActionSchema).optional(),
|
|
48060
|
+
sound: string().optional(),
|
|
48061
|
+
ttl: number().optional(),
|
|
48062
|
+
tag: string().optional(),
|
|
48063
|
+
deviceId: number().optional(),
|
|
48064
|
+
eventId: string().optional(),
|
|
48065
|
+
metadata: record(string(), unknown()).optional()
|
|
48066
|
+
});
|
|
48067
|
+
/** One declared native severity/priority level for a kind. */
|
|
48068
|
+
var TargetKindLevelSchema = object({
|
|
48069
|
+
id: string(),
|
|
48070
|
+
label: string(),
|
|
48071
|
+
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
48072
|
+
ordinal: number().int().min(1).max(5).nullable(),
|
|
48073
|
+
flags: object({
|
|
48074
|
+
critical: boolean().optional(),
|
|
48075
|
+
silent: boolean().optional(),
|
|
48076
|
+
noPush: boolean().optional()
|
|
48077
|
+
}).optional(),
|
|
48078
|
+
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
48079
|
+
requires: array(string()).optional(),
|
|
48080
|
+
description: string().optional()
|
|
48081
|
+
});
|
|
48082
|
+
/** The full capability block consulted before dispatch. */
|
|
48083
|
+
var TargetKindCapsSchema = object({
|
|
48084
|
+
attachments: object({
|
|
48085
|
+
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
48086
|
+
mode: _enum([
|
|
48087
|
+
"url",
|
|
48088
|
+
"bytes",
|
|
48089
|
+
"both"
|
|
48090
|
+
]),
|
|
48091
|
+
max: number().int().nonnegative(),
|
|
48092
|
+
maxBytes: number().int().positive().optional()
|
|
48093
|
+
}),
|
|
48094
|
+
/** Max action buttons (0 = none). */
|
|
48095
|
+
actions: number().int().nonnegative(),
|
|
48096
|
+
/**
|
|
48097
|
+
* Whether this kind renders a per-action ICON.
|
|
48098
|
+
*
|
|
48099
|
+
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
48100
|
+
* run on the addon cap path — three production failures in one day taught
|
|
48101
|
+
* this repo that once. Absent is read as false by the degrade engine, which
|
|
48102
|
+
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
48103
|
+
* assumed and dropped costs the operator's trust in the field.
|
|
48104
|
+
*/
|
|
48105
|
+
actionIcons: boolean().optional(),
|
|
48106
|
+
levels: array(TargetKindLevelSchema),
|
|
48107
|
+
format: array(NotificationFormatSchema),
|
|
48108
|
+
clickUrl: boolean(),
|
|
48109
|
+
sound: boolean(),
|
|
48110
|
+
ttl: boolean(),
|
|
48111
|
+
bodyMaxLen: number().int().positive()
|
|
48112
|
+
});
|
|
48113
|
+
/**
|
|
48114
|
+
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
48115
|
+
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
48116
|
+
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
48117
|
+
* the union is large and not meant for runtime validation here; the exported
|
|
48118
|
+
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
48119
|
+
*/
|
|
48120
|
+
var ConfigSchemaPassthrough$2 = unknown();
|
|
48121
|
+
var TargetKindSchema = object({
|
|
48122
|
+
kind: string(),
|
|
48123
|
+
label: string(),
|
|
48124
|
+
icon: string(),
|
|
48125
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
48126
|
+
addonId: string(),
|
|
48127
|
+
/**
|
|
48128
|
+
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
48129
|
+
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
48130
|
+
* when the addon bundles no icon for that kind — the client then falls back
|
|
48131
|
+
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
48132
|
+
*
|
|
48133
|
+
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
48134
|
+
* client, and a native client joins it onto its own hub base.
|
|
48135
|
+
*
|
|
48136
|
+
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
48137
|
+
* field that survived only because the runtime cap-router forwards provider
|
|
48138
|
+
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
48139
|
+
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
48140
|
+
* broken silently the moment output validation was tightened anywhere.
|
|
48141
|
+
*/
|
|
48142
|
+
iconUrl: string().optional(),
|
|
48143
|
+
/**
|
|
48144
|
+
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
48145
|
+
*
|
|
48146
|
+
* The server knows this and therefore says it, because the client cannot
|
|
48147
|
+
* safely guess: a React-Native client renders SVG and raster through two
|
|
48148
|
+
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
48149
|
+
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
48150
|
+
* placeholder glyph for every vector icon while the web build looked fine.
|
|
48151
|
+
*
|
|
48152
|
+
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
48153
|
+
* not been updated — a client that cannot determine the type should prefer
|
|
48154
|
+
* its raster path, which is the safe default for an unknown image.
|
|
48155
|
+
*/
|
|
48156
|
+
iconMediaType: string().optional(),
|
|
48157
|
+
configSchema: ConfigSchemaPassthrough$2,
|
|
48158
|
+
/**
|
|
48159
|
+
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
48160
|
+
* base URL, instance, chat, recipient). A save that changes any of them
|
|
48161
|
+
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
48162
|
+
* editor uses this to ask for the secret on exactly those edits instead of
|
|
48163
|
+
* on every edit.
|
|
48164
|
+
*
|
|
48165
|
+
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
48166
|
+
* kinds) and from a provider older than this field — an editor that finds
|
|
48167
|
+
* it absent must assume ANY edit may need the secret.
|
|
48168
|
+
*/
|
|
48169
|
+
destinationKeys: array(string()).optional(),
|
|
48170
|
+
supportsDiscovery: boolean(),
|
|
48171
|
+
caps: TargetKindCapsSchema
|
|
48172
|
+
});
|
|
48173
|
+
/**
|
|
48174
|
+
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
48175
|
+
* (return a presence marker only) when serving `listTargets` — never
|
|
48176
|
+
* round-trip a stored secret to the UI.
|
|
48177
|
+
*/
|
|
48178
|
+
var TargetSchema = object({
|
|
48179
|
+
id: string(),
|
|
48180
|
+
name: string(),
|
|
48181
|
+
kind: string(),
|
|
48182
|
+
addonId: string(),
|
|
48183
|
+
enabled: boolean(),
|
|
48184
|
+
config: record(string(), unknown())
|
|
48185
|
+
});
|
|
48186
|
+
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
48187
|
+
var DiscoveredTargetSchema = object({
|
|
48188
|
+
kind: string(),
|
|
48189
|
+
suggestedName: string(),
|
|
48190
|
+
config: record(string(), unknown())
|
|
48191
|
+
});
|
|
48192
|
+
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
48193
|
+
var RenderedAsSchema = object({
|
|
48194
|
+
level: string(),
|
|
48195
|
+
format: NotificationFormatSchema,
|
|
48196
|
+
attachmentsSent: number().int().nonnegative(),
|
|
48197
|
+
actionsSent: number().int().nonnegative(),
|
|
48198
|
+
truncated: boolean(),
|
|
48199
|
+
dropped: array(string())
|
|
48200
|
+
});
|
|
48201
|
+
var SendResultSchema = object({
|
|
48202
|
+
success: boolean(),
|
|
48203
|
+
error: string().optional(),
|
|
48204
|
+
renderedAs: RenderedAsSchema.optional()
|
|
48205
|
+
});
|
|
48206
|
+
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
48207
|
+
var TestResultSchema = SendResultSchema;
|
|
48208
|
+
/**
|
|
48209
|
+
* Pre-save credential probe against a partial (unsaved) target config.
|
|
48210
|
+
* `resolvedUrl` never includes secrets (token stripped from path).
|
|
48211
|
+
*/
|
|
48212
|
+
var ProbeTargetConfigResultSchema = object({
|
|
48213
|
+
ok: boolean(),
|
|
48214
|
+
resolvedUrl: string(),
|
|
48215
|
+
httpStatus: number().int().optional(),
|
|
48216
|
+
stateInstance: string().optional(),
|
|
48217
|
+
error: string().optional()
|
|
48218
|
+
});
|
|
48219
|
+
method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({ targetId: string() }), TargetSchema, { auth: "admin" }), method(object({
|
|
48220
|
+
kind: string(),
|
|
48221
|
+
config: record(string(), unknown()).optional(),
|
|
48222
|
+
/**
|
|
48223
|
+
* When editing / duplicating, the blank password field never round-trips
|
|
48224
|
+
* the stored token. Pass the source target id so the provider can restore
|
|
48225
|
+
* secrets server-side before dialling (same merge as upsert).
|
|
48226
|
+
*/
|
|
48227
|
+
targetId: string().optional()
|
|
48228
|
+
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
48229
|
+
kind: string(),
|
|
48230
|
+
config: record(string(), unknown()),
|
|
48231
|
+
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
48232
|
+
targetId: string().optional()
|
|
48233
|
+
}), ProbeTargetConfigResultSchema, {
|
|
48234
|
+
kind: "mutation",
|
|
48235
|
+
auth: "admin"
|
|
48236
|
+
}), method(object({
|
|
48237
|
+
targetId: string(),
|
|
48238
|
+
notification: NotificationSchema
|
|
48239
|
+
}), SendResultSchema, {
|
|
48240
|
+
kind: "mutation",
|
|
48241
|
+
auth: "admin"
|
|
48242
|
+
}), method(object({
|
|
48243
|
+
targetId: string(),
|
|
48244
|
+
sample: NotificationSchema.optional()
|
|
48245
|
+
}), TestResultSchema, {
|
|
48246
|
+
kind: "mutation",
|
|
48247
|
+
auth: "admin"
|
|
48248
|
+
}), method(object({
|
|
48249
|
+
target: TargetSchema,
|
|
48250
|
+
/**
|
|
48251
|
+
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
48252
|
+
* existing row. The UI never holds the stored token; blank / redacted
|
|
48253
|
+
* password fields are restored from this source (D634 still applies if
|
|
48254
|
+
* destinations diverge without a re-typed secret).
|
|
48255
|
+
*/
|
|
48256
|
+
cloneSecretsFrom: string().optional()
|
|
48257
|
+
}), TargetSchema, {
|
|
48258
|
+
kind: "mutation",
|
|
48259
|
+
auth: "admin"
|
|
48260
|
+
}), method(object({ targetId: string() }), _void(), {
|
|
48261
|
+
kind: "mutation",
|
|
48262
|
+
auth: "admin"
|
|
48263
|
+
}), method(object({
|
|
48264
|
+
targetId: string(),
|
|
48265
|
+
enabled: boolean()
|
|
48266
|
+
}), _void(), {
|
|
48267
|
+
kind: "mutation",
|
|
48268
|
+
auth: "admin"
|
|
48269
|
+
});
|
|
47872
48270
|
var AUDIO_MACRO_LABELS = [
|
|
47873
48271
|
{
|
|
47874
48272
|
id: "speech",
|
|
@@ -61149,9 +61547,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
61149
61547
|
}), LogStreamEntrySchema, { kind: "subscription" });
|
|
61150
61548
|
object({
|
|
61151
61549
|
/** Carbon dioxide concentration in ppm. */
|
|
61152
|
-
|
|
61550
|
+
co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
61153
61551
|
/** Total volatile organic compounds in ppb. */
|
|
61154
|
-
|
|
61552
|
+
voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
61155
61553
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
61156
61554
|
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
61157
61555
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
@@ -61164,7 +61562,7 @@ object({
|
|
|
61164
61562
|
DeviceType.Sensor;
|
|
61165
61563
|
object({
|
|
61166
61564
|
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
61167
|
-
|
|
61565
|
+
illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
61168
61566
|
precision: 0,
|
|
61169
61567
|
label: "Illuminance"
|
|
61170
61568
|
})),
|
|
@@ -61261,6 +61659,84 @@ DeviceType.Camera, method(object({ deviceId: number() }), AudioMetricsSnapshotSc
|
|
|
61261
61659
|
* would produce more than `maxPoints` samples. */
|
|
61262
61660
|
sampleEveryMs: number().int().positive().optional()
|
|
61263
61661
|
}), AudioMetricsHistorySchema);
|
|
61662
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
61663
|
+
actionId: string().min(1),
|
|
61664
|
+
variables: record(string(), ActionVariableValueSchema).optional()
|
|
61665
|
+
}), _void(), {
|
|
61666
|
+
kind: "mutation",
|
|
61667
|
+
auth: "admin"
|
|
61668
|
+
}), method(object({ actionId: string().min(1) }), _void(), {
|
|
61669
|
+
kind: "mutation",
|
|
61670
|
+
auth: "admin"
|
|
61671
|
+
});
|
|
61672
|
+
/**
|
|
61673
|
+
* actions — the ONE surface for runnable actions, for the UI and for
|
|
61674
|
+
* automations. Provided by the core-blocks builtin on the hub.
|
|
61675
|
+
*
|
|
61676
|
+
* An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
|
|
61677
|
+
* steps, kept in the hub's `actions` record kind) or declared by an addon over
|
|
61678
|
+
* `action-source`. `list` asks every `action-source` provider LIVE and overlays
|
|
61679
|
+
* the run status from the hub's `runs` ledger; there is no catalog here to
|
|
61680
|
+
* drift (D3).
|
|
61681
|
+
*
|
|
61682
|
+
* **Permissions follow the camera, as linked devices do.** An action is linked
|
|
61683
|
+
* to cameras by the operator (`setLinks`, D356: nothing links itself). A
|
|
61684
|
+
* non-admin may `get` / `run` an action only through a camera they may see that
|
|
61685
|
+
* links it: they name the camera (`deviceId`, a top-level field so the scope
|
|
61686
|
+
* gate sees it) and the provider refuses an action that camera does not link.
|
|
61687
|
+
* An admin, and an in-process caller (an automation block), may run any action.
|
|
61688
|
+
* Every refusal is typed (`[not-linked] …`) and logged.
|
|
61689
|
+
*
|
|
61690
|
+
* A second `run` of an action that is already running is REFUSED — traced and
|
|
61691
|
+
* logged — never queued.
|
|
61692
|
+
*/
|
|
61693
|
+
var ActionIdInput = object({ actionId: string().min(1) });
|
|
61694
|
+
var ActionLinksSchema = object({
|
|
61695
|
+
deviceId: number().int().nonnegative(),
|
|
61696
|
+
actionIds: array(string().min(1)).max(100)
|
|
61697
|
+
});
|
|
61698
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
61699
|
+
actionId: string().min(1),
|
|
61700
|
+
deviceId: number().int().nonnegative().optional()
|
|
61701
|
+
}), object({ action: ActionEntrySchema.nullable() }), {
|
|
61702
|
+
role: "viewer",
|
|
61703
|
+
auth: "protected",
|
|
61704
|
+
kind: "query",
|
|
61705
|
+
caller: "required"
|
|
61706
|
+
}), method(object({
|
|
61707
|
+
actionId: string().min(1),
|
|
61708
|
+
variables: record(string(), ActionVariableValueSchema).optional(),
|
|
61709
|
+
wait: boolean().optional(),
|
|
61710
|
+
origin: ActionRunOriginSchema.optional(),
|
|
61711
|
+
deviceId: number().int().nonnegative().optional()
|
|
61712
|
+
}), ActionRunResultSchema, {
|
|
61713
|
+
role: "viewer",
|
|
61714
|
+
auth: "protected",
|
|
61715
|
+
kind: "mutation",
|
|
61716
|
+
caller: "required"
|
|
61717
|
+
}), method(ActionIdInput, _void(), {
|
|
61718
|
+
kind: "mutation",
|
|
61719
|
+
auth: "admin"
|
|
61720
|
+
}), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
|
|
61721
|
+
kind: "mutation",
|
|
61722
|
+
auth: "admin"
|
|
61723
|
+
}), method(object({
|
|
61724
|
+
actionId: string().min(1),
|
|
61725
|
+
action: ActionInputSchema.partial()
|
|
61726
|
+
}), object({ action: ActionEntrySchema }), {
|
|
61727
|
+
kind: "mutation",
|
|
61728
|
+
auth: "admin"
|
|
61729
|
+
}), method(ActionIdInput, _void(), {
|
|
61730
|
+
kind: "mutation",
|
|
61731
|
+
auth: "admin"
|
|
61732
|
+
}), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
|
|
61733
|
+
role: "viewer",
|
|
61734
|
+
auth: "protected",
|
|
61735
|
+
kind: "query"
|
|
61736
|
+
}), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
|
|
61737
|
+
kind: "mutation",
|
|
61738
|
+
auth: "admin"
|
|
61739
|
+
});
|
|
61264
61740
|
object({
|
|
61265
61741
|
/** Whether the automation is currently enabled. Disabled automations
|
|
61266
61742
|
* ignore their trigger block — manual `trigger` still works. */
|
|
@@ -61314,7 +61790,7 @@ var BatteryStatusSchema = object({
|
|
|
61314
61790
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
61315
61791
|
* declines to band a non-finite reading, which is the correct shape.
|
|
61316
61792
|
*/
|
|
61317
|
-
|
|
61793
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
61318
61794
|
/**
|
|
61319
61795
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
61320
61796
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -61324,7 +61800,7 @@ var BatteryStatusSchema = object({
|
|
|
61324
61800
|
* `'unknown'` is the absence of an answer: the provider has not read the
|
|
61325
61801
|
* power fields yet, or the firmware reported neither. Before it existed,
|
|
61326
61802
|
* two unreadable fields produced `'none'`, and a camera nobody had reached
|
|
61327
|
-
* was drawn "on battery alone" — the same D315 defect `
|
|
61803
|
+
* was drawn "on battery alone" — the same D315 defect `level` closed
|
|
61328
61804
|
* by going nullable. Consumers SKIP it (no charger state published, no
|
|
61329
61805
|
* glyph, no HomeKit ChargingState) rather than coerce it either way.
|
|
61330
61806
|
*/
|
|
@@ -61348,7 +61824,7 @@ var BatteryStatusSchema = object({
|
|
|
61348
61824
|
* that only report charge. A second reading on this cap, not a second
|
|
61349
61825
|
* `numeric-sensor` on the same device.
|
|
61350
61826
|
*/
|
|
61351
|
-
|
|
61827
|
+
voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
61352
61828
|
/**
|
|
61353
61829
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
61354
61830
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -61382,7 +61858,7 @@ var BatteryStatusSchema = object({
|
|
|
61382
61858
|
/**
|
|
61383
61859
|
* True when the source is a BINARY low-battery indicator (HA
|
|
61384
61860
|
* `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
|
|
61385
|
-
* charge level — `
|
|
61861
|
+
* charge level — `level` is then a coarse stand-in (100 = normal,
|
|
61386
61862
|
* sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
|
|
61387
61863
|
* misleading exact percentage. Absent/false → genuine 0–100 % reading.
|
|
61388
61864
|
*/
|
|
@@ -61395,21 +61871,22 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
61395
61871
|
DeviceType.Switch,
|
|
61396
61872
|
DeviceType.PetFeeder,
|
|
61397
61873
|
DeviceType.Container,
|
|
61398
|
-
DeviceType.Thermostat
|
|
61874
|
+
DeviceType.Thermostat,
|
|
61875
|
+
DeviceType.EventEmitter
|
|
61399
61876
|
];
|
|
61400
61877
|
/** Generic Customize target for the percentage reading (D936). */
|
|
61401
61878
|
var batteryCustomize = {
|
|
61402
61879
|
version: 1,
|
|
61403
61880
|
targets: [{
|
|
61404
|
-
id: "
|
|
61881
|
+
id: "level",
|
|
61405
61882
|
cardinality: "scalar",
|
|
61406
61883
|
vocabulary: {
|
|
61407
61884
|
cap: "battery",
|
|
61408
|
-
stateField: "
|
|
61885
|
+
stateField: "level"
|
|
61409
61886
|
},
|
|
61410
61887
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
61411
61888
|
apply: "add",
|
|
61412
|
-
path: "
|
|
61889
|
+
path: "level",
|
|
61413
61890
|
sources: [{
|
|
61414
61891
|
kind: "field",
|
|
61415
61892
|
valueKinds: ["number", "string"],
|
|
@@ -61503,7 +61980,7 @@ onStatusChanged: { data: object({
|
|
|
61503
61980
|
schema: BatteryStatusSchema,
|
|
61504
61981
|
kind: "push",
|
|
61505
61982
|
empty: {
|
|
61506
|
-
|
|
61983
|
+
level: null,
|
|
61507
61984
|
charging: "unknown",
|
|
61508
61985
|
sleeping: false,
|
|
61509
61986
|
lastUpdated: 0
|
|
@@ -61530,11 +62007,11 @@ onStatusChanged: { data: object({
|
|
|
61530
62007
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
61531
62008
|
*/
|
|
61532
62009
|
history: { fields: {
|
|
61533
|
-
|
|
62010
|
+
level: {
|
|
61534
62011
|
kind: "gauge",
|
|
61535
62012
|
unit: "%"
|
|
61536
62013
|
},
|
|
61537
|
-
|
|
62014
|
+
voltage: {
|
|
61538
62015
|
kind: "gauge",
|
|
61539
62016
|
unit: "V"
|
|
61540
62017
|
}
|
|
@@ -61552,19 +62029,19 @@ object({
|
|
|
61552
62029
|
DeviceType.Sensor;
|
|
61553
62030
|
object({
|
|
61554
62031
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
61555
|
-
|
|
62032
|
+
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
61556
62033
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
61557
62034
|
lastChangedAt: number()
|
|
61558
62035
|
});
|
|
61559
62036
|
DeviceType.Light, method(object({
|
|
61560
62037
|
deviceId: number().int().nonnegative(),
|
|
61561
|
-
|
|
62038
|
+
level: number().min(0).max(100)
|
|
61562
62039
|
}), _void(), {
|
|
61563
62040
|
kind: "mutation",
|
|
61564
62041
|
auth: "protected"
|
|
61565
62042
|
}), object({
|
|
61566
62043
|
deviceId: number(),
|
|
61567
|
-
|
|
62044
|
+
level: number().min(0).max(100),
|
|
61568
62045
|
lastChangedAt: number()
|
|
61569
62046
|
});
|
|
61570
62047
|
DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
@@ -62065,11 +62542,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
|
|
|
62065
62542
|
}),
|
|
62066
62543
|
object({
|
|
62067
62544
|
mode: literal("mired"),
|
|
62068
|
-
|
|
62545
|
+
colorTemperature: number().int().min(50).max(1e3)
|
|
62069
62546
|
})
|
|
62070
62547
|
]);
|
|
62071
62548
|
object({
|
|
62072
|
-
/** Active color mode — which of `rgb` / `hsv` / `
|
|
62549
|
+
/** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
|
|
62073
62550
|
* the bulb's current state. */
|
|
62074
62551
|
mode: _enum([
|
|
62075
62552
|
"rgb",
|
|
@@ -62081,7 +62558,7 @@ object({
|
|
|
62081
62558
|
/** Populated when `mode === 'hsv'`. */
|
|
62082
62559
|
hsv: HsvTripletSchema.optional(),
|
|
62083
62560
|
/** Populated when `mode === 'mired'`. */
|
|
62084
|
-
|
|
62561
|
+
colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
62085
62562
|
/** Ms epoch of the last operator-driven change. */
|
|
62086
62563
|
lastChangedAt: number()
|
|
62087
62564
|
});
|
|
@@ -62100,7 +62577,7 @@ DeviceType.Light, method(object({
|
|
|
62100
62577
|
]),
|
|
62101
62578
|
rgb: RgbTripletSchema.optional(),
|
|
62102
62579
|
hsv: HsvTripletSchema.optional(),
|
|
62103
|
-
|
|
62580
|
+
colorTemperature: number().int().optional(),
|
|
62104
62581
|
lastChangedAt: number()
|
|
62105
62582
|
});
|
|
62106
62583
|
var ConnectionTestOutcomeSchema = discriminatedUnion("outcome", [
|
|
@@ -62526,11 +63003,11 @@ object({
|
|
|
62526
63003
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
62527
63004
|
availablePrograms: array(string()),
|
|
62528
63005
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
62529
|
-
|
|
63006
|
+
remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
62530
63007
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
62531
|
-
|
|
63008
|
+
delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
62532
63009
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
62533
|
-
|
|
63010
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62534
63011
|
/** True when the door is reported open; null when the door state is not known. */
|
|
62535
63012
|
doorOpen: boolean().nullable(),
|
|
62536
63013
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -63148,7 +63625,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
63148
63625
|
object({
|
|
63149
63626
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
63150
63627
|
* speed surface (single-speed fan). */
|
|
63151
|
-
|
|
63628
|
+
speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63152
63629
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
63153
63630
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
63154
63631
|
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
@@ -63168,7 +63645,7 @@ object({
|
|
|
63168
63645
|
});
|
|
63169
63646
|
DeviceType.Fan, method(object({
|
|
63170
63647
|
deviceId: number().int().nonnegative(),
|
|
63171
|
-
|
|
63648
|
+
speed: number().min(0).max(100)
|
|
63172
63649
|
}), _void(), {
|
|
63173
63650
|
kind: "mutation",
|
|
63174
63651
|
auth: "protected"
|
|
@@ -63351,7 +63828,7 @@ DeviceType.Humidifier, method(object({
|
|
|
63351
63828
|
});
|
|
63352
63829
|
object({
|
|
63353
63830
|
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
63354
|
-
|
|
63831
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
63355
63832
|
precision: 0,
|
|
63356
63833
|
label: "Humidity"
|
|
63357
63834
|
})),
|
|
@@ -63794,7 +64271,7 @@ object({
|
|
|
63794
64271
|
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63795
64272
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
63796
64273
|
* task is active / progress is unavailable. */
|
|
63797
|
-
|
|
64274
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63798
64275
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
63799
64276
|
* or null when unknown. */
|
|
63800
64277
|
currentCode: number().nullable().meta(readingMeta("none")),
|
|
@@ -64284,10 +64761,10 @@ object({
|
|
|
64284
64761
|
currentMedia: MediaInfoSchema.nullable(),
|
|
64285
64762
|
/** Current playback position in ms. Null when not seekable or
|
|
64286
64763
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
64287
|
-
|
|
64764
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
64288
64765
|
/** Total duration of the current media in ms. Null when unknown
|
|
64289
64766
|
* (live stream) or nothing is playing. */
|
|
64290
|
-
|
|
64767
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
64291
64768
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
64292
64769
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
64293
64770
|
shuffle: boolean().nullable(),
|
|
@@ -64314,7 +64791,7 @@ DeviceType.MediaPlayer, method(object({ deviceId: number().int().nonnegative() }
|
|
|
64314
64791
|
auth: "protected"
|
|
64315
64792
|
}), method(object({
|
|
64316
64793
|
deviceId: number().int().nonnegative(),
|
|
64317
|
-
|
|
64794
|
+
position: number().int().nonnegative()
|
|
64318
64795
|
}), _void(), {
|
|
64319
64796
|
kind: "mutation",
|
|
64320
64797
|
auth: "protected"
|
|
@@ -64987,9 +65464,9 @@ var NetworkLinkStatusSchema = object({
|
|
|
64987
65464
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
64988
65465
|
* SKIP a null rather than coerce it.
|
|
64989
65466
|
*/
|
|
64990
|
-
|
|
65467
|
+
signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64991
65468
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
64992
|
-
|
|
65469
|
+
rssi: number().optional().meta(readingMeta("signal-level")),
|
|
64993
65470
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
64994
65471
|
ssid: string().optional(),
|
|
64995
65472
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
@@ -65650,10 +66127,20 @@ var PetFeederDeviceStatusSchema = _enum([
|
|
|
65650
66127
|
]);
|
|
65651
66128
|
var gramsPortion = number().int().min(4).max(200);
|
|
65652
66129
|
object({
|
|
65653
|
-
/** Food
|
|
65654
|
-
*
|
|
65655
|
-
* bowl
|
|
66130
|
+
/** Food in the TANK (hopper), as the feeder's own sensor reports it
|
|
66131
|
+
* (`state.food`). A coarse level code, not grams — `0` is empty. The
|
|
66132
|
+
* amount in the bowl is `bowlFood`. On dual-hopper models this is the
|
|
66133
|
+
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
66134
|
+
* the device has not reported yet. */
|
|
65656
66135
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
66136
|
+
/** Food in the BOWL as the feeder reports it (`state.bowl`), in the
|
|
66137
|
+
* feeder's own units. Null when the model reports none. */
|
|
66138
|
+
bowlFood: number().nullable().meta(readingMeta("none")),
|
|
66139
|
+
/** True while the pet is eating (`state.eating`). */
|
|
66140
|
+
eating: boolean(),
|
|
66141
|
+
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
66142
|
+
* the client must not offer it. */
|
|
66143
|
+
supportsMarkRefilled: boolean(),
|
|
65657
66144
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
65658
66145
|
* single-hopper models. */
|
|
65659
66146
|
food1: number().nullable().meta(readingMeta("none")),
|
|
@@ -65661,8 +66148,8 @@ object({
|
|
|
65661
66148
|
* single-hopper models. */
|
|
65662
66149
|
food2: number().nullable().meta(readingMeta("none")),
|
|
65663
66150
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
65664
|
-
* (`device_class: problem`, on = low). True when the
|
|
65665
|
-
*
|
|
66151
|
+
* (`device_class: problem`, on = low). True when the tank reports empty,
|
|
66152
|
+
* or, on a dual-hopper model, when EITHER hopper is empty. */
|
|
65666
66153
|
lowFood: boolean(),
|
|
65667
66154
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
65668
66155
|
* device has no battery reading. */
|
|
@@ -66129,22 +66616,38 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
66129
66616
|
auth: "admin"
|
|
66130
66617
|
});
|
|
66131
66618
|
object({
|
|
66132
|
-
/** Instantaneous power draw
|
|
66133
|
-
|
|
66619
|
+
/** Instantaneous power draw (watts). */
|
|
66620
|
+
power: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
66134
66621
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
66135
|
-
|
|
66136
|
-
/** Voltage
|
|
66137
|
-
|
|
66622
|
+
energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
66623
|
+
/** Voltage (volts). */
|
|
66624
|
+
voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
66138
66625
|
/** Current in amperes. */
|
|
66139
|
-
|
|
66626
|
+
current: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
66140
66627
|
/** Apparent power in volt-amperes. */
|
|
66141
|
-
|
|
66628
|
+
apparentPower: number().optional().meta(readingMeta("apparent-power")),
|
|
66142
66629
|
/** Power factor (0..1) if reported. */
|
|
66143
66630
|
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
66144
66631
|
/** Ms epoch when the slice was last updated. */
|
|
66145
66632
|
lastFetchedAt: number()
|
|
66146
66633
|
});
|
|
66147
|
-
|
|
66634
|
+
[
|
|
66635
|
+
DeviceType.Sensor,
|
|
66636
|
+
DeviceType.Switch,
|
|
66637
|
+
DeviceType.Light,
|
|
66638
|
+
DeviceType.Fan,
|
|
66639
|
+
DeviceType.Climate,
|
|
66640
|
+
DeviceType.Thermostat,
|
|
66641
|
+
DeviceType.Humidifier,
|
|
66642
|
+
DeviceType.WaterHeater,
|
|
66643
|
+
DeviceType.MediaPlayer,
|
|
66644
|
+
DeviceType.Washer,
|
|
66645
|
+
DeviceType.Dryer,
|
|
66646
|
+
DeviceType.Dishwasher,
|
|
66647
|
+
DeviceType.Vacuum,
|
|
66648
|
+
DeviceType.LawnMower,
|
|
66649
|
+
DeviceType.PetFeeder
|
|
66650
|
+
].filter((type) => type !== DeviceType.Sensor);
|
|
66148
66651
|
/**
|
|
66149
66652
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
66150
66653
|
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
@@ -66177,16 +66680,13 @@ object({
|
|
|
66177
66680
|
location: string().nullable(),
|
|
66178
66681
|
/** GPS coordinates when available. Null otherwise. */
|
|
66179
66682
|
gps: GpsLocationSchema.nullable(),
|
|
66180
|
-
/** Optional battery level of the tracking device (0..100). Null
|
|
66181
|
-
* when not reported. */
|
|
66182
|
-
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
66183
66683
|
/** Ms epoch when the slice was last updated. */
|
|
66184
66684
|
lastChangedAt: number()
|
|
66185
66685
|
});
|
|
66186
66686
|
DeviceType.Presence;
|
|
66187
66687
|
object({
|
|
66188
66688
|
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
66189
|
-
|
|
66689
|
+
pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
66190
66690
|
/** Ms epoch when the slice was last updated. */
|
|
66191
66691
|
lastFetchedAt: number()
|
|
66192
66692
|
});
|
|
@@ -69713,7 +70213,7 @@ object({
|
|
|
69713
70213
|
DeviceType.Sensor;
|
|
69714
70214
|
object({
|
|
69715
70215
|
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
69716
|
-
|
|
70216
|
+
temperature: number().nullable().meta(readingMeta("temperature", {
|
|
69717
70217
|
precision: 1,
|
|
69718
70218
|
label: "Temperature"
|
|
69719
70219
|
})),
|
|
@@ -69977,7 +70477,7 @@ object({
|
|
|
69977
70477
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
69978
70478
|
availableFanSpeeds: array(string()),
|
|
69979
70479
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
69980
|
-
|
|
70480
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
69981
70481
|
/** Current error code (0 / null = no error). */
|
|
69982
70482
|
errorCode: number().nullable().meta(readingMeta("none")),
|
|
69983
70483
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
@@ -70888,7 +71388,7 @@ function dominantClipLabels(events) {
|
|
|
70888
71388
|
}
|
|
70889
71389
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
70890
71390
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
70891
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
71391
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
70892
71392
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
70893
71393
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
70894
71394
|
new Set(CAP_NAMES);
|
|
@@ -70976,6 +71476,104 @@ Object.freeze({
|
|
|
70976
71476
|
access: "create",
|
|
70977
71477
|
role: "admin"
|
|
70978
71478
|
},
|
|
71479
|
+
"actions.create": {
|
|
71480
|
+
capName: "actions",
|
|
71481
|
+
capScope: "system",
|
|
71482
|
+
addonId: null,
|
|
71483
|
+
access: "create",
|
|
71484
|
+
role: "admin"
|
|
71485
|
+
},
|
|
71486
|
+
"actions.delete": {
|
|
71487
|
+
capName: "actions",
|
|
71488
|
+
capScope: "system",
|
|
71489
|
+
addonId: null,
|
|
71490
|
+
access: "delete",
|
|
71491
|
+
role: "admin"
|
|
71492
|
+
},
|
|
71493
|
+
"actions.get": {
|
|
71494
|
+
capName: "actions",
|
|
71495
|
+
capScope: "system",
|
|
71496
|
+
addonId: null,
|
|
71497
|
+
access: "view",
|
|
71498
|
+
role: "viewer"
|
|
71499
|
+
},
|
|
71500
|
+
"actions.getLinks": {
|
|
71501
|
+
capName: "actions",
|
|
71502
|
+
capScope: "system",
|
|
71503
|
+
addonId: null,
|
|
71504
|
+
access: "view",
|
|
71505
|
+
role: "admin"
|
|
71506
|
+
},
|
|
71507
|
+
"actions.getTraces": {
|
|
71508
|
+
capName: "actions",
|
|
71509
|
+
capScope: "system",
|
|
71510
|
+
addonId: null,
|
|
71511
|
+
access: "view",
|
|
71512
|
+
role: "admin"
|
|
71513
|
+
},
|
|
71514
|
+
"actions.list": {
|
|
71515
|
+
capName: "actions",
|
|
71516
|
+
capScope: "system",
|
|
71517
|
+
addonId: null,
|
|
71518
|
+
access: "view",
|
|
71519
|
+
role: "admin"
|
|
71520
|
+
},
|
|
71521
|
+
"actions.listForCamera": {
|
|
71522
|
+
capName: "actions",
|
|
71523
|
+
capScope: "system",
|
|
71524
|
+
addonId: null,
|
|
71525
|
+
access: "view",
|
|
71526
|
+
role: "viewer"
|
|
71527
|
+
},
|
|
71528
|
+
"actions.run": {
|
|
71529
|
+
capName: "actions",
|
|
71530
|
+
capScope: "system",
|
|
71531
|
+
addonId: null,
|
|
71532
|
+
access: "create",
|
|
71533
|
+
role: "viewer"
|
|
71534
|
+
},
|
|
71535
|
+
"actions.setLinks": {
|
|
71536
|
+
capName: "actions",
|
|
71537
|
+
capScope: "system",
|
|
71538
|
+
addonId: null,
|
|
71539
|
+
access: "create",
|
|
71540
|
+
role: "admin"
|
|
71541
|
+
},
|
|
71542
|
+
"actions.stop": {
|
|
71543
|
+
capName: "actions",
|
|
71544
|
+
capScope: "system",
|
|
71545
|
+
addonId: null,
|
|
71546
|
+
access: "create",
|
|
71547
|
+
role: "admin"
|
|
71548
|
+
},
|
|
71549
|
+
"actions.update": {
|
|
71550
|
+
capName: "actions",
|
|
71551
|
+
capScope: "system",
|
|
71552
|
+
addonId: null,
|
|
71553
|
+
access: "create",
|
|
71554
|
+
role: "admin"
|
|
71555
|
+
},
|
|
71556
|
+
"actionSource.listActions": {
|
|
71557
|
+
capName: "action-source",
|
|
71558
|
+
capScope: "system",
|
|
71559
|
+
addonId: null,
|
|
71560
|
+
access: "view",
|
|
71561
|
+
role: "admin"
|
|
71562
|
+
},
|
|
71563
|
+
"actionSource.runAction": {
|
|
71564
|
+
capName: "action-source",
|
|
71565
|
+
capScope: "system",
|
|
71566
|
+
addonId: null,
|
|
71567
|
+
access: "create",
|
|
71568
|
+
role: "admin"
|
|
71569
|
+
},
|
|
71570
|
+
"actionSource.stopAction": {
|
|
71571
|
+
capName: "action-source",
|
|
71572
|
+
capScope: "system",
|
|
71573
|
+
addonId: null,
|
|
71574
|
+
access: "create",
|
|
71575
|
+
role: "admin"
|
|
71576
|
+
},
|
|
70979
71577
|
"addonPages.listPages": {
|
|
70980
71578
|
capName: "addon-pages",
|
|
70981
71579
|
capScope: "system",
|
|
@@ -72257,6 +72855,13 @@ Object.freeze({
|
|
|
72257
72855
|
access: "view",
|
|
72258
72856
|
role: "admin"
|
|
72259
72857
|
},
|
|
72858
|
+
"coreBlocks.getAutomationRunState": {
|
|
72859
|
+
capName: "core-blocks",
|
|
72860
|
+
capScope: "system",
|
|
72861
|
+
addonId: null,
|
|
72862
|
+
access: "view",
|
|
72863
|
+
role: "admin"
|
|
72864
|
+
},
|
|
72260
72865
|
"coreBlocks.getBlockSettings": {
|
|
72261
72866
|
capName: "core-blocks",
|
|
72262
72867
|
capScope: "system",
|
|
@@ -72278,6 +72883,13 @@ Object.freeze({
|
|
|
72278
72883
|
access: "view",
|
|
72279
72884
|
role: "admin"
|
|
72280
72885
|
},
|
|
72886
|
+
"coreBlocks.getTraces": {
|
|
72887
|
+
capName: "core-blocks",
|
|
72888
|
+
capScope: "system",
|
|
72889
|
+
addonId: null,
|
|
72890
|
+
access: "view",
|
|
72891
|
+
role: "admin"
|
|
72892
|
+
},
|
|
72281
72893
|
"coreBlocks.getTypeDefs": {
|
|
72282
72894
|
capName: "core-blocks",
|
|
72283
72895
|
capScope: "system",
|
|
@@ -72313,6 +72925,13 @@ Object.freeze({
|
|
|
72313
72925
|
access: "create",
|
|
72314
72926
|
role: "admin"
|
|
72315
72927
|
},
|
|
72928
|
+
"coreBlocks.reportAutomationRun": {
|
|
72929
|
+
capName: "core-blocks",
|
|
72930
|
+
capScope: "system",
|
|
72931
|
+
addonId: null,
|
|
72932
|
+
access: "create",
|
|
72933
|
+
role: "admin"
|
|
72934
|
+
},
|
|
72316
72935
|
"coreBlocks.restart": {
|
|
72317
72936
|
capName: "core-blocks",
|
|
72318
72937
|
capScope: "system",
|
|
@@ -80886,7 +81505,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
80886
81505
|
label: t("Full brightness", "Luminosità massima"),
|
|
80887
81506
|
phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
|
|
80888
81507
|
icon: "sun",
|
|
80889
|
-
args: {
|
|
81508
|
+
args: { level: 100 }
|
|
80890
81509
|
},
|
|
80891
81510
|
{
|
|
80892
81511
|
id: "brightness.half",
|
|
@@ -80894,7 +81513,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
80894
81513
|
label: t("Half brightness", "Luminosità a metà"),
|
|
80895
81514
|
phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
|
|
80896
81515
|
icon: "sun-dim",
|
|
80897
|
-
args: {
|
|
81516
|
+
args: { level: 50 }
|
|
80898
81517
|
},
|
|
80899
81518
|
{
|
|
80900
81519
|
id: "brightness.off",
|
|
@@ -80902,16 +81521,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
80902
81521
|
label: t("Brightness off", "Luminosità a zero"),
|
|
80903
81522
|
phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
|
|
80904
81523
|
icon: "lightbulb-off",
|
|
80905
|
-
args: {
|
|
81524
|
+
args: { level: 0 }
|
|
80906
81525
|
},
|
|
80907
81526
|
{
|
|
80908
81527
|
id: "brightness.set",
|
|
80909
81528
|
method: "setBrightness",
|
|
80910
81529
|
label: t("Set brightness", "Imposta la luminosità"),
|
|
80911
|
-
phrase: t("Set the brightness of {device} to {
|
|
81530
|
+
phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
|
|
80912
81531
|
icon: "sun-medium",
|
|
80913
81532
|
openArgs: [{
|
|
80914
|
-
key: "
|
|
81533
|
+
key: "level",
|
|
80915
81534
|
label: PERCENT,
|
|
80916
81535
|
offer: {
|
|
80917
81536
|
kind: "number",
|
|
@@ -80925,13 +81544,13 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
80925
81544
|
],
|
|
80926
81545
|
state: [{
|
|
80927
81546
|
kind: "numeric",
|
|
80928
|
-
field: "
|
|
81547
|
+
field: "level",
|
|
80929
81548
|
quantity: t("the brightness", "la luminosità"),
|
|
80930
81549
|
label: t("Brightness", "Luminosità"),
|
|
80931
81550
|
unit: "%",
|
|
80932
81551
|
icon: "sun"
|
|
80933
81552
|
}],
|
|
80934
|
-
events: { onBrightnessChanged: { notOperatorFacing: "the `
|
|
81553
|
+
events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
|
|
80935
81554
|
},
|
|
80936
81555
|
color: {
|
|
80937
81556
|
scope: "device",
|
|
@@ -80945,7 +81564,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
80945
81564
|
icon: "sun",
|
|
80946
81565
|
args: { color: {
|
|
80947
81566
|
mode: "mired",
|
|
80948
|
-
|
|
81567
|
+
colorTemperature: 370
|
|
80949
81568
|
} }
|
|
80950
81569
|
},
|
|
80951
81570
|
{
|
|
@@ -80956,7 +81575,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
80956
81575
|
icon: "snowflake",
|
|
80957
81576
|
args: { color: {
|
|
80958
81577
|
mode: "mired",
|
|
80959
|
-
|
|
81578
|
+
colorTemperature: 200
|
|
80960
81579
|
} }
|
|
80961
81580
|
},
|
|
80962
81581
|
{
|
|
@@ -81570,7 +82189,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81570
82189
|
label: t("Fan off", "Ventilatore spento"),
|
|
81571
82190
|
phrase: t("Stop {device}", "Ferma {device}"),
|
|
81572
82191
|
icon: "power-off",
|
|
81573
|
-
args: {
|
|
82192
|
+
args: { speed: 0 }
|
|
81574
82193
|
},
|
|
81575
82194
|
{
|
|
81576
82195
|
id: "fan.full",
|
|
@@ -81578,16 +82197,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81578
82197
|
label: t("Fan full", "Ventilatore al massimo"),
|
|
81579
82198
|
phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
|
|
81580
82199
|
icon: "fan",
|
|
81581
|
-
args: {
|
|
82200
|
+
args: { speed: 100 }
|
|
81582
82201
|
},
|
|
81583
82202
|
{
|
|
81584
82203
|
id: "fan.speed",
|
|
81585
82204
|
method: "setPercentage",
|
|
81586
82205
|
label: t("Set speed", "Imposta la velocità"),
|
|
81587
|
-
phrase: t("Set the speed of {device} to {
|
|
82206
|
+
phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
|
|
81588
82207
|
icon: "fan",
|
|
81589
82208
|
openArgs: [{
|
|
81590
|
-
key: "
|
|
82209
|
+
key: "speed",
|
|
81591
82210
|
label: t("Speed", "Velocità"),
|
|
81592
82211
|
offer: {
|
|
81593
82212
|
kind: "number",
|
|
@@ -81649,7 +82268,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81649
82268
|
],
|
|
81650
82269
|
state: [{
|
|
81651
82270
|
kind: "numeric",
|
|
81652
|
-
field: "
|
|
82271
|
+
field: "speed",
|
|
81653
82272
|
quantity: t("the speed", "la velocità"),
|
|
81654
82273
|
label: t("Speed", "Velocità"),
|
|
81655
82274
|
unit: "%",
|
|
@@ -82447,7 +83066,7 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
82447
83066
|
},
|
|
82448
83067
|
{
|
|
82449
83068
|
kind: "numeric",
|
|
82450
|
-
field: "
|
|
83069
|
+
field: "progress",
|
|
82451
83070
|
quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
|
|
82452
83071
|
label: t("Progress", "Avanzamento"),
|
|
82453
83072
|
unit: "%",
|
|
@@ -82805,10 +83424,10 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
82805
83424
|
id: "media.seek",
|
|
82806
83425
|
method: "seek",
|
|
82807
83426
|
label: t("Seek", "Vai al punto"),
|
|
82808
|
-
phrase: t("Move {device} to {
|
|
83427
|
+
phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
|
|
82809
83428
|
icon: "fast-forward",
|
|
82810
83429
|
openArgs: [{
|
|
82811
|
-
key: "
|
|
83430
|
+
key: "position",
|
|
82812
83431
|
label: t("Position", "Posizione"),
|
|
82813
83432
|
offer: {
|
|
82814
83433
|
kind: "number",
|
|
@@ -83857,7 +84476,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83857
84476
|
state: [
|
|
83858
84477
|
{
|
|
83859
84478
|
kind: "numeric",
|
|
83860
|
-
field: "
|
|
84479
|
+
field: "co2",
|
|
83861
84480
|
quantity: t("the CO₂", "la CO₂"),
|
|
83862
84481
|
label: t("CO₂", "CO₂"),
|
|
83863
84482
|
unit: "ppm",
|
|
@@ -83865,7 +84484,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83865
84484
|
},
|
|
83866
84485
|
{
|
|
83867
84486
|
kind: "numeric",
|
|
83868
|
-
field: "
|
|
84487
|
+
field: "voc",
|
|
83869
84488
|
quantity: t("the VOC level", "il livello di COV"),
|
|
83870
84489
|
label: t("VOC", "COV"),
|
|
83871
84490
|
unit: "ppb",
|
|
@@ -83901,7 +84520,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83901
84520
|
noun: t("Light level", "Luminosità ambiente"),
|
|
83902
84521
|
state: [{
|
|
83903
84522
|
kind: "numeric",
|
|
83904
|
-
field: "
|
|
84523
|
+
field: "illuminance",
|
|
83905
84524
|
quantity: t("the light level", "la luminosità"),
|
|
83906
84525
|
label: t("Illuminance", "Illuminamento"),
|
|
83907
84526
|
unit: "lx",
|
|
@@ -84104,7 +84723,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84104
84723
|
noun: t("Humidity", "Umidità"),
|
|
84105
84724
|
state: [{
|
|
84106
84725
|
kind: "numeric",
|
|
84107
|
-
field: "
|
|
84726
|
+
field: "humidity",
|
|
84108
84727
|
quantity: t("the humidity", "l’umidità"),
|
|
84109
84728
|
label: t("Humidity", "Umidità"),
|
|
84110
84729
|
unit: "%",
|
|
@@ -84152,7 +84771,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84152
84771
|
state: [
|
|
84153
84772
|
{
|
|
84154
84773
|
kind: "numeric",
|
|
84155
|
-
field: "
|
|
84774
|
+
field: "power",
|
|
84156
84775
|
quantity: t("the power draw", "il consumo"),
|
|
84157
84776
|
label: t("Power", "Potenza"),
|
|
84158
84777
|
unit: "W",
|
|
@@ -84160,7 +84779,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84160
84779
|
},
|
|
84161
84780
|
{
|
|
84162
84781
|
kind: "numeric",
|
|
84163
|
-
field: "
|
|
84782
|
+
field: "energy",
|
|
84164
84783
|
quantity: t("the energy used", "l’energia consumata"),
|
|
84165
84784
|
label: t("Energy", "Energia"),
|
|
84166
84785
|
unit: "kWh",
|
|
@@ -84168,7 +84787,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84168
84787
|
},
|
|
84169
84788
|
{
|
|
84170
84789
|
kind: "numeric",
|
|
84171
|
-
field: "
|
|
84790
|
+
field: "voltage",
|
|
84172
84791
|
quantity: t("the voltage", "la tensione"),
|
|
84173
84792
|
label: t("Voltage", "Tensione"),
|
|
84174
84793
|
unit: "V",
|
|
@@ -84176,7 +84795,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84176
84795
|
},
|
|
84177
84796
|
{
|
|
84178
84797
|
kind: "numeric",
|
|
84179
|
-
field: "
|
|
84798
|
+
field: "current",
|
|
84180
84799
|
quantity: t("the current", "la corrente"),
|
|
84181
84800
|
label: t("Current", "Corrente"),
|
|
84182
84801
|
unit: "A",
|
|
@@ -84193,13 +84812,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84193
84812
|
label: t("Where", "Posizione"),
|
|
84194
84813
|
phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
|
|
84195
84814
|
icon: "map-pin"
|
|
84196
|
-
}, {
|
|
84197
|
-
kind: "numeric",
|
|
84198
|
-
field: "batteryPercent",
|
|
84199
|
-
quantity: t("the phone battery", "la batteria del telefono"),
|
|
84200
|
-
label: t("Battery", "Batteria"),
|
|
84201
|
-
unit: "%",
|
|
84202
|
-
icon: "battery"
|
|
84203
84815
|
}]
|
|
84204
84816
|
},
|
|
84205
84817
|
"pressure-sensor": {
|
|
@@ -84207,7 +84819,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84207
84819
|
noun: t("Pressure", "Pressione"),
|
|
84208
84820
|
state: [{
|
|
84209
84821
|
kind: "numeric",
|
|
84210
|
-
field: "
|
|
84822
|
+
field: "pressure",
|
|
84211
84823
|
quantity: t("the pressure", "la pressione"),
|
|
84212
84824
|
label: t("Pressure", "Pressione"),
|
|
84213
84825
|
unit: "hPa",
|
|
@@ -84243,7 +84855,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84243
84855
|
noun: t("Temperature", "Temperatura"),
|
|
84244
84856
|
state: [{
|
|
84245
84857
|
kind: "numeric",
|
|
84246
|
-
field: "
|
|
84858
|
+
field: "temperature",
|
|
84247
84859
|
quantity: t("the temperature", "la temperatura"),
|
|
84248
84860
|
label: t("Temperature", "Temperatura"),
|
|
84249
84861
|
unit: "°C",
|
|
@@ -84314,7 +84926,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84314
84926
|
state: [
|
|
84315
84927
|
{
|
|
84316
84928
|
kind: "numeric",
|
|
84317
|
-
field: "
|
|
84929
|
+
field: "level",
|
|
84318
84930
|
quantity: t("the battery level", "il livello della batteria"),
|
|
84319
84931
|
label: t("Battery", "Batteria"),
|
|
84320
84932
|
unit: "%",
|
|
@@ -84356,7 +84968,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84356
84968
|
icon: "battery-charging"
|
|
84357
84969
|
}
|
|
84358
84970
|
],
|
|
84359
|
-
events: { onStatusChanged: { notOperatorFacing: "the
|
|
84971
|
+
events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
|
|
84360
84972
|
extraEvents: [{
|
|
84361
84973
|
category: EventCategory.DeviceAwake,
|
|
84362
84974
|
label: t("Woke up", "Risvegliato"),
|
|
@@ -84374,7 +84986,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84374
84986
|
noun: t("Network link", "Collegamento di rete"),
|
|
84375
84987
|
state: [{
|
|
84376
84988
|
kind: "numeric",
|
|
84377
|
-
field: "
|
|
84989
|
+
field: "signal",
|
|
84378
84990
|
quantity: t("the signal strength", "l’intensità del segnale"),
|
|
84379
84991
|
label: t("Signal", "Segnale"),
|
|
84380
84992
|
unit: "%",
|
|
@@ -84467,6 +85079,8 @@ var ML = "model and training machinery — no device action";
|
|
|
84467
85079
|
var INTERNAL = "internal plumbing between addons — never operator-facing";
|
|
84468
85080
|
/** Every system-scoped cap, with why it is not a device's vocabulary. */
|
|
84469
85081
|
var SYSTEM_CAP_VOCABULARY = {
|
|
85082
|
+
"action-source": system(INTERNAL),
|
|
85083
|
+
actions: system("declared actions — run from the Actions page and from the cameras that link them"),
|
|
84470
85084
|
"addon-pages": system(UI_PLUMBING),
|
|
84471
85085
|
"addon-pages-source": system(UI_PLUMBING),
|
|
84472
85086
|
"addon-routes": system(UI_PLUMBING),
|
|
@@ -84663,6 +85277,31 @@ Object.freeze({
|
|
|
84663
85277
|
form: "single",
|
|
84664
85278
|
optional: false
|
|
84665
85279
|
}],
|
|
85280
|
+
"actions.get": [{
|
|
85281
|
+
name: "deviceId",
|
|
85282
|
+
form: "single",
|
|
85283
|
+
optional: true
|
|
85284
|
+
}],
|
|
85285
|
+
"actions.getLinks": [{
|
|
85286
|
+
name: "deviceId",
|
|
85287
|
+
form: "single",
|
|
85288
|
+
optional: false
|
|
85289
|
+
}],
|
|
85290
|
+
"actions.listForCamera": [{
|
|
85291
|
+
name: "deviceId",
|
|
85292
|
+
form: "single",
|
|
85293
|
+
optional: false
|
|
85294
|
+
}],
|
|
85295
|
+
"actions.run": [{
|
|
85296
|
+
name: "deviceId",
|
|
85297
|
+
form: "single",
|
|
85298
|
+
optional: true
|
|
85299
|
+
}],
|
|
85300
|
+
"actions.setLinks": [{
|
|
85301
|
+
name: "deviceId",
|
|
85302
|
+
form: "single",
|
|
85303
|
+
optional: false
|
|
85304
|
+
}],
|
|
84666
85305
|
"addonSettings.getDeviceSettings": [{
|
|
84667
85306
|
name: "deviceId",
|
|
84668
85307
|
form: "single",
|
|
@@ -89706,6 +90345,7 @@ var BackgroundRunRegistry = class {
|
|
|
89706
90345
|
}
|
|
89707
90346
|
}
|
|
89708
90347
|
};
|
|
90348
|
+
String.raw`(?<=^|["'(\s>,\[{])`;
|
|
89709
90349
|
var MB = 1024 * 1024;
|
|
89710
90350
|
var DEFAULT_POOL_MEMORY_POLICY = {
|
|
89711
90351
|
sampleIntervalMs: 6e4,
|