@camstack/addon-waste-collections 0.1.31 → 0.1.32
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 +6 -0
- package/dist/waste-collections.addon.js +2371 -668
- package/dist/waste-collections.addon.mjs +2371 -668
- package/package.json +3 -2
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
//#region ../types/dist/event-category-
|
|
5
|
+
//#region ../types/dist/event-category-DmZDPDf_.mjs
|
|
6
6
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
7
7
|
EventCategory["SystemBoot"] = "system.boot";
|
|
8
8
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -629,7 +629,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
629
629
|
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
630
630
|
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
631
631
|
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
632
|
-
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
632
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
|
|
633
633
|
*/
|
|
634
634
|
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
635
635
|
/**
|
|
@@ -767,6 +767,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
767
767
|
*/
|
|
768
768
|
EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
|
|
769
769
|
/**
|
|
770
|
+
* An action run started or ended. Emitted by the `actions` surface.
|
|
771
|
+
* Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
|
|
772
|
+
*/
|
|
773
|
+
EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
|
|
774
|
+
/**
|
|
770
775
|
* Progress update from a running model conversion job.
|
|
771
776
|
* Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
|
|
772
777
|
* Emitted by `addon-model-studio` on the converting node.
|
|
@@ -5199,6 +5204,14 @@ function object(shape, params) {
|
|
|
5199
5204
|
...normalizeParams(params)
|
|
5200
5205
|
});
|
|
5201
5206
|
}
|
|
5207
|
+
function strictObject(shape, params) {
|
|
5208
|
+
return new ZodObject({
|
|
5209
|
+
type: "object",
|
|
5210
|
+
shape,
|
|
5211
|
+
catchall: never(),
|
|
5212
|
+
...normalizeParams(params)
|
|
5213
|
+
});
|
|
5214
|
+
}
|
|
5202
5215
|
function looseObject(shape, params) {
|
|
5203
5216
|
return new ZodObject({
|
|
5204
5217
|
type: "object",
|
|
@@ -5578,7 +5591,7 @@ var ZodIssueCode = {
|
|
|
5578
5591
|
var ZodFirstPartyTypeKind;
|
|
5579
5592
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5580
5593
|
//#endregion
|
|
5581
|
-
//#region ../types/dist/sleep
|
|
5594
|
+
//#region ../types/dist/sleep-6eG9C8Xl.mjs
|
|
5582
5595
|
/**
|
|
5583
5596
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5584
5597
|
* window to float samples (D455).
|
|
@@ -8819,6 +8832,52 @@ function optionalMethod(input, output, options) {
|
|
|
8819
8832
|
providerOptional: true
|
|
8820
8833
|
};
|
|
8821
8834
|
}
|
|
8835
|
+
/**
|
|
8836
|
+
* `addon-pages-source` — collection cap exposing per-provider raw page
|
|
8837
|
+
* declarations. Every addon that contributes a UI page registers a
|
|
8838
|
+
* provider here. The hub-side singleton aggregator (`addon-pages` cap,
|
|
8839
|
+
* see `addon-pages.cap.ts`) walks this collection, stamps versioned
|
|
8840
|
+
* `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
|
|
8841
|
+
* that admin-ui consumes.
|
|
8842
|
+
*
|
|
8843
|
+
* The split exists because the public listing has a different output
|
|
8844
|
+
* shape than the per-provider raw declarations, and we want both ends
|
|
8845
|
+
* to flow through codegen instead of relying on a hand-written wrapper.
|
|
8846
|
+
*/
|
|
8847
|
+
var AddonPageDeclarationSchema = object({
|
|
8848
|
+
id: string(),
|
|
8849
|
+
label: string(),
|
|
8850
|
+
icon: string(),
|
|
8851
|
+
path: string(),
|
|
8852
|
+
/**
|
|
8853
|
+
* Module Federation remote name — must match the `name` field on the
|
|
8854
|
+
* page addon's `federation()` plugin config. Used by admin-ui's
|
|
8855
|
+
* `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
|
|
8856
|
+
* Conventionally `addon_<id>_page` (snake_case; MF names cannot
|
|
8857
|
+
* contain hyphens).
|
|
8858
|
+
*/
|
|
8859
|
+
remoteName: string(),
|
|
8860
|
+
/**
|
|
8861
|
+
* Bundle filename inside the addon's `dist/` dir served at
|
|
8862
|
+
* `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
|
|
8863
|
+
* is always `'remoteEntry.js'`; the value is kept on the metadata so
|
|
8864
|
+
* the static-file route can compute an mtime-based cache-buster URL
|
|
8865
|
+
* without a separate filesystem stat.
|
|
8866
|
+
*/
|
|
8867
|
+
bundle: string(),
|
|
8868
|
+
/**
|
|
8869
|
+
* Sidebar section this page docks into. Well-known ids: `'detection'`,
|
|
8870
|
+
* `'cluster'`, `'administration'` — the page renders inside that group.
|
|
8871
|
+
* Any OTHER string creates (or joins) a custom section rendered after
|
|
8872
|
+
* the built-in groups; its label comes from `sectionLabel` (first
|
|
8873
|
+
* declaration wins), falling back to the id. Absent → the legacy
|
|
8874
|
+
* "Addon Pages" group.
|
|
8875
|
+
*/
|
|
8876
|
+
section: string().optional(),
|
|
8877
|
+
/** Display label for a CUSTOM `section` id (ignored for well-known ids). */
|
|
8878
|
+
sectionLabel: string().optional()
|
|
8879
|
+
});
|
|
8880
|
+
method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
|
|
8822
8881
|
var StaticDirOutputSchema$1 = object({ staticDir: string() });
|
|
8823
8882
|
var VersionOutputSchema$1 = object({ version: string() });
|
|
8824
8883
|
method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
|
|
@@ -8844,6 +8903,27 @@ function customAction(input, output, options) {
|
|
|
8844
8903
|
...options?.caller ? { caller: "required" } : {}
|
|
8845
8904
|
};
|
|
8846
8905
|
}
|
|
8906
|
+
/**
|
|
8907
|
+
* The pipeline step-input shape and the run-result bridge, in a module that
|
|
8908
|
+
* imports nothing but zod and types.
|
|
8909
|
+
*
|
|
8910
|
+
* WHY A SEPARATE FILE (addon footprint, D28): both were declared in
|
|
8911
|
+
* `pipeline-executor.cap.ts`, whose import evaluates the whole executor schema
|
|
8912
|
+
* graph. An addon that only needs these two (the benchmark's `runPipelineTest`
|
|
8913
|
+
* action) paid ~130 MB of heap at load. `pipeline-executor.cap.ts` re-exports
|
|
8914
|
+
* them, so every existing import keeps working; addon server code takes them
|
|
8915
|
+
* from `@camstack/types/addon`.
|
|
8916
|
+
*/
|
|
8917
|
+
var PipelineStepInputSchema = lazy(() => object({
|
|
8918
|
+
addonId: string(),
|
|
8919
|
+
modelId: string().optional(),
|
|
8920
|
+
enabled: boolean().default(true),
|
|
8921
|
+
children: array(PipelineStepInputSchema).optional(),
|
|
8922
|
+
settings: record(string(), unknown()).optional(),
|
|
8923
|
+
jumpDeviceKey: string().optional()
|
|
8924
|
+
}));
|
|
8925
|
+
/** Typed bridge to the recursive `FrameResult` graph; validation is structural. */
|
|
8926
|
+
var PipelineRunResultBridge = custom();
|
|
8847
8927
|
var StaticDirOutputSchema = object({ staticDir: string() });
|
|
8848
8928
|
var VersionOutputSchema = object({ version: string() });
|
|
8849
8929
|
method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
|
|
@@ -8914,6 +8994,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
|
|
|
8914
8994
|
/** Every capability name, so addon code can check a name without the schema graph. */
|
|
8915
8995
|
var CAP_NAMES = Object.freeze([
|
|
8916
8996
|
"accessories",
|
|
8997
|
+
"action-source",
|
|
8998
|
+
"actions",
|
|
8917
8999
|
"addon-pages",
|
|
8918
9000
|
"addon-pages-source",
|
|
8919
9001
|
"addon-routes",
|
|
@@ -8997,6 +9079,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8997
9079
|
"image-settings",
|
|
8998
9080
|
"integrations",
|
|
8999
9081
|
"intercom",
|
|
9082
|
+
"ir-transmitter",
|
|
9000
9083
|
"keychain",
|
|
9001
9084
|
"keychain-admin",
|
|
9002
9085
|
"lawn-mower-control",
|
|
@@ -9025,6 +9108,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9025
9108
|
"network-link",
|
|
9026
9109
|
"network-quality",
|
|
9027
9110
|
"node-certificates",
|
|
9111
|
+
"node-removal-hook",
|
|
9028
9112
|
"nodes",
|
|
9029
9113
|
"notification-output",
|
|
9030
9114
|
"notification-rules",
|
|
@@ -9035,6 +9119,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9035
9119
|
"osd",
|
|
9036
9120
|
"osd-manager",
|
|
9037
9121
|
"pet-feeder",
|
|
9122
|
+
"pet-fountain",
|
|
9038
9123
|
"pipeline-analytics",
|
|
9039
9124
|
"pipeline-executor",
|
|
9040
9125
|
"pipeline-orchestrator",
|
|
@@ -9055,6 +9140,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9055
9140
|
"recording-onboard",
|
|
9056
9141
|
"recording-signal",
|
|
9057
9142
|
"records",
|
|
9143
|
+
"remote",
|
|
9058
9144
|
"running-state",
|
|
9059
9145
|
"scene-monitor",
|
|
9060
9146
|
"script-runner",
|
|
@@ -9078,6 +9164,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9078
9164
|
"system",
|
|
9079
9165
|
"tamper",
|
|
9080
9166
|
"temperature-sensor",
|
|
9167
|
+
"template",
|
|
9081
9168
|
"terminal-session",
|
|
9082
9169
|
"toast",
|
|
9083
9170
|
"training",
|
|
@@ -9102,11 +9189,11 @@ var CAP_NAMES = Object.freeze([
|
|
|
9102
9189
|
Object.freeze({
|
|
9103
9190
|
"air-quality-sensor": [
|
|
9104
9191
|
{
|
|
9105
|
-
"path": "
|
|
9192
|
+
"path": "co2",
|
|
9106
9193
|
"valueType": "number"
|
|
9107
9194
|
},
|
|
9108
9195
|
{
|
|
9109
|
-
"path": "
|
|
9196
|
+
"path": "voc",
|
|
9110
9197
|
"valueType": "number"
|
|
9111
9198
|
},
|
|
9112
9199
|
{
|
|
@@ -9156,7 +9243,7 @@ Object.freeze({
|
|
|
9156
9243
|
}
|
|
9157
9244
|
],
|
|
9158
9245
|
"ambient-light-sensor": [{
|
|
9159
|
-
"path": "
|
|
9246
|
+
"path": "illuminance",
|
|
9160
9247
|
"valueType": "number"
|
|
9161
9248
|
}],
|
|
9162
9249
|
"audio-metrics": [
|
|
@@ -9217,7 +9304,7 @@ Object.freeze({
|
|
|
9217
9304
|
],
|
|
9218
9305
|
"battery": [
|
|
9219
9306
|
{
|
|
9220
|
-
"path": "
|
|
9307
|
+
"path": "level",
|
|
9221
9308
|
"valueType": "number"
|
|
9222
9309
|
},
|
|
9223
9310
|
{
|
|
@@ -9239,7 +9326,7 @@ Object.freeze({
|
|
|
9239
9326
|
"valueType": "number"
|
|
9240
9327
|
},
|
|
9241
9328
|
{
|
|
9242
|
-
"path": "
|
|
9329
|
+
"path": "voltage",
|
|
9243
9330
|
"valueType": "number"
|
|
9244
9331
|
},
|
|
9245
9332
|
{
|
|
@@ -9260,7 +9347,7 @@ Object.freeze({
|
|
|
9260
9347
|
"valueType": "boolean"
|
|
9261
9348
|
}],
|
|
9262
9349
|
"brightness": [{
|
|
9263
|
-
"path": "
|
|
9350
|
+
"path": "level",
|
|
9264
9351
|
"valueType": "number"
|
|
9265
9352
|
}],
|
|
9266
9353
|
"broker": [{
|
|
@@ -9461,7 +9548,7 @@ Object.freeze({
|
|
|
9461
9548
|
"valueType": "number"
|
|
9462
9549
|
},
|
|
9463
9550
|
{
|
|
9464
|
-
"path": "
|
|
9551
|
+
"path": "colorTemperature",
|
|
9465
9552
|
"valueType": "number"
|
|
9466
9553
|
}
|
|
9467
9554
|
],
|
|
@@ -9640,15 +9727,15 @@ Object.freeze({
|
|
|
9640
9727
|
"valueType": "string"
|
|
9641
9728
|
},
|
|
9642
9729
|
{
|
|
9643
|
-
"path": "
|
|
9730
|
+
"path": "remaining",
|
|
9644
9731
|
"valueType": "number"
|
|
9645
9732
|
},
|
|
9646
9733
|
{
|
|
9647
|
-
"path": "
|
|
9734
|
+
"path": "delayedStart",
|
|
9648
9735
|
"valueType": "number"
|
|
9649
9736
|
},
|
|
9650
9737
|
{
|
|
9651
|
-
"path": "
|
|
9738
|
+
"path": "progress",
|
|
9652
9739
|
"valueType": "number"
|
|
9653
9740
|
},
|
|
9654
9741
|
{
|
|
@@ -9762,7 +9849,7 @@ Object.freeze({
|
|
|
9762
9849
|
],
|
|
9763
9850
|
"fan-control": [
|
|
9764
9851
|
{
|
|
9765
|
-
"path": "
|
|
9852
|
+
"path": "speed",
|
|
9766
9853
|
"valueType": "number"
|
|
9767
9854
|
},
|
|
9768
9855
|
{
|
|
@@ -9862,7 +9949,7 @@ Object.freeze({
|
|
|
9862
9949
|
}
|
|
9863
9950
|
],
|
|
9864
9951
|
"humidity-sensor": [{
|
|
9865
|
-
"path": "
|
|
9952
|
+
"path": "humidity",
|
|
9866
9953
|
"valueType": "number"
|
|
9867
9954
|
}],
|
|
9868
9955
|
"image": [{
|
|
@@ -9972,7 +10059,7 @@ Object.freeze({
|
|
|
9972
10059
|
"valueType": "number"
|
|
9973
10060
|
},
|
|
9974
10061
|
{
|
|
9975
|
-
"path": "
|
|
10062
|
+
"path": "progress",
|
|
9976
10063
|
"valueType": "number"
|
|
9977
10064
|
},
|
|
9978
10065
|
{
|
|
@@ -10018,6 +10105,11 @@ Object.freeze({
|
|
|
10018
10105
|
"standby"
|
|
10019
10106
|
]
|
|
10020
10107
|
},
|
|
10108
|
+
{
|
|
10109
|
+
"path": "stateSource",
|
|
10110
|
+
"valueType": "enum",
|
|
10111
|
+
"enumValues": ["reported", "assumed"]
|
|
10112
|
+
},
|
|
10021
10113
|
{
|
|
10022
10114
|
"path": "volumeLevel",
|
|
10023
10115
|
"valueType": "number"
|
|
@@ -10051,11 +10143,11 @@ Object.freeze({
|
|
|
10051
10143
|
"valueType": "string"
|
|
10052
10144
|
},
|
|
10053
10145
|
{
|
|
10054
|
-
"path": "
|
|
10146
|
+
"path": "position",
|
|
10055
10147
|
"valueType": "number"
|
|
10056
10148
|
},
|
|
10057
10149
|
{
|
|
10058
|
-
"path": "
|
|
10150
|
+
"path": "duration",
|
|
10059
10151
|
"valueType": "number"
|
|
10060
10152
|
},
|
|
10061
10153
|
{
|
|
@@ -10157,11 +10249,11 @@ Object.freeze({
|
|
|
10157
10249
|
]
|
|
10158
10250
|
},
|
|
10159
10251
|
{
|
|
10160
|
-
"path": "
|
|
10252
|
+
"path": "signal",
|
|
10161
10253
|
"valueType": "number"
|
|
10162
10254
|
},
|
|
10163
10255
|
{
|
|
10164
|
-
"path": "
|
|
10256
|
+
"path": "rssi",
|
|
10165
10257
|
"valueType": "number"
|
|
10166
10258
|
},
|
|
10167
10259
|
{
|
|
@@ -10216,6 +10308,18 @@ Object.freeze({
|
|
|
10216
10308
|
"path": "foodLevel",
|
|
10217
10309
|
"valueType": "number"
|
|
10218
10310
|
},
|
|
10311
|
+
{
|
|
10312
|
+
"path": "bowlFood",
|
|
10313
|
+
"valueType": "number"
|
|
10314
|
+
},
|
|
10315
|
+
{
|
|
10316
|
+
"path": "eating",
|
|
10317
|
+
"valueType": "boolean"
|
|
10318
|
+
},
|
|
10319
|
+
{
|
|
10320
|
+
"path": "supportsMarkRefilled",
|
|
10321
|
+
"valueType": "boolean"
|
|
10322
|
+
},
|
|
10219
10323
|
{
|
|
10220
10324
|
"path": "food1",
|
|
10221
10325
|
"valueType": "number"
|
|
@@ -10232,6 +10336,11 @@ Object.freeze({
|
|
|
10232
10336
|
"path": "batteryPower",
|
|
10233
10337
|
"valueType": "number"
|
|
10234
10338
|
},
|
|
10339
|
+
{
|
|
10340
|
+
"path": "powerSource",
|
|
10341
|
+
"valueType": "enum",
|
|
10342
|
+
"enumValues": ["mains", "battery"]
|
|
10343
|
+
},
|
|
10235
10344
|
{
|
|
10236
10345
|
"path": "desiccantLeftDays",
|
|
10237
10346
|
"valueType": "number"
|
|
@@ -10276,27 +10385,89 @@ Object.freeze({
|
|
|
10276
10385
|
{
|
|
10277
10386
|
"path": "volume",
|
|
10278
10387
|
"valueType": "number"
|
|
10388
|
+
},
|
|
10389
|
+
{
|
|
10390
|
+
"path": "lastFeedGrams",
|
|
10391
|
+
"valueType": "number"
|
|
10392
|
+
},
|
|
10393
|
+
{
|
|
10394
|
+
"path": "lastFeedHopper1",
|
|
10395
|
+
"valueType": "number"
|
|
10396
|
+
},
|
|
10397
|
+
{
|
|
10398
|
+
"path": "lastFeedHopper2",
|
|
10399
|
+
"valueType": "number"
|
|
10400
|
+
}
|
|
10401
|
+
],
|
|
10402
|
+
"pet-fountain": [
|
|
10403
|
+
{
|
|
10404
|
+
"path": "waterLevel",
|
|
10405
|
+
"valueType": "number"
|
|
10406
|
+
},
|
|
10407
|
+
{
|
|
10408
|
+
"path": "waterVolume",
|
|
10409
|
+
"valueType": "number"
|
|
10410
|
+
},
|
|
10411
|
+
{
|
|
10412
|
+
"path": "empty",
|
|
10413
|
+
"valueType": "boolean"
|
|
10414
|
+
},
|
|
10415
|
+
{
|
|
10416
|
+
"path": "pumpRunning",
|
|
10417
|
+
"valueType": "boolean"
|
|
10418
|
+
},
|
|
10419
|
+
{
|
|
10420
|
+
"path": "pumpIntervalEnabled",
|
|
10421
|
+
"valueType": "boolean"
|
|
10422
|
+
},
|
|
10423
|
+
{
|
|
10424
|
+
"path": "pumpOnTime",
|
|
10425
|
+
"valueType": "number"
|
|
10426
|
+
},
|
|
10427
|
+
{
|
|
10428
|
+
"path": "pumpOffTime",
|
|
10429
|
+
"valueType": "number"
|
|
10430
|
+
},
|
|
10431
|
+
{
|
|
10432
|
+
"path": "lastDrink",
|
|
10433
|
+
"valueType": "number"
|
|
10434
|
+
},
|
|
10435
|
+
{
|
|
10436
|
+
"path": "currentHourDrink",
|
|
10437
|
+
"valueType": "number"
|
|
10438
|
+
},
|
|
10439
|
+
{
|
|
10440
|
+
"path": "last24hDrink",
|
|
10441
|
+
"valueType": "number"
|
|
10442
|
+
},
|
|
10443
|
+
{
|
|
10444
|
+
"path": "todayDrink",
|
|
10445
|
+
"valueType": "number"
|
|
10446
|
+
},
|
|
10447
|
+
{
|
|
10448
|
+
"path": "error",
|
|
10449
|
+
"valueType": "string"
|
|
10279
10450
|
}
|
|
10280
10451
|
],
|
|
10281
10452
|
"power-meter": [
|
|
10282
10453
|
{
|
|
10283
|
-
"path": "
|
|
10454
|
+
"path": "power",
|
|
10284
10455
|
"valueType": "number"
|
|
10285
10456
|
},
|
|
10286
10457
|
{
|
|
10287
|
-
"path": "
|
|
10458
|
+
"path": "energy",
|
|
10288
10459
|
"valueType": "number"
|
|
10289
10460
|
},
|
|
10290
10461
|
{
|
|
10291
|
-
"path": "
|
|
10462
|
+
"path": "voltage",
|
|
10292
10463
|
"valueType": "number"
|
|
10293
10464
|
},
|
|
10294
10465
|
{
|
|
10295
|
-
"path": "
|
|
10466
|
+
"path": "current",
|
|
10296
10467
|
"valueType": "number"
|
|
10297
10468
|
},
|
|
10298
10469
|
{
|
|
10299
|
-
"path": "
|
|
10470
|
+
"path": "apparentPower",
|
|
10300
10471
|
"valueType": "number"
|
|
10301
10472
|
},
|
|
10302
10473
|
{
|
|
@@ -10324,14 +10495,10 @@ Object.freeze({
|
|
|
10324
10495
|
{
|
|
10325
10496
|
"path": "gps.accuracyMeters",
|
|
10326
10497
|
"valueType": "number"
|
|
10327
|
-
},
|
|
10328
|
-
{
|
|
10329
|
-
"path": "batteryPercent",
|
|
10330
|
-
"valueType": "number"
|
|
10331
10498
|
}
|
|
10332
10499
|
],
|
|
10333
10500
|
"pressure-sensor": [{
|
|
10334
|
-
"path": "
|
|
10501
|
+
"path": "pressure",
|
|
10335
10502
|
"valueType": "number"
|
|
10336
10503
|
}],
|
|
10337
10504
|
"privacy-mask": [{
|
|
@@ -10645,7 +10812,7 @@ Object.freeze({
|
|
|
10645
10812
|
"valueType": "boolean"
|
|
10646
10813
|
}],
|
|
10647
10814
|
"temperature-sensor": [{
|
|
10648
|
-
"path": "
|
|
10815
|
+
"path": "temperature",
|
|
10649
10816
|
"valueType": "number"
|
|
10650
10817
|
}],
|
|
10651
10818
|
"update": [
|
|
@@ -10693,7 +10860,7 @@ Object.freeze({
|
|
|
10693
10860
|
"valueType": "string"
|
|
10694
10861
|
},
|
|
10695
10862
|
{
|
|
10696
|
-
"path": "
|
|
10863
|
+
"path": "progress",
|
|
10697
10864
|
"valueType": "number"
|
|
10698
10865
|
},
|
|
10699
10866
|
{
|
|
@@ -10819,7 +10986,7 @@ object({
|
|
|
10819
10986
|
params: record(string(), union([string(), number()])).optional()
|
|
10820
10987
|
});
|
|
10821
10988
|
//#endregion
|
|
10822
|
-
//#region ../types/dist/err-msg-
|
|
10989
|
+
//#region ../types/dist/err-msg-DWXWc0Di.mjs
|
|
10823
10990
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10824
10991
|
DeviceType["Camera"] = "camera";
|
|
10825
10992
|
DeviceType["Hub"] = "hub";
|
|
@@ -10911,6 +11078,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10911
11078
|
* native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
|
|
10912
11079
|
* integrations sharing the same food/desiccant/hopper surface. */
|
|
10913
11080
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
11081
|
+
/** Pet water fountain — tank level, water drunk, pump and its duty cycle.
|
|
11082
|
+
* Installed with the `pet-fountain` cap. Reaches CamStack as loose entities
|
|
11083
|
+
* of a container and is BUILT as a composed device (D967). */
|
|
11084
|
+
DeviceType["PetFountain"] = "pet-fountain";
|
|
10914
11085
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10915
11086
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10916
11087
|
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
@@ -11165,10 +11336,13 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
11165
11336
|
/** Fallback when the notifier service name doesn't match a known
|
|
11166
11337
|
* pattern. */
|
|
11167
11338
|
DeviceRole["GenericNotifier"] = "generic-notifier";
|
|
11339
|
+
/** Emits a pulse train (infrared or RF) on request — the `ir-transmitter`
|
|
11340
|
+
* cap. A `DeviceType.Generic` device. */
|
|
11341
|
+
DeviceRole["IrTransmitter"] = "ir-transmitter";
|
|
11168
11342
|
return DeviceRole;
|
|
11169
11343
|
}({});
|
|
11170
11344
|
//#endregion
|
|
11171
|
-
//#region ../types/dist/composition-
|
|
11345
|
+
//#region ../types/dist/composition-kSuOifuG.mjs
|
|
11172
11346
|
/**
|
|
11173
11347
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11174
11348
|
*
|
|
@@ -12253,12 +12427,28 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
12253
12427
|
])
|
|
12254
12428
|
})
|
|
12255
12429
|
]);
|
|
12256
|
-
var
|
|
12430
|
+
var forwardArgUnion = union([
|
|
12431
|
+
string(),
|
|
12432
|
+
number(),
|
|
12433
|
+
boolean(),
|
|
12434
|
+
strictObject({ ["$input"]: string().min(1) }),
|
|
12435
|
+
record(string().min(1), lazy(() => forwardArgUnion))
|
|
12436
|
+
]);
|
|
12437
|
+
var CompositionForwardArgSchema = forwardArgUnion;
|
|
12438
|
+
/**
|
|
12439
|
+
* `forward` (D659, amended by D967): one method of a BUILT capability is one
|
|
12440
|
+
* call of `cap.method` on `source`. Without `args` the built method's input
|
|
12441
|
+
* passes through minus `deviceId`; with `args` the template IS the source's
|
|
12442
|
+
* whole input. `deviceId` is always the source's own, never an argument.
|
|
12443
|
+
*/
|
|
12444
|
+
var CompositionForwardCommandSchema = object({
|
|
12257
12445
|
kind: literal("forward"),
|
|
12258
12446
|
source: CompositionSourceRefSchema,
|
|
12259
12447
|
cap: string().min(1),
|
|
12260
|
-
method: string().min(1)
|
|
12261
|
-
|
|
12448
|
+
method: string().min(1),
|
|
12449
|
+
args: record(string().min(1), CompositionForwardArgSchema).optional()
|
|
12450
|
+
});
|
|
12451
|
+
var CompositionCommandTargetSchema = discriminatedUnion("kind", [CompositionForwardCommandSchema, CompositionCodeSourceSchema]);
|
|
12262
12452
|
/** An item key inside an item-array cap (`consumables.items[<key>]`). */
|
|
12263
12453
|
var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
|
|
12264
12454
|
/**
|
|
@@ -12268,7 +12458,13 @@ var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
|
|
|
12268
12458
|
*/
|
|
12269
12459
|
var CompositionItemEntrySchema = object({
|
|
12270
12460
|
label: string().min(1).max(80),
|
|
12271
|
-
fields: record(string().min(1), CompositionFieldSourceSchema)
|
|
12461
|
+
fields: record(string().min(1), CompositionFieldSourceSchema),
|
|
12462
|
+
/**
|
|
12463
|
+
* What the cap's `reset` does for THIS item (consumables). Present = the item
|
|
12464
|
+
* is resettable and a `reset` call naming its key forwards here. The item key
|
|
12465
|
+
* selects the command, so it is never an argument: there is no pass-through.
|
|
12466
|
+
*/
|
|
12467
|
+
reset: CompositionForwardCommandSchema.optional()
|
|
12272
12468
|
});
|
|
12273
12469
|
var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
12274
12470
|
kind: literal("fields"),
|
|
@@ -12280,7 +12476,7 @@ var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
|
12280
12476
|
* owns the WHOLE array field: the composed value holds exactly these items.
|
|
12281
12477
|
*/
|
|
12282
12478
|
items: record(string().regex(COMPOSITION_ITEM_KEY_RE), CompositionItemEntrySchema).refine((items) => Object.keys(items).length <= 16, { message: `at most 16 items` }).optional(),
|
|
12283
|
-
/**
|
|
12479
|
+
/** Method name → what serves it. Only a BUILT capability takes commands (D967). */
|
|
12284
12480
|
commands: record(string().min(1), CompositionCommandTargetSchema).optional()
|
|
12285
12481
|
}), object({
|
|
12286
12482
|
kind: literal("passthrough"),
|
|
@@ -15688,6 +15884,14 @@ var AutomationConditionLeafSchema = object({
|
|
|
15688
15884
|
array(union([string(), number()]))
|
|
15689
15885
|
])
|
|
15690
15886
|
});
|
|
15887
|
+
var AutomationConditionStationaryPresentSchema = object({
|
|
15888
|
+
kind: literal("stationary-present"),
|
|
15889
|
+
deviceId: number().int().nonnegative(),
|
|
15890
|
+
/** Class names; a macro class matches every raw class that maps to it. */
|
|
15891
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
15892
|
+
/** The parked object must be in at least one of these zones. */
|
|
15893
|
+
zones: array(string().min(1)).min(1).optional()
|
|
15894
|
+
});
|
|
15691
15895
|
/**
|
|
15692
15896
|
* The expression leaf, declared as a plain object rather than an intersection
|
|
15693
15897
|
* with {@link ExpressionSourceSchema}: a discriminated union has to be able to
|
|
@@ -15721,7 +15925,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
|
15721
15925
|
child: AutomationConditionSchema
|
|
15722
15926
|
}),
|
|
15723
15927
|
AutomationConditionLeafSchema,
|
|
15724
|
-
AutomationConditionExpressionSchema
|
|
15928
|
+
AutomationConditionExpressionSchema,
|
|
15929
|
+
AutomationConditionStationaryPresentSchema
|
|
15725
15930
|
]));
|
|
15726
15931
|
/**
|
|
15727
15932
|
* What starts a run.
|
|
@@ -15762,7 +15967,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15762
15967
|
kind: literal("schedule"),
|
|
15763
15968
|
cron: string().min(1).max(120)
|
|
15764
15969
|
}),
|
|
15765
|
-
object({ kind: literal("manual") })
|
|
15970
|
+
object({ kind: literal("manual") }),
|
|
15971
|
+
object({
|
|
15972
|
+
kind: literal("detection"),
|
|
15973
|
+
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
15974
|
+
* matches every raw class that maps to it; any other name matches
|
|
15975
|
+
* exactly. */
|
|
15976
|
+
classes: array(string().min(1)).min(1),
|
|
15977
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
15978
|
+
/** The track must have visited at least one of these zones. */
|
|
15979
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
15980
|
+
/** Best-so-far confidence the track must reach. */
|
|
15981
|
+
minScore: number().min(0).max(1).optional()
|
|
15982
|
+
}),
|
|
15983
|
+
object({
|
|
15984
|
+
kind: literal("stationary"),
|
|
15985
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
15986
|
+
/** Class names; a macro class matches like the detection trigger. */
|
|
15987
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
15988
|
+
/** The parked object must be in at least one of these zones. */
|
|
15989
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
15990
|
+
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
15991
|
+
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
15992
|
+
phase: _enum(["appeared", "departed"])
|
|
15993
|
+
}),
|
|
15994
|
+
object({
|
|
15995
|
+
kind: literal("interval"),
|
|
15996
|
+
minutes: number().int().min(1).max(1440)
|
|
15997
|
+
})
|
|
15766
15998
|
]);
|
|
15767
15999
|
/**
|
|
15768
16000
|
* One action step.
|
|
@@ -15782,28 +16014,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15782
16014
|
* is called. The runner still receives a number and is untouched; the
|
|
15783
16015
|
* resolution is the recipe's job, not the runner's.
|
|
15784
16016
|
*/
|
|
16017
|
+
var AutomationWaitStepSchema = object({
|
|
16018
|
+
kind: literal("wait"),
|
|
16019
|
+
seconds: number().min(0).max(300)
|
|
16020
|
+
});
|
|
16021
|
+
var AutomationCapStepSchema = object({
|
|
16022
|
+
kind: literal("cap"),
|
|
16023
|
+
/**
|
|
16024
|
+
* One device, several same-family devices (locks / climate family / …),
|
|
16025
|
+
* or `{ $var }` for the device that triggered the run. An array is
|
|
16026
|
+
* fan-out at codegen — each id gets the same cap.method call.
|
|
16027
|
+
*/
|
|
16028
|
+
deviceId: union([
|
|
16029
|
+
number().int(),
|
|
16030
|
+
array(number().int()).min(1),
|
|
16031
|
+
object({ $var: string().min(1) })
|
|
16032
|
+
]),
|
|
16033
|
+
cap: string().min(1),
|
|
16034
|
+
method: string().min(1),
|
|
16035
|
+
/** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
16036
|
+
* SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
|
|
16037
|
+
* the expression leaf. */
|
|
16038
|
+
args: record(string(), unknown()).optional()
|
|
16039
|
+
});
|
|
16040
|
+
/**
|
|
16041
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
16042
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
16043
|
+
*/
|
|
16044
|
+
var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
|
|
15785
16045
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
16046
|
+
AutomationWaitStepSchema,
|
|
16047
|
+
AutomationCapStepSchema,
|
|
15786
16048
|
object({
|
|
15787
|
-
kind: literal("
|
|
15788
|
-
|
|
15789
|
-
|
|
15790
|
-
|
|
15791
|
-
|
|
15792
|
-
|
|
15793
|
-
|
|
15794
|
-
|
|
15795
|
-
|
|
15796
|
-
*/
|
|
15797
|
-
deviceId: union([
|
|
15798
|
-
number().int(),
|
|
15799
|
-
array(number().int()).min(1),
|
|
15800
|
-
object({ $var: string().min(1) })
|
|
15801
|
-
]),
|
|
15802
|
-
cap: string().min(1),
|
|
15803
|
-
method: string().min(1),
|
|
15804
|
-
/** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
|
|
15805
|
-
* evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
|
|
15806
|
-
args: record(string(), unknown()).optional()
|
|
16049
|
+
kind: literal("action"),
|
|
16050
|
+
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
16051
|
+
actionId: string().min(1),
|
|
16052
|
+
/** Values for the action's declared variables; the others take their default. */
|
|
16053
|
+
variables: record(string(), union([
|
|
16054
|
+
string(),
|
|
16055
|
+
number(),
|
|
16056
|
+
boolean()
|
|
16057
|
+
])).optional()
|
|
15807
16058
|
}),
|
|
15808
16059
|
object({
|
|
15809
16060
|
kind: literal("code"),
|
|
@@ -15812,10 +16063,778 @@ var AutomationActionSchema = discriminatedUnion("kind", [
|
|
|
15812
16063
|
code: string().min(1).max(2e4)
|
|
15813
16064
|
})
|
|
15814
16065
|
]);
|
|
16066
|
+
/**
|
|
16067
|
+
* How a trigger that arrives while the automation is busy (or cooling down) is
|
|
16068
|
+
* handled. One of the automation's execution modes, never a property of its
|
|
16069
|
+
* actions. Absent means `single`.
|
|
16070
|
+
*
|
|
16071
|
+
* - `single` — drop the trigger while a run is in progress (traced + logged).
|
|
16072
|
+
* - `restart` — abort the run in progress (at its next step boundary), start fresh.
|
|
16073
|
+
* - `queued` — queue up to `max` triggers, drop the rest (traced).
|
|
16074
|
+
* - `parallel` — up to `max` runs at once, drop the rest (traced).
|
|
16075
|
+
* - `cooldown` — `single`, and no new run until `minutes` after the last run
|
|
16076
|
+
* STARTED; persisted across a restart. A manual trigger bypasses only this
|
|
16077
|
+
* wait, never the single/queued limits.
|
|
16078
|
+
*/
|
|
16079
|
+
var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
16080
|
+
object({ mode: literal("single") }),
|
|
16081
|
+
object({ mode: literal("restart") }),
|
|
16082
|
+
object({
|
|
16083
|
+
mode: literal("queued"),
|
|
16084
|
+
max: number().int().min(1).max(10)
|
|
16085
|
+
}),
|
|
16086
|
+
object({
|
|
16087
|
+
mode: literal("parallel"),
|
|
16088
|
+
max: number().int().min(1).max(10)
|
|
16089
|
+
}),
|
|
16090
|
+
object({
|
|
16091
|
+
mode: literal("cooldown"),
|
|
16092
|
+
minutes: number().int().min(1).max(1440)
|
|
16093
|
+
})
|
|
16094
|
+
]);
|
|
15815
16095
|
var AutomationRecipeSchema = object({
|
|
15816
16096
|
triggers: array(AutomationTriggerSchema),
|
|
15817
16097
|
conditions: AutomationConditionSchema.optional(),
|
|
15818
|
-
actions: array(AutomationActionSchema)
|
|
16098
|
+
actions: array(AutomationActionSchema),
|
|
16099
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
16100
|
+
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
16101
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
16102
|
+
});
|
|
16103
|
+
new Set([
|
|
16104
|
+
{
|
|
16105
|
+
id: "person",
|
|
16106
|
+
name: "Person"
|
|
16107
|
+
},
|
|
16108
|
+
{
|
|
16109
|
+
id: "vehicle",
|
|
16110
|
+
name: "Vehicle"
|
|
16111
|
+
},
|
|
16112
|
+
{
|
|
16113
|
+
id: "animal",
|
|
16114
|
+
name: "Animal"
|
|
16115
|
+
},
|
|
16116
|
+
{
|
|
16117
|
+
id: "package",
|
|
16118
|
+
name: "Package"
|
|
16119
|
+
}
|
|
16120
|
+
].map((l) => l.id));
|
|
16121
|
+
var COCO_TO_MACRO = {
|
|
16122
|
+
mapping: {
|
|
16123
|
+
person: "person",
|
|
16124
|
+
bicycle: "vehicle",
|
|
16125
|
+
car: "vehicle",
|
|
16126
|
+
motorcycle: "vehicle",
|
|
16127
|
+
airplane: "vehicle",
|
|
16128
|
+
bus: "vehicle",
|
|
16129
|
+
train: "vehicle",
|
|
16130
|
+
truck: "vehicle",
|
|
16131
|
+
boat: "vehicle",
|
|
16132
|
+
bird: "animal",
|
|
16133
|
+
cat: "animal",
|
|
16134
|
+
dog: "animal",
|
|
16135
|
+
horse: "animal",
|
|
16136
|
+
sheep: "animal",
|
|
16137
|
+
cow: "animal",
|
|
16138
|
+
elephant: "animal",
|
|
16139
|
+
bear: "animal",
|
|
16140
|
+
zebra: "animal",
|
|
16141
|
+
giraffe: "animal",
|
|
16142
|
+
suitcase: "package",
|
|
16143
|
+
backpack: "package",
|
|
16144
|
+
handbag: "package"
|
|
16145
|
+
},
|
|
16146
|
+
preserveOriginal: false
|
|
16147
|
+
};
|
|
16148
|
+
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
16149
|
+
/**
|
|
16150
|
+
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
16151
|
+
* so the editor can paint every block green or red with its reason.
|
|
16152
|
+
*
|
|
16153
|
+
* A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
|
|
16154
|
+
* every condition node (containers and leaves, including the ones a
|
|
16155
|
+
* short-circuit never reached), the run-mode verdict and every action step —
|
|
16156
|
+
* each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
|
|
16157
|
+
* for an Action run). Skipped
|
|
16158
|
+
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
16159
|
+
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
16160
|
+
*
|
|
16161
|
+
* {@link AutomationStepTraceSchema} is deliberately free of automation
|
|
16162
|
+
* vocabulary so an Action run records the same step shape.
|
|
16163
|
+
*/
|
|
16164
|
+
/** A value a trace may show: JSON primitives and short lists, nothing deeper. */
|
|
16165
|
+
var TraceValueSchema = union([
|
|
16166
|
+
string(),
|
|
16167
|
+
number(),
|
|
16168
|
+
boolean(),
|
|
16169
|
+
_null(),
|
|
16170
|
+
array(union([string(), number()]))
|
|
16171
|
+
]);
|
|
16172
|
+
var AutomationRunOutcomeSchema = _enum([
|
|
16173
|
+
"ran",
|
|
16174
|
+
"skipped-trigger",
|
|
16175
|
+
"skipped-conditions",
|
|
16176
|
+
"skipped-cooldown",
|
|
16177
|
+
"skipped-overlap",
|
|
16178
|
+
"failed",
|
|
16179
|
+
"aborted"
|
|
16180
|
+
]);
|
|
16181
|
+
/** `not-evaluated` = a short-circuit never reached the node. */
|
|
16182
|
+
var TraceNodeOutcomeSchema = _enum([
|
|
16183
|
+
"pass",
|
|
16184
|
+
"fail",
|
|
16185
|
+
"not-evaluated"
|
|
16186
|
+
]);
|
|
16187
|
+
var AutomationTriggerTraceSchema = object({
|
|
16188
|
+
kind: string().min(1),
|
|
16189
|
+
outcome: _enum(["pass", "fail"]),
|
|
16190
|
+
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16191
|
+
reason: string(),
|
|
16192
|
+
/** deviceId, className, trackId, score … — only what the trigger exposes. */
|
|
16193
|
+
vars: record(string(), TraceValueSchema)
|
|
16194
|
+
});
|
|
16195
|
+
var AutomationConditionTraceNodeSchema = object({
|
|
16196
|
+
/** `conditions` for the root, `conditions.<i>` for its i-th child, … */
|
|
16197
|
+
path: string().min(1),
|
|
16198
|
+
kind: _enum([
|
|
16199
|
+
"all",
|
|
16200
|
+
"any",
|
|
16201
|
+
"not",
|
|
16202
|
+
"condition",
|
|
16203
|
+
"expression",
|
|
16204
|
+
"stationary-present"
|
|
16205
|
+
]),
|
|
16206
|
+
outcome: TraceNodeOutcomeSchema,
|
|
16207
|
+
reason: string(),
|
|
16208
|
+
deviceId: number().int().optional(),
|
|
16209
|
+
cap: string().optional(),
|
|
16210
|
+
fieldPath: string().optional(),
|
|
16211
|
+
operator: string().optional(),
|
|
16212
|
+
expected: TraceValueSchema.optional(),
|
|
16213
|
+
/** What the mirror held; `null` when it held nothing (unknown). */
|
|
16214
|
+
observed: TraceValueSchema.optional()
|
|
16215
|
+
});
|
|
16216
|
+
var AutomationRunVerdictSchema = object({
|
|
16217
|
+
mode: AutomationRunModeSchema,
|
|
16218
|
+
decision: _enum([
|
|
16219
|
+
"run",
|
|
16220
|
+
"queued",
|
|
16221
|
+
"restart",
|
|
16222
|
+
"drop",
|
|
16223
|
+
"cooldown",
|
|
16224
|
+
"bypass"
|
|
16225
|
+
]),
|
|
16226
|
+
reason: string()
|
|
16227
|
+
});
|
|
16228
|
+
/** One executed (or not-run) step — shared by automations and Action runs. */
|
|
16229
|
+
var AutomationStepTraceSchema = object({
|
|
16230
|
+
/** `actions.<i>` */
|
|
16231
|
+
path: string().min(1),
|
|
16232
|
+
kind: string().min(1),
|
|
16233
|
+
cap: string().optional(),
|
|
16234
|
+
method: string().optional(),
|
|
16235
|
+
deviceId: number().int().optional(),
|
|
16236
|
+
startedAt: number().optional(),
|
|
16237
|
+
endedAt: number().optional(),
|
|
16238
|
+
outcome: _enum([
|
|
16239
|
+
"ok",
|
|
16240
|
+
"error",
|
|
16241
|
+
"not-run"
|
|
16242
|
+
]),
|
|
16243
|
+
message: string().optional()
|
|
16244
|
+
});
|
|
16245
|
+
var AutomationRunTraceSchema = object({
|
|
16246
|
+
runId: string().min(1),
|
|
16247
|
+
startedAt: number(),
|
|
16248
|
+
endedAt: number(),
|
|
16249
|
+
trigger: AutomationTriggerTraceSchema,
|
|
16250
|
+
conditions: array(AutomationConditionTraceNodeSchema),
|
|
16251
|
+
verdict: AutomationRunVerdictSchema.optional(),
|
|
16252
|
+
steps: array(AutomationStepTraceSchema),
|
|
16253
|
+
outcome: AutomationRunOutcomeSchema,
|
|
16254
|
+
/** How many identical consecutive skips this trace stands for (>= 1). */
|
|
16255
|
+
repeat: number().int().min(1),
|
|
16256
|
+
/** When the latest of those repeats happened. */
|
|
16257
|
+
lastAt: number()
|
|
16258
|
+
});
|
|
16259
|
+
/**
|
|
16260
|
+
* What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
|
|
16261
|
+
* `action:<actionId>`): the cooldown stamp and the trace ring.
|
|
16262
|
+
*/
|
|
16263
|
+
var AutomationStateDocSchema = object({
|
|
16264
|
+
version: literal(1),
|
|
16265
|
+
lastRunStartedAt: number().nullable(),
|
|
16266
|
+
traces: array(AutomationRunTraceSchema)
|
|
16267
|
+
});
|
|
16268
|
+
/**
|
|
16269
|
+
* Actions — named, runnable things an operator (or an automation) can start,
|
|
16270
|
+
* declared as LIST ENTRIES, never as devices.
|
|
16271
|
+
*
|
|
16272
|
+
* An entry is either **internal** (a short list of cap/wait steps kept in the
|
|
16273
|
+
* hub's `actions` record kind) or declared by an addon through the
|
|
16274
|
+
* `action-source` collection (a Home Assistant script, a scene …). The one
|
|
16275
|
+
* surface the UI and automations talk to is the `actions` cap; it keeps no
|
|
16276
|
+
* catalog of its own (D3) — every `list` asks the providers live.
|
|
16277
|
+
*
|
|
16278
|
+
* This file is the data half: the entry schema, the variables an entry may
|
|
16279
|
+
* declare, and the pure resolution of the values a run was given. It holds no
|
|
16280
|
+
* runtime.
|
|
16281
|
+
*/
|
|
16282
|
+
/** What a variable's value is. `select` takes one of `options`. */
|
|
16283
|
+
var ActionVariableTypeSchema = _enum([
|
|
16284
|
+
"string",
|
|
16285
|
+
"number",
|
|
16286
|
+
"boolean",
|
|
16287
|
+
"select"
|
|
16288
|
+
]);
|
|
16289
|
+
var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16290
|
+
var ActionVariableValueSchema = union([
|
|
16291
|
+
string(),
|
|
16292
|
+
number(),
|
|
16293
|
+
boolean()
|
|
16294
|
+
]);
|
|
16295
|
+
/**
|
|
16296
|
+
* One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
|
|
16297
|
+
* value) or `{{vars.name}}` (inside a string) in their args.
|
|
16298
|
+
*/
|
|
16299
|
+
var ActionVariableSchema = object({
|
|
16300
|
+
name: string().regex(ACTION_VARIABLE_NAME_RE),
|
|
16301
|
+
label: string().min(1).max(120).optional(),
|
|
16302
|
+
type: ActionVariableTypeSchema,
|
|
16303
|
+
required: boolean().optional(),
|
|
16304
|
+
default: ActionVariableValueSchema.optional(),
|
|
16305
|
+
/** The allowed values of a `select`. */
|
|
16306
|
+
options: array(string().min(1)).min(1).optional()
|
|
16307
|
+
}).superRefine((v, ctx) => {
|
|
16308
|
+
if (v.type === "select" && v.options === void 0) ctx.addIssue({
|
|
16309
|
+
code: "custom",
|
|
16310
|
+
message: "a select variable needs options",
|
|
16311
|
+
path: ["options"]
|
|
16312
|
+
});
|
|
16313
|
+
if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
|
|
16314
|
+
code: "custom",
|
|
16315
|
+
message: variableValueError(v, v.default) ?? "invalid default",
|
|
16316
|
+
path: ["default"]
|
|
16317
|
+
});
|
|
16318
|
+
});
|
|
16319
|
+
/** Why `value` is not a legal value for the variable, or null when it is. */
|
|
16320
|
+
function variableValueError(variable, value) {
|
|
16321
|
+
switch (variable.type) {
|
|
16322
|
+
case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
|
|
16323
|
+
case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
|
|
16324
|
+
case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
|
|
16325
|
+
case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
|
|
16326
|
+
}
|
|
16327
|
+
}
|
|
16328
|
+
var ActionSourceKindSchema = _enum(["internal", "addon"]);
|
|
16329
|
+
function isInternalActionId(actionId) {
|
|
16330
|
+
return actionId.startsWith("internal:") && actionId.length > 9;
|
|
16331
|
+
}
|
|
16332
|
+
/** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
|
|
16333
|
+
function addonIdOfActionId(actionId) {
|
|
16334
|
+
if (isInternalActionId(actionId)) return null;
|
|
16335
|
+
const at = actionId.indexOf(":");
|
|
16336
|
+
return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
|
|
16337
|
+
}
|
|
16338
|
+
var ActionStatusSchema = object({
|
|
16339
|
+
isRunning: boolean(),
|
|
16340
|
+
/** Ms epoch the latest run STARTED; null when it never ran. */
|
|
16341
|
+
lastRunAt: number().nullable(),
|
|
16342
|
+
/** The latest run's failure; null on success or when it never ran. */
|
|
16343
|
+
lastError: string().nullable()
|
|
16344
|
+
});
|
|
16345
|
+
var ActionEntrySchema = object({
|
|
16346
|
+
/** `internal:<uuid>` or `<addonId>:<localId>`. */
|
|
16347
|
+
id: string().min(3),
|
|
16348
|
+
name: string().min(1).max(120),
|
|
16349
|
+
/** The addon that declared it; `core-blocks` for an internal one. */
|
|
16350
|
+
addonId: string().min(1),
|
|
16351
|
+
source: ActionSourceKindSchema,
|
|
16352
|
+
/** An addon-declared entry is edited where it was declared. */
|
|
16353
|
+
readOnly: boolean(),
|
|
16354
|
+
/** Internal only: cap and wait steps. No action-in-action, so no cycles. */
|
|
16355
|
+
steps: array(ActionStepSchema).min(1).max(50).optional(),
|
|
16356
|
+
variables: array(ActionVariableSchema).max(20).optional(),
|
|
16357
|
+
status: ActionStatusSchema
|
|
16358
|
+
}).superRefine((entry, ctx) => {
|
|
16359
|
+
if (entry.source === "internal") {
|
|
16360
|
+
if (!isInternalActionId(entry.id)) ctx.addIssue({
|
|
16361
|
+
code: "custom",
|
|
16362
|
+
message: "an internal action id is internal:<uuid>",
|
|
16363
|
+
path: ["id"]
|
|
16364
|
+
});
|
|
16365
|
+
return;
|
|
16366
|
+
}
|
|
16367
|
+
if (entry.steps !== void 0) ctx.addIssue({
|
|
16368
|
+
code: "custom",
|
|
16369
|
+
message: "only an internal action carries steps",
|
|
16370
|
+
path: ["steps"]
|
|
16371
|
+
});
|
|
16372
|
+
if (!entry.readOnly) ctx.addIssue({
|
|
16373
|
+
code: "custom",
|
|
16374
|
+
message: "an addon action is read-only here",
|
|
16375
|
+
path: ["readOnly"]
|
|
16376
|
+
});
|
|
16377
|
+
if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
|
|
16378
|
+
code: "custom",
|
|
16379
|
+
message: "an addon action id is <addonId>:<localId>",
|
|
16380
|
+
path: ["id"]
|
|
16381
|
+
});
|
|
16382
|
+
});
|
|
16383
|
+
/** What an operator writes for an internal action. */
|
|
16384
|
+
var ActionInputSchema = object({
|
|
16385
|
+
name: string().min(1).max(120),
|
|
16386
|
+
steps: array(ActionStepSchema).min(1).max(50),
|
|
16387
|
+
variables: array(ActionVariableSchema).max(20).optional()
|
|
16388
|
+
});
|
|
16389
|
+
/** Who started a run. Absent = the operator pressed it. */
|
|
16390
|
+
var ActionRunOriginSchema = object({
|
|
16391
|
+
/** The automation block whose step started it. */
|
|
16392
|
+
blockId: string().min(1).optional() });
|
|
16393
|
+
/** `started` = the caller did not ask to wait. */
|
|
16394
|
+
var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
|
|
16395
|
+
var ActionRunResultSchema = object({
|
|
16396
|
+
runId: string().min(1),
|
|
16397
|
+
outcome: ActionRunOutcomeSchema,
|
|
16398
|
+
/** Why it did not run, or what failed. */
|
|
16399
|
+
message: string().optional()
|
|
16400
|
+
});
|
|
16401
|
+
_enum([
|
|
16402
|
+
"not-found",
|
|
16403
|
+
"read-only",
|
|
16404
|
+
"invalid-variables",
|
|
16405
|
+
"invalid-steps",
|
|
16406
|
+
"not-linked",
|
|
16407
|
+
"busy"
|
|
16408
|
+
]);
|
|
16409
|
+
object({
|
|
16410
|
+
actionId: string(),
|
|
16411
|
+
runId: string(),
|
|
16412
|
+
isRunning: boolean(),
|
|
16413
|
+
/** Set when the run ended. */
|
|
16414
|
+
outcome: AutomationRunOutcomeSchema.optional(),
|
|
16415
|
+
lastRunAt: number().nullable(),
|
|
16416
|
+
lastError: string().nullable()
|
|
16417
|
+
});
|
|
16418
|
+
/**
|
|
16419
|
+
* notification-output — canonical, capability-gated notification delivery.
|
|
16420
|
+
*
|
|
16421
|
+
* Apprise-derived model (see
|
|
16422
|
+
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
16423
|
+
* callers emit ONE canonical `Notification`; each provider declares a
|
|
16424
|
+
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
16425
|
+
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
16426
|
+
* message to what the kind supports — callers never special-case a service.
|
|
16427
|
+
*
|
|
16428
|
+
* DESIGN DECISIONS (locked):
|
|
16429
|
+
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
16430
|
+
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
16431
|
+
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
16432
|
+
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
16433
|
+
* alternative would fork the UI per addon and cannot host the
|
|
16434
|
+
* discovery→adopt flow.
|
|
16435
|
+
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
16436
|
+
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
16437
|
+
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
16438
|
+
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
16439
|
+
* `addonId` the generated collection router extracts from the call input.
|
|
16440
|
+
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
16441
|
+
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
16442
|
+
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
16443
|
+
* base64 fallback needed.
|
|
16444
|
+
*
|
|
16445
|
+
* TODO (deferred, closed-set change — separate decision): add
|
|
16446
|
+
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
16447
|
+
* admin "Integrations" page.
|
|
16448
|
+
*/
|
|
16449
|
+
/**
|
|
16450
|
+
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
16451
|
+
* adapter picks what it supports and the degrade engine filters the rest.
|
|
16452
|
+
*/
|
|
16453
|
+
var AttachmentMediaTypeSchema = _enum([
|
|
16454
|
+
"image",
|
|
16455
|
+
"video",
|
|
16456
|
+
"gif",
|
|
16457
|
+
"audio",
|
|
16458
|
+
"icon"
|
|
16459
|
+
]);
|
|
16460
|
+
/**
|
|
16461
|
+
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
16462
|
+
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
16463
|
+
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
16464
|
+
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
16465
|
+
*/
|
|
16466
|
+
var AttachmentSchema = object({
|
|
16467
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
16468
|
+
url: string().optional(),
|
|
16469
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
16470
|
+
mime: string().optional(),
|
|
16471
|
+
name: string().optional()
|
|
16472
|
+
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
16473
|
+
var NotificationFormatSchema = _enum([
|
|
16474
|
+
"text",
|
|
16475
|
+
"markdown",
|
|
16476
|
+
"html"
|
|
16477
|
+
]);
|
|
16478
|
+
/**
|
|
16479
|
+
* The CLOSED icon vocabulary an action button may use.
|
|
16480
|
+
*
|
|
16481
|
+
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
16482
|
+
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
16483
|
+
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
16484
|
+
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
16485
|
+
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
16486
|
+
*
|
|
16487
|
+
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
16488
|
+
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
16489
|
+
* differently.
|
|
16490
|
+
*
|
|
16491
|
+
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
16492
|
+
*
|
|
16493
|
+
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
16494
|
+
* the whole set onto its own renderer's vocabulary through a
|
|
16495
|
+
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
16496
|
+
* fallback, so adding a member here fails every adapter's build until someone
|
|
16497
|
+
* decides its glyph, which is the only place that decision can be made
|
|
16498
|
+
* honestly.
|
|
16499
|
+
*
|
|
16500
|
+
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
16501
|
+
* `disarm` straight through; iOS feeds that string to
|
|
16502
|
+
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
16503
|
+
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
16504
|
+
* mapping, and "the field is documented" is not "the value renders".
|
|
16505
|
+
*
|
|
16506
|
+
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
16507
|
+
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
16508
|
+
* so an addon that emits a token the running hub does not know does not lose an
|
|
16509
|
+
* icon — its whole `send` fails Zod validation and the notification never
|
|
16510
|
+
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
16511
|
+
*/
|
|
16512
|
+
var NotificationActionIconSchema = _enum([
|
|
16513
|
+
"acknowledge",
|
|
16514
|
+
"dismiss",
|
|
16515
|
+
"silence",
|
|
16516
|
+
"snooze",
|
|
16517
|
+
"view",
|
|
16518
|
+
"play",
|
|
16519
|
+
"open",
|
|
16520
|
+
"close",
|
|
16521
|
+
"lock",
|
|
16522
|
+
"unlock",
|
|
16523
|
+
"arm",
|
|
16524
|
+
"arm-home",
|
|
16525
|
+
"arm-away",
|
|
16526
|
+
"arm-night",
|
|
16527
|
+
"disarm",
|
|
16528
|
+
"light",
|
|
16529
|
+
"alert",
|
|
16530
|
+
"camera"
|
|
16531
|
+
]);
|
|
16532
|
+
/** A single tap-through action button. */
|
|
16533
|
+
var NotificationActionSchema = object({
|
|
16534
|
+
id: string(),
|
|
16535
|
+
label: string(),
|
|
16536
|
+
url: string().optional(),
|
|
16537
|
+
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
16538
|
+
icon: NotificationActionIconSchema.optional(),
|
|
16539
|
+
/**
|
|
16540
|
+
* Renders in a warning style where the notifier supports it.
|
|
16541
|
+
*
|
|
16542
|
+
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
16543
|
+
* else — see `notification-center/action-token.ts` for what that does and
|
|
16544
|
+
* does not buy.
|
|
16545
|
+
*/
|
|
16546
|
+
destructive: boolean().optional(),
|
|
16547
|
+
/**
|
|
16548
|
+
* How the tap should REACH the url.
|
|
16549
|
+
*
|
|
16550
|
+
* `navigate` (absent, and every button authored before this field) opens it:
|
|
16551
|
+
* the phone leaves the notification and shows whatever the callback returns.
|
|
16552
|
+
* That is right for a button whose answer the operator wants to read.
|
|
16553
|
+
*
|
|
16554
|
+
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
16555
|
+
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
16556
|
+
* an answer to the notification, and being thrown into a browser tab to
|
|
16557
|
+
* confirm it costs more attention than the notification did. A backend that
|
|
16558
|
+
* cannot do a background call renders it as an ordinary link (the adapters
|
|
16559
|
+
* fall back rather than dropping the button), so this is a preference, never
|
|
16560
|
+
* a requirement.
|
|
16561
|
+
*/
|
|
16562
|
+
mode: _enum(["navigate", "background"]).optional()
|
|
16563
|
+
});
|
|
16564
|
+
/** A delivery parameter value: what a notifier declared it takes. */
|
|
16565
|
+
var DeliveryParamValueSchema = union([
|
|
16566
|
+
string(),
|
|
16567
|
+
number(),
|
|
16568
|
+
boolean()
|
|
16569
|
+
]);
|
|
16570
|
+
/**
|
|
16571
|
+
* One extra delivery parameter a notifier KIND declares (D938). A rule that
|
|
16572
|
+
* addresses a target of that kind may set it; a kind that declares nothing is
|
|
16573
|
+
* never handed one. The admin UI derives its control from this descriptor —
|
|
16574
|
+
* no editor knows a service by name.
|
|
16575
|
+
*
|
|
16576
|
+
* Optional fields are `.optional()`, never `.default()` (a Zod default does not
|
|
16577
|
+
* run on the addon cap path).
|
|
16578
|
+
*/
|
|
16579
|
+
var DeliveryParamSchema = object({
|
|
16580
|
+
key: string().min(1),
|
|
16581
|
+
label: string(),
|
|
16582
|
+
/** i18n key of the label, resolved by the declaring addon's namespace. */
|
|
16583
|
+
labelKey: string().optional(),
|
|
16584
|
+
type: _enum([
|
|
16585
|
+
"enum",
|
|
16586
|
+
"number",
|
|
16587
|
+
"boolean",
|
|
16588
|
+
"string"
|
|
16589
|
+
]),
|
|
16590
|
+
/** `enum` only: the closed set of accepted values. */
|
|
16591
|
+
options: array(object({
|
|
16592
|
+
value: string(),
|
|
16593
|
+
label: string()
|
|
16594
|
+
})).optional(),
|
|
16595
|
+
/** `number` only. */
|
|
16596
|
+
min: number().optional(),
|
|
16597
|
+
max: number().optional(),
|
|
16598
|
+
/** What the notifier does when the rule sets nothing; shown as the placeholder. */
|
|
16599
|
+
default: DeliveryParamValueSchema.optional()
|
|
16600
|
+
});
|
|
16601
|
+
/**
|
|
16602
|
+
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
16603
|
+
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
16604
|
+
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
16605
|
+
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
16606
|
+
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
16607
|
+
* `priority` for that one target.
|
|
16608
|
+
*/
|
|
16609
|
+
var NotificationSchema = object({
|
|
16610
|
+
body: string(),
|
|
16611
|
+
title: string().optional(),
|
|
16612
|
+
format: NotificationFormatSchema.default("text"),
|
|
16613
|
+
priority: number().int().min(1).max(5).default(3),
|
|
16614
|
+
level: string().optional(),
|
|
16615
|
+
attachments: array(AttachmentSchema).optional(),
|
|
16616
|
+
clickUrl: string().optional(),
|
|
16617
|
+
actions: array(NotificationActionSchema).optional(),
|
|
16618
|
+
sound: string().optional(),
|
|
16619
|
+
ttl: number().optional(),
|
|
16620
|
+
tag: string().optional(),
|
|
16621
|
+
deviceId: number().optional(),
|
|
16622
|
+
eventId: string().optional(),
|
|
16623
|
+
metadata: record(string(), unknown()).optional(),
|
|
16624
|
+
/**
|
|
16625
|
+
* Values for the parameters the target's kind DECLARES in
|
|
16626
|
+
* {@link TargetKindSchema} `deliveryParams`. Already validated against the
|
|
16627
|
+
* declaration by the sender; a notifier reads only the keys it declared.
|
|
16628
|
+
*/
|
|
16629
|
+
delivery: record(string(), DeliveryParamValueSchema).optional()
|
|
16630
|
+
});
|
|
16631
|
+
/** One declared native severity/priority level for a kind. */
|
|
16632
|
+
var TargetKindLevelSchema = object({
|
|
16633
|
+
id: string(),
|
|
16634
|
+
label: string(),
|
|
16635
|
+
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
16636
|
+
ordinal: number().int().min(1).max(5).nullable(),
|
|
16637
|
+
flags: object({
|
|
16638
|
+
critical: boolean().optional(),
|
|
16639
|
+
silent: boolean().optional(),
|
|
16640
|
+
noPush: boolean().optional()
|
|
16641
|
+
}).optional(),
|
|
16642
|
+
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
16643
|
+
requires: array(string()).optional(),
|
|
16644
|
+
description: string().optional()
|
|
16645
|
+
});
|
|
16646
|
+
/** The full capability block consulted before dispatch. */
|
|
16647
|
+
var TargetKindCapsSchema = object({
|
|
16648
|
+
attachments: object({
|
|
16649
|
+
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
16650
|
+
mode: _enum([
|
|
16651
|
+
"url",
|
|
16652
|
+
"bytes",
|
|
16653
|
+
"both"
|
|
16654
|
+
]),
|
|
16655
|
+
max: number().int().nonnegative(),
|
|
16656
|
+
maxBytes: number().int().positive().optional()
|
|
16657
|
+
}),
|
|
16658
|
+
/** Max action buttons (0 = none). */
|
|
16659
|
+
actions: number().int().nonnegative(),
|
|
16660
|
+
/**
|
|
16661
|
+
* Whether this kind renders a per-action ICON.
|
|
16662
|
+
*
|
|
16663
|
+
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
16664
|
+
* run on the addon cap path — three production failures in one day taught
|
|
16665
|
+
* this repo that once. Absent is read as false by the degrade engine, which
|
|
16666
|
+
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
16667
|
+
* assumed and dropped costs the operator's trust in the field.
|
|
16668
|
+
*/
|
|
16669
|
+
actionIcons: boolean().optional(),
|
|
16670
|
+
levels: array(TargetKindLevelSchema),
|
|
16671
|
+
format: array(NotificationFormatSchema),
|
|
16672
|
+
clickUrl: boolean(),
|
|
16673
|
+
sound: boolean(),
|
|
16674
|
+
ttl: boolean(),
|
|
16675
|
+
bodyMaxLen: number().int().positive()
|
|
16676
|
+
});
|
|
16677
|
+
/**
|
|
16678
|
+
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
16679
|
+
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
16680
|
+
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
16681
|
+
* the union is large and not meant for runtime validation here; the exported
|
|
16682
|
+
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
16683
|
+
*/
|
|
16684
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
16685
|
+
var TargetKindSchema = object({
|
|
16686
|
+
kind: string(),
|
|
16687
|
+
label: string(),
|
|
16688
|
+
icon: string(),
|
|
16689
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
16690
|
+
addonId: string(),
|
|
16691
|
+
/**
|
|
16692
|
+
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
16693
|
+
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
16694
|
+
* when the addon bundles no icon for that kind — the client then falls back
|
|
16695
|
+
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
16696
|
+
*
|
|
16697
|
+
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
16698
|
+
* client, and a native client joins it onto its own hub base.
|
|
16699
|
+
*
|
|
16700
|
+
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
16701
|
+
* field that survived only because the runtime cap-router forwards provider
|
|
16702
|
+
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
16703
|
+
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
16704
|
+
* broken silently the moment output validation was tightened anywhere.
|
|
16705
|
+
*/
|
|
16706
|
+
iconUrl: string().optional(),
|
|
16707
|
+
/**
|
|
16708
|
+
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
16709
|
+
*
|
|
16710
|
+
* The server knows this and therefore says it, because the client cannot
|
|
16711
|
+
* safely guess: a React-Native client renders SVG and raster through two
|
|
16712
|
+
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
16713
|
+
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
16714
|
+
* placeholder glyph for every vector icon while the web build looked fine.
|
|
16715
|
+
*
|
|
16716
|
+
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
16717
|
+
* not been updated — a client that cannot determine the type should prefer
|
|
16718
|
+
* its raster path, which is the safe default for an unknown image.
|
|
16719
|
+
*/
|
|
16720
|
+
iconMediaType: string().optional(),
|
|
16721
|
+
configSchema: ConfigSchemaPassthrough$3,
|
|
16722
|
+
/**
|
|
16723
|
+
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
16724
|
+
* base URL, instance, chat, recipient). A save that changes any of them
|
|
16725
|
+
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
16726
|
+
* editor uses this to ask for the secret on exactly those edits instead of
|
|
16727
|
+
* on every edit.
|
|
16728
|
+
*
|
|
16729
|
+
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
16730
|
+
* kinds) and from a provider older than this field — an editor that finds
|
|
16731
|
+
* it absent must assume ANY edit may need the secret.
|
|
16732
|
+
*/
|
|
16733
|
+
destinationKeys: array(string()).optional(),
|
|
16734
|
+
supportsDiscovery: boolean(),
|
|
16735
|
+
/**
|
|
16736
|
+
* Extra delivery parameters a rule may set for a target of this kind (D938).
|
|
16737
|
+
* Absent = none; the rule editor then offers nothing.
|
|
16738
|
+
*/
|
|
16739
|
+
deliveryParams: array(DeliveryParamSchema).optional(),
|
|
16740
|
+
caps: TargetKindCapsSchema
|
|
16741
|
+
});
|
|
16742
|
+
/**
|
|
16743
|
+
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
16744
|
+
* (return a presence marker only) when serving `listTargets` — never
|
|
16745
|
+
* round-trip a stored secret to the UI.
|
|
16746
|
+
*/
|
|
16747
|
+
var TargetSchema = object({
|
|
16748
|
+
id: string(),
|
|
16749
|
+
name: string(),
|
|
16750
|
+
kind: string(),
|
|
16751
|
+
addonId: string(),
|
|
16752
|
+
enabled: boolean(),
|
|
16753
|
+
config: record(string(), unknown())
|
|
16754
|
+
});
|
|
16755
|
+
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
16756
|
+
var DiscoveredTargetSchema = object({
|
|
16757
|
+
kind: string(),
|
|
16758
|
+
suggestedName: string(),
|
|
16759
|
+
config: record(string(), unknown())
|
|
16760
|
+
});
|
|
16761
|
+
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
16762
|
+
var RenderedAsSchema = object({
|
|
16763
|
+
level: string(),
|
|
16764
|
+
format: NotificationFormatSchema,
|
|
16765
|
+
attachmentsSent: number().int().nonnegative(),
|
|
16766
|
+
actionsSent: number().int().nonnegative(),
|
|
16767
|
+
truncated: boolean(),
|
|
16768
|
+
dropped: array(string())
|
|
16769
|
+
});
|
|
16770
|
+
var SendResultSchema = object({
|
|
16771
|
+
success: boolean(),
|
|
16772
|
+
error: string().optional(),
|
|
16773
|
+
renderedAs: RenderedAsSchema.optional()
|
|
16774
|
+
});
|
|
16775
|
+
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
16776
|
+
var TestResultSchema = SendResultSchema;
|
|
16777
|
+
/**
|
|
16778
|
+
* Pre-save credential probe against a partial (unsaved) target config.
|
|
16779
|
+
* `resolvedUrl` never includes secrets (token stripped from path).
|
|
16780
|
+
*/
|
|
16781
|
+
var ProbeTargetConfigResultSchema = object({
|
|
16782
|
+
ok: boolean(),
|
|
16783
|
+
resolvedUrl: string(),
|
|
16784
|
+
httpStatus: number().int().optional(),
|
|
16785
|
+
stateInstance: string().optional(),
|
|
16786
|
+
error: string().optional()
|
|
16787
|
+
});
|
|
16788
|
+
method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({ targetId: string() }), TargetSchema, { auth: "admin" }), method(object({
|
|
16789
|
+
kind: string(),
|
|
16790
|
+
config: record(string(), unknown()).optional(),
|
|
16791
|
+
/**
|
|
16792
|
+
* When editing / duplicating, the blank password field never round-trips
|
|
16793
|
+
* the stored token. Pass the source target id so the provider can restore
|
|
16794
|
+
* secrets server-side before dialling (same merge as upsert).
|
|
16795
|
+
*/
|
|
16796
|
+
targetId: string().optional()
|
|
16797
|
+
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
16798
|
+
kind: string(),
|
|
16799
|
+
config: record(string(), unknown()),
|
|
16800
|
+
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
16801
|
+
targetId: string().optional()
|
|
16802
|
+
}), ProbeTargetConfigResultSchema, {
|
|
16803
|
+
kind: "mutation",
|
|
16804
|
+
auth: "admin"
|
|
16805
|
+
}), method(object({
|
|
16806
|
+
targetId: string(),
|
|
16807
|
+
notification: NotificationSchema
|
|
16808
|
+
}), SendResultSchema, {
|
|
16809
|
+
kind: "mutation",
|
|
16810
|
+
auth: "admin"
|
|
16811
|
+
}), method(object({
|
|
16812
|
+
targetId: string(),
|
|
16813
|
+
sample: NotificationSchema.optional()
|
|
16814
|
+
}), TestResultSchema, {
|
|
16815
|
+
kind: "mutation",
|
|
16816
|
+
auth: "admin"
|
|
16817
|
+
}), method(object({
|
|
16818
|
+
target: TargetSchema,
|
|
16819
|
+
/**
|
|
16820
|
+
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
16821
|
+
* existing row. The UI never holds the stored token; blank / redacted
|
|
16822
|
+
* password fields are restored from this source (D634 still applies if
|
|
16823
|
+
* destinations diverge without a re-typed secret).
|
|
16824
|
+
*/
|
|
16825
|
+
cloneSecretsFrom: string().optional()
|
|
16826
|
+
}), TargetSchema, {
|
|
16827
|
+
kind: "mutation",
|
|
16828
|
+
auth: "admin"
|
|
16829
|
+
}), method(object({ targetId: string() }), _void(), {
|
|
16830
|
+
kind: "mutation",
|
|
16831
|
+
auth: "admin"
|
|
16832
|
+
}), method(object({
|
|
16833
|
+
targetId: string(),
|
|
16834
|
+
enabled: boolean()
|
|
16835
|
+
}), _void(), {
|
|
16836
|
+
kind: "mutation",
|
|
16837
|
+
auth: "admin"
|
|
15819
16838
|
});
|
|
15820
16839
|
/**
|
|
15821
16840
|
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
@@ -15849,52 +16868,6 @@ var AddonPageInfoSchema = object({
|
|
|
15849
16868
|
bundleUrl: string()
|
|
15850
16869
|
});
|
|
15851
16870
|
method(_void(), array(AddonPageInfoSchema).readonly());
|
|
15852
|
-
/**
|
|
15853
|
-
* `addon-pages-source` — collection cap exposing per-provider raw page
|
|
15854
|
-
* declarations. Every addon that contributes a UI page registers a
|
|
15855
|
-
* provider here. The hub-side singleton aggregator (`addon-pages` cap,
|
|
15856
|
-
* see `addon-pages.cap.ts`) walks this collection, stamps versioned
|
|
15857
|
-
* `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
|
|
15858
|
-
* that admin-ui consumes.
|
|
15859
|
-
*
|
|
15860
|
-
* The split exists because the public listing has a different output
|
|
15861
|
-
* shape than the per-provider raw declarations, and we want both ends
|
|
15862
|
-
* to flow through codegen instead of relying on a hand-written wrapper.
|
|
15863
|
-
*/
|
|
15864
|
-
var AddonPageDeclarationSchema = object({
|
|
15865
|
-
id: string(),
|
|
15866
|
-
label: string(),
|
|
15867
|
-
icon: string(),
|
|
15868
|
-
path: string(),
|
|
15869
|
-
/**
|
|
15870
|
-
* Module Federation remote name — must match the `name` field on the
|
|
15871
|
-
* page addon's `federation()` plugin config. Used by admin-ui's
|
|
15872
|
-
* `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
|
|
15873
|
-
* Conventionally `addon_<id>_page` (snake_case; MF names cannot
|
|
15874
|
-
* contain hyphens).
|
|
15875
|
-
*/
|
|
15876
|
-
remoteName: string(),
|
|
15877
|
-
/**
|
|
15878
|
-
* Bundle filename inside the addon's `dist/` dir served at
|
|
15879
|
-
* `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
|
|
15880
|
-
* is always `'remoteEntry.js'`; the value is kept on the metadata so
|
|
15881
|
-
* the static-file route can compute an mtime-based cache-buster URL
|
|
15882
|
-
* without a separate filesystem stat.
|
|
15883
|
-
*/
|
|
15884
|
-
bundle: string(),
|
|
15885
|
-
/**
|
|
15886
|
-
* Sidebar section this page docks into. Well-known ids: `'detection'`,
|
|
15887
|
-
* `'cluster'`, `'administration'` — the page renders inside that group.
|
|
15888
|
-
* Any OTHER string creates (or joins) a custom section rendered after
|
|
15889
|
-
* the built-in groups; its label comes from `sectionLabel` (first
|
|
15890
|
-
* declaration wins), falling back to the id. Absent → the legacy
|
|
15891
|
-
* "Addon Pages" group.
|
|
15892
|
-
*/
|
|
15893
|
-
section: string().optional(),
|
|
15894
|
-
/** Display label for a CUSTOM `section` id (ignored for well-known ids). */
|
|
15895
|
-
sectionLabel: string().optional()
|
|
15896
|
-
});
|
|
15897
|
-
method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
|
|
15898
16871
|
var AddonHttpRouteSchema = object({
|
|
15899
16872
|
method: _enum([
|
|
15900
16873
|
"GET",
|
|
@@ -16176,9 +17149,21 @@ var AlertSeveritySchema = _enum([
|
|
|
16176
17149
|
"error",
|
|
16177
17150
|
"critical"
|
|
16178
17151
|
]);
|
|
17152
|
+
/**
|
|
17153
|
+
* An action that RUNS something instead of going somewhere: the admin UI calls
|
|
17154
|
+
* the addon's custom action (`addons.custom`) with exactly these fields. The
|
|
17155
|
+
* producer owns the meaning; the UI knows nothing about the addon or the kind.
|
|
17156
|
+
*/
|
|
17157
|
+
var AlertActionInvokeSchema = object({
|
|
17158
|
+
addonId: string().min(1),
|
|
17159
|
+
action: string().min(1),
|
|
17160
|
+
input: record(string(), unknown()).optional()
|
|
17161
|
+
});
|
|
17162
|
+
/** An action has `href` (a link) OR `invoke` (a button that runs a custom action). */
|
|
16179
17163
|
var AlertActionSchema = object({
|
|
16180
17164
|
label: string(),
|
|
16181
17165
|
href: string().optional(),
|
|
17166
|
+
invoke: AlertActionInvokeSchema.optional(),
|
|
16182
17167
|
/**
|
|
16183
17168
|
* How `href` is followed. Absent / `app`: an admin-ui route (client-side).
|
|
16184
17169
|
* `server`: a route the HUB serves (an addon HTTP route such as an OAuth
|
|
@@ -18396,7 +19381,7 @@ var CompositionEventPlanSchema = object({
|
|
|
18396
19381
|
stateField: string(),
|
|
18397
19382
|
fieldKey: string()
|
|
18398
19383
|
});
|
|
18399
|
-
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.
|
|
19384
|
+
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
|
|
18400
19385
|
var CompositionProblemSchema = object({
|
|
18401
19386
|
code: _enum([
|
|
18402
19387
|
"reserved-variant",
|
|
@@ -18425,7 +19410,11 @@ var CompositionProblemSchema = object({
|
|
|
18425
19410
|
"mixed-quantity",
|
|
18426
19411
|
"helper-mismatch",
|
|
18427
19412
|
"member-constraint",
|
|
18428
|
-
"retention"
|
|
19413
|
+
"retention",
|
|
19414
|
+
"command-method-unknown",
|
|
19415
|
+
"command-source-unknown",
|
|
19416
|
+
"command-input-unknown",
|
|
19417
|
+
"command-input-missing"
|
|
18429
19418
|
]),
|
|
18430
19419
|
path: string(),
|
|
18431
19420
|
message: string()
|
|
@@ -18464,7 +19453,9 @@ var CompositionFieldRoleSchema = _enum([
|
|
|
18464
19453
|
var CompositionFieldItemSchema = object({
|
|
18465
19454
|
arrayPath: string(),
|
|
18466
19455
|
key: string(),
|
|
18467
|
-
label: string()
|
|
19456
|
+
label: string(),
|
|
19457
|
+
/** True when the item carries a `reset` forward: the composer writes `resettable` from this (D967). */
|
|
19458
|
+
resettable: boolean().optional()
|
|
18468
19459
|
});
|
|
18469
19460
|
var CompositionFieldPlanSchema = object({
|
|
18470
19461
|
cap: string(),
|
|
@@ -18669,7 +19660,7 @@ Object.freeze({
|
|
|
18669
19660
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18670
19661
|
* Data, not a form default written into the block.
|
|
18671
19662
|
*/
|
|
18672
|
-
var NOW$
|
|
19663
|
+
var NOW$3 = {
|
|
18673
19664
|
fieldPath: "lastFetchedAt",
|
|
18674
19665
|
expr: "now"
|
|
18675
19666
|
};
|
|
@@ -18678,12 +19669,13 @@ var CHANGED = {
|
|
|
18678
19669
|
expr: "now"
|
|
18679
19670
|
};
|
|
18680
19671
|
function numeric(input) {
|
|
19672
|
+
const { alsoFrom, ...base } = input;
|
|
18681
19673
|
return {
|
|
18682
|
-
...
|
|
19674
|
+
...base,
|
|
18683
19675
|
family: "numeric",
|
|
18684
19676
|
deviceType: DeviceType.Sensor,
|
|
18685
19677
|
memberDeviceType: DeviceType.Sensor,
|
|
18686
|
-
memberDeviceTypes: [DeviceType.Sensor],
|
|
19678
|
+
memberDeviceTypes: [DeviceType.Sensor, ...alsoFrom?.deviceTypes ?? []],
|
|
18687
19679
|
activeWhen: void 0,
|
|
18688
19680
|
commandCaps: [],
|
|
18689
19681
|
baseUnit: baseUnitOf(input.quantity),
|
|
@@ -18691,9 +19683,10 @@ function numeric(input) {
|
|
|
18691
19683
|
accept: [{
|
|
18692
19684
|
cap: input.outputCap,
|
|
18693
19685
|
fieldPath: input.outputField
|
|
18694
|
-
}]
|
|
19686
|
+
}, ...alsoFrom === void 0 ? [] : [alsoFrom.accept]]
|
|
18695
19687
|
};
|
|
18696
19688
|
}
|
|
19689
|
+
var CLIMATE_DEVICES = [DeviceType.Thermostat, DeviceType.Climate];
|
|
18697
19690
|
function binary(input) {
|
|
18698
19691
|
return {
|
|
18699
19692
|
...input,
|
|
@@ -18715,73 +19708,87 @@ var GROUP_TEMPLATES = [
|
|
|
18715
19708
|
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18716
19709
|
role: DeviceRole.TemperatureSensor,
|
|
18717
19710
|
outputCap: "temperature-sensor",
|
|
18718
|
-
outputField: "
|
|
19711
|
+
outputField: "temperature",
|
|
18719
19712
|
quantity: "temperature",
|
|
18720
19713
|
defaultAggregate: "mean",
|
|
18721
19714
|
empty: "null",
|
|
18722
|
-
companions: [NOW$
|
|
19715
|
+
companions: [NOW$3],
|
|
19716
|
+
alsoFrom: {
|
|
19717
|
+
deviceTypes: CLIMATE_DEVICES,
|
|
19718
|
+
accept: {
|
|
19719
|
+
cap: "climate-control",
|
|
19720
|
+
fieldPath: "currentTemp"
|
|
19721
|
+
}
|
|
19722
|
+
}
|
|
18723
19723
|
}),
|
|
18724
19724
|
numeric({
|
|
18725
19725
|
id: "humidity",
|
|
18726
19726
|
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18727
19727
|
role: DeviceRole.HumiditySensor,
|
|
18728
19728
|
outputCap: "humidity-sensor",
|
|
18729
|
-
outputField: "
|
|
19729
|
+
outputField: "humidity",
|
|
18730
19730
|
quantity: "humidity",
|
|
18731
19731
|
defaultAggregate: "mean",
|
|
18732
19732
|
empty: "null",
|
|
18733
|
-
companions: [NOW$
|
|
19733
|
+
companions: [NOW$3],
|
|
19734
|
+
alsoFrom: {
|
|
19735
|
+
deviceTypes: CLIMATE_DEVICES,
|
|
19736
|
+
accept: {
|
|
19737
|
+
cap: "climate-control",
|
|
19738
|
+
fieldPath: "currentHumidity"
|
|
19739
|
+
}
|
|
19740
|
+
}
|
|
18734
19741
|
}),
|
|
18735
19742
|
numeric({
|
|
18736
19743
|
id: "pressure",
|
|
18737
19744
|
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18738
19745
|
role: DeviceRole.PressureSensor,
|
|
18739
19746
|
outputCap: "pressure-sensor",
|
|
18740
|
-
outputField: "
|
|
19747
|
+
outputField: "pressure",
|
|
18741
19748
|
quantity: "pressure",
|
|
18742
19749
|
defaultAggregate: "mean",
|
|
18743
19750
|
empty: "null",
|
|
18744
|
-
companions: [NOW$
|
|
19751
|
+
companions: [NOW$3]
|
|
18745
19752
|
}),
|
|
18746
19753
|
numeric({
|
|
18747
19754
|
id: "illuminance",
|
|
18748
19755
|
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18749
19756
|
role: DeviceRole.AmbientLightSensor,
|
|
18750
19757
|
outputCap: "ambient-light-sensor",
|
|
18751
|
-
outputField: "
|
|
19758
|
+
outputField: "illuminance",
|
|
18752
19759
|
quantity: "illuminance",
|
|
18753
19760
|
defaultAggregate: "mean",
|
|
18754
19761
|
empty: "null",
|
|
18755
|
-
companions: [NOW$
|
|
19762
|
+
companions: [NOW$3]
|
|
18756
19763
|
}),
|
|
18757
19764
|
numeric({
|
|
18758
19765
|
id: "power",
|
|
18759
19766
|
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18760
19767
|
role: DeviceRole.PowerSensor,
|
|
18761
19768
|
outputCap: "power-meter",
|
|
18762
|
-
outputField: "
|
|
19769
|
+
outputField: "power",
|
|
18763
19770
|
quantity: "power",
|
|
18764
19771
|
defaultAggregate: "sum",
|
|
18765
19772
|
empty: "omit",
|
|
18766
|
-
companions: [NOW$
|
|
19773
|
+
companions: [NOW$3]
|
|
18767
19774
|
}),
|
|
18768
19775
|
numeric({
|
|
18769
19776
|
id: "energy",
|
|
18770
19777
|
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18771
19778
|
role: DeviceRole.EnergySensor,
|
|
18772
19779
|
outputCap: "power-meter",
|
|
18773
|
-
outputField: "
|
|
19780
|
+
outputField: "energy",
|
|
18774
19781
|
quantity: "energy",
|
|
18775
19782
|
defaultAggregate: "sum",
|
|
18776
19783
|
empty: "omit",
|
|
18777
|
-
companions: [NOW$
|
|
19784
|
+
companions: [NOW$3]
|
|
18778
19785
|
}),
|
|
18779
19786
|
numeric({
|
|
18780
19787
|
id: "battery",
|
|
18781
19788
|
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18782
19789
|
role: DeviceRole.BatterySensor,
|
|
18783
19790
|
outputCap: "battery",
|
|
18784
|
-
outputField: "
|
|
19791
|
+
outputField: "level",
|
|
18785
19792
|
quantity: "percentage",
|
|
18786
19793
|
defaultAggregate: "mean",
|
|
18787
19794
|
empty: "null",
|
|
@@ -18809,7 +19816,7 @@ var GROUP_TEMPLATES = [
|
|
|
18809
19816
|
quantity: "device",
|
|
18810
19817
|
defaultAggregate: "mean",
|
|
18811
19818
|
empty: "null",
|
|
18812
|
-
companions: [NOW$
|
|
19819
|
+
companions: [NOW$3]
|
|
18813
19820
|
}),
|
|
18814
19821
|
binary({
|
|
18815
19822
|
id: "motion",
|
|
@@ -19359,7 +20366,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
19359
20366
|
kind: "mutation",
|
|
19360
20367
|
auth: "admin",
|
|
19361
20368
|
access: "view"
|
|
19362
|
-
})
|
|
20369
|
+
}), method(object({
|
|
20370
|
+
blockId: string().min(1),
|
|
20371
|
+
trace: AutomationRunTraceSchema.optional(),
|
|
20372
|
+
runStartedAt: number().optional(),
|
|
20373
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
20374
|
+
}), _void(), {
|
|
20375
|
+
kind: "mutation",
|
|
20376
|
+
auth: "admin"
|
|
20377
|
+
}), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
|
|
20378
|
+
traces: array(AutomationRunTraceSchema),
|
|
20379
|
+
lastError: string().nullable()
|
|
20380
|
+
}), { auth: "admin" });
|
|
19363
20381
|
/**
|
|
19364
20382
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
19365
20383
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
@@ -21144,12 +22162,48 @@ var ExposeInputSchema = object({
|
|
|
21144
22162
|
capabilities: array(string()).optional()
|
|
21145
22163
|
});
|
|
21146
22164
|
var UnexposeInputSchema = object({ deviceId: string() });
|
|
22165
|
+
/**
|
|
22166
|
+
* What an exporter may expose besides devices (D968). An action and a
|
|
22167
|
+
* notification target are LIST ENTRIES, never devices (D954): they enter an
|
|
22168
|
+
* exporter's own selection, not the device registry.
|
|
22169
|
+
*/
|
|
22170
|
+
var ExportEntityKindSchema = _enum([
|
|
22171
|
+
"device",
|
|
22172
|
+
"action",
|
|
22173
|
+
"notifier"
|
|
22174
|
+
]);
|
|
22175
|
+
var ExportEntityRefSchema = discriminatedUnion("kind", [
|
|
22176
|
+
object({
|
|
22177
|
+
kind: literal("device"),
|
|
22178
|
+
deviceId: string()
|
|
22179
|
+
}),
|
|
22180
|
+
object({
|
|
22181
|
+
kind: literal("action"),
|
|
22182
|
+
actionId: string().min(1)
|
|
22183
|
+
}),
|
|
22184
|
+
object({
|
|
22185
|
+
kind: literal("notifier"),
|
|
22186
|
+
targetId: string().min(1)
|
|
22187
|
+
})
|
|
22188
|
+
]);
|
|
22189
|
+
var ExposedEntitySchema = object({
|
|
22190
|
+
entity: ExportEntityRefSchema,
|
|
22191
|
+
exposedAs: string().optional()
|
|
22192
|
+
});
|
|
22193
|
+
var ExposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
|
|
22194
|
+
var UnexposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
|
|
21147
22195
|
method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchema)), method(_void(), array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
|
|
21148
22196
|
kind: "mutation",
|
|
21149
22197
|
auth: "admin"
|
|
21150
22198
|
}), method(UnexposeInputSchema, _void(), {
|
|
21151
22199
|
kind: "mutation",
|
|
21152
22200
|
auth: "admin"
|
|
22201
|
+
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(_void(), array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
|
|
22202
|
+
kind: "mutation",
|
|
22203
|
+
auth: "admin"
|
|
22204
|
+
}), optionalMethod(UnexposeEntityInputSchema, _void(), {
|
|
22205
|
+
kind: "mutation",
|
|
22206
|
+
auth: "admin"
|
|
21153
22207
|
});
|
|
21154
22208
|
/** Lucide category ids, alphabetical. `ICON_META` indexes into this. */
|
|
21155
22209
|
var ICON_CATEGORY_IDS = [
|
|
@@ -42290,6 +43344,14 @@ method(object({
|
|
|
42290
43344
|
kind: "mutation",
|
|
42291
43345
|
auth: "admin"
|
|
42292
43346
|
});
|
|
43347
|
+
method(object({
|
|
43348
|
+
nodeId: string().min(1),
|
|
43349
|
+
/** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
|
|
43350
|
+
removedAt: number()
|
|
43351
|
+
}), object({ counts: record(string(), number().int().nonnegative()) }), {
|
|
43352
|
+
kind: "mutation",
|
|
43353
|
+
auth: "admin"
|
|
43354
|
+
});
|
|
42293
43355
|
/**
|
|
42294
43356
|
* `device-removal-hook` — the reliable way an addon learns a device was DELETED,
|
|
42295
43357
|
* so it can delete what it keeps for that device (D865).
|
|
@@ -42937,6 +43999,17 @@ var LlmProfileSchema = object({
|
|
|
42937
43999
|
* field in its editor only when its descriptor lists it in `profileFields`. */
|
|
42938
44000
|
topK: number().int().positive().optional(),
|
|
42939
44001
|
maxTokens: number().int().positive().optional(),
|
|
44002
|
+
/** How hard the model "thinks" before it answers. Omitted = the model's own
|
|
44003
|
+
* default. `off` matters for structured output: a thinking model can spend
|
|
44004
|
+
* the whole completion on reasoning and return an empty `content`. Carried
|
|
44005
|
+
* only by the wires that have it — a kind offers the field in its editor
|
|
44006
|
+
* only when its descriptor lists it in `profileFields`. */
|
|
44007
|
+
reasoningEffort: _enum([
|
|
44008
|
+
"off",
|
|
44009
|
+
"low",
|
|
44010
|
+
"medium",
|
|
44011
|
+
"high"
|
|
44012
|
+
]).optional(),
|
|
42940
44013
|
/** Prompt context window. Advisory for cloud kinds (they enforce their own). */
|
|
42941
44014
|
contextLength: number().int().positive().optional(),
|
|
42942
44015
|
/** Default system prompt. A caller's `system` REPLACES it (never appends —
|
|
@@ -42975,6 +44048,7 @@ var LlmProfileFieldSchema = _enum([
|
|
|
42975
44048
|
"topP",
|
|
42976
44049
|
"topK",
|
|
42977
44050
|
"maxTokens",
|
|
44051
|
+
"reasoningEffort",
|
|
42978
44052
|
"contextLength",
|
|
42979
44053
|
"systemPrompt",
|
|
42980
44054
|
"timeoutMs",
|
|
@@ -42998,7 +44072,7 @@ var LlmProviderWidgetSchema = object({
|
|
|
42998
44072
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
42999
44073
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
43000
44074
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
43001
|
-
var ConfigSchemaPassthrough$
|
|
44075
|
+
var ConfigSchemaPassthrough$2 = unknown();
|
|
43002
44076
|
var LlmProfileKindDescriptorSchema = object({
|
|
43003
44077
|
kind: LlmProfileKindSchema,
|
|
43004
44078
|
label: string(),
|
|
@@ -43007,9 +44081,9 @@ var LlmProfileKindDescriptorSchema = object({
|
|
|
43007
44081
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
43008
44082
|
addonId: string(),
|
|
43009
44083
|
/** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
|
|
43010
|
-
connectionSchema: ConfigSchemaPassthrough$
|
|
44084
|
+
connectionSchema: ConfigSchemaPassthrough$2,
|
|
43011
44085
|
/** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
|
|
43012
|
-
advancedSchema: ConfigSchemaPassthrough$
|
|
44086
|
+
advancedSchema: ConfigSchemaPassthrough$2.optional(),
|
|
43013
44087
|
/** The kind's defaults; a model from `listModels` may override any of them. */
|
|
43014
44088
|
features: LlmModelFeaturesSchema,
|
|
43015
44089
|
/** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
|
|
@@ -43535,10 +44609,10 @@ var CpuBreakdownSchema = object({
|
|
|
43535
44609
|
cores: number()
|
|
43536
44610
|
});
|
|
43537
44611
|
var MemoryInfoSchema = object({
|
|
43538
|
-
percent: number(),
|
|
44612
|
+
percent: number().nullable(),
|
|
43539
44613
|
totalBytes: number(),
|
|
43540
|
-
usedBytes: number(),
|
|
43541
|
-
availableBytes: number(),
|
|
44614
|
+
usedBytes: number().nullable(),
|
|
44615
|
+
availableBytes: number().nullable(),
|
|
43542
44616
|
swapUsedBytes: number(),
|
|
43543
44617
|
swapTotalBytes: number()
|
|
43544
44618
|
});
|
|
@@ -43842,8 +44916,9 @@ var GetLoadSeriesInputSchema = object({
|
|
|
43842
44916
|
});
|
|
43843
44917
|
var SystemMetricsSchema = object({
|
|
43844
44918
|
cpuPercent: number(),
|
|
43845
|
-
|
|
43846
|
-
|
|
44919
|
+
/** `null` when the memory sample failed or was not taken yet (D393). */
|
|
44920
|
+
memoryPercent: number().nullable(),
|
|
44921
|
+
memoryUsedMB: number().nullable(),
|
|
43847
44922
|
memoryTotalMB: number(),
|
|
43848
44923
|
diskPercent: number().optional(),
|
|
43849
44924
|
temperature: number().optional(),
|
|
@@ -44079,424 +45154,6 @@ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
|
|
|
44079
45154
|
sourcePort: number().optional()
|
|
44080
45155
|
});
|
|
44081
45156
|
method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
|
|
44082
|
-
/**
|
|
44083
|
-
* notification-output — canonical, capability-gated notification delivery.
|
|
44084
|
-
*
|
|
44085
|
-
* Apprise-derived model (see
|
|
44086
|
-
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
44087
|
-
* callers emit ONE canonical `Notification`; each provider declares a
|
|
44088
|
-
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
44089
|
-
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
44090
|
-
* message to what the kind supports — callers never special-case a service.
|
|
44091
|
-
*
|
|
44092
|
-
* DESIGN DECISIONS (locked):
|
|
44093
|
-
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
44094
|
-
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
44095
|
-
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
44096
|
-
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
44097
|
-
* alternative would fork the UI per addon and cannot host the
|
|
44098
|
-
* discovery→adopt flow.
|
|
44099
|
-
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
44100
|
-
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
44101
|
-
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
44102
|
-
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
44103
|
-
* `addonId` the generated collection router extracts from the call input.
|
|
44104
|
-
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
44105
|
-
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
44106
|
-
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
44107
|
-
* base64 fallback needed.
|
|
44108
|
-
*
|
|
44109
|
-
* TODO (deferred, closed-set change — separate decision): add
|
|
44110
|
-
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
44111
|
-
* admin "Integrations" page.
|
|
44112
|
-
*/
|
|
44113
|
-
/**
|
|
44114
|
-
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
44115
|
-
* adapter picks what it supports and the degrade engine filters the rest.
|
|
44116
|
-
*/
|
|
44117
|
-
var AttachmentMediaTypeSchema = _enum([
|
|
44118
|
-
"image",
|
|
44119
|
-
"video",
|
|
44120
|
-
"gif",
|
|
44121
|
-
"audio",
|
|
44122
|
-
"icon"
|
|
44123
|
-
]);
|
|
44124
|
-
/**
|
|
44125
|
-
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
44126
|
-
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
44127
|
-
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
44128
|
-
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
44129
|
-
*/
|
|
44130
|
-
var AttachmentSchema = object({
|
|
44131
|
-
mediaType: AttachmentMediaTypeSchema,
|
|
44132
|
-
url: string().optional(),
|
|
44133
|
-
bytes: _instanceof(Uint8Array).optional(),
|
|
44134
|
-
mime: string().optional(),
|
|
44135
|
-
name: string().optional()
|
|
44136
|
-
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
44137
|
-
var NotificationFormatSchema = _enum([
|
|
44138
|
-
"text",
|
|
44139
|
-
"markdown",
|
|
44140
|
-
"html"
|
|
44141
|
-
]);
|
|
44142
|
-
/**
|
|
44143
|
-
* The CLOSED icon vocabulary an action button may use.
|
|
44144
|
-
*
|
|
44145
|
-
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
44146
|
-
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
44147
|
-
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
44148
|
-
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
44149
|
-
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
44150
|
-
*
|
|
44151
|
-
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
44152
|
-
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
44153
|
-
* differently.
|
|
44154
|
-
*
|
|
44155
|
-
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
44156
|
-
*
|
|
44157
|
-
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
44158
|
-
* the whole set onto its own renderer's vocabulary through a
|
|
44159
|
-
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
44160
|
-
* fallback, so adding a member here fails every adapter's build until someone
|
|
44161
|
-
* decides its glyph, which is the only place that decision can be made
|
|
44162
|
-
* honestly.
|
|
44163
|
-
*
|
|
44164
|
-
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
44165
|
-
* `disarm` straight through; iOS feeds that string to
|
|
44166
|
-
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
44167
|
-
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
44168
|
-
* mapping, and "the field is documented" is not "the value renders".
|
|
44169
|
-
*
|
|
44170
|
-
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
44171
|
-
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
44172
|
-
* so an addon that emits a token the running hub does not know does not lose an
|
|
44173
|
-
* icon — its whole `send` fails Zod validation and the notification never
|
|
44174
|
-
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
44175
|
-
*/
|
|
44176
|
-
var NotificationActionIconSchema = _enum([
|
|
44177
|
-
"acknowledge",
|
|
44178
|
-
"dismiss",
|
|
44179
|
-
"silence",
|
|
44180
|
-
"snooze",
|
|
44181
|
-
"view",
|
|
44182
|
-
"play",
|
|
44183
|
-
"open",
|
|
44184
|
-
"close",
|
|
44185
|
-
"lock",
|
|
44186
|
-
"unlock",
|
|
44187
|
-
"arm",
|
|
44188
|
-
"arm-home",
|
|
44189
|
-
"arm-away",
|
|
44190
|
-
"arm-night",
|
|
44191
|
-
"disarm",
|
|
44192
|
-
"light",
|
|
44193
|
-
"alert",
|
|
44194
|
-
"camera"
|
|
44195
|
-
]);
|
|
44196
|
-
/** A single tap-through action button. */
|
|
44197
|
-
var NotificationActionSchema = object({
|
|
44198
|
-
id: string(),
|
|
44199
|
-
label: string(),
|
|
44200
|
-
url: string().optional(),
|
|
44201
|
-
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
44202
|
-
icon: NotificationActionIconSchema.optional(),
|
|
44203
|
-
/**
|
|
44204
|
-
* Renders in a warning style where the notifier supports it.
|
|
44205
|
-
*
|
|
44206
|
-
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
44207
|
-
* else — see `notification-center/action-token.ts` for what that does and
|
|
44208
|
-
* does not buy.
|
|
44209
|
-
*/
|
|
44210
|
-
destructive: boolean().optional(),
|
|
44211
|
-
/**
|
|
44212
|
-
* How the tap should REACH the url.
|
|
44213
|
-
*
|
|
44214
|
-
* `navigate` (absent, and every button authored before this field) opens it:
|
|
44215
|
-
* the phone leaves the notification and shows whatever the callback returns.
|
|
44216
|
-
* That is right for a button whose answer the operator wants to read.
|
|
44217
|
-
*
|
|
44218
|
-
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
44219
|
-
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
44220
|
-
* an answer to the notification, and being thrown into a browser tab to
|
|
44221
|
-
* confirm it costs more attention than the notification did. A backend that
|
|
44222
|
-
* cannot do a background call renders it as an ordinary link (the adapters
|
|
44223
|
-
* fall back rather than dropping the button), so this is a preference, never
|
|
44224
|
-
* a requirement.
|
|
44225
|
-
*/
|
|
44226
|
-
mode: _enum(["navigate", "background"]).optional()
|
|
44227
|
-
});
|
|
44228
|
-
/**
|
|
44229
|
-
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
44230
|
-
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
44231
|
-
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
44232
|
-
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
44233
|
-
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
44234
|
-
* `priority` for that one target.
|
|
44235
|
-
*/
|
|
44236
|
-
var NotificationSchema = object({
|
|
44237
|
-
body: string(),
|
|
44238
|
-
title: string().optional(),
|
|
44239
|
-
format: NotificationFormatSchema.default("text"),
|
|
44240
|
-
priority: number().int().min(1).max(5).default(3),
|
|
44241
|
-
level: string().optional(),
|
|
44242
|
-
attachments: array(AttachmentSchema).optional(),
|
|
44243
|
-
clickUrl: string().optional(),
|
|
44244
|
-
actions: array(NotificationActionSchema).optional(),
|
|
44245
|
-
sound: string().optional(),
|
|
44246
|
-
ttl: number().optional(),
|
|
44247
|
-
tag: string().optional(),
|
|
44248
|
-
deviceId: number().optional(),
|
|
44249
|
-
eventId: string().optional(),
|
|
44250
|
-
metadata: record(string(), unknown()).optional()
|
|
44251
|
-
});
|
|
44252
|
-
/** One declared native severity/priority level for a kind. */
|
|
44253
|
-
var TargetKindLevelSchema = object({
|
|
44254
|
-
id: string(),
|
|
44255
|
-
label: string(),
|
|
44256
|
-
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
44257
|
-
ordinal: number().int().min(1).max(5).nullable(),
|
|
44258
|
-
flags: object({
|
|
44259
|
-
critical: boolean().optional(),
|
|
44260
|
-
silent: boolean().optional(),
|
|
44261
|
-
noPush: boolean().optional()
|
|
44262
|
-
}).optional(),
|
|
44263
|
-
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
44264
|
-
requires: array(string()).optional(),
|
|
44265
|
-
description: string().optional()
|
|
44266
|
-
});
|
|
44267
|
-
/** The full capability block consulted before dispatch. */
|
|
44268
|
-
var TargetKindCapsSchema = object({
|
|
44269
|
-
attachments: object({
|
|
44270
|
-
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
44271
|
-
mode: _enum([
|
|
44272
|
-
"url",
|
|
44273
|
-
"bytes",
|
|
44274
|
-
"both"
|
|
44275
|
-
]),
|
|
44276
|
-
max: number().int().nonnegative(),
|
|
44277
|
-
maxBytes: number().int().positive().optional()
|
|
44278
|
-
}),
|
|
44279
|
-
/** Max action buttons (0 = none). */
|
|
44280
|
-
actions: number().int().nonnegative(),
|
|
44281
|
-
/**
|
|
44282
|
-
* Whether this kind renders a per-action ICON.
|
|
44283
|
-
*
|
|
44284
|
-
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
44285
|
-
* run on the addon cap path — three production failures in one day taught
|
|
44286
|
-
* this repo that once. Absent is read as false by the degrade engine, which
|
|
44287
|
-
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
44288
|
-
* assumed and dropped costs the operator's trust in the field.
|
|
44289
|
-
*/
|
|
44290
|
-
actionIcons: boolean().optional(),
|
|
44291
|
-
levels: array(TargetKindLevelSchema),
|
|
44292
|
-
format: array(NotificationFormatSchema),
|
|
44293
|
-
clickUrl: boolean(),
|
|
44294
|
-
sound: boolean(),
|
|
44295
|
-
ttl: boolean(),
|
|
44296
|
-
bodyMaxLen: number().int().positive()
|
|
44297
|
-
});
|
|
44298
|
-
/**
|
|
44299
|
-
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
44300
|
-
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
44301
|
-
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
44302
|
-
* the union is large and not meant for runtime validation here; the exported
|
|
44303
|
-
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
44304
|
-
*/
|
|
44305
|
-
var ConfigSchemaPassthrough$2 = unknown();
|
|
44306
|
-
var TargetKindSchema = object({
|
|
44307
|
-
kind: string(),
|
|
44308
|
-
label: string(),
|
|
44309
|
-
icon: string(),
|
|
44310
|
-
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
44311
|
-
addonId: string(),
|
|
44312
|
-
/**
|
|
44313
|
-
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
44314
|
-
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
44315
|
-
* when the addon bundles no icon for that kind — the client then falls back
|
|
44316
|
-
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
44317
|
-
*
|
|
44318
|
-
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
44319
|
-
* client, and a native client joins it onto its own hub base.
|
|
44320
|
-
*
|
|
44321
|
-
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
44322
|
-
* field that survived only because the runtime cap-router forwards provider
|
|
44323
|
-
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
44324
|
-
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
44325
|
-
* broken silently the moment output validation was tightened anywhere.
|
|
44326
|
-
*/
|
|
44327
|
-
iconUrl: string().optional(),
|
|
44328
|
-
/**
|
|
44329
|
-
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
44330
|
-
*
|
|
44331
|
-
* The server knows this and therefore says it, because the client cannot
|
|
44332
|
-
* safely guess: a React-Native client renders SVG and raster through two
|
|
44333
|
-
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
44334
|
-
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
44335
|
-
* placeholder glyph for every vector icon while the web build looked fine.
|
|
44336
|
-
*
|
|
44337
|
-
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
44338
|
-
* not been updated — a client that cannot determine the type should prefer
|
|
44339
|
-
* its raster path, which is the safe default for an unknown image.
|
|
44340
|
-
*/
|
|
44341
|
-
iconMediaType: string().optional(),
|
|
44342
|
-
configSchema: ConfigSchemaPassthrough$2,
|
|
44343
|
-
/**
|
|
44344
|
-
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
44345
|
-
* base URL, instance, chat, recipient). A save that changes any of them
|
|
44346
|
-
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
44347
|
-
* editor uses this to ask for the secret on exactly those edits instead of
|
|
44348
|
-
* on every edit.
|
|
44349
|
-
*
|
|
44350
|
-
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
44351
|
-
* kinds) and from a provider older than this field — an editor that finds
|
|
44352
|
-
* it absent must assume ANY edit may need the secret.
|
|
44353
|
-
*/
|
|
44354
|
-
destinationKeys: array(string()).optional(),
|
|
44355
|
-
supportsDiscovery: boolean(),
|
|
44356
|
-
caps: TargetKindCapsSchema
|
|
44357
|
-
});
|
|
44358
|
-
/**
|
|
44359
|
-
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
44360
|
-
* (return a presence marker only) when serving `listTargets` — never
|
|
44361
|
-
* round-trip a stored secret to the UI.
|
|
44362
|
-
*/
|
|
44363
|
-
var TargetSchema = object({
|
|
44364
|
-
id: string(),
|
|
44365
|
-
name: string(),
|
|
44366
|
-
kind: string(),
|
|
44367
|
-
addonId: string(),
|
|
44368
|
-
enabled: boolean(),
|
|
44369
|
-
config: record(string(), unknown())
|
|
44370
|
-
});
|
|
44371
|
-
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
44372
|
-
var DiscoveredTargetSchema = object({
|
|
44373
|
-
kind: string(),
|
|
44374
|
-
suggestedName: string(),
|
|
44375
|
-
config: record(string(), unknown())
|
|
44376
|
-
});
|
|
44377
|
-
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
44378
|
-
var RenderedAsSchema = object({
|
|
44379
|
-
level: string(),
|
|
44380
|
-
format: NotificationFormatSchema,
|
|
44381
|
-
attachmentsSent: number().int().nonnegative(),
|
|
44382
|
-
actionsSent: number().int().nonnegative(),
|
|
44383
|
-
truncated: boolean(),
|
|
44384
|
-
dropped: array(string())
|
|
44385
|
-
});
|
|
44386
|
-
var SendResultSchema = object({
|
|
44387
|
-
success: boolean(),
|
|
44388
|
-
error: string().optional(),
|
|
44389
|
-
renderedAs: RenderedAsSchema.optional()
|
|
44390
|
-
});
|
|
44391
|
-
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
44392
|
-
var TestResultSchema = SendResultSchema;
|
|
44393
|
-
/**
|
|
44394
|
-
* Pre-save credential probe against a partial (unsaved) target config.
|
|
44395
|
-
* `resolvedUrl` never includes secrets (token stripped from path).
|
|
44396
|
-
*/
|
|
44397
|
-
var ProbeTargetConfigResultSchema = object({
|
|
44398
|
-
ok: boolean(),
|
|
44399
|
-
resolvedUrl: string(),
|
|
44400
|
-
httpStatus: number().int().optional(),
|
|
44401
|
-
stateInstance: string().optional(),
|
|
44402
|
-
error: string().optional()
|
|
44403
|
-
});
|
|
44404
|
-
method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({ targetId: string() }), TargetSchema, { auth: "admin" }), method(object({
|
|
44405
|
-
kind: string(),
|
|
44406
|
-
config: record(string(), unknown()).optional(),
|
|
44407
|
-
/**
|
|
44408
|
-
* When editing / duplicating, the blank password field never round-trips
|
|
44409
|
-
* the stored token. Pass the source target id so the provider can restore
|
|
44410
|
-
* secrets server-side before dialling (same merge as upsert).
|
|
44411
|
-
*/
|
|
44412
|
-
targetId: string().optional()
|
|
44413
|
-
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
44414
|
-
kind: string(),
|
|
44415
|
-
config: record(string(), unknown()),
|
|
44416
|
-
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
44417
|
-
targetId: string().optional()
|
|
44418
|
-
}), ProbeTargetConfigResultSchema, {
|
|
44419
|
-
kind: "mutation",
|
|
44420
|
-
auth: "admin"
|
|
44421
|
-
}), method(object({
|
|
44422
|
-
targetId: string(),
|
|
44423
|
-
notification: NotificationSchema
|
|
44424
|
-
}), SendResultSchema, {
|
|
44425
|
-
kind: "mutation",
|
|
44426
|
-
auth: "admin"
|
|
44427
|
-
}), method(object({
|
|
44428
|
-
targetId: string(),
|
|
44429
|
-
sample: NotificationSchema.optional()
|
|
44430
|
-
}), TestResultSchema, {
|
|
44431
|
-
kind: "mutation",
|
|
44432
|
-
auth: "admin"
|
|
44433
|
-
}), method(object({
|
|
44434
|
-
target: TargetSchema,
|
|
44435
|
-
/**
|
|
44436
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
44437
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
44438
|
-
* password fields are restored from this source (D634 still applies if
|
|
44439
|
-
* destinations diverge without a re-typed secret).
|
|
44440
|
-
*/
|
|
44441
|
-
cloneSecretsFrom: string().optional()
|
|
44442
|
-
}), TargetSchema, {
|
|
44443
|
-
kind: "mutation",
|
|
44444
|
-
auth: "admin"
|
|
44445
|
-
}), method(object({ targetId: string() }), _void(), {
|
|
44446
|
-
kind: "mutation",
|
|
44447
|
-
auth: "admin"
|
|
44448
|
-
}), method(object({
|
|
44449
|
-
targetId: string(),
|
|
44450
|
-
enabled: boolean()
|
|
44451
|
-
}), _void(), {
|
|
44452
|
-
kind: "mutation",
|
|
44453
|
-
auth: "admin"
|
|
44454
|
-
});
|
|
44455
|
-
new Set([
|
|
44456
|
-
{
|
|
44457
|
-
id: "person",
|
|
44458
|
-
name: "Person"
|
|
44459
|
-
},
|
|
44460
|
-
{
|
|
44461
|
-
id: "vehicle",
|
|
44462
|
-
name: "Vehicle"
|
|
44463
|
-
},
|
|
44464
|
-
{
|
|
44465
|
-
id: "animal",
|
|
44466
|
-
name: "Animal"
|
|
44467
|
-
},
|
|
44468
|
-
{
|
|
44469
|
-
id: "package",
|
|
44470
|
-
name: "Package"
|
|
44471
|
-
}
|
|
44472
|
-
].map((l) => l.id));
|
|
44473
|
-
var COCO_TO_MACRO = {
|
|
44474
|
-
mapping: {
|
|
44475
|
-
person: "person",
|
|
44476
|
-
bicycle: "vehicle",
|
|
44477
|
-
car: "vehicle",
|
|
44478
|
-
motorcycle: "vehicle",
|
|
44479
|
-
airplane: "vehicle",
|
|
44480
|
-
bus: "vehicle",
|
|
44481
|
-
train: "vehicle",
|
|
44482
|
-
truck: "vehicle",
|
|
44483
|
-
boat: "vehicle",
|
|
44484
|
-
bird: "animal",
|
|
44485
|
-
cat: "animal",
|
|
44486
|
-
dog: "animal",
|
|
44487
|
-
horse: "animal",
|
|
44488
|
-
sheep: "animal",
|
|
44489
|
-
cow: "animal",
|
|
44490
|
-
elephant: "animal",
|
|
44491
|
-
bear: "animal",
|
|
44492
|
-
zebra: "animal",
|
|
44493
|
-
giraffe: "animal",
|
|
44494
|
-
suitcase: "package",
|
|
44495
|
-
backpack: "package",
|
|
44496
|
-
handbag: "package"
|
|
44497
|
-
},
|
|
44498
|
-
preserveOriginal: false
|
|
44499
|
-
};
|
|
44500
45157
|
var AUDIO_MACRO_LABELS = [
|
|
44501
45158
|
{
|
|
44502
45159
|
id: "speech",
|
|
@@ -50897,14 +51554,6 @@ var EngineProvisioningSchema = object({
|
|
|
50897
51554
|
*/
|
|
50898
51555
|
configIssues: array(string()).optional()
|
|
50899
51556
|
});
|
|
50900
|
-
var PipelineStepInputSchema = lazy(() => object({
|
|
50901
|
-
addonId: string(),
|
|
50902
|
-
modelId: string().optional(),
|
|
50903
|
-
enabled: boolean().default(true),
|
|
50904
|
-
children: array(PipelineStepInputSchema).optional(),
|
|
50905
|
-
settings: record(string(), unknown()).optional(),
|
|
50906
|
-
jumpDeviceKey: string().optional()
|
|
50907
|
-
}));
|
|
50908
51557
|
var ModelSubstitutionSchema = object({
|
|
50909
51558
|
addonId: string(),
|
|
50910
51559
|
chosen: string(),
|
|
@@ -50983,7 +51632,6 @@ var ConfigUISchemaBridge = custom();
|
|
|
50983
51632
|
var ConfigUISchemaNullableBridge = custom();
|
|
50984
51633
|
var InferenceCapabilitiesBridge = custom();
|
|
50985
51634
|
var ModelAvailabilityListBridge = custom();
|
|
50986
|
-
var PipelineRunResultBridge = custom();
|
|
50987
51635
|
method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
|
|
50988
51636
|
modelId: string(),
|
|
50989
51637
|
settings: record(string(), unknown()).readonly()
|
|
@@ -54145,6 +54793,12 @@ var ServerUpdateActionResultSchema = object({
|
|
|
54145
54793
|
*/
|
|
54146
54794
|
blockers: array(UpdateBlockerSchema).optional()
|
|
54147
54795
|
});
|
|
54796
|
+
var LaunchOverridesSchema = object({
|
|
54797
|
+
/** The persisted heap ceiling in MB, or `null` when none is set. */
|
|
54798
|
+
heapMb: number().int().nullable() });
|
|
54799
|
+
var SetLaunchOverridesResultSchema = LaunchOverridesSchema.extend({
|
|
54800
|
+
/** Always true: the override is read at launch, not by the running process. */
|
|
54801
|
+
restartRequired: boolean() });
|
|
54148
54802
|
method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
|
|
54149
54803
|
kind: "mutation",
|
|
54150
54804
|
auth: "admin"
|
|
@@ -54167,6 +54821,9 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
|
|
|
54167
54821
|
}), method(_void(), ServerUpdateActionResultSchema, {
|
|
54168
54822
|
kind: "mutation",
|
|
54169
54823
|
auth: "admin"
|
|
54824
|
+
}), method(_void(), LaunchOverridesSchema, { auth: "admin" }), method(object({ heapMb: number().int().min(512).max(65536).nullable() }), SetLaunchOverridesResultSchema, {
|
|
54825
|
+
kind: "mutation",
|
|
54826
|
+
auth: "admin"
|
|
54170
54827
|
});
|
|
54171
54828
|
/**
|
|
54172
54829
|
* `smtp-provider` — pluggable email delivery surface.
|
|
@@ -56568,7 +57225,9 @@ var ManifestShapeSchema = object({
|
|
|
56568
57225
|
icon: string().optional()
|
|
56569
57226
|
}).optional(),
|
|
56570
57227
|
protected: boolean().optional(),
|
|
56571
|
-
removable: boolean().optional()
|
|
57228
|
+
removable: boolean().optional(),
|
|
57229
|
+
/** Declared Integrations-page block (mirror of `AddonDeclaration.integrationSection`). */
|
|
57230
|
+
integrationSection: _enum(["system", "providers"]).optional()
|
|
56572
57231
|
});
|
|
56573
57232
|
var AddonProcessSchema = object({
|
|
56574
57233
|
pid: number().optional(),
|
|
@@ -56933,9 +57592,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56933
57592
|
}), LogStreamEntrySchema, { kind: "subscription" });
|
|
56934
57593
|
object({
|
|
56935
57594
|
/** Carbon dioxide concentration in ppm. */
|
|
56936
|
-
|
|
57595
|
+
co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
56937
57596
|
/** Total volatile organic compounds in ppb. */
|
|
56938
|
-
|
|
57597
|
+
voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
56939
57598
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
56940
57599
|
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56941
57600
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
@@ -56948,7 +57607,7 @@ object({
|
|
|
56948
57607
|
DeviceType.Sensor;
|
|
56949
57608
|
object({
|
|
56950
57609
|
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
56951
|
-
|
|
57610
|
+
illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
56952
57611
|
precision: 0,
|
|
56953
57612
|
label: "Illuminance"
|
|
56954
57613
|
})),
|
|
@@ -57045,6 +57704,84 @@ DeviceType.Camera, method(object({ deviceId: number() }), AudioMetricsSnapshotSc
|
|
|
57045
57704
|
* would produce more than `maxPoints` samples. */
|
|
57046
57705
|
sampleEveryMs: number().int().positive().optional()
|
|
57047
57706
|
}), AudioMetricsHistorySchema);
|
|
57707
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
57708
|
+
actionId: string().min(1),
|
|
57709
|
+
variables: record(string(), ActionVariableValueSchema).optional()
|
|
57710
|
+
}), _void(), {
|
|
57711
|
+
kind: "mutation",
|
|
57712
|
+
auth: "admin"
|
|
57713
|
+
}), method(object({ actionId: string().min(1) }), _void(), {
|
|
57714
|
+
kind: "mutation",
|
|
57715
|
+
auth: "admin"
|
|
57716
|
+
});
|
|
57717
|
+
/**
|
|
57718
|
+
* actions — the ONE surface for runnable actions, for the UI and for
|
|
57719
|
+
* automations. Provided by the core-blocks builtin on the hub.
|
|
57720
|
+
*
|
|
57721
|
+
* An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
|
|
57722
|
+
* steps, kept in the hub's `actions` record kind) or declared by an addon over
|
|
57723
|
+
* `action-source`. `list` asks every `action-source` provider LIVE and overlays
|
|
57724
|
+
* the run status from the hub's `runs` ledger; there is no catalog here to
|
|
57725
|
+
* drift (D3).
|
|
57726
|
+
*
|
|
57727
|
+
* **Permissions follow the camera, as linked devices do.** An action is linked
|
|
57728
|
+
* to cameras by the operator (`setLinks`, D356: nothing links itself). A
|
|
57729
|
+
* non-admin may `get` / `run` an action only through a camera they may see that
|
|
57730
|
+
* links it: they name the camera (`deviceId`, a top-level field so the scope
|
|
57731
|
+
* gate sees it) and the provider refuses an action that camera does not link.
|
|
57732
|
+
* An admin, and an in-process caller (an automation block), may run any action.
|
|
57733
|
+
* Every refusal is typed (`[not-linked] …`) and logged.
|
|
57734
|
+
*
|
|
57735
|
+
* A second `run` of an action that is already running is REFUSED — traced and
|
|
57736
|
+
* logged — never queued.
|
|
57737
|
+
*/
|
|
57738
|
+
var ActionIdInput = object({ actionId: string().min(1) });
|
|
57739
|
+
var ActionLinksSchema = object({
|
|
57740
|
+
deviceId: number().int().nonnegative(),
|
|
57741
|
+
actionIds: array(string().min(1)).max(100)
|
|
57742
|
+
});
|
|
57743
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
57744
|
+
actionId: string().min(1),
|
|
57745
|
+
deviceId: number().int().nonnegative().optional()
|
|
57746
|
+
}), object({ action: ActionEntrySchema.nullable() }), {
|
|
57747
|
+
role: "viewer",
|
|
57748
|
+
auth: "protected",
|
|
57749
|
+
kind: "query",
|
|
57750
|
+
caller: "required"
|
|
57751
|
+
}), method(object({
|
|
57752
|
+
actionId: string().min(1),
|
|
57753
|
+
variables: record(string(), ActionVariableValueSchema).optional(),
|
|
57754
|
+
wait: boolean().optional(),
|
|
57755
|
+
origin: ActionRunOriginSchema.optional(),
|
|
57756
|
+
deviceId: number().int().nonnegative().optional()
|
|
57757
|
+
}), ActionRunResultSchema, {
|
|
57758
|
+
role: "viewer",
|
|
57759
|
+
auth: "protected",
|
|
57760
|
+
kind: "mutation",
|
|
57761
|
+
caller: "required"
|
|
57762
|
+
}), method(ActionIdInput, _void(), {
|
|
57763
|
+
kind: "mutation",
|
|
57764
|
+
auth: "admin"
|
|
57765
|
+
}), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
|
|
57766
|
+
kind: "mutation",
|
|
57767
|
+
auth: "admin"
|
|
57768
|
+
}), method(object({
|
|
57769
|
+
actionId: string().min(1),
|
|
57770
|
+
action: ActionInputSchema.partial()
|
|
57771
|
+
}), object({ action: ActionEntrySchema }), {
|
|
57772
|
+
kind: "mutation",
|
|
57773
|
+
auth: "admin"
|
|
57774
|
+
}), method(ActionIdInput, _void(), {
|
|
57775
|
+
kind: "mutation",
|
|
57776
|
+
auth: "admin"
|
|
57777
|
+
}), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
|
|
57778
|
+
role: "viewer",
|
|
57779
|
+
auth: "protected",
|
|
57780
|
+
kind: "query"
|
|
57781
|
+
}), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
|
|
57782
|
+
kind: "mutation",
|
|
57783
|
+
auth: "admin"
|
|
57784
|
+
});
|
|
57048
57785
|
object({
|
|
57049
57786
|
/** Whether the automation is currently enabled. Disabled automations
|
|
57050
57787
|
* ignore their trigger block — manual `trigger` still works. */
|
|
@@ -57098,7 +57835,7 @@ var BatteryStatusSchema = object({
|
|
|
57098
57835
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
57099
57836
|
* declines to band a non-finite reading, which is the correct shape.
|
|
57100
57837
|
*/
|
|
57101
|
-
|
|
57838
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
57102
57839
|
/**
|
|
57103
57840
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
57104
57841
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -57108,7 +57845,7 @@ var BatteryStatusSchema = object({
|
|
|
57108
57845
|
* `'unknown'` is the absence of an answer: the provider has not read the
|
|
57109
57846
|
* power fields yet, or the firmware reported neither. Before it existed,
|
|
57110
57847
|
* two unreadable fields produced `'none'`, and a camera nobody had reached
|
|
57111
|
-
* was drawn "on battery alone" — the same D315 defect `
|
|
57848
|
+
* was drawn "on battery alone" — the same D315 defect `level` closed
|
|
57112
57849
|
* by going nullable. Consumers SKIP it (no charger state published, no
|
|
57113
57850
|
* glyph, no HomeKit ChargingState) rather than coerce it either way.
|
|
57114
57851
|
*/
|
|
@@ -57132,7 +57869,7 @@ var BatteryStatusSchema = object({
|
|
|
57132
57869
|
* that only report charge. A second reading on this cap, not a second
|
|
57133
57870
|
* `numeric-sensor` on the same device.
|
|
57134
57871
|
*/
|
|
57135
|
-
|
|
57872
|
+
voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
57136
57873
|
/**
|
|
57137
57874
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
57138
57875
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -57166,7 +57903,7 @@ var BatteryStatusSchema = object({
|
|
|
57166
57903
|
/**
|
|
57167
57904
|
* True when the source is a BINARY low-battery indicator (HA
|
|
57168
57905
|
* `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
|
|
57169
|
-
* charge level — `
|
|
57906
|
+
* charge level — `level` is then a coarse stand-in (100 = normal,
|
|
57170
57907
|
* sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
|
|
57171
57908
|
* misleading exact percentage. Absent/false → genuine 0–100 % reading.
|
|
57172
57909
|
*/
|
|
@@ -57192,26 +57929,388 @@ object({
|
|
|
57192
57929
|
DeviceType.Sensor;
|
|
57193
57930
|
object({
|
|
57194
57931
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57195
|
-
|
|
57932
|
+
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57196
57933
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57197
57934
|
lastChangedAt: number()
|
|
57198
57935
|
});
|
|
57199
57936
|
DeviceType.Light, method(object({
|
|
57200
57937
|
deviceId: number().int().nonnegative(),
|
|
57201
|
-
|
|
57938
|
+
level: number().min(0).max(100)
|
|
57202
57939
|
}), _void(), {
|
|
57203
57940
|
kind: "mutation",
|
|
57204
57941
|
auth: "protected"
|
|
57205
57942
|
}), object({
|
|
57206
57943
|
deviceId: number(),
|
|
57207
|
-
|
|
57944
|
+
level: number().min(0).max(100),
|
|
57208
57945
|
lastChangedAt: number()
|
|
57209
57946
|
});
|
|
57947
|
+
/**
|
|
57948
|
+
* The role vocabulary of a remote (D963): what a button MEANS, independent of
|
|
57949
|
+
* how it is wired. A layout template places buttons by role; a device binds a
|
|
57950
|
+
* role to an action; an exporter asks for a role by name. Roles are shared so
|
|
57951
|
+
* one layout fits every appliance and one voice command fits every binding.
|
|
57952
|
+
*
|
|
57953
|
+
* A role outside this list is `custom:<key>` — the operator's own button (a
|
|
57954
|
+
* SmartIR source, a channel preset). The key is a lowercase slug.
|
|
57955
|
+
*/
|
|
57956
|
+
var REMOTE_KNOWN_ROLES = [
|
|
57957
|
+
"power",
|
|
57958
|
+
"power_on",
|
|
57959
|
+
"power_off",
|
|
57960
|
+
"volume_up",
|
|
57961
|
+
"volume_down",
|
|
57962
|
+
"mute",
|
|
57963
|
+
"channel_up",
|
|
57964
|
+
"channel_down",
|
|
57965
|
+
"up",
|
|
57966
|
+
"down",
|
|
57967
|
+
"left",
|
|
57968
|
+
"right",
|
|
57969
|
+
"ok",
|
|
57970
|
+
"back",
|
|
57971
|
+
"home",
|
|
57972
|
+
"menu",
|
|
57973
|
+
"info",
|
|
57974
|
+
"guide",
|
|
57975
|
+
"exit",
|
|
57976
|
+
"input",
|
|
57977
|
+
"play",
|
|
57978
|
+
"pause",
|
|
57979
|
+
"play_pause",
|
|
57980
|
+
"stop",
|
|
57981
|
+
"next",
|
|
57982
|
+
"previous",
|
|
57983
|
+
"fast_forward",
|
|
57984
|
+
"rewind",
|
|
57985
|
+
"digit_0",
|
|
57986
|
+
"digit_1",
|
|
57987
|
+
"digit_2",
|
|
57988
|
+
"digit_3",
|
|
57989
|
+
"digit_4",
|
|
57990
|
+
"digit_5",
|
|
57991
|
+
"digit_6",
|
|
57992
|
+
"digit_7",
|
|
57993
|
+
"digit_8",
|
|
57994
|
+
"digit_9"
|
|
57995
|
+
];
|
|
57996
|
+
var CUSTOM_KEY_RE = /^[a-z0-9][a-z0-9_]{0,47}$/;
|
|
57997
|
+
var KNOWN_ROLES = new Set(REMOTE_KNOWN_ROLES);
|
|
57998
|
+
function isRemoteKnownRole(value) {
|
|
57999
|
+
return KNOWN_ROLES.has(value);
|
|
58000
|
+
}
|
|
58001
|
+
function isRemoteCustomKey(key) {
|
|
58002
|
+
return CUSTOM_KEY_RE.test(key);
|
|
58003
|
+
}
|
|
58004
|
+
/** The key of a `custom:<key>` role, or null for any other string. */
|
|
58005
|
+
function customKeyOfRole(role) {
|
|
58006
|
+
if (!role.startsWith("custom:")) return null;
|
|
58007
|
+
const key = role.slice(7);
|
|
58008
|
+
return isRemoteCustomKey(key) ? key : null;
|
|
58009
|
+
}
|
|
58010
|
+
function isRemoteRole(value) {
|
|
58011
|
+
return isRemoteKnownRole(value) || customKeyOfRole(value) !== null;
|
|
58012
|
+
}
|
|
58013
|
+
var RemoteRoleSchema = string().refine(isRemoteRole, { message: "a role is one of the known roles or custom:<key> (lowercase letters, digits, _)" });
|
|
58014
|
+
var RemoteBindingSchema = object({ action: AutomationCapStepSchema.extend({ deviceId: number().int().nonnegative() }) });
|
|
58015
|
+
var RemoteCustomButtonSchema = object({
|
|
58016
|
+
/** The slug after `custom:` in the role. */
|
|
58017
|
+
key: string().refine(isRemoteCustomKey, { message: "a lowercase slug" }),
|
|
58018
|
+
/** What the operator calls it, and what a voice command may name. */
|
|
58019
|
+
name: string().min(1).max(60)
|
|
58020
|
+
});
|
|
58021
|
+
var RemotePowerModeSchema = _enum(["toggle", "discrete"]);
|
|
58022
|
+
var RemoteConfigSchema = object({
|
|
58023
|
+
layoutTemplateId: string().min(1).nullable(),
|
|
58024
|
+
/**
|
|
58025
|
+
* The transmitter device new bindings and an import send through. A binding
|
|
58026
|
+
* names its own device, so one command may override it.
|
|
58027
|
+
*/
|
|
58028
|
+
defaultTransmitterId: number().int().nonnegative().nullable().default(null),
|
|
58029
|
+
/** Keyed by role. A role with no entry falls back to the native cap. */
|
|
58030
|
+
bindings: record(string(), RemoteBindingSchema),
|
|
58031
|
+
customButtons: array(RemoteCustomButtonSchema).max(100),
|
|
58032
|
+
power: object({ mode: RemotePowerModeSchema })
|
|
58033
|
+
}).superRefine((config, ctx) => {
|
|
58034
|
+
const roles = Object.keys(config.bindings);
|
|
58035
|
+
if (roles.length > 200) ctx.addIssue({
|
|
58036
|
+
code: "custom",
|
|
58037
|
+
message: "too many bindings",
|
|
58038
|
+
path: ["bindings"]
|
|
58039
|
+
});
|
|
58040
|
+
for (const role of roles) if (!isRemoteRole(role)) ctx.addIssue({
|
|
58041
|
+
code: "custom",
|
|
58042
|
+
message: `"${role}" is not a role`,
|
|
58043
|
+
path: ["bindings", role]
|
|
58044
|
+
});
|
|
58045
|
+
const keys = /* @__PURE__ */ new Set();
|
|
58046
|
+
config.customButtons.forEach((button, index) => {
|
|
58047
|
+
if (keys.has(button.key)) ctx.addIssue({
|
|
58048
|
+
code: "custom",
|
|
58049
|
+
message: `custom button "${button.key}" is listed twice`,
|
|
58050
|
+
path: [
|
|
58051
|
+
"customButtons",
|
|
58052
|
+
index,
|
|
58053
|
+
"key"
|
|
58054
|
+
]
|
|
58055
|
+
});
|
|
58056
|
+
keys.add(button.key);
|
|
58057
|
+
});
|
|
58058
|
+
for (const role of roles) {
|
|
58059
|
+
const key = customKeyOfRole(role);
|
|
58060
|
+
if (key !== null && !keys.has(key)) ctx.addIssue({
|
|
58061
|
+
code: "custom",
|
|
58062
|
+
message: `${role} is bound but has no entry in customButtons`,
|
|
58063
|
+
path: ["bindings", role]
|
|
58064
|
+
});
|
|
58065
|
+
}
|
|
58066
|
+
});
|
|
58067
|
+
object({
|
|
58068
|
+
power: object({
|
|
58069
|
+
state: _enum(["on", "off"]),
|
|
58070
|
+
at: number()
|
|
58071
|
+
}).nullable(),
|
|
58072
|
+
muted: object({
|
|
58073
|
+
value: boolean(),
|
|
58074
|
+
at: number()
|
|
58075
|
+
}).nullable()
|
|
58076
|
+
});
|
|
58077
|
+
var RemoteLayoutButtonSchema = object({
|
|
58078
|
+
id: string().min(1).max(40),
|
|
58079
|
+
/** 0-based grid position. */
|
|
58080
|
+
row: number().int().min(0).max(24),
|
|
58081
|
+
col: number().int().min(0).max(24),
|
|
58082
|
+
rowSpan: number().int().min(1).max(24),
|
|
58083
|
+
colSpan: number().int().min(1).max(24),
|
|
58084
|
+
icon: string().min(1).max(60).optional(),
|
|
58085
|
+
label: string().min(1).max(40).optional(),
|
|
58086
|
+
role: RemoteRoleSchema
|
|
58087
|
+
});
|
|
58088
|
+
object({
|
|
58089
|
+
columns: number().int().min(1).max(24),
|
|
58090
|
+
rows: number().int().min(1).max(24),
|
|
58091
|
+
buttons: array(RemoteLayoutButtonSchema).max(200)
|
|
58092
|
+
}).superRefine((grid, ctx) => {
|
|
58093
|
+
const ids = /* @__PURE__ */ new Set();
|
|
58094
|
+
const taken = /* @__PURE__ */ new Map();
|
|
58095
|
+
grid.buttons.forEach((button, index) => {
|
|
58096
|
+
if (ids.has(button.id)) ctx.addIssue({
|
|
58097
|
+
code: "custom",
|
|
58098
|
+
message: `button id "${button.id}" is used twice`,
|
|
58099
|
+
path: [
|
|
58100
|
+
"buttons",
|
|
58101
|
+
index,
|
|
58102
|
+
"id"
|
|
58103
|
+
]
|
|
58104
|
+
});
|
|
58105
|
+
ids.add(button.id);
|
|
58106
|
+
if (button.row + button.rowSpan > grid.rows || button.col + button.colSpan > grid.columns) {
|
|
58107
|
+
ctx.addIssue({
|
|
58108
|
+
code: "custom",
|
|
58109
|
+
message: `button "${button.id}" leaves the grid`,
|
|
58110
|
+
path: ["buttons", index]
|
|
58111
|
+
});
|
|
58112
|
+
return;
|
|
58113
|
+
}
|
|
58114
|
+
for (let r = button.row; r < button.row + button.rowSpan; r += 1) for (let c = button.col; c < button.col + button.colSpan; c += 1) {
|
|
58115
|
+
const cell = `${r}:${c}`;
|
|
58116
|
+
const other = taken.get(cell);
|
|
58117
|
+
if (other !== void 0) {
|
|
58118
|
+
ctx.addIssue({
|
|
58119
|
+
code: "custom",
|
|
58120
|
+
message: `buttons "${other}" and "${button.id}" overlap`,
|
|
58121
|
+
path: ["buttons", index]
|
|
58122
|
+
});
|
|
58123
|
+
return;
|
|
58124
|
+
}
|
|
58125
|
+
taken.set(cell, button.id);
|
|
58126
|
+
}
|
|
58127
|
+
});
|
|
58128
|
+
});
|
|
58129
|
+
var RemoteBoundCommandSchema = object({
|
|
58130
|
+
role: RemoteRoleSchema,
|
|
58131
|
+
/** The operator's name for a custom button; absent for a known role. */
|
|
58132
|
+
label: string().optional(),
|
|
58133
|
+
custom: boolean()
|
|
58134
|
+
});
|
|
58135
|
+
var RemoteCommandListSchema = object({
|
|
58136
|
+
layoutTemplateId: string().nullable(),
|
|
58137
|
+
power: object({ mode: RemotePowerModeSchema }),
|
|
58138
|
+
/** Only the roles that are BOUND — the ones a press can reach. */
|
|
58139
|
+
commands: array(RemoteBoundCommandSchema)
|
|
58140
|
+
});
|
|
58141
|
+
var RemoteRefusalReasonSchema = _enum([
|
|
58142
|
+
"no-remote",
|
|
58143
|
+
"unbound-role",
|
|
58144
|
+
"action-failed"
|
|
58145
|
+
]);
|
|
58146
|
+
var RemoteSendResultSchema = discriminatedUnion("status", [
|
|
58147
|
+
object({
|
|
58148
|
+
status: literal("sent"),
|
|
58149
|
+
/** How many times the action ran. */
|
|
58150
|
+
sends: number().int().min(1)
|
|
58151
|
+
}),
|
|
58152
|
+
object({
|
|
58153
|
+
status: literal("skipped"),
|
|
58154
|
+
reason: literal("already-in-state")
|
|
58155
|
+
}),
|
|
58156
|
+
object({
|
|
58157
|
+
status: literal("refused"),
|
|
58158
|
+
reason: RemoteRefusalReasonSchema,
|
|
58159
|
+
detail: string().optional()
|
|
58160
|
+
})
|
|
58161
|
+
]);
|
|
58162
|
+
var RemoteMediaCommandSchema = _enum([
|
|
58163
|
+
"play",
|
|
58164
|
+
"pause",
|
|
58165
|
+
"stop",
|
|
58166
|
+
"next",
|
|
58167
|
+
"previous",
|
|
58168
|
+
"setMute",
|
|
58169
|
+
"selectSource"
|
|
58170
|
+
]);
|
|
58171
|
+
/** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
|
|
58172
|
+
var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
|
|
58173
|
+
var RemoteSetConfigResultSchema = discriminatedUnion("status", [object({ status: literal("saved") }), object({
|
|
58174
|
+
status: literal("refused"),
|
|
58175
|
+
reason: _enum(["unknown-template", "not-a-media-player"]),
|
|
58176
|
+
detail: string()
|
|
58177
|
+
})]);
|
|
58178
|
+
var deviceInput = object({ deviceId: number().int().nonnegative() });
|
|
58179
|
+
DeviceType.MediaPlayer;
|
|
58180
|
+
method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
|
|
58181
|
+
kind: "mutation",
|
|
58182
|
+
auth: "admin"
|
|
58183
|
+
}), method(deviceInput, RemoteCommandListSchema, { auth: "protected" }), method(deviceInput.extend({
|
|
58184
|
+
role: RemoteRoleSchema,
|
|
58185
|
+
/** Sends in a row (volume steps). */
|
|
58186
|
+
repeat: number().int().min(1).max(50).default(1)
|
|
58187
|
+
}), RemoteSendResultSchema, {
|
|
58188
|
+
kind: "mutation",
|
|
58189
|
+
auth: "protected"
|
|
58190
|
+
}), method(deviceInput.extend({
|
|
58191
|
+
command: RemoteMediaCommandSchema,
|
|
58192
|
+
muted: boolean().optional(),
|
|
58193
|
+
source: string().optional(),
|
|
58194
|
+
repeat: number().int().min(1).max(50).default(1)
|
|
58195
|
+
}), RemoteMediaCommandResultSchema, {
|
|
58196
|
+
kind: "mutation",
|
|
58197
|
+
auth: "protected"
|
|
58198
|
+
});
|
|
58199
|
+
var TemplateKindSchema = _enum(["remote"]);
|
|
58200
|
+
/**
|
|
58201
|
+
* template — the generic store of reusable, named operator data (D963). A
|
|
58202
|
+
* record is `{ id, kind, name, payload }`; the kinds (today only `remote`, the
|
|
58203
|
+
* button grid of a remote) and the schema each payload must satisfy are
|
|
58204
|
+
* declared by the system in `template-kinds.ts`. Nothing here is specific to a
|
|
58205
|
+
* kind: the engine validates a payload against its kind's schema and, on a
|
|
58206
|
+
* delete, asks the kind's owner whether anything still points at the record.
|
|
58207
|
+
*
|
|
58208
|
+
* Every refusal is a typed result — a UI says "unknown kind" or names the
|
|
58209
|
+
* devices that still use a template, without parsing text.
|
|
58210
|
+
*/
|
|
58211
|
+
var TemplateRecordSchema = object({
|
|
58212
|
+
id: string().min(1),
|
|
58213
|
+
kind: TemplateKindSchema,
|
|
58214
|
+
name: string().min(1).max(120),
|
|
58215
|
+
/** Shaped by the kind; `checkTemplatePayload(kind, payload)` reads it. */
|
|
58216
|
+
payload: unknown(),
|
|
58217
|
+
createdAt: number(),
|
|
58218
|
+
updatedAt: number()
|
|
58219
|
+
});
|
|
58220
|
+
var TemplateWriteResultSchema = discriminatedUnion("status", [object({
|
|
58221
|
+
status: literal("saved"),
|
|
58222
|
+
template: TemplateRecordSchema
|
|
58223
|
+
}), object({
|
|
58224
|
+
status: literal("refused"),
|
|
58225
|
+
reason: _enum(["invalid-payload", "not-found"]),
|
|
58226
|
+
detail: string()
|
|
58227
|
+
})]);
|
|
58228
|
+
/** A device still pointing at a template. */
|
|
58229
|
+
var TemplateUserSchema = object({
|
|
58230
|
+
deviceId: number().int().nonnegative(),
|
|
58231
|
+
name: string()
|
|
58232
|
+
});
|
|
58233
|
+
var TemplateDeleteResultSchema = discriminatedUnion("status", [
|
|
58234
|
+
object({ status: literal("deleted") }),
|
|
58235
|
+
object({ status: literal("not-found") }),
|
|
58236
|
+
object({
|
|
58237
|
+
status: literal("in-use"),
|
|
58238
|
+
devices: array(TemplateUserSchema).min(1)
|
|
58239
|
+
})
|
|
58240
|
+
]);
|
|
58241
|
+
method(object({ kind: TemplateKindSchema.optional() }), array(TemplateRecordSchema), { auth: "protected" }), method(object({ id: string().min(1) }), TemplateRecordSchema.nullable(), { auth: "protected" }), method(object({
|
|
58242
|
+
kind: TemplateKindSchema,
|
|
58243
|
+
name: string().min(1).max(120),
|
|
58244
|
+
payload: unknown()
|
|
58245
|
+
}), TemplateWriteResultSchema, {
|
|
58246
|
+
kind: "mutation",
|
|
58247
|
+
auth: "admin"
|
|
58248
|
+
}), method(object({
|
|
58249
|
+
id: string().min(1),
|
|
58250
|
+
name: string().min(1).max(120).optional(),
|
|
58251
|
+
payload: unknown().optional()
|
|
58252
|
+
}), TemplateWriteResultSchema, {
|
|
58253
|
+
kind: "mutation",
|
|
58254
|
+
auth: "admin"
|
|
58255
|
+
}), method(object({ id: string().min(1) }), TemplateDeleteResultSchema, {
|
|
58256
|
+
kind: "mutation",
|
|
58257
|
+
auth: "admin"
|
|
58258
|
+
});
|
|
57210
58259
|
DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
57211
58260
|
kind: "mutation",
|
|
57212
58261
|
auth: "protected"
|
|
57213
58262
|
});
|
|
57214
58263
|
/**
|
|
58264
|
+
* A pulse train in signed microseconds: mark (+) first, then space (-), strictly
|
|
58265
|
+
* alternating, no zeros, 2..1024 entries.
|
|
58266
|
+
*/
|
|
58267
|
+
var IrTimingsSchema = array(number().int()).min(2).max(1024).superRefine((timings, ctx) => {
|
|
58268
|
+
timings.forEach((value, index) => {
|
|
58269
|
+
const expectMark = index % 2 === 0;
|
|
58270
|
+
if (value === 0 || value > 0 !== expectMark) ctx.addIssue({
|
|
58271
|
+
code: "custom",
|
|
58272
|
+
message: `timing ${index} must be a non-zero ${expectMark ? "mark (positive)" : "space (negative)"}`,
|
|
58273
|
+
path: [index]
|
|
58274
|
+
});
|
|
58275
|
+
});
|
|
58276
|
+
});
|
|
58277
|
+
/** Per-command transmit options. Every key is optional; a transmitter applies what it can. */
|
|
58278
|
+
var TransmitSettingsSchema = object({
|
|
58279
|
+
carrierHz: number().int().positive().optional(),
|
|
58280
|
+
dutyCyclePercent: number().int().min(1).max(100).optional(),
|
|
58281
|
+
repeat: number().int().min(0).max(20).optional(),
|
|
58282
|
+
repeatDelayMs: number().int().min(0).optional()
|
|
58283
|
+
});
|
|
58284
|
+
/** The setting keys a transmitter may declare as settable (`getInfo.settable`). */
|
|
58285
|
+
var TransmitSettingKeySchema = _enum([
|
|
58286
|
+
"carrierHz",
|
|
58287
|
+
"dutyCyclePercent",
|
|
58288
|
+
"repeat",
|
|
58289
|
+
"repeatDelayMs"
|
|
58290
|
+
]);
|
|
58291
|
+
var IrMediumSchema = _enum(["ir", "rf"]);
|
|
58292
|
+
var IrSendResultSchema = object({
|
|
58293
|
+
ok: literal(true),
|
|
58294
|
+
/** Settings the transmitter could not apply; the train was still sent. */
|
|
58295
|
+
ignoredSettings: array(string())
|
|
58296
|
+
});
|
|
58297
|
+
var IrTransmitterInfoSchema = object({
|
|
58298
|
+
media: array(IrMediumSchema),
|
|
58299
|
+
settable: array(TransmitSettingKeySchema),
|
|
58300
|
+
maxTimings: number().int().positive()
|
|
58301
|
+
});
|
|
58302
|
+
DeviceType.Generic, method(object({
|
|
58303
|
+
deviceId: number().int().nonnegative(),
|
|
58304
|
+
timings: IrTimingsSchema,
|
|
58305
|
+
settings: TransmitSettingsSchema.optional()
|
|
58306
|
+
}), IrSendResultSchema, {
|
|
58307
|
+
kind: "mutation",
|
|
58308
|
+
auth: "protected"
|
|
58309
|
+
}), method(object({ deviceId: number().int().nonnegative() }), IrTransmitterInfoSchema, {
|
|
58310
|
+
kind: "query",
|
|
58311
|
+
auth: "protected"
|
|
58312
|
+
});
|
|
58313
|
+
/**
|
|
57215
58314
|
* `calendar-source` — collection cap exposing per-provider calendars and
|
|
57216
58315
|
* their events. Every addon that contributes calendars (Home Assistant,
|
|
57217
58316
|
* a future Google Calendar addon, …) registers a provider here. The hub
|
|
@@ -57715,11 +58814,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
|
|
|
57715
58814
|
}),
|
|
57716
58815
|
object({
|
|
57717
58816
|
mode: literal("mired"),
|
|
57718
|
-
|
|
58817
|
+
colorTemperature: number().int().min(50).max(1e3)
|
|
57719
58818
|
})
|
|
57720
58819
|
]);
|
|
57721
58820
|
object({
|
|
57722
|
-
/** Active color mode — which of `rgb` / `hsv` / `
|
|
58821
|
+
/** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
|
|
57723
58822
|
* the bulb's current state. */
|
|
57724
58823
|
mode: _enum([
|
|
57725
58824
|
"rgb",
|
|
@@ -57731,7 +58830,7 @@ object({
|
|
|
57731
58830
|
/** Populated when `mode === 'hsv'`. */
|
|
57732
58831
|
hsv: HsvTripletSchema.optional(),
|
|
57733
58832
|
/** Populated when `mode === 'mired'`. */
|
|
57734
|
-
|
|
58833
|
+
colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
57735
58834
|
/** Ms epoch of the last operator-driven change. */
|
|
57736
58835
|
lastChangedAt: number()
|
|
57737
58836
|
});
|
|
@@ -57750,7 +58849,7 @@ DeviceType.Light, method(object({
|
|
|
57750
58849
|
]),
|
|
57751
58850
|
rgb: RgbTripletSchema.optional(),
|
|
57752
58851
|
hsv: HsvTripletSchema.optional(),
|
|
57753
|
-
|
|
58852
|
+
colorTemperature: number().int().optional(),
|
|
57754
58853
|
lastChangedAt: number()
|
|
57755
58854
|
});
|
|
57756
58855
|
var ConnectionTestOutcomeSchema = discriminatedUnion("outcome", [
|
|
@@ -58176,11 +59275,11 @@ object({
|
|
|
58176
59275
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
58177
59276
|
availablePrograms: array(string()),
|
|
58178
59277
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
58179
|
-
|
|
59278
|
+
remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58180
59279
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
58181
|
-
|
|
59280
|
+
delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58182
59281
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
58183
|
-
|
|
59282
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58184
59283
|
/** True when the door is reported open; null when the door state is not known. */
|
|
58185
59284
|
doorOpen: boolean().nullable(),
|
|
58186
59285
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -58225,7 +59324,7 @@ object({
|
|
|
58225
59324
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
58226
59325
|
lastFetchedAt: number()
|
|
58227
59326
|
});
|
|
58228
|
-
var DeviceIdSchema = number().int().nonnegative();
|
|
59327
|
+
var DeviceIdSchema$1 = number().int().nonnegative();
|
|
58229
59328
|
/** A value a program option or setting can take. */
|
|
58230
59329
|
var DishwasherValueSchema = union([
|
|
58231
59330
|
boolean(),
|
|
@@ -58289,77 +59388,77 @@ var DishwasherOptionsSchema = object({
|
|
|
58289
59388
|
options: array(DishwasherOptionDescriptorSchema)
|
|
58290
59389
|
});
|
|
58291
59390
|
DeviceType.Dishwasher, method(object({
|
|
58292
|
-
deviceId: DeviceIdSchema,
|
|
59391
|
+
deviceId: DeviceIdSchema$1,
|
|
58293
59392
|
program: string().min(1).optional(),
|
|
58294
59393
|
options: record(string(), DishwasherValueSchema).optional()
|
|
58295
59394
|
}), _void(), {
|
|
58296
59395
|
kind: "mutation",
|
|
58297
59396
|
auth: "protected"
|
|
58298
|
-
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
59397
|
+
}), method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
58299
59398
|
kind: "mutation",
|
|
58300
59399
|
auth: "protected"
|
|
58301
|
-
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
59400
|
+
}), method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
58302
59401
|
kind: "mutation",
|
|
58303
59402
|
auth: "protected"
|
|
58304
|
-
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
59403
|
+
}), method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
58305
59404
|
kind: "mutation",
|
|
58306
59405
|
auth: "protected"
|
|
58307
59406
|
}), method(object({
|
|
58308
|
-
deviceId: DeviceIdSchema,
|
|
59407
|
+
deviceId: DeviceIdSchema$1,
|
|
58309
59408
|
program: string().min(1)
|
|
58310
59409
|
}), _void(), {
|
|
58311
59410
|
kind: "mutation",
|
|
58312
59411
|
auth: "protected"
|
|
58313
59412
|
}), method(object({
|
|
58314
|
-
deviceId: DeviceIdSchema,
|
|
59413
|
+
deviceId: DeviceIdSchema$1,
|
|
58315
59414
|
on: boolean()
|
|
58316
59415
|
}), _void(), {
|
|
58317
59416
|
kind: "mutation",
|
|
58318
59417
|
auth: "protected"
|
|
58319
59418
|
}), method(object({
|
|
58320
|
-
deviceId: DeviceIdSchema,
|
|
59419
|
+
deviceId: DeviceIdSchema$1,
|
|
58321
59420
|
on: boolean()
|
|
58322
59421
|
}), _void(), {
|
|
58323
59422
|
kind: "mutation",
|
|
58324
59423
|
auth: "protected"
|
|
58325
59424
|
}), method(object({
|
|
58326
|
-
deviceId: DeviceIdSchema,
|
|
59425
|
+
deviceId: DeviceIdSchema$1,
|
|
58327
59426
|
on: boolean()
|
|
58328
59427
|
}), _void(), {
|
|
58329
59428
|
kind: "mutation",
|
|
58330
59429
|
auth: "protected"
|
|
58331
59430
|
}), method(object({
|
|
58332
|
-
deviceId: DeviceIdSchema,
|
|
59431
|
+
deviceId: DeviceIdSchema$1,
|
|
58333
59432
|
on: boolean()
|
|
58334
59433
|
}), _void(), {
|
|
58335
59434
|
kind: "mutation",
|
|
58336
59435
|
auth: "protected"
|
|
58337
59436
|
}), method(object({
|
|
58338
|
-
deviceId: DeviceIdSchema,
|
|
59437
|
+
deviceId: DeviceIdSchema$1,
|
|
58339
59438
|
on: boolean()
|
|
58340
59439
|
}), _void(), {
|
|
58341
59440
|
kind: "mutation",
|
|
58342
59441
|
auth: "protected"
|
|
58343
59442
|
}), method(object({
|
|
58344
|
-
deviceId: DeviceIdSchema,
|
|
59443
|
+
deviceId: DeviceIdSchema$1,
|
|
58345
59444
|
on: boolean()
|
|
58346
59445
|
}), _void(), {
|
|
58347
59446
|
kind: "mutation",
|
|
58348
59447
|
auth: "protected"
|
|
58349
59448
|
}), method(object({
|
|
58350
|
-
deviceId: DeviceIdSchema,
|
|
59449
|
+
deviceId: DeviceIdSchema$1,
|
|
58351
59450
|
on: boolean()
|
|
58352
59451
|
}), _void(), {
|
|
58353
59452
|
kind: "mutation",
|
|
58354
59453
|
auth: "protected"
|
|
58355
59454
|
}), method(object({
|
|
58356
|
-
deviceId: DeviceIdSchema,
|
|
59455
|
+
deviceId: DeviceIdSchema$1,
|
|
58357
59456
|
key: string().min(1),
|
|
58358
59457
|
value: DishwasherValueSchema
|
|
58359
59458
|
}), _void(), {
|
|
58360
59459
|
kind: "mutation",
|
|
58361
59460
|
auth: "protected"
|
|
58362
|
-
}), method(object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" });
|
|
59461
|
+
}), method(object({ deviceId: DeviceIdSchema$1 }), DishwasherOptionsSchema, { auth: "protected" });
|
|
58363
59462
|
DeviceType.Camera;
|
|
58364
59463
|
object({
|
|
58365
59464
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
@@ -58798,7 +59897,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
58798
59897
|
object({
|
|
58799
59898
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
58800
59899
|
* speed surface (single-speed fan). */
|
|
58801
|
-
|
|
59900
|
+
speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58802
59901
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
58803
59902
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
58804
59903
|
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
@@ -58818,7 +59917,7 @@ object({
|
|
|
58818
59917
|
});
|
|
58819
59918
|
DeviceType.Fan, method(object({
|
|
58820
59919
|
deviceId: number().int().nonnegative(),
|
|
58821
|
-
|
|
59920
|
+
speed: number().min(0).max(100)
|
|
58822
59921
|
}), _void(), {
|
|
58823
59922
|
kind: "mutation",
|
|
58824
59923
|
auth: "protected"
|
|
@@ -59001,7 +60100,7 @@ DeviceType.Humidifier, method(object({
|
|
|
59001
60100
|
});
|
|
59002
60101
|
object({
|
|
59003
60102
|
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
59004
|
-
|
|
60103
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
59005
60104
|
precision: 0,
|
|
59006
60105
|
label: "Humidity"
|
|
59007
60106
|
})),
|
|
@@ -59444,7 +60543,7 @@ object({
|
|
|
59444
60543
|
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59445
60544
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
59446
60545
|
* task is active / progress is unavailable. */
|
|
59447
|
-
|
|
60546
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59448
60547
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
59449
60548
|
* or null when unknown. */
|
|
59450
60549
|
currentCode: number().nullable().meta(readingMeta("none")),
|
|
@@ -59916,9 +61015,35 @@ var MediaInfoSchema = object({
|
|
|
59916
61015
|
/** Optional cover-art / thumbnail URL. */
|
|
59917
61016
|
imageUrl: string().optional()
|
|
59918
61017
|
});
|
|
61018
|
+
/**
|
|
61019
|
+
* Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
|
|
61020
|
+
* the device said so. `assumed`: the driver has no feedback (infrared) and
|
|
61021
|
+
* tracks what it last SENT; it moves only after a send was accepted, and a
|
|
61022
|
+
* human at the real remote makes it wrong. A UI shows an assumed value as a
|
|
61023
|
+
* guess, never as a reading.
|
|
61024
|
+
*/
|
|
61025
|
+
var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
|
|
61026
|
+
/** The fields of a media-player status a driver can only guess. */
|
|
61027
|
+
var MediaPlayerAssumedFieldSchema = _enum([
|
|
61028
|
+
"state",
|
|
61029
|
+
"volumeLevel",
|
|
61030
|
+
"isMuted",
|
|
61031
|
+
"source"
|
|
61032
|
+
]);
|
|
59919
61033
|
object({
|
|
59920
61034
|
/** Playback lifecycle state. */
|
|
59921
61035
|
state: MediaPlayerStateSchema,
|
|
61036
|
+
/**
|
|
61037
|
+
* Whether the state fields are readings or the driver's own bookkeeping.
|
|
61038
|
+
* A slice written before this field existed parses as `reported`.
|
|
61039
|
+
*/
|
|
61040
|
+
stateSource: MediaPlayerStateSourceSchema.default("reported"),
|
|
61041
|
+
/**
|
|
61042
|
+
* Which fields are the driver's own guess. `stateSource` is `assumed` when
|
|
61043
|
+
* this is not empty; a field not listed here is a reading even then (power
|
|
61044
|
+
* from a running-state source, mute guessed from the last send).
|
|
61045
|
+
*/
|
|
61046
|
+
assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
|
|
59922
61047
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
59923
61048
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
59924
61049
|
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
@@ -59934,10 +61059,10 @@ object({
|
|
|
59934
61059
|
currentMedia: MediaInfoSchema.nullable(),
|
|
59935
61060
|
/** Current playback position in ms. Null when not seekable or
|
|
59936
61061
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
59937
|
-
|
|
61062
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
59938
61063
|
/** Total duration of the current media in ms. Null when unknown
|
|
59939
61064
|
* (live stream) or nothing is playing. */
|
|
59940
|
-
|
|
61065
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
59941
61066
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
59942
61067
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
59943
61068
|
shuffle: boolean().nullable(),
|
|
@@ -59964,7 +61089,7 @@ DeviceType.MediaPlayer, method(object({ deviceId: number().int().nonnegative() }
|
|
|
59964
61089
|
auth: "protected"
|
|
59965
61090
|
}), method(object({
|
|
59966
61091
|
deviceId: number().int().nonnegative(),
|
|
59967
|
-
|
|
61092
|
+
position: number().int().nonnegative()
|
|
59968
61093
|
}), _void(), {
|
|
59969
61094
|
kind: "mutation",
|
|
59970
61095
|
auth: "protected"
|
|
@@ -60637,9 +61762,9 @@ var NetworkLinkStatusSchema = object({
|
|
|
60637
61762
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
60638
61763
|
* SKIP a null rather than coerce it.
|
|
60639
61764
|
*/
|
|
60640
|
-
|
|
61765
|
+
signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60641
61766
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
60642
|
-
|
|
61767
|
+
rssi: number().optional().meta(readingMeta("signal-level")),
|
|
60643
61768
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
60644
61769
|
ssid: string().optional(),
|
|
60645
61770
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
@@ -60771,7 +61896,8 @@ var TopologyNodeSchema = object({
|
|
|
60771
61896
|
isHub: boolean(),
|
|
60772
61897
|
isOnline: boolean(),
|
|
60773
61898
|
cpuPercent: number(),
|
|
60774
|
-
|
|
61899
|
+
/** `null` when the node's memory read failed or it has not reported one (D393). */
|
|
61900
|
+
memoryPercent: number().nullable(),
|
|
60775
61901
|
uptime: number(),
|
|
60776
61902
|
lastSeen: string(),
|
|
60777
61903
|
localIps: array(string()).readonly(),
|
|
@@ -61300,10 +62426,21 @@ var PetFeederDeviceStatusSchema = _enum([
|
|
|
61300
62426
|
]);
|
|
61301
62427
|
var gramsPortion = number().int().min(4).max(200);
|
|
61302
62428
|
object({
|
|
61303
|
-
/** Food
|
|
61304
|
-
*
|
|
61305
|
-
* bowl
|
|
62429
|
+
/** Food in the TANK (hopper), as the feeder's own sensor reports it
|
|
62430
|
+
* (`state.food`). A coarse level code, not grams — `0` is empty. The
|
|
62431
|
+
* amount in the bowl is `bowlFood`. On dual-hopper models this is the
|
|
62432
|
+
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
62433
|
+
* the device has not reported yet. */
|
|
61306
62434
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
62435
|
+
/** Food in the BOWL, a mass (stored in kilograms, D944). Reported only by a
|
|
62436
|
+
* feeder with a bowl scale; null when the model has none or its reading is
|
|
62437
|
+
* of an unproven unit. */
|
|
62438
|
+
bowlFood: number().nullable().meta(readingMeta("mass")),
|
|
62439
|
+
/** True while the pet is eating (`state.eating`). */
|
|
62440
|
+
eating: boolean(),
|
|
62441
|
+
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
62442
|
+
* the client must not offer it. */
|
|
62443
|
+
supportsMarkRefilled: boolean(),
|
|
61307
62444
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
61308
62445
|
* single-hopper models. */
|
|
61309
62446
|
food1: number().nullable().meta(readingMeta("none")),
|
|
@@ -61311,12 +62448,17 @@ object({
|
|
|
61311
62448
|
* single-hopper models. */
|
|
61312
62449
|
food2: number().nullable().meta(readingMeta("none")),
|
|
61313
62450
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
61314
|
-
* (`device_class: problem`, on = low). True when the
|
|
61315
|
-
*
|
|
62451
|
+
* (`device_class: problem`, on = low). True when the tank reports empty,
|
|
62452
|
+
* or, on a dual-hopper model, when EITHER hopper is empty. */
|
|
61316
62453
|
lowFood: boolean(),
|
|
61317
|
-
/** Battery charge 0..100 (%).
|
|
61318
|
-
*
|
|
62454
|
+
/** Battery charge 0..100 (%). On a feeder running on `mains` this is the
|
|
62455
|
+
* BACKUP battery's charge, not the power source. Null when the device has
|
|
62456
|
+
* no battery reading. */
|
|
61319
62457
|
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62458
|
+
/** What the feeder is powered from right now: `mains` (plugged in, the
|
|
62459
|
+
* battery is only a backup) or `battery`. Null when the device does not
|
|
62460
|
+
* report it. */
|
|
62461
|
+
powerSource: _enum(["mains", "battery"]).nullable(),
|
|
61320
62462
|
/**
|
|
61321
62463
|
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
61322
62464
|
* Providers convert from the days the feeder reports. Null when the
|
|
@@ -61344,6 +62486,14 @@ object({
|
|
|
61344
62486
|
feedSound: boolean(),
|
|
61345
62487
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
61346
62488
|
volume: number().meta(readingMeta("none")),
|
|
62489
|
+
/** Portion (grams) of the last manual feed sent from a single-hopper
|
|
62490
|
+
* stepper — the default the next manual feed starts from. Persisted per
|
|
62491
|
+
* device by the provider; null until a manual feed has been sent. */
|
|
62492
|
+
lastFeedGrams: gramsPortion.nullable(),
|
|
62493
|
+
/** Hopper-1 portion of the last manual feed (dual-hopper); null until sent. */
|
|
62494
|
+
lastFeedHopper1: gramsPortion.nullable(),
|
|
62495
|
+
/** Hopper-2 portion of the last manual feed (dual-hopper); null until sent. */
|
|
62496
|
+
lastFeedHopper2: gramsPortion.nullable(),
|
|
61347
62497
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
61348
62498
|
lastFetchedAt: number()
|
|
61349
62499
|
});
|
|
@@ -61398,6 +62548,64 @@ DeviceType.PetFeeder, method(object({
|
|
|
61398
62548
|
kind: "mutation",
|
|
61399
62549
|
auth: "protected"
|
|
61400
62550
|
});
|
|
62551
|
+
object({
|
|
62552
|
+
/** Water in the tank, 0..100 (%). Null while the source has not reported. */
|
|
62553
|
+
waterLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62554
|
+
/** Water in the tank, in litres. Null while the source has not reported. */
|
|
62555
|
+
waterVolume: number().min(0).nullable().meta(readingMeta("volume")),
|
|
62556
|
+
/** True when the tank is empty. Null while unknown. */
|
|
62557
|
+
empty: boolean().nullable(),
|
|
62558
|
+
/** True while the pump is running. Null while unknown. */
|
|
62559
|
+
pumpRunning: boolean().nullable(),
|
|
62560
|
+
/** True when the pump works on an on/off duty cycle rather than continuously. */
|
|
62561
|
+
pumpIntervalEnabled: boolean().nullable(),
|
|
62562
|
+
/** How long the pump runs per cycle, in seconds. */
|
|
62563
|
+
pumpOnTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
62564
|
+
/** How long the pump rests per cycle, in seconds. */
|
|
62565
|
+
pumpOffTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
62566
|
+
/** Water drunk in the last drink, in litres. */
|
|
62567
|
+
lastDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
62568
|
+
/** Water drunk in the current hour, in litres. */
|
|
62569
|
+
currentHourDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
62570
|
+
/** Water drunk over the last 24 hours (rolling), in litres. */
|
|
62571
|
+
last24hDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
62572
|
+
/** Water drunk since local midnight, in litres. */
|
|
62573
|
+
todayDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
62574
|
+
/** Decoded fault message. Null means healthy; a non-null string is an active fault. */
|
|
62575
|
+
error: string().nullable(),
|
|
62576
|
+
/** Ms epoch when the slice was last refreshed. */
|
|
62577
|
+
lastFetchedAt: number()
|
|
62578
|
+
});
|
|
62579
|
+
var DeviceIdSchema = number().int().nonnegative();
|
|
62580
|
+
var MinutesSchema = number().int().min(0);
|
|
62581
|
+
DeviceType.PetFountain, method(object({
|
|
62582
|
+
deviceId: DeviceIdSchema,
|
|
62583
|
+
on: boolean()
|
|
62584
|
+
}), _void(), {
|
|
62585
|
+
kind: "mutation",
|
|
62586
|
+
auth: "protected"
|
|
62587
|
+
}), method(object({
|
|
62588
|
+
deviceId: DeviceIdSchema,
|
|
62589
|
+
on: boolean()
|
|
62590
|
+
}), _void(), {
|
|
62591
|
+
kind: "mutation",
|
|
62592
|
+
auth: "protected"
|
|
62593
|
+
}), method(object({
|
|
62594
|
+
deviceId: DeviceIdSchema,
|
|
62595
|
+
minutes: MinutesSchema
|
|
62596
|
+
}), _void(), {
|
|
62597
|
+
kind: "mutation",
|
|
62598
|
+
auth: "protected"
|
|
62599
|
+
}), method(object({
|
|
62600
|
+
deviceId: DeviceIdSchema,
|
|
62601
|
+
minutes: MinutesSchema
|
|
62602
|
+
}), _void(), {
|
|
62603
|
+
kind: "mutation",
|
|
62604
|
+
auth: "protected"
|
|
62605
|
+
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
62606
|
+
kind: "mutation",
|
|
62607
|
+
auth: "protected"
|
|
62608
|
+
});
|
|
61401
62609
|
var VehicleSchema = object({
|
|
61402
62610
|
id: string(),
|
|
61403
62611
|
name: string(),
|
|
@@ -61779,16 +62987,16 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
61779
62987
|
auth: "admin"
|
|
61780
62988
|
});
|
|
61781
62989
|
object({
|
|
61782
|
-
/** Instantaneous power draw
|
|
61783
|
-
|
|
62990
|
+
/** Instantaneous power draw (watts). */
|
|
62991
|
+
power: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
61784
62992
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
61785
|
-
|
|
61786
|
-
/** Voltage
|
|
61787
|
-
|
|
62993
|
+
energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
62994
|
+
/** Voltage (volts). */
|
|
62995
|
+
voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
61788
62996
|
/** Current in amperes. */
|
|
61789
|
-
|
|
62997
|
+
current: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
61790
62998
|
/** Apparent power in volt-amperes. */
|
|
61791
|
-
|
|
62999
|
+
apparentPower: number().optional().meta(readingMeta("apparent-power")),
|
|
61792
63000
|
/** Power factor (0..1) if reported. */
|
|
61793
63001
|
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
61794
63002
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -61843,16 +63051,13 @@ object({
|
|
|
61843
63051
|
location: string().nullable(),
|
|
61844
63052
|
/** GPS coordinates when available. Null otherwise. */
|
|
61845
63053
|
gps: GpsLocationSchema.nullable(),
|
|
61846
|
-
/** Optional battery level of the tracking device (0..100). Null
|
|
61847
|
-
* when not reported. */
|
|
61848
|
-
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61849
63054
|
/** Ms epoch when the slice was last updated. */
|
|
61850
63055
|
lastChangedAt: number()
|
|
61851
63056
|
});
|
|
61852
63057
|
DeviceType.Presence;
|
|
61853
63058
|
object({
|
|
61854
63059
|
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
61855
|
-
|
|
63060
|
+
pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
61856
63061
|
/** Ms epoch when the slice was last updated. */
|
|
61857
63062
|
lastFetchedAt: number()
|
|
61858
63063
|
});
|
|
@@ -64855,7 +66060,7 @@ object({
|
|
|
64855
66060
|
DeviceType.Sensor;
|
|
64856
66061
|
object({
|
|
64857
66062
|
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
64858
|
-
|
|
66063
|
+
temperature: number().nullable().meta(readingMeta("temperature", {
|
|
64859
66064
|
precision: 1,
|
|
64860
66065
|
label: "Temperature"
|
|
64861
66066
|
})),
|
|
@@ -65119,7 +66324,7 @@ object({
|
|
|
65119
66324
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
65120
66325
|
availableFanSpeeds: array(string()),
|
|
65121
66326
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
65122
|
-
|
|
66327
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
65123
66328
|
/** Current error code (0 / null = no error). */
|
|
65124
66329
|
errorCode: number().nullable().meta(readingMeta("none")),
|
|
65125
66330
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
@@ -65988,9 +67193,17 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
65988
67193
|
kind: "mutation",
|
|
65989
67194
|
auth: "admin"
|
|
65990
67195
|
});
|
|
67196
|
+
discriminatedUnion("kind", [object({
|
|
67197
|
+
kind: literal("raw"),
|
|
67198
|
+
timings: array(number().int()),
|
|
67199
|
+
settings: TransmitSettingsSchema.optional()
|
|
67200
|
+
}), object({
|
|
67201
|
+
kind: literal("pronto"),
|
|
67202
|
+
code: string()
|
|
67203
|
+
})]);
|
|
65991
67204
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
65992
67205
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
65993
|
-
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;
|
|
67206
|
+
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.PetFountain, 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.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
65994
67207
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
65995
67208
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
65996
67209
|
new Set(CAP_NAMES);
|
|
@@ -66043,7 +67256,7 @@ var AccessoryKind = {
|
|
|
66043
67256
|
PrivacyMask: DeviceRole.PrivacyMask
|
|
66044
67257
|
};
|
|
66045
67258
|
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
66046
|
-
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, 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;
|
|
67259
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
|
|
66047
67260
|
new Set(Object.values(DeviceType));
|
|
66048
67261
|
Object.freeze({
|
|
66049
67262
|
"accessories.getStatus": {
|
|
@@ -66060,6 +67273,104 @@ Object.freeze({
|
|
|
66060
67273
|
access: "create",
|
|
66061
67274
|
role: "admin"
|
|
66062
67275
|
},
|
|
67276
|
+
"actions.create": {
|
|
67277
|
+
capName: "actions",
|
|
67278
|
+
capScope: "system",
|
|
67279
|
+
addonId: null,
|
|
67280
|
+
access: "create",
|
|
67281
|
+
role: "admin"
|
|
67282
|
+
},
|
|
67283
|
+
"actions.delete": {
|
|
67284
|
+
capName: "actions",
|
|
67285
|
+
capScope: "system",
|
|
67286
|
+
addonId: null,
|
|
67287
|
+
access: "delete",
|
|
67288
|
+
role: "admin"
|
|
67289
|
+
},
|
|
67290
|
+
"actions.get": {
|
|
67291
|
+
capName: "actions",
|
|
67292
|
+
capScope: "system",
|
|
67293
|
+
addonId: null,
|
|
67294
|
+
access: "view",
|
|
67295
|
+
role: "viewer"
|
|
67296
|
+
},
|
|
67297
|
+
"actions.getLinks": {
|
|
67298
|
+
capName: "actions",
|
|
67299
|
+
capScope: "system",
|
|
67300
|
+
addonId: null,
|
|
67301
|
+
access: "view",
|
|
67302
|
+
role: "admin"
|
|
67303
|
+
},
|
|
67304
|
+
"actions.getTraces": {
|
|
67305
|
+
capName: "actions",
|
|
67306
|
+
capScope: "system",
|
|
67307
|
+
addonId: null,
|
|
67308
|
+
access: "view",
|
|
67309
|
+
role: "admin"
|
|
67310
|
+
},
|
|
67311
|
+
"actions.list": {
|
|
67312
|
+
capName: "actions",
|
|
67313
|
+
capScope: "system",
|
|
67314
|
+
addonId: null,
|
|
67315
|
+
access: "view",
|
|
67316
|
+
role: "admin"
|
|
67317
|
+
},
|
|
67318
|
+
"actions.listForCamera": {
|
|
67319
|
+
capName: "actions",
|
|
67320
|
+
capScope: "system",
|
|
67321
|
+
addonId: null,
|
|
67322
|
+
access: "view",
|
|
67323
|
+
role: "viewer"
|
|
67324
|
+
},
|
|
67325
|
+
"actions.run": {
|
|
67326
|
+
capName: "actions",
|
|
67327
|
+
capScope: "system",
|
|
67328
|
+
addonId: null,
|
|
67329
|
+
access: "create",
|
|
67330
|
+
role: "viewer"
|
|
67331
|
+
},
|
|
67332
|
+
"actions.setLinks": {
|
|
67333
|
+
capName: "actions",
|
|
67334
|
+
capScope: "system",
|
|
67335
|
+
addonId: null,
|
|
67336
|
+
access: "create",
|
|
67337
|
+
role: "admin"
|
|
67338
|
+
},
|
|
67339
|
+
"actions.stop": {
|
|
67340
|
+
capName: "actions",
|
|
67341
|
+
capScope: "system",
|
|
67342
|
+
addonId: null,
|
|
67343
|
+
access: "create",
|
|
67344
|
+
role: "admin"
|
|
67345
|
+
},
|
|
67346
|
+
"actions.update": {
|
|
67347
|
+
capName: "actions",
|
|
67348
|
+
capScope: "system",
|
|
67349
|
+
addonId: null,
|
|
67350
|
+
access: "create",
|
|
67351
|
+
role: "admin"
|
|
67352
|
+
},
|
|
67353
|
+
"actionSource.listActions": {
|
|
67354
|
+
capName: "action-source",
|
|
67355
|
+
capScope: "system",
|
|
67356
|
+
addonId: null,
|
|
67357
|
+
access: "view",
|
|
67358
|
+
role: "admin"
|
|
67359
|
+
},
|
|
67360
|
+
"actionSource.runAction": {
|
|
67361
|
+
capName: "action-source",
|
|
67362
|
+
capScope: "system",
|
|
67363
|
+
addonId: null,
|
|
67364
|
+
access: "create",
|
|
67365
|
+
role: "admin"
|
|
67366
|
+
},
|
|
67367
|
+
"actionSource.stopAction": {
|
|
67368
|
+
capName: "action-source",
|
|
67369
|
+
capScope: "system",
|
|
67370
|
+
addonId: null,
|
|
67371
|
+
access: "create",
|
|
67372
|
+
role: "admin"
|
|
67373
|
+
},
|
|
66063
67374
|
"addonPages.listPages": {
|
|
66064
67375
|
capName: "addon-pages",
|
|
66065
67376
|
capScope: "system",
|
|
@@ -67341,6 +68652,13 @@ Object.freeze({
|
|
|
67341
68652
|
access: "view",
|
|
67342
68653
|
role: "admin"
|
|
67343
68654
|
},
|
|
68655
|
+
"coreBlocks.getAutomationRunState": {
|
|
68656
|
+
capName: "core-blocks",
|
|
68657
|
+
capScope: "system",
|
|
68658
|
+
addonId: null,
|
|
68659
|
+
access: "view",
|
|
68660
|
+
role: "admin"
|
|
68661
|
+
},
|
|
67344
68662
|
"coreBlocks.getBlockSettings": {
|
|
67345
68663
|
capName: "core-blocks",
|
|
67346
68664
|
capScope: "system",
|
|
@@ -67362,6 +68680,13 @@ Object.freeze({
|
|
|
67362
68680
|
access: "view",
|
|
67363
68681
|
role: "admin"
|
|
67364
68682
|
},
|
|
68683
|
+
"coreBlocks.getTraces": {
|
|
68684
|
+
capName: "core-blocks",
|
|
68685
|
+
capScope: "system",
|
|
68686
|
+
addonId: null,
|
|
68687
|
+
access: "view",
|
|
68688
|
+
role: "admin"
|
|
68689
|
+
},
|
|
67365
68690
|
"coreBlocks.getTypeDefs": {
|
|
67366
68691
|
capName: "core-blocks",
|
|
67367
68692
|
capScope: "system",
|
|
@@ -67397,6 +68722,13 @@ Object.freeze({
|
|
|
67397
68722
|
access: "create",
|
|
67398
68723
|
role: "admin"
|
|
67399
68724
|
},
|
|
68725
|
+
"coreBlocks.reportAutomationRun": {
|
|
68726
|
+
capName: "core-blocks",
|
|
68727
|
+
capScope: "system",
|
|
68728
|
+
addonId: null,
|
|
68729
|
+
access: "create",
|
|
68730
|
+
role: "admin"
|
|
68731
|
+
},
|
|
67400
68732
|
"coreBlocks.restart": {
|
|
67401
68733
|
capName: "core-blocks",
|
|
67402
68734
|
capScope: "system",
|
|
@@ -67859,6 +69191,13 @@ Object.freeze({
|
|
|
67859
69191
|
access: "create",
|
|
67860
69192
|
role: "admin"
|
|
67861
69193
|
},
|
|
69194
|
+
"deviceExport.exposeEntity": {
|
|
69195
|
+
capName: "device-export",
|
|
69196
|
+
capScope: "system",
|
|
69197
|
+
addonId: null,
|
|
69198
|
+
access: "create",
|
|
69199
|
+
role: "admin"
|
|
69200
|
+
},
|
|
67862
69201
|
"deviceExport.getDeviceLiveContribution": {
|
|
67863
69202
|
capName: "device-export",
|
|
67864
69203
|
capScope: "system",
|
|
@@ -67887,6 +69226,13 @@ Object.freeze({
|
|
|
67887
69226
|
access: "view",
|
|
67888
69227
|
role: "admin"
|
|
67889
69228
|
},
|
|
69229
|
+
"deviceExport.listExposedEntities": {
|
|
69230
|
+
capName: "device-export",
|
|
69231
|
+
capScope: "system",
|
|
69232
|
+
addonId: null,
|
|
69233
|
+
access: "view",
|
|
69234
|
+
role: "admin"
|
|
69235
|
+
},
|
|
67890
69236
|
"deviceExport.listSupportedDeviceKinds": {
|
|
67891
69237
|
capName: "device-export",
|
|
67892
69238
|
capScope: "system",
|
|
@@ -67894,6 +69240,13 @@ Object.freeze({
|
|
|
67894
69240
|
access: "view",
|
|
67895
69241
|
role: "admin"
|
|
67896
69242
|
},
|
|
69243
|
+
"deviceExport.listSupportedEntityKinds": {
|
|
69244
|
+
capName: "device-export",
|
|
69245
|
+
capScope: "system",
|
|
69246
|
+
addonId: null,
|
|
69247
|
+
access: "view",
|
|
69248
|
+
role: "admin"
|
|
69249
|
+
},
|
|
67897
69250
|
"deviceExport.unexposeDevice": {
|
|
67898
69251
|
capName: "device-export",
|
|
67899
69252
|
capScope: "system",
|
|
@@ -67901,6 +69254,13 @@ Object.freeze({
|
|
|
67901
69254
|
access: "create",
|
|
67902
69255
|
role: "admin"
|
|
67903
69256
|
},
|
|
69257
|
+
"deviceExport.unexposeEntity": {
|
|
69258
|
+
capName: "device-export",
|
|
69259
|
+
capScope: "system",
|
|
69260
|
+
addonId: null,
|
|
69261
|
+
access: "create",
|
|
69262
|
+
role: "admin"
|
|
69263
|
+
},
|
|
67904
69264
|
"deviceExtension.listCandidates": {
|
|
67905
69265
|
capName: "device-extension",
|
|
67906
69266
|
capScope: "device",
|
|
@@ -69476,6 +70836,20 @@ Object.freeze({
|
|
|
69476
70836
|
access: "create",
|
|
69477
70837
|
role: "viewer"
|
|
69478
70838
|
},
|
|
70839
|
+
"irTransmitter.getInfo": {
|
|
70840
|
+
capName: "ir-transmitter",
|
|
70841
|
+
capScope: "device",
|
|
70842
|
+
addonId: null,
|
|
70843
|
+
access: "view",
|
|
70844
|
+
role: "viewer"
|
|
70845
|
+
},
|
|
70846
|
+
"irTransmitter.send": {
|
|
70847
|
+
capName: "ir-transmitter",
|
|
70848
|
+
capScope: "device",
|
|
70849
|
+
addonId: null,
|
|
70850
|
+
access: "create",
|
|
70851
|
+
role: "viewer"
|
|
70852
|
+
},
|
|
69479
70853
|
"keychain.delete": {
|
|
69480
70854
|
capName: "keychain",
|
|
69481
70855
|
capScope: "system",
|
|
@@ -70708,6 +72082,13 @@ Object.freeze({
|
|
|
70708
72082
|
access: "create",
|
|
70709
72083
|
role: "admin"
|
|
70710
72084
|
},
|
|
72085
|
+
"nodeRemovalHook.onNodeRemoved": {
|
|
72086
|
+
capName: "node-removal-hook",
|
|
72087
|
+
capScope: "system",
|
|
72088
|
+
addonId: null,
|
|
72089
|
+
access: "create",
|
|
72090
|
+
role: "admin"
|
|
72091
|
+
},
|
|
70711
72092
|
"nodes.clusterAddonStatus": {
|
|
70712
72093
|
capName: "nodes",
|
|
70713
72094
|
capScope: "system",
|
|
@@ -71345,6 +72726,48 @@ Object.freeze({
|
|
|
71345
72726
|
access: "create",
|
|
71346
72727
|
role: "viewer"
|
|
71347
72728
|
},
|
|
72729
|
+
"petFountain.getStatus": {
|
|
72730
|
+
capName: "pet-fountain",
|
|
72731
|
+
capScope: "device",
|
|
72732
|
+
addonId: null,
|
|
72733
|
+
access: "view",
|
|
72734
|
+
role: "viewer"
|
|
72735
|
+
},
|
|
72736
|
+
"petFountain.resetDailyConsumption": {
|
|
72737
|
+
capName: "pet-fountain",
|
|
72738
|
+
capScope: "device",
|
|
72739
|
+
addonId: null,
|
|
72740
|
+
access: "delete",
|
|
72741
|
+
role: "viewer"
|
|
72742
|
+
},
|
|
72743
|
+
"petFountain.setPump": {
|
|
72744
|
+
capName: "pet-fountain",
|
|
72745
|
+
capScope: "device",
|
|
72746
|
+
addonId: null,
|
|
72747
|
+
access: "create",
|
|
72748
|
+
role: "viewer"
|
|
72749
|
+
},
|
|
72750
|
+
"petFountain.setPumpInterval": {
|
|
72751
|
+
capName: "pet-fountain",
|
|
72752
|
+
capScope: "device",
|
|
72753
|
+
addonId: null,
|
|
72754
|
+
access: "create",
|
|
72755
|
+
role: "viewer"
|
|
72756
|
+
},
|
|
72757
|
+
"petFountain.setPumpOffMinutes": {
|
|
72758
|
+
capName: "pet-fountain",
|
|
72759
|
+
capScope: "device",
|
|
72760
|
+
addonId: null,
|
|
72761
|
+
access: "create",
|
|
72762
|
+
role: "viewer"
|
|
72763
|
+
},
|
|
72764
|
+
"petFountain.setPumpOnMinutes": {
|
|
72765
|
+
capName: "pet-fountain",
|
|
72766
|
+
capScope: "device",
|
|
72767
|
+
addonId: null,
|
|
72768
|
+
access: "create",
|
|
72769
|
+
role: "viewer"
|
|
72770
|
+
},
|
|
71348
72771
|
"pipelineAnalytics.addRetrainFrameFromRecording": {
|
|
71349
72772
|
capName: "pipeline-analytics",
|
|
71350
72773
|
capScope: "device",
|
|
@@ -73599,6 +75022,41 @@ Object.freeze({
|
|
|
73599
75022
|
access: "create",
|
|
73600
75023
|
role: "admin"
|
|
73601
75024
|
},
|
|
75025
|
+
"remote.getConfig": {
|
|
75026
|
+
capName: "remote",
|
|
75027
|
+
capScope: "system",
|
|
75028
|
+
addonId: null,
|
|
75029
|
+
access: "view",
|
|
75030
|
+
role: "admin"
|
|
75031
|
+
},
|
|
75032
|
+
"remote.listCommands": {
|
|
75033
|
+
capName: "remote",
|
|
75034
|
+
capScope: "system",
|
|
75035
|
+
addonId: null,
|
|
75036
|
+
access: "view",
|
|
75037
|
+
role: "admin"
|
|
75038
|
+
},
|
|
75039
|
+
"remote.sendCommand": {
|
|
75040
|
+
capName: "remote",
|
|
75041
|
+
capScope: "system",
|
|
75042
|
+
addonId: null,
|
|
75043
|
+
access: "create",
|
|
75044
|
+
role: "admin"
|
|
75045
|
+
},
|
|
75046
|
+
"remote.sendMediaCommand": {
|
|
75047
|
+
capName: "remote",
|
|
75048
|
+
capScope: "system",
|
|
75049
|
+
addonId: null,
|
|
75050
|
+
access: "create",
|
|
75051
|
+
role: "admin"
|
|
75052
|
+
},
|
|
75053
|
+
"remote.setConfig": {
|
|
75054
|
+
capName: "remote",
|
|
75055
|
+
capScope: "system",
|
|
75056
|
+
addonId: null,
|
|
75057
|
+
access: "create",
|
|
75058
|
+
role: "admin"
|
|
75059
|
+
},
|
|
73602
75060
|
"runningState.getStatus": {
|
|
73603
75061
|
capName: "running-state",
|
|
73604
75062
|
capScope: "device",
|
|
@@ -73711,6 +75169,13 @@ Object.freeze({
|
|
|
73711
75169
|
access: "create",
|
|
73712
75170
|
role: "admin"
|
|
73713
75171
|
},
|
|
75172
|
+
"serverManagement.getLaunchOverrides": {
|
|
75173
|
+
capName: "server-management",
|
|
75174
|
+
capScope: "system",
|
|
75175
|
+
addonId: null,
|
|
75176
|
+
access: "view",
|
|
75177
|
+
role: "admin"
|
|
75178
|
+
},
|
|
73714
75179
|
"serverManagement.getServerPackageStatus": {
|
|
73715
75180
|
capName: "server-management",
|
|
73716
75181
|
capScope: "system",
|
|
@@ -73732,6 +75197,13 @@ Object.freeze({
|
|
|
73732
75197
|
access: "create",
|
|
73733
75198
|
role: "admin"
|
|
73734
75199
|
},
|
|
75200
|
+
"serverManagement.setLaunchOverrides": {
|
|
75201
|
+
capName: "server-management",
|
|
75202
|
+
capScope: "system",
|
|
75203
|
+
addonId: null,
|
|
75204
|
+
access: "create",
|
|
75205
|
+
role: "admin"
|
|
75206
|
+
},
|
|
73735
75207
|
"settingsStore.aggregate": {
|
|
73736
75208
|
capName: "settings-store",
|
|
73737
75209
|
capScope: "system",
|
|
@@ -74789,6 +76261,41 @@ Object.freeze({
|
|
|
74789
76261
|
access: "view",
|
|
74790
76262
|
role: "viewer"
|
|
74791
76263
|
},
|
|
76264
|
+
"template.create": {
|
|
76265
|
+
capName: "template",
|
|
76266
|
+
capScope: "system",
|
|
76267
|
+
addonId: null,
|
|
76268
|
+
access: "create",
|
|
76269
|
+
role: "admin"
|
|
76270
|
+
},
|
|
76271
|
+
"template.delete": {
|
|
76272
|
+
capName: "template",
|
|
76273
|
+
capScope: "system",
|
|
76274
|
+
addonId: null,
|
|
76275
|
+
access: "delete",
|
|
76276
|
+
role: "admin"
|
|
76277
|
+
},
|
|
76278
|
+
"template.get": {
|
|
76279
|
+
capName: "template",
|
|
76280
|
+
capScope: "system",
|
|
76281
|
+
addonId: null,
|
|
76282
|
+
access: "view",
|
|
76283
|
+
role: "admin"
|
|
76284
|
+
},
|
|
76285
|
+
"template.list": {
|
|
76286
|
+
capName: "template",
|
|
76287
|
+
capScope: "system",
|
|
76288
|
+
addonId: null,
|
|
76289
|
+
access: "view",
|
|
76290
|
+
role: "admin"
|
|
76291
|
+
},
|
|
76292
|
+
"template.update": {
|
|
76293
|
+
capName: "template",
|
|
76294
|
+
capScope: "system",
|
|
76295
|
+
addonId: null,
|
|
76296
|
+
access: "create",
|
|
76297
|
+
role: "admin"
|
|
76298
|
+
},
|
|
74792
76299
|
"terminalSession.adoptLegacyMonitor": {
|
|
74793
76300
|
capName: "terminal-session",
|
|
74794
76301
|
capScope: "system",
|
|
@@ -75970,7 +77477,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
75970
77477
|
label: t("Full brightness", "Luminosità massima"),
|
|
75971
77478
|
phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
|
|
75972
77479
|
icon: "sun",
|
|
75973
|
-
args: {
|
|
77480
|
+
args: { level: 100 }
|
|
75974
77481
|
},
|
|
75975
77482
|
{
|
|
75976
77483
|
id: "brightness.half",
|
|
@@ -75978,7 +77485,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
75978
77485
|
label: t("Half brightness", "Luminosità a metà"),
|
|
75979
77486
|
phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
|
|
75980
77487
|
icon: "sun-dim",
|
|
75981
|
-
args: {
|
|
77488
|
+
args: { level: 50 }
|
|
75982
77489
|
},
|
|
75983
77490
|
{
|
|
75984
77491
|
id: "brightness.off",
|
|
@@ -75986,16 +77493,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
75986
77493
|
label: t("Brightness off", "Luminosità a zero"),
|
|
75987
77494
|
phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
|
|
75988
77495
|
icon: "lightbulb-off",
|
|
75989
|
-
args: {
|
|
77496
|
+
args: { level: 0 }
|
|
75990
77497
|
},
|
|
75991
77498
|
{
|
|
75992
77499
|
id: "brightness.set",
|
|
75993
77500
|
method: "setBrightness",
|
|
75994
77501
|
label: t("Set brightness", "Imposta la luminosità"),
|
|
75995
|
-
phrase: t("Set the brightness of {device} to {
|
|
77502
|
+
phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
|
|
75996
77503
|
icon: "sun-medium",
|
|
75997
77504
|
openArgs: [{
|
|
75998
|
-
key: "
|
|
77505
|
+
key: "level",
|
|
75999
77506
|
label: PERCENT,
|
|
76000
77507
|
offer: {
|
|
76001
77508
|
kind: "number",
|
|
@@ -76009,13 +77516,13 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
76009
77516
|
],
|
|
76010
77517
|
state: [{
|
|
76011
77518
|
kind: "numeric",
|
|
76012
|
-
field: "
|
|
77519
|
+
field: "level",
|
|
76013
77520
|
quantity: t("the brightness", "la luminosità"),
|
|
76014
77521
|
label: t("Brightness", "Luminosità"),
|
|
76015
77522
|
unit: "%",
|
|
76016
77523
|
icon: "sun"
|
|
76017
77524
|
}],
|
|
76018
|
-
events: { onBrightnessChanged: { notOperatorFacing: "the `
|
|
77525
|
+
events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
|
|
76019
77526
|
},
|
|
76020
77527
|
color: {
|
|
76021
77528
|
scope: "device",
|
|
@@ -76029,7 +77536,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
76029
77536
|
icon: "sun",
|
|
76030
77537
|
args: { color: {
|
|
76031
77538
|
mode: "mired",
|
|
76032
|
-
|
|
77539
|
+
colorTemperature: 370
|
|
76033
77540
|
} }
|
|
76034
77541
|
},
|
|
76035
77542
|
{
|
|
@@ -76040,7 +77547,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
76040
77547
|
icon: "snowflake",
|
|
76041
77548
|
args: { color: {
|
|
76042
77549
|
mode: "mired",
|
|
76043
|
-
|
|
77550
|
+
colorTemperature: 200
|
|
76044
77551
|
} }
|
|
76045
77552
|
},
|
|
76046
77553
|
{
|
|
@@ -76654,7 +78161,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
76654
78161
|
label: t("Fan off", "Ventilatore spento"),
|
|
76655
78162
|
phrase: t("Stop {device}", "Ferma {device}"),
|
|
76656
78163
|
icon: "power-off",
|
|
76657
|
-
args: {
|
|
78164
|
+
args: { speed: 0 }
|
|
76658
78165
|
},
|
|
76659
78166
|
{
|
|
76660
78167
|
id: "fan.full",
|
|
@@ -76662,16 +78169,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
76662
78169
|
label: t("Fan full", "Ventilatore al massimo"),
|
|
76663
78170
|
phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
|
|
76664
78171
|
icon: "fan",
|
|
76665
|
-
args: {
|
|
78172
|
+
args: { speed: 100 }
|
|
76666
78173
|
},
|
|
76667
78174
|
{
|
|
76668
78175
|
id: "fan.speed",
|
|
76669
78176
|
method: "setPercentage",
|
|
76670
78177
|
label: t("Set speed", "Imposta la velocità"),
|
|
76671
|
-
phrase: t("Set the speed of {device} to {
|
|
78178
|
+
phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
|
|
76672
78179
|
icon: "fan",
|
|
76673
78180
|
openArgs: [{
|
|
76674
|
-
key: "
|
|
78181
|
+
key: "speed",
|
|
76675
78182
|
label: t("Speed", "Velocità"),
|
|
76676
78183
|
offer: {
|
|
76677
78184
|
kind: "number",
|
|
@@ -76733,7 +78240,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
76733
78240
|
],
|
|
76734
78241
|
state: [{
|
|
76735
78242
|
kind: "numeric",
|
|
76736
|
-
field: "
|
|
78243
|
+
field: "speed",
|
|
76737
78244
|
quantity: t("the speed", "la velocità"),
|
|
76738
78245
|
label: t("Speed", "Velocità"),
|
|
76739
78246
|
unit: "%",
|
|
@@ -77099,6 +78606,11 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
77099
78606
|
icon: "circle-dot"
|
|
77100
78607
|
}]
|
|
77101
78608
|
},
|
|
78609
|
+
"ir-transmitter": {
|
|
78610
|
+
scope: "device",
|
|
78611
|
+
noun: t("IR / RF transmitter", "Trasmettitore IR / RF"),
|
|
78612
|
+
notOperatorFacing: { send: "a raw pulse train is not something an operator composes in an automation; the remote that owns the command table sends it" }
|
|
78613
|
+
},
|
|
77102
78614
|
control: {
|
|
77103
78615
|
scope: "device",
|
|
77104
78616
|
noun: t("Control", "Controllo"),
|
|
@@ -77531,7 +79043,7 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
77531
79043
|
},
|
|
77532
79044
|
{
|
|
77533
79045
|
kind: "numeric",
|
|
77534
|
-
field: "
|
|
79046
|
+
field: "progress",
|
|
77535
79047
|
quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
|
|
77536
79048
|
label: t("Progress", "Avanzamento"),
|
|
77537
79049
|
unit: "%",
|
|
@@ -77752,6 +79264,100 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
77752
79264
|
}
|
|
77753
79265
|
]
|
|
77754
79266
|
},
|
|
79267
|
+
"pet-fountain": {
|
|
79268
|
+
scope: "device",
|
|
79269
|
+
noun: t("Pet fountain", "Fontana per animali"),
|
|
79270
|
+
actions: [
|
|
79271
|
+
{
|
|
79272
|
+
id: "petFountain.pumpOn",
|
|
79273
|
+
method: "setPump",
|
|
79274
|
+
label: t("Pump on", "Pompa accesa"),
|
|
79275
|
+
phrase: t("Start the pump of {device}", "Avvia la pompa di {device}"),
|
|
79276
|
+
icon: "play",
|
|
79277
|
+
args: { on: true }
|
|
79278
|
+
},
|
|
79279
|
+
{
|
|
79280
|
+
id: "petFountain.pumpOff",
|
|
79281
|
+
method: "setPump",
|
|
79282
|
+
label: t("Pump off", "Pompa spenta"),
|
|
79283
|
+
phrase: t("Stop the pump of {device}", "Ferma la pompa di {device}"),
|
|
79284
|
+
icon: "square",
|
|
79285
|
+
args: { on: false }
|
|
79286
|
+
},
|
|
79287
|
+
{
|
|
79288
|
+
id: "petFountain.intervalOn",
|
|
79289
|
+
method: "setPumpInterval",
|
|
79290
|
+
label: t("Pump cycle on", "Ciclo pompa attivo"),
|
|
79291
|
+
phrase: t("Run the pump of {device} on its on/off cycle", "Fai lavorare la pompa di {device} a ciclo acceso/spento"),
|
|
79292
|
+
icon: "timer",
|
|
79293
|
+
args: { on: true }
|
|
79294
|
+
},
|
|
79295
|
+
{
|
|
79296
|
+
id: "petFountain.intervalOff",
|
|
79297
|
+
method: "setPumpInterval",
|
|
79298
|
+
label: t("Pump cycle off", "Ciclo pompa spento"),
|
|
79299
|
+
phrase: t("Run the pump of {device} continuously", "Fai lavorare la pompa di {device} in continuo"),
|
|
79300
|
+
icon: "timer-off",
|
|
79301
|
+
args: { on: false }
|
|
79302
|
+
},
|
|
79303
|
+
{
|
|
79304
|
+
id: "petFountain.pumpOnMinutes",
|
|
79305
|
+
method: "setPumpOnMinutes",
|
|
79306
|
+
label: t("Set pump run time", "Imposta il tempo di lavoro della pompa"),
|
|
79307
|
+
phrase: t("Set the pump of {device} to run {minutes} minutes per cycle", "Imposta la pompa di {device} a lavorare {minutes} minuti per ciclo"),
|
|
79308
|
+
icon: "timer",
|
|
79309
|
+
openArgs: [{
|
|
79310
|
+
key: "minutes",
|
|
79311
|
+
label: t("Minutes", "Minuti"),
|
|
79312
|
+
offer: {
|
|
79313
|
+
kind: "number",
|
|
79314
|
+
min: 0,
|
|
79315
|
+
step: 1,
|
|
79316
|
+
unit: "min"
|
|
79317
|
+
}
|
|
79318
|
+
}]
|
|
79319
|
+
},
|
|
79320
|
+
{
|
|
79321
|
+
id: "petFountain.pumpOffMinutes",
|
|
79322
|
+
method: "setPumpOffMinutes",
|
|
79323
|
+
label: t("Set pump rest time", "Imposta il tempo di riposo della pompa"),
|
|
79324
|
+
phrase: t("Set the pump of {device} to rest {minutes} minutes per cycle", "Imposta la pompa di {device} a riposare {minutes} minuti per ciclo"),
|
|
79325
|
+
icon: "timer-off",
|
|
79326
|
+
openArgs: [{
|
|
79327
|
+
key: "minutes",
|
|
79328
|
+
label: t("Minutes", "Minuti"),
|
|
79329
|
+
offer: {
|
|
79330
|
+
kind: "number",
|
|
79331
|
+
min: 0,
|
|
79332
|
+
step: 1,
|
|
79333
|
+
unit: "min"
|
|
79334
|
+
}
|
|
79335
|
+
}]
|
|
79336
|
+
},
|
|
79337
|
+
{
|
|
79338
|
+
id: "petFountain.resetDailyConsumption",
|
|
79339
|
+
method: "resetDailyConsumption",
|
|
79340
|
+
label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
|
|
79341
|
+
phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
|
|
79342
|
+
icon: "rotate-ccw"
|
|
79343
|
+
}
|
|
79344
|
+
],
|
|
79345
|
+
state: [{
|
|
79346
|
+
kind: "boolean",
|
|
79347
|
+
field: "empty",
|
|
79348
|
+
label: t("Tank empty", "Serbatoio vuoto"),
|
|
79349
|
+
whenTrue: t("When the tank of {device} is empty", "Quando il serbatoio di {device} è vuoto"),
|
|
79350
|
+
whenFalse: t("When the tank of {device} is refilled", "Quando il serbatoio di {device} viene riempito"),
|
|
79351
|
+
icon: "droplet"
|
|
79352
|
+
}, {
|
|
79353
|
+
kind: "numeric",
|
|
79354
|
+
field: "waterLevel",
|
|
79355
|
+
quantity: t("the water level", "il livello dell’acqua"),
|
|
79356
|
+
label: t("Water level", "Livello dell’acqua"),
|
|
79357
|
+
unit: "%",
|
|
79358
|
+
icon: "droplets"
|
|
79359
|
+
}]
|
|
79360
|
+
},
|
|
77755
79361
|
"media-player": {
|
|
77756
79362
|
scope: "device",
|
|
77757
79363
|
noun: t("Media player", "Lettore multimediale"),
|
|
@@ -77889,10 +79495,10 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
77889
79495
|
id: "media.seek",
|
|
77890
79496
|
method: "seek",
|
|
77891
79497
|
label: t("Seek", "Vai al punto"),
|
|
77892
|
-
phrase: t("Move {device} to {
|
|
79498
|
+
phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
|
|
77893
79499
|
icon: "fast-forward",
|
|
77894
79500
|
openArgs: [{
|
|
77895
|
-
key: "
|
|
79501
|
+
key: "position",
|
|
77896
79502
|
label: t("Position", "Posizione"),
|
|
77897
79503
|
offer: {
|
|
77898
79504
|
kind: "number",
|
|
@@ -78941,7 +80547,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
78941
80547
|
state: [
|
|
78942
80548
|
{
|
|
78943
80549
|
kind: "numeric",
|
|
78944
|
-
field: "
|
|
80550
|
+
field: "co2",
|
|
78945
80551
|
quantity: t("the CO₂", "la CO₂"),
|
|
78946
80552
|
label: t("CO₂", "CO₂"),
|
|
78947
80553
|
unit: "ppm",
|
|
@@ -78949,7 +80555,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
78949
80555
|
},
|
|
78950
80556
|
{
|
|
78951
80557
|
kind: "numeric",
|
|
78952
|
-
field: "
|
|
80558
|
+
field: "voc",
|
|
78953
80559
|
quantity: t("the VOC level", "il livello di COV"),
|
|
78954
80560
|
label: t("VOC", "COV"),
|
|
78955
80561
|
unit: "ppb",
|
|
@@ -78985,7 +80591,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
78985
80591
|
noun: t("Light level", "Luminosità ambiente"),
|
|
78986
80592
|
state: [{
|
|
78987
80593
|
kind: "numeric",
|
|
78988
|
-
field: "
|
|
80594
|
+
field: "illuminance",
|
|
78989
80595
|
quantity: t("the light level", "la luminosità"),
|
|
78990
80596
|
label: t("Illuminance", "Illuminamento"),
|
|
78991
80597
|
unit: "lx",
|
|
@@ -79188,7 +80794,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79188
80794
|
noun: t("Humidity", "Umidità"),
|
|
79189
80795
|
state: [{
|
|
79190
80796
|
kind: "numeric",
|
|
79191
|
-
field: "
|
|
80797
|
+
field: "humidity",
|
|
79192
80798
|
quantity: t("the humidity", "l’umidità"),
|
|
79193
80799
|
label: t("Humidity", "Umidità"),
|
|
79194
80800
|
unit: "%",
|
|
@@ -79236,7 +80842,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79236
80842
|
state: [
|
|
79237
80843
|
{
|
|
79238
80844
|
kind: "numeric",
|
|
79239
|
-
field: "
|
|
80845
|
+
field: "power",
|
|
79240
80846
|
quantity: t("the power draw", "il consumo"),
|
|
79241
80847
|
label: t("Power", "Potenza"),
|
|
79242
80848
|
unit: "W",
|
|
@@ -79244,7 +80850,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79244
80850
|
},
|
|
79245
80851
|
{
|
|
79246
80852
|
kind: "numeric",
|
|
79247
|
-
field: "
|
|
80853
|
+
field: "energy",
|
|
79248
80854
|
quantity: t("the energy used", "l’energia consumata"),
|
|
79249
80855
|
label: t("Energy", "Energia"),
|
|
79250
80856
|
unit: "kWh",
|
|
@@ -79252,7 +80858,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79252
80858
|
},
|
|
79253
80859
|
{
|
|
79254
80860
|
kind: "numeric",
|
|
79255
|
-
field: "
|
|
80861
|
+
field: "voltage",
|
|
79256
80862
|
quantity: t("the voltage", "la tensione"),
|
|
79257
80863
|
label: t("Voltage", "Tensione"),
|
|
79258
80864
|
unit: "V",
|
|
@@ -79260,7 +80866,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79260
80866
|
},
|
|
79261
80867
|
{
|
|
79262
80868
|
kind: "numeric",
|
|
79263
|
-
field: "
|
|
80869
|
+
field: "current",
|
|
79264
80870
|
quantity: t("the current", "la corrente"),
|
|
79265
80871
|
label: t("Current", "Corrente"),
|
|
79266
80872
|
unit: "A",
|
|
@@ -79277,13 +80883,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79277
80883
|
label: t("Where", "Posizione"),
|
|
79278
80884
|
phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
|
|
79279
80885
|
icon: "map-pin"
|
|
79280
|
-
}, {
|
|
79281
|
-
kind: "numeric",
|
|
79282
|
-
field: "batteryPercent",
|
|
79283
|
-
quantity: t("the phone battery", "la batteria del telefono"),
|
|
79284
|
-
label: t("Battery", "Batteria"),
|
|
79285
|
-
unit: "%",
|
|
79286
|
-
icon: "battery"
|
|
79287
80886
|
}]
|
|
79288
80887
|
},
|
|
79289
80888
|
"pressure-sensor": {
|
|
@@ -79291,7 +80890,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79291
80890
|
noun: t("Pressure", "Pressione"),
|
|
79292
80891
|
state: [{
|
|
79293
80892
|
kind: "numeric",
|
|
79294
|
-
field: "
|
|
80893
|
+
field: "pressure",
|
|
79295
80894
|
quantity: t("the pressure", "la pressione"),
|
|
79296
80895
|
label: t("Pressure", "Pressione"),
|
|
79297
80896
|
unit: "hPa",
|
|
@@ -79327,7 +80926,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79327
80926
|
noun: t("Temperature", "Temperatura"),
|
|
79328
80927
|
state: [{
|
|
79329
80928
|
kind: "numeric",
|
|
79330
|
-
field: "
|
|
80929
|
+
field: "temperature",
|
|
79331
80930
|
quantity: t("the temperature", "la temperatura"),
|
|
79332
80931
|
label: t("Temperature", "Temperatura"),
|
|
79333
80932
|
unit: "°C",
|
|
@@ -79398,7 +80997,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79398
80997
|
state: [
|
|
79399
80998
|
{
|
|
79400
80999
|
kind: "numeric",
|
|
79401
|
-
field: "
|
|
81000
|
+
field: "level",
|
|
79402
81001
|
quantity: t("the battery level", "il livello della batteria"),
|
|
79403
81002
|
label: t("Battery", "Batteria"),
|
|
79404
81003
|
unit: "%",
|
|
@@ -79440,7 +81039,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79440
81039
|
icon: "battery-charging"
|
|
79441
81040
|
}
|
|
79442
81041
|
],
|
|
79443
|
-
events: { onStatusChanged: { notOperatorFacing: "the
|
|
81042
|
+
events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
|
|
79444
81043
|
extraEvents: [{
|
|
79445
81044
|
category: EventCategory.DeviceAwake,
|
|
79446
81045
|
label: t("Woke up", "Risvegliato"),
|
|
@@ -79458,7 +81057,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
79458
81057
|
noun: t("Network link", "Collegamento di rete"),
|
|
79459
81058
|
state: [{
|
|
79460
81059
|
kind: "numeric",
|
|
79461
|
-
field: "
|
|
81060
|
+
field: "signal",
|
|
79462
81061
|
quantity: t("the signal strength", "l’intensità del segnale"),
|
|
79463
81062
|
label: t("Signal", "Segnale"),
|
|
79464
81063
|
unit: "%",
|
|
@@ -79551,6 +81150,8 @@ var ML = "model and training machinery — no device action";
|
|
|
79551
81150
|
var INTERNAL = "internal plumbing between addons — never operator-facing";
|
|
79552
81151
|
/** Every system-scoped cap, with why it is not a device's vocabulary. */
|
|
79553
81152
|
var SYSTEM_CAP_VOCABULARY = {
|
|
81153
|
+
"action-source": system(INTERNAL),
|
|
81154
|
+
actions: system("declared actions — run from the Actions page and from the cameras that link them"),
|
|
79554
81155
|
"addon-pages": system(UI_PLUMBING),
|
|
79555
81156
|
"addon-pages-source": system(UI_PLUMBING),
|
|
79556
81157
|
"addon-routes": system(UI_PLUMBING),
|
|
@@ -79581,6 +81182,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
79581
81182
|
"device-manager": system("the device registry itself — rename, move and remove are administration"),
|
|
79582
81183
|
"device-provider": system(INTERNAL),
|
|
79583
81184
|
"device-removal-hook": system(INTERNAL),
|
|
81185
|
+
"node-removal-hook": system(INTERNAL),
|
|
79584
81186
|
"device-state": system("the runtime-state mirror every trigger already reads through"),
|
|
79585
81187
|
"display-palette": system("the cluster display palette — one colour table for every client, not a device action"),
|
|
79586
81188
|
"embedding-encoder": system(ML),
|
|
@@ -79620,6 +81222,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
79620
81222
|
"recording-archive": system(STORAGE),
|
|
79621
81223
|
"recording-export": system("recording exports — requested from the timeline, not an automation step"),
|
|
79622
81224
|
records: system(INTERNAL),
|
|
81225
|
+
remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
|
|
79623
81226
|
"server-management": system(CLUSTER),
|
|
79624
81227
|
"settings-store": system(INTERNAL),
|
|
79625
81228
|
"smtp-provider": system(INTERNAL),
|
|
@@ -79632,6 +81235,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
79632
81235
|
"storage-provider": system(STORAGE),
|
|
79633
81236
|
"stream-broker": system(PIPELINE),
|
|
79634
81237
|
system: system(CLUSTER),
|
|
81238
|
+
template: system("operator-authored templates — edited in their own editor, never actuated"),
|
|
79635
81239
|
"terminal-session": system(CLUSTER),
|
|
79636
81240
|
toast: system(UI_PLUMBING),
|
|
79637
81241
|
training: system(ML),
|
|
@@ -79691,7 +81295,7 @@ function projectAction(cap, action) {
|
|
|
79691
81295
|
};
|
|
79692
81296
|
}
|
|
79693
81297
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
79694
|
-
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;
|
|
81298
|
+
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.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
|
|
79695
81299
|
DeviceFeature.BatteryOperated;
|
|
79696
81300
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
79697
81301
|
var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
|
|
@@ -79747,6 +81351,31 @@ Object.freeze({
|
|
|
79747
81351
|
form: "single",
|
|
79748
81352
|
optional: false
|
|
79749
81353
|
}],
|
|
81354
|
+
"actions.get": [{
|
|
81355
|
+
name: "deviceId",
|
|
81356
|
+
form: "single",
|
|
81357
|
+
optional: true
|
|
81358
|
+
}],
|
|
81359
|
+
"actions.getLinks": [{
|
|
81360
|
+
name: "deviceId",
|
|
81361
|
+
form: "single",
|
|
81362
|
+
optional: false
|
|
81363
|
+
}],
|
|
81364
|
+
"actions.listForCamera": [{
|
|
81365
|
+
name: "deviceId",
|
|
81366
|
+
form: "single",
|
|
81367
|
+
optional: false
|
|
81368
|
+
}],
|
|
81369
|
+
"actions.run": [{
|
|
81370
|
+
name: "deviceId",
|
|
81371
|
+
form: "single",
|
|
81372
|
+
optional: true
|
|
81373
|
+
}],
|
|
81374
|
+
"actions.setLinks": [{
|
|
81375
|
+
name: "deviceId",
|
|
81376
|
+
form: "single",
|
|
81377
|
+
optional: false
|
|
81378
|
+
}],
|
|
79750
81379
|
"addonSettings.getDeviceSettings": [{
|
|
79751
81380
|
name: "deviceId",
|
|
79752
81381
|
form: "single",
|
|
@@ -80798,6 +82427,16 @@ Object.freeze({
|
|
|
80798
82427
|
form: "single",
|
|
80799
82428
|
optional: false
|
|
80800
82429
|
}],
|
|
82430
|
+
"irTransmitter.getInfo": [{
|
|
82431
|
+
name: "deviceId",
|
|
82432
|
+
form: "single",
|
|
82433
|
+
optional: false
|
|
82434
|
+
}],
|
|
82435
|
+
"irTransmitter.send": [{
|
|
82436
|
+
name: "deviceId",
|
|
82437
|
+
form: "single",
|
|
82438
|
+
optional: false
|
|
82439
|
+
}],
|
|
80801
82440
|
"keychain.deleteDeviceScope": [{
|
|
80802
82441
|
name: "deviceId",
|
|
80803
82442
|
form: "single",
|
|
@@ -81207,6 +82846,36 @@ Object.freeze({
|
|
|
81207
82846
|
form: "single",
|
|
81208
82847
|
optional: false
|
|
81209
82848
|
}],
|
|
82849
|
+
"petFountain.getStatus": [{
|
|
82850
|
+
name: "deviceId",
|
|
82851
|
+
form: "single",
|
|
82852
|
+
optional: false
|
|
82853
|
+
}],
|
|
82854
|
+
"petFountain.resetDailyConsumption": [{
|
|
82855
|
+
name: "deviceId",
|
|
82856
|
+
form: "single",
|
|
82857
|
+
optional: false
|
|
82858
|
+
}],
|
|
82859
|
+
"petFountain.setPump": [{
|
|
82860
|
+
name: "deviceId",
|
|
82861
|
+
form: "single",
|
|
82862
|
+
optional: false
|
|
82863
|
+
}],
|
|
82864
|
+
"petFountain.setPumpInterval": [{
|
|
82865
|
+
name: "deviceId",
|
|
82866
|
+
form: "single",
|
|
82867
|
+
optional: false
|
|
82868
|
+
}],
|
|
82869
|
+
"petFountain.setPumpOffMinutes": [{
|
|
82870
|
+
name: "deviceId",
|
|
82871
|
+
form: "single",
|
|
82872
|
+
optional: false
|
|
82873
|
+
}],
|
|
82874
|
+
"petFountain.setPumpOnMinutes": [{
|
|
82875
|
+
name: "deviceId",
|
|
82876
|
+
form: "single",
|
|
82877
|
+
optional: false
|
|
82878
|
+
}],
|
|
81210
82879
|
"pipelineAnalytics.addRetrainFrameFromRecording": [{
|
|
81211
82880
|
name: "deviceId",
|
|
81212
82881
|
form: "single",
|
|
@@ -82023,6 +83692,31 @@ Object.freeze({
|
|
|
82023
83692
|
form: "single",
|
|
82024
83693
|
optional: true
|
|
82025
83694
|
}],
|
|
83695
|
+
"remote.getConfig": [{
|
|
83696
|
+
name: "deviceId",
|
|
83697
|
+
form: "single",
|
|
83698
|
+
optional: false
|
|
83699
|
+
}],
|
|
83700
|
+
"remote.listCommands": [{
|
|
83701
|
+
name: "deviceId",
|
|
83702
|
+
form: "single",
|
|
83703
|
+
optional: false
|
|
83704
|
+
}],
|
|
83705
|
+
"remote.sendCommand": [{
|
|
83706
|
+
name: "deviceId",
|
|
83707
|
+
form: "single",
|
|
83708
|
+
optional: false
|
|
83709
|
+
}],
|
|
83710
|
+
"remote.sendMediaCommand": [{
|
|
83711
|
+
name: "deviceId",
|
|
83712
|
+
form: "single",
|
|
83713
|
+
optional: false
|
|
83714
|
+
}],
|
|
83715
|
+
"remote.setConfig": [{
|
|
83716
|
+
name: "deviceId",
|
|
83717
|
+
form: "single",
|
|
83718
|
+
optional: false
|
|
83719
|
+
}],
|
|
82026
83720
|
"runningState.getStatus": [{
|
|
82027
83721
|
name: "deviceId",
|
|
82028
83722
|
form: "single",
|
|
@@ -83828,8 +85522,17 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
|
83828
85522
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
83829
85523
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
83830
85524
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
85525
|
+
String.raw`(?<=^|["'(\s>,\[{])`;
|
|
83831
85526
|
var MB = 1024 * 1024;
|
|
83832
85527
|
1024 * MB, 3072 * MB;
|
|
85528
|
+
object({
|
|
85529
|
+
manufacturer: string().optional(),
|
|
85530
|
+
supportedModels: array(string()).optional(),
|
|
85531
|
+
supportedController: string().optional(),
|
|
85532
|
+
commandsEncoding: string(),
|
|
85533
|
+
delay: number().nonnegative().optional(),
|
|
85534
|
+
commands: record(string(), unknown())
|
|
85535
|
+
});
|
|
83833
85536
|
//#endregion
|
|
83834
85537
|
//#region src/custom-actions.ts
|
|
83835
85538
|
var SourceIdSchema = string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug");
|