@camstack/addon-provider-homeassistant 1.2.636 → 1.2.638
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 +18 -0
- package/dist/addon.js +1 -1
- package/dist/addon.mjs +1 -1
- package/dist/{dist-BS9DCd76.js → dist-C_XoeULr.js} +1388 -721
- package/dist/{dist-BusXPPvn.mjs → dist-Cvz76AD1.mjs} +1383 -734
- package/dist/ha-export/ha-export.addon.js +300 -3
- package/dist/ha-export/ha-export.addon.mjs +300 -3
- package/package.json +3 -2
|
@@ -5200,6 +5200,14 @@ function object(shape, params) {
|
|
|
5200
5200
|
...normalizeParams(params)
|
|
5201
5201
|
});
|
|
5202
5202
|
}
|
|
5203
|
+
function strictObject(shape, params) {
|
|
5204
|
+
return new ZodObject({
|
|
5205
|
+
type: "object",
|
|
5206
|
+
shape,
|
|
5207
|
+
catchall: never(),
|
|
5208
|
+
...normalizeParams(params)
|
|
5209
|
+
});
|
|
5210
|
+
}
|
|
5203
5211
|
function looseObject(shape, params) {
|
|
5204
5212
|
return new ZodObject({
|
|
5205
5213
|
type: "object",
|
|
@@ -5579,7 +5587,7 @@ var ZodIssueCode = {
|
|
|
5579
5587
|
var ZodFirstPartyTypeKind;
|
|
5580
5588
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5581
5589
|
//#endregion
|
|
5582
|
-
//#region ../types/dist/sleep-
|
|
5590
|
+
//#region ../types/dist/sleep-6eG9C8Xl.mjs
|
|
5583
5591
|
/**
|
|
5584
5592
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5585
5593
|
* window to float samples (D455).
|
|
@@ -8725,65 +8733,6 @@ var MODEL_FORMATS = [
|
|
|
8725
8733
|
"pt",
|
|
8726
8734
|
"gguf"
|
|
8727
8735
|
];
|
|
8728
|
-
/**
|
|
8729
|
-
* Per-call node pinning for `ctx.api` capability calls.
|
|
8730
|
-
*
|
|
8731
|
-
* A capability call normally resolves to its DEFAULT provider — a `singleton`
|
|
8732
|
-
* cap resolves to the hub, a device-scoped cap to the device's owning node. To
|
|
8733
|
-
* query a SPECIFIC node's provider instead (e.g. a remote agent's own
|
|
8734
|
-
* in-process `platform-probe` hardware, which the hub cannot probe), pin the
|
|
8735
|
-
* call to that node.
|
|
8736
|
-
*
|
|
8737
|
-
* The nodeId rides OUT-OF-BAND in the tRPC call context (NOT in the validated
|
|
8738
|
-
* method args), so capability method signatures stay `nodeId`-free — node
|
|
8739
|
-
* targeting is a property of the CALL, not of the method. The transport lifts
|
|
8740
|
-
* it from `op.context` onto the `CapCallInput.nodeId` field (`ipcParentLink`),
|
|
8741
|
-
* and the hub parent's `onUnownedCall` passes it to the `CapRouteResolver`,
|
|
8742
|
-
* which classifies a pinned agent node as `agent-child-forward`
|
|
8743
|
-
* (`$agent-cap-fwd.forward` → the agent's in-process provider).
|
|
8744
|
-
*
|
|
8745
|
-
* Usage at a call site:
|
|
8746
|
-
*
|
|
8747
|
-
* await api.platformProbe.getCapabilities.query(undefined, nodePin(nodeId))
|
|
8748
|
-
*/
|
|
8749
|
-
/** tRPC `op.context` key carrying a per-call node pin. */
|
|
8750
|
-
var CAP_NODE_PIN_CONTEXT_KEY = "__camstackNodePin";
|
|
8751
|
-
/**
|
|
8752
|
-
* Build the tRPC request options that pin a single capability call to `nodeId`.
|
|
8753
|
-
* Pass as the second argument to `.query(input, …)` / `.mutate(input, …)`.
|
|
8754
|
-
*
|
|
8755
|
-
* ## The id is normalised here, and it has to be
|
|
8756
|
-
*
|
|
8757
|
-
* A forked addon reads its own node from `ctx.kernel.localNodeId`, and inside a
|
|
8758
|
-
* worker that value is a RUNNER id — `hub/export-hap`, not `hub`. Routing
|
|
8759
|
-
* compares a pin against real node ids, so such a pin matches nothing and the
|
|
8760
|
-
* call fails with `no provider registered for cap "…"`. The local-first
|
|
8761
|
-
* resolver already guarded against this (`localNodeId.split('/')[0]`), which
|
|
8762
|
-
* made the hazard invisible: unpinned calls worked, and only an explicit pin —
|
|
8763
|
-
* the thing you reach for when you specifically need THIS node — silently
|
|
8764
|
-
* addressed a node that does not exist.
|
|
8765
|
-
*
|
|
8766
|
-
* Cost of it being missing: `addon-export-hap` pinned `decoder.getInfo` to its
|
|
8767
|
-
* own node to read the host's hardware-decode backend. It never once answered,
|
|
8768
|
-
* so every HomeKit egress transcode decoded in SOFTWARE — including 4K H.265 —
|
|
8769
|
-
* while D67's whole premise was that the decoder addon is the authority on
|
|
8770
|
-
* hardware. The warn said `decoding in SOFTWARE` and read as "this node has no
|
|
8771
|
-
* hardware", which was false.
|
|
8772
|
-
*
|
|
8773
|
-
* Normalising in the ONE constructor fixes every caller at once, which is why
|
|
8774
|
-
* it is here and not at the call sites.
|
|
8775
|
-
*/
|
|
8776
|
-
function nodePin(nodeId) {
|
|
8777
|
-
return { context: { [CAP_NODE_PIN_CONTEXT_KEY]: toNodeId(nodeId) } };
|
|
8778
|
-
}
|
|
8779
|
-
/**
|
|
8780
|
-
* A runner id is `<nodeId>/<addonId>`; a node id has no slash. Taking the head
|
|
8781
|
-
* is idempotent, so passing an already-clean id costs nothing.
|
|
8782
|
-
*/
|
|
8783
|
-
function toNodeId(idOrRunnerId) {
|
|
8784
|
-
const head = idOrRunnerId.split("/")[0];
|
|
8785
|
-
return head === void 0 || head.length === 0 ? idOrRunnerId : head;
|
|
8786
|
-
}
|
|
8787
8736
|
object({
|
|
8788
8737
|
field: string().min(1),
|
|
8789
8738
|
on: literal("advance")
|
|
@@ -8876,9 +8825,135 @@ function optionalMethod(input, output, options) {
|
|
|
8876
8825
|
function event(data) {
|
|
8877
8826
|
return { data };
|
|
8878
8827
|
}
|
|
8828
|
+
/**
|
|
8829
|
+
* Per-call node pinning for `ctx.api` capability calls.
|
|
8830
|
+
*
|
|
8831
|
+
* A capability call normally resolves to its DEFAULT provider — a `singleton`
|
|
8832
|
+
* cap resolves to the hub, a device-scoped cap to the device's owning node. To
|
|
8833
|
+
* query a SPECIFIC node's provider instead (e.g. a remote agent's own
|
|
8834
|
+
* in-process `platform-probe` hardware, which the hub cannot probe), pin the
|
|
8835
|
+
* call to that node.
|
|
8836
|
+
*
|
|
8837
|
+
* The nodeId rides OUT-OF-BAND in the tRPC call context (NOT in the validated
|
|
8838
|
+
* method args), so capability method signatures stay `nodeId`-free — node
|
|
8839
|
+
* targeting is a property of the CALL, not of the method. The transport lifts
|
|
8840
|
+
* it from `op.context` onto the `CapCallInput.nodeId` field (`ipcParentLink`),
|
|
8841
|
+
* and the hub parent's `onUnownedCall` passes it to the `CapRouteResolver`,
|
|
8842
|
+
* which classifies a pinned agent node as `agent-child-forward`
|
|
8843
|
+
* (`$agent-cap-fwd.forward` → the agent's in-process provider).
|
|
8844
|
+
*
|
|
8845
|
+
* Usage at a call site:
|
|
8846
|
+
*
|
|
8847
|
+
* await api.platformProbe.getCapabilities.query(undefined, nodePin(nodeId))
|
|
8848
|
+
*/
|
|
8849
|
+
/** tRPC `op.context` key carrying a per-call node pin. */
|
|
8850
|
+
var CAP_NODE_PIN_CONTEXT_KEY = "__camstackNodePin";
|
|
8851
|
+
/**
|
|
8852
|
+
* Build the tRPC request options that pin a single capability call to `nodeId`.
|
|
8853
|
+
* Pass as the second argument to `.query(input, …)` / `.mutate(input, …)`.
|
|
8854
|
+
*
|
|
8855
|
+
* ## The id is normalised here, and it has to be
|
|
8856
|
+
*
|
|
8857
|
+
* A forked addon reads its own node from `ctx.kernel.localNodeId`, and inside a
|
|
8858
|
+
* worker that value is a RUNNER id — `hub/export-hap`, not `hub`. Routing
|
|
8859
|
+
* compares a pin against real node ids, so such a pin matches nothing and the
|
|
8860
|
+
* call fails with `no provider registered for cap "…"`. The local-first
|
|
8861
|
+
* resolver already guarded against this (`localNodeId.split('/')[0]`), which
|
|
8862
|
+
* made the hazard invisible: unpinned calls worked, and only an explicit pin —
|
|
8863
|
+
* the thing you reach for when you specifically need THIS node — silently
|
|
8864
|
+
* addressed a node that does not exist.
|
|
8865
|
+
*
|
|
8866
|
+
* Cost of it being missing: `addon-export-hap` pinned `decoder.getInfo` to its
|
|
8867
|
+
* own node to read the host's hardware-decode backend. It never once answered,
|
|
8868
|
+
* so every HomeKit egress transcode decoded in SOFTWARE — including 4K H.265 —
|
|
8869
|
+
* while D67's whole premise was that the decoder addon is the authority on
|
|
8870
|
+
* hardware. The warn said `decoding in SOFTWARE` and read as "this node has no
|
|
8871
|
+
* hardware", which was false.
|
|
8872
|
+
*
|
|
8873
|
+
* Normalising in the ONE constructor fixes every caller at once, which is why
|
|
8874
|
+
* it is here and not at the call sites.
|
|
8875
|
+
*/
|
|
8876
|
+
function nodePin(nodeId) {
|
|
8877
|
+
return { context: { [CAP_NODE_PIN_CONTEXT_KEY]: toNodeId(nodeId) } };
|
|
8878
|
+
}
|
|
8879
|
+
/**
|
|
8880
|
+
* A runner id is `<nodeId>/<addonId>`; a node id has no slash. Taking the head
|
|
8881
|
+
* is idempotent, so passing an already-clean id costs nothing.
|
|
8882
|
+
*/
|
|
8883
|
+
function toNodeId(idOrRunnerId) {
|
|
8884
|
+
const head = idOrRunnerId.split("/")[0];
|
|
8885
|
+
return head === void 0 || head.length === 0 ? idOrRunnerId : head;
|
|
8886
|
+
}
|
|
8887
|
+
/**
|
|
8888
|
+
* `addon-pages-source` — collection cap exposing per-provider raw page
|
|
8889
|
+
* declarations. Every addon that contributes a UI page registers a
|
|
8890
|
+
* provider here. The hub-side singleton aggregator (`addon-pages` cap,
|
|
8891
|
+
* see `addon-pages.cap.ts`) walks this collection, stamps versioned
|
|
8892
|
+
* `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
|
|
8893
|
+
* that admin-ui consumes.
|
|
8894
|
+
*
|
|
8895
|
+
* The split exists because the public listing has a different output
|
|
8896
|
+
* shape than the per-provider raw declarations, and we want both ends
|
|
8897
|
+
* to flow through codegen instead of relying on a hand-written wrapper.
|
|
8898
|
+
*/
|
|
8899
|
+
var AddonPageDeclarationSchema = object({
|
|
8900
|
+
id: string(),
|
|
8901
|
+
label: string(),
|
|
8902
|
+
icon: string(),
|
|
8903
|
+
path: string(),
|
|
8904
|
+
/**
|
|
8905
|
+
* Module Federation remote name — must match the `name` field on the
|
|
8906
|
+
* page addon's `federation()` plugin config. Used by admin-ui's
|
|
8907
|
+
* `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
|
|
8908
|
+
* Conventionally `addon_<id>_page` (snake_case; MF names cannot
|
|
8909
|
+
* contain hyphens).
|
|
8910
|
+
*/
|
|
8911
|
+
remoteName: string(),
|
|
8912
|
+
/**
|
|
8913
|
+
* Bundle filename inside the addon's `dist/` dir served at
|
|
8914
|
+
* `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
|
|
8915
|
+
* is always `'remoteEntry.js'`; the value is kept on the metadata so
|
|
8916
|
+
* the static-file route can compute an mtime-based cache-buster URL
|
|
8917
|
+
* without a separate filesystem stat.
|
|
8918
|
+
*/
|
|
8919
|
+
bundle: string(),
|
|
8920
|
+
/**
|
|
8921
|
+
* Sidebar section this page docks into. Well-known ids: `'detection'`,
|
|
8922
|
+
* `'cluster'`, `'administration'` — the page renders inside that group.
|
|
8923
|
+
* Any OTHER string creates (or joins) a custom section rendered after
|
|
8924
|
+
* the built-in groups; its label comes from `sectionLabel` (first
|
|
8925
|
+
* declaration wins), falling back to the id. Absent → the legacy
|
|
8926
|
+
* "Addon Pages" group.
|
|
8927
|
+
*/
|
|
8928
|
+
section: string().optional(),
|
|
8929
|
+
/** Display label for a CUSTOM `section` id (ignored for well-known ids). */
|
|
8930
|
+
sectionLabel: string().optional()
|
|
8931
|
+
});
|
|
8932
|
+
method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
|
|
8879
8933
|
var StaticDirOutputSchema$1 = object({ staticDir: string() });
|
|
8880
8934
|
var VersionOutputSchema$1 = object({ version: string() });
|
|
8881
8935
|
method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
|
|
8936
|
+
/**
|
|
8937
|
+
* The pipeline step-input shape and the run-result bridge, in a module that
|
|
8938
|
+
* imports nothing but zod and types.
|
|
8939
|
+
*
|
|
8940
|
+
* WHY A SEPARATE FILE (addon footprint, D28): both were declared in
|
|
8941
|
+
* `pipeline-executor.cap.ts`, whose import evaluates the whole executor schema
|
|
8942
|
+
* graph. An addon that only needs these two (the benchmark's `runPipelineTest`
|
|
8943
|
+
* action) paid ~130 MB of heap at load. `pipeline-executor.cap.ts` re-exports
|
|
8944
|
+
* them, so every existing import keeps working; addon server code takes them
|
|
8945
|
+
* from `@camstack/types/addon`.
|
|
8946
|
+
*/
|
|
8947
|
+
var PipelineStepInputSchema = lazy(() => object({
|
|
8948
|
+
addonId: string(),
|
|
8949
|
+
modelId: string().optional(),
|
|
8950
|
+
enabled: boolean().default(true),
|
|
8951
|
+
children: array(PipelineStepInputSchema).optional(),
|
|
8952
|
+
settings: record(string(), unknown()).optional(),
|
|
8953
|
+
jumpDeviceKey: string().optional()
|
|
8954
|
+
}));
|
|
8955
|
+
/** Typed bridge to the recursive `FrameResult` graph; validation is structural. */
|
|
8956
|
+
var PipelineRunResultBridge = custom();
|
|
8882
8957
|
var StaticDirOutputSchema = object({ staticDir: string() });
|
|
8883
8958
|
var VersionOutputSchema = object({ version: string() });
|
|
8884
8959
|
method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
|
|
@@ -9063,6 +9138,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9063
9138
|
"network-link",
|
|
9064
9139
|
"network-quality",
|
|
9065
9140
|
"node-certificates",
|
|
9141
|
+
"node-removal-hook",
|
|
9066
9142
|
"nodes",
|
|
9067
9143
|
"notification-output",
|
|
9068
9144
|
"notification-rules",
|
|
@@ -9073,6 +9149,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9073
9149
|
"osd",
|
|
9074
9150
|
"osd-manager",
|
|
9075
9151
|
"pet-feeder",
|
|
9152
|
+
"pet-fountain",
|
|
9076
9153
|
"pipeline-analytics",
|
|
9077
9154
|
"pipeline-executor",
|
|
9078
9155
|
"pipeline-orchestrator",
|
|
@@ -10352,6 +10429,56 @@ Object.freeze({
|
|
|
10352
10429
|
"valueType": "number"
|
|
10353
10430
|
}
|
|
10354
10431
|
],
|
|
10432
|
+
"pet-fountain": [
|
|
10433
|
+
{
|
|
10434
|
+
"path": "waterLevel",
|
|
10435
|
+
"valueType": "number"
|
|
10436
|
+
},
|
|
10437
|
+
{
|
|
10438
|
+
"path": "waterVolume",
|
|
10439
|
+
"valueType": "number"
|
|
10440
|
+
},
|
|
10441
|
+
{
|
|
10442
|
+
"path": "empty",
|
|
10443
|
+
"valueType": "boolean"
|
|
10444
|
+
},
|
|
10445
|
+
{
|
|
10446
|
+
"path": "pumpRunning",
|
|
10447
|
+
"valueType": "boolean"
|
|
10448
|
+
},
|
|
10449
|
+
{
|
|
10450
|
+
"path": "pumpIntervalEnabled",
|
|
10451
|
+
"valueType": "boolean"
|
|
10452
|
+
},
|
|
10453
|
+
{
|
|
10454
|
+
"path": "pumpOnTime",
|
|
10455
|
+
"valueType": "number"
|
|
10456
|
+
},
|
|
10457
|
+
{
|
|
10458
|
+
"path": "pumpOffTime",
|
|
10459
|
+
"valueType": "number"
|
|
10460
|
+
},
|
|
10461
|
+
{
|
|
10462
|
+
"path": "lastDrink",
|
|
10463
|
+
"valueType": "number"
|
|
10464
|
+
},
|
|
10465
|
+
{
|
|
10466
|
+
"path": "currentHourDrink",
|
|
10467
|
+
"valueType": "number"
|
|
10468
|
+
},
|
|
10469
|
+
{
|
|
10470
|
+
"path": "last24hDrink",
|
|
10471
|
+
"valueType": "number"
|
|
10472
|
+
},
|
|
10473
|
+
{
|
|
10474
|
+
"path": "todayDrink",
|
|
10475
|
+
"valueType": "number"
|
|
10476
|
+
},
|
|
10477
|
+
{
|
|
10478
|
+
"path": "error",
|
|
10479
|
+
"valueType": "string"
|
|
10480
|
+
}
|
|
10481
|
+
],
|
|
10355
10482
|
"power-meter": [
|
|
10356
10483
|
{
|
|
10357
10484
|
"path": "power",
|
|
@@ -10889,7 +11016,7 @@ object({
|
|
|
10889
11016
|
params: record(string(), union([string(), number()])).optional()
|
|
10890
11017
|
});
|
|
10891
11018
|
//#endregion
|
|
10892
|
-
//#region ../types/dist/err-msg-
|
|
11019
|
+
//#region ../types/dist/err-msg-DWXWc0Di.mjs
|
|
10893
11020
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10894
11021
|
DeviceType["Camera"] = "camera";
|
|
10895
11022
|
DeviceType["Hub"] = "hub";
|
|
@@ -10981,6 +11108,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10981
11108
|
* native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
|
|
10982
11109
|
* integrations sharing the same food/desiccant/hopper surface. */
|
|
10983
11110
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
11111
|
+
/** Pet water fountain — tank level, water drunk, pump and its duty cycle.
|
|
11112
|
+
* Installed with the `pet-fountain` cap. Reaches CamStack as loose entities
|
|
11113
|
+
* of a container and is BUILT as a composed device (D967). */
|
|
11114
|
+
DeviceType["PetFountain"] = "pet-fountain";
|
|
10984
11115
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10985
11116
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10986
11117
|
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
@@ -11251,7 +11382,7 @@ function errMsg(err) {
|
|
|
11251
11382
|
return String(err);
|
|
11252
11383
|
}
|
|
11253
11384
|
//#endregion
|
|
11254
|
-
//#region ../types/dist/composition-
|
|
11385
|
+
//#region ../types/dist/composition-kSuOifuG.mjs
|
|
11255
11386
|
/**
|
|
11256
11387
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11257
11388
|
*
|
|
@@ -12336,12 +12467,28 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
12336
12467
|
])
|
|
12337
12468
|
})
|
|
12338
12469
|
]);
|
|
12339
|
-
var
|
|
12470
|
+
var forwardArgUnion = union([
|
|
12471
|
+
string(),
|
|
12472
|
+
number(),
|
|
12473
|
+
boolean(),
|
|
12474
|
+
strictObject({ ["$input"]: string().min(1) }),
|
|
12475
|
+
record(string().min(1), lazy(() => forwardArgUnion))
|
|
12476
|
+
]);
|
|
12477
|
+
var CompositionForwardArgSchema = forwardArgUnion;
|
|
12478
|
+
/**
|
|
12479
|
+
* `forward` (D659, amended by D967): one method of a BUILT capability is one
|
|
12480
|
+
* call of `cap.method` on `source`. Without `args` the built method's input
|
|
12481
|
+
* passes through minus `deviceId`; with `args` the template IS the source's
|
|
12482
|
+
* whole input. `deviceId` is always the source's own, never an argument.
|
|
12483
|
+
*/
|
|
12484
|
+
var CompositionForwardCommandSchema = object({
|
|
12340
12485
|
kind: literal("forward"),
|
|
12341
12486
|
source: CompositionSourceRefSchema,
|
|
12342
12487
|
cap: string().min(1),
|
|
12343
|
-
method: string().min(1)
|
|
12344
|
-
|
|
12488
|
+
method: string().min(1),
|
|
12489
|
+
args: record(string().min(1), CompositionForwardArgSchema).optional()
|
|
12490
|
+
});
|
|
12491
|
+
var CompositionCommandTargetSchema = discriminatedUnion("kind", [CompositionForwardCommandSchema, CompositionCodeSourceSchema]);
|
|
12345
12492
|
/** An item key inside an item-array cap (`consumables.items[<key>]`). */
|
|
12346
12493
|
var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
|
|
12347
12494
|
/**
|
|
@@ -12351,7 +12498,13 @@ var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
|
|
|
12351
12498
|
*/
|
|
12352
12499
|
var CompositionItemEntrySchema = object({
|
|
12353
12500
|
label: string().min(1).max(80),
|
|
12354
|
-
fields: record(string().min(1), CompositionFieldSourceSchema)
|
|
12501
|
+
fields: record(string().min(1), CompositionFieldSourceSchema),
|
|
12502
|
+
/**
|
|
12503
|
+
* What the cap's `reset` does for THIS item (consumables). Present = the item
|
|
12504
|
+
* is resettable and a `reset` call naming its key forwards here. The item key
|
|
12505
|
+
* selects the command, so it is never an argument: there is no pass-through.
|
|
12506
|
+
*/
|
|
12507
|
+
reset: CompositionForwardCommandSchema.optional()
|
|
12355
12508
|
});
|
|
12356
12509
|
var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
12357
12510
|
kind: literal("fields"),
|
|
@@ -12363,7 +12516,7 @@ var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
|
12363
12516
|
* owns the WHOLE array field: the composed value holds exactly these items.
|
|
12364
12517
|
*/
|
|
12365
12518
|
items: record(string().regex(COMPOSITION_ITEM_KEY_RE), CompositionItemEntrySchema).refine((items) => Object.keys(items).length <= 16, { message: `at most 16 items` }).optional(),
|
|
12366
|
-
/**
|
|
12519
|
+
/** Method name → what serves it. Only a BUILT capability takes commands (D967). */
|
|
12367
12520
|
commands: record(string().min(1), CompositionCommandTargetSchema).optional()
|
|
12368
12521
|
}), object({
|
|
12369
12522
|
kind: literal("passthrough"),
|
|
@@ -16447,6 +16600,587 @@ object({
|
|
|
16447
16600
|
lastError: string().nullable()
|
|
16448
16601
|
});
|
|
16449
16602
|
/**
|
|
16603
|
+
* notification-output — canonical, capability-gated notification delivery.
|
|
16604
|
+
*
|
|
16605
|
+
* Apprise-derived model (see
|
|
16606
|
+
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
16607
|
+
* callers emit ONE canonical `Notification`; each provider declares a
|
|
16608
|
+
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
16609
|
+
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
16610
|
+
* message to what the kind supports — callers never special-case a service.
|
|
16611
|
+
*
|
|
16612
|
+
* DESIGN DECISIONS (locked):
|
|
16613
|
+
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
16614
|
+
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
16615
|
+
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
16616
|
+
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
16617
|
+
* alternative would fork the UI per addon and cannot host the
|
|
16618
|
+
* discovery→adopt flow.
|
|
16619
|
+
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
16620
|
+
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
16621
|
+
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
16622
|
+
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
16623
|
+
* `addonId` the generated collection router extracts from the call input.
|
|
16624
|
+
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
16625
|
+
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
16626
|
+
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
16627
|
+
* base64 fallback needed.
|
|
16628
|
+
*
|
|
16629
|
+
* TODO (deferred, closed-set change — separate decision): add
|
|
16630
|
+
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
16631
|
+
* admin "Integrations" page.
|
|
16632
|
+
*/
|
|
16633
|
+
/**
|
|
16634
|
+
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
16635
|
+
* adapter picks what it supports and the degrade engine filters the rest.
|
|
16636
|
+
*/
|
|
16637
|
+
var AttachmentMediaTypeSchema = _enum([
|
|
16638
|
+
"image",
|
|
16639
|
+
"video",
|
|
16640
|
+
"gif",
|
|
16641
|
+
"audio",
|
|
16642
|
+
"icon"
|
|
16643
|
+
]);
|
|
16644
|
+
/**
|
|
16645
|
+
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
16646
|
+
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
16647
|
+
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
16648
|
+
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
16649
|
+
*/
|
|
16650
|
+
var AttachmentSchema = object({
|
|
16651
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
16652
|
+
url: string().optional(),
|
|
16653
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
16654
|
+
mime: string().optional(),
|
|
16655
|
+
name: string().optional()
|
|
16656
|
+
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
16657
|
+
var NotificationFormatSchema = _enum([
|
|
16658
|
+
"text",
|
|
16659
|
+
"markdown",
|
|
16660
|
+
"html"
|
|
16661
|
+
]);
|
|
16662
|
+
/**
|
|
16663
|
+
* The CLOSED icon vocabulary an action button may use.
|
|
16664
|
+
*
|
|
16665
|
+
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
16666
|
+
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
16667
|
+
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
16668
|
+
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
16669
|
+
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
16670
|
+
*
|
|
16671
|
+
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
16672
|
+
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
16673
|
+
* differently.
|
|
16674
|
+
*
|
|
16675
|
+
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
16676
|
+
*
|
|
16677
|
+
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
16678
|
+
* the whole set onto its own renderer's vocabulary through a
|
|
16679
|
+
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
16680
|
+
* fallback, so adding a member here fails every adapter's build until someone
|
|
16681
|
+
* decides its glyph, which is the only place that decision can be made
|
|
16682
|
+
* honestly.
|
|
16683
|
+
*
|
|
16684
|
+
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
16685
|
+
* `disarm` straight through; iOS feeds that string to
|
|
16686
|
+
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
16687
|
+
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
16688
|
+
* mapping, and "the field is documented" is not "the value renders".
|
|
16689
|
+
*
|
|
16690
|
+
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
16691
|
+
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
16692
|
+
* so an addon that emits a token the running hub does not know does not lose an
|
|
16693
|
+
* icon — its whole `send` fails Zod validation and the notification never
|
|
16694
|
+
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
16695
|
+
*/
|
|
16696
|
+
var NotificationActionIconSchema = _enum([
|
|
16697
|
+
"acknowledge",
|
|
16698
|
+
"dismiss",
|
|
16699
|
+
"silence",
|
|
16700
|
+
"snooze",
|
|
16701
|
+
"view",
|
|
16702
|
+
"play",
|
|
16703
|
+
"open",
|
|
16704
|
+
"close",
|
|
16705
|
+
"lock",
|
|
16706
|
+
"unlock",
|
|
16707
|
+
"arm",
|
|
16708
|
+
"arm-home",
|
|
16709
|
+
"arm-away",
|
|
16710
|
+
"arm-night",
|
|
16711
|
+
"disarm",
|
|
16712
|
+
"light",
|
|
16713
|
+
"alert",
|
|
16714
|
+
"camera"
|
|
16715
|
+
]);
|
|
16716
|
+
/** A single tap-through action button. */
|
|
16717
|
+
var NotificationActionSchema = object({
|
|
16718
|
+
id: string(),
|
|
16719
|
+
label: string(),
|
|
16720
|
+
url: string().optional(),
|
|
16721
|
+
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
16722
|
+
icon: NotificationActionIconSchema.optional(),
|
|
16723
|
+
/**
|
|
16724
|
+
* Renders in a warning style where the notifier supports it.
|
|
16725
|
+
*
|
|
16726
|
+
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
16727
|
+
* else — see `notification-center/action-token.ts` for what that does and
|
|
16728
|
+
* does not buy.
|
|
16729
|
+
*/
|
|
16730
|
+
destructive: boolean().optional(),
|
|
16731
|
+
/**
|
|
16732
|
+
* How the tap should REACH the url.
|
|
16733
|
+
*
|
|
16734
|
+
* `navigate` (absent, and every button authored before this field) opens it:
|
|
16735
|
+
* the phone leaves the notification and shows whatever the callback returns.
|
|
16736
|
+
* That is right for a button whose answer the operator wants to read.
|
|
16737
|
+
*
|
|
16738
|
+
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
16739
|
+
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
16740
|
+
* an answer to the notification, and being thrown into a browser tab to
|
|
16741
|
+
* confirm it costs more attention than the notification did. A backend that
|
|
16742
|
+
* cannot do a background call renders it as an ordinary link (the adapters
|
|
16743
|
+
* fall back rather than dropping the button), so this is a preference, never
|
|
16744
|
+
* a requirement.
|
|
16745
|
+
*/
|
|
16746
|
+
mode: _enum(["navigate", "background"]).optional()
|
|
16747
|
+
});
|
|
16748
|
+
/** A delivery parameter value: what a notifier declared it takes. */
|
|
16749
|
+
var DeliveryParamValueSchema = union([
|
|
16750
|
+
string(),
|
|
16751
|
+
number(),
|
|
16752
|
+
boolean()
|
|
16753
|
+
]);
|
|
16754
|
+
/**
|
|
16755
|
+
* One extra delivery parameter a notifier KIND declares (D938). A rule that
|
|
16756
|
+
* addresses a target of that kind may set it; a kind that declares nothing is
|
|
16757
|
+
* never handed one. The admin UI derives its control from this descriptor —
|
|
16758
|
+
* no editor knows a service by name.
|
|
16759
|
+
*
|
|
16760
|
+
* Optional fields are `.optional()`, never `.default()` (a Zod default does not
|
|
16761
|
+
* run on the addon cap path).
|
|
16762
|
+
*/
|
|
16763
|
+
var DeliveryParamSchema = object({
|
|
16764
|
+
key: string().min(1),
|
|
16765
|
+
label: string(),
|
|
16766
|
+
/** i18n key of the label, resolved by the declaring addon's namespace. */
|
|
16767
|
+
labelKey: string().optional(),
|
|
16768
|
+
type: _enum([
|
|
16769
|
+
"enum",
|
|
16770
|
+
"number",
|
|
16771
|
+
"boolean",
|
|
16772
|
+
"string"
|
|
16773
|
+
]),
|
|
16774
|
+
/** `enum` only: the closed set of accepted values. */
|
|
16775
|
+
options: array(object({
|
|
16776
|
+
value: string(),
|
|
16777
|
+
label: string()
|
|
16778
|
+
})).optional(),
|
|
16779
|
+
/** `number` only. */
|
|
16780
|
+
min: number().optional(),
|
|
16781
|
+
max: number().optional(),
|
|
16782
|
+
/** What the notifier does when the rule sets nothing; shown as the placeholder. */
|
|
16783
|
+
default: DeliveryParamValueSchema.optional()
|
|
16784
|
+
});
|
|
16785
|
+
/**
|
|
16786
|
+
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
16787
|
+
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
16788
|
+
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
16789
|
+
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
16790
|
+
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
16791
|
+
* `priority` for that one target.
|
|
16792
|
+
*/
|
|
16793
|
+
var NotificationSchema = object({
|
|
16794
|
+
body: string(),
|
|
16795
|
+
title: string().optional(),
|
|
16796
|
+
format: NotificationFormatSchema.default("text"),
|
|
16797
|
+
priority: number().int().min(1).max(5).default(3),
|
|
16798
|
+
level: string().optional(),
|
|
16799
|
+
attachments: array(AttachmentSchema).optional(),
|
|
16800
|
+
clickUrl: string().optional(),
|
|
16801
|
+
actions: array(NotificationActionSchema).optional(),
|
|
16802
|
+
sound: string().optional(),
|
|
16803
|
+
ttl: number().optional(),
|
|
16804
|
+
tag: string().optional(),
|
|
16805
|
+
deviceId: number().optional(),
|
|
16806
|
+
eventId: string().optional(),
|
|
16807
|
+
metadata: record(string(), unknown()).optional(),
|
|
16808
|
+
/**
|
|
16809
|
+
* Values for the parameters the target's kind DECLARES in
|
|
16810
|
+
* {@link TargetKindSchema} `deliveryParams`. Already validated against the
|
|
16811
|
+
* declaration by the sender; a notifier reads only the keys it declared.
|
|
16812
|
+
*/
|
|
16813
|
+
delivery: record(string(), DeliveryParamValueSchema).optional()
|
|
16814
|
+
});
|
|
16815
|
+
/** One declared native severity/priority level for a kind. */
|
|
16816
|
+
var TargetKindLevelSchema = object({
|
|
16817
|
+
id: string(),
|
|
16818
|
+
label: string(),
|
|
16819
|
+
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
16820
|
+
ordinal: number().int().min(1).max(5).nullable(),
|
|
16821
|
+
flags: object({
|
|
16822
|
+
critical: boolean().optional(),
|
|
16823
|
+
silent: boolean().optional(),
|
|
16824
|
+
noPush: boolean().optional()
|
|
16825
|
+
}).optional(),
|
|
16826
|
+
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
16827
|
+
requires: array(string()).optional(),
|
|
16828
|
+
description: string().optional()
|
|
16829
|
+
});
|
|
16830
|
+
/** The full capability block consulted before dispatch. */
|
|
16831
|
+
var TargetKindCapsSchema = object({
|
|
16832
|
+
attachments: object({
|
|
16833
|
+
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
16834
|
+
mode: _enum([
|
|
16835
|
+
"url",
|
|
16836
|
+
"bytes",
|
|
16837
|
+
"both"
|
|
16838
|
+
]),
|
|
16839
|
+
max: number().int().nonnegative(),
|
|
16840
|
+
maxBytes: number().int().positive().optional()
|
|
16841
|
+
}),
|
|
16842
|
+
/** Max action buttons (0 = none). */
|
|
16843
|
+
actions: number().int().nonnegative(),
|
|
16844
|
+
/**
|
|
16845
|
+
* Whether this kind renders a per-action ICON.
|
|
16846
|
+
*
|
|
16847
|
+
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
16848
|
+
* run on the addon cap path — three production failures in one day taught
|
|
16849
|
+
* this repo that once. Absent is read as false by the degrade engine, which
|
|
16850
|
+
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
16851
|
+
* assumed and dropped costs the operator's trust in the field.
|
|
16852
|
+
*/
|
|
16853
|
+
actionIcons: boolean().optional(),
|
|
16854
|
+
levels: array(TargetKindLevelSchema),
|
|
16855
|
+
format: array(NotificationFormatSchema),
|
|
16856
|
+
clickUrl: boolean(),
|
|
16857
|
+
sound: boolean(),
|
|
16858
|
+
ttl: boolean(),
|
|
16859
|
+
bodyMaxLen: number().int().positive()
|
|
16860
|
+
});
|
|
16861
|
+
/**
|
|
16862
|
+
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
16863
|
+
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
16864
|
+
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
16865
|
+
* the union is large and not meant for runtime validation here; the exported
|
|
16866
|
+
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
16867
|
+
*/
|
|
16868
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
16869
|
+
var TargetKindSchema = object({
|
|
16870
|
+
kind: string(),
|
|
16871
|
+
label: string(),
|
|
16872
|
+
icon: string(),
|
|
16873
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
16874
|
+
addonId: string(),
|
|
16875
|
+
/**
|
|
16876
|
+
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
16877
|
+
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
16878
|
+
* when the addon bundles no icon for that kind — the client then falls back
|
|
16879
|
+
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
16880
|
+
*
|
|
16881
|
+
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
16882
|
+
* client, and a native client joins it onto its own hub base.
|
|
16883
|
+
*
|
|
16884
|
+
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
16885
|
+
* field that survived only because the runtime cap-router forwards provider
|
|
16886
|
+
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
16887
|
+
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
16888
|
+
* broken silently the moment output validation was tightened anywhere.
|
|
16889
|
+
*/
|
|
16890
|
+
iconUrl: string().optional(),
|
|
16891
|
+
/**
|
|
16892
|
+
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
16893
|
+
*
|
|
16894
|
+
* The server knows this and therefore says it, because the client cannot
|
|
16895
|
+
* safely guess: a React-Native client renders SVG and raster through two
|
|
16896
|
+
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
16897
|
+
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
16898
|
+
* placeholder glyph for every vector icon while the web build looked fine.
|
|
16899
|
+
*
|
|
16900
|
+
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
16901
|
+
* not been updated — a client that cannot determine the type should prefer
|
|
16902
|
+
* its raster path, which is the safe default for an unknown image.
|
|
16903
|
+
*/
|
|
16904
|
+
iconMediaType: string().optional(),
|
|
16905
|
+
configSchema: ConfigSchemaPassthrough$3,
|
|
16906
|
+
/**
|
|
16907
|
+
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
16908
|
+
* base URL, instance, chat, recipient). A save that changes any of them
|
|
16909
|
+
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
16910
|
+
* editor uses this to ask for the secret on exactly those edits instead of
|
|
16911
|
+
* on every edit.
|
|
16912
|
+
*
|
|
16913
|
+
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
16914
|
+
* kinds) and from a provider older than this field — an editor that finds
|
|
16915
|
+
* it absent must assume ANY edit may need the secret.
|
|
16916
|
+
*/
|
|
16917
|
+
destinationKeys: array(string()).optional(),
|
|
16918
|
+
supportsDiscovery: boolean(),
|
|
16919
|
+
/**
|
|
16920
|
+
* Extra delivery parameters a rule may set for a target of this kind (D938).
|
|
16921
|
+
* Absent = none; the rule editor then offers nothing.
|
|
16922
|
+
*/
|
|
16923
|
+
deliveryParams: array(DeliveryParamSchema).optional(),
|
|
16924
|
+
caps: TargetKindCapsSchema
|
|
16925
|
+
});
|
|
16926
|
+
/**
|
|
16927
|
+
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
16928
|
+
* (return a presence marker only) when serving `listTargets` — never
|
|
16929
|
+
* round-trip a stored secret to the UI.
|
|
16930
|
+
*/
|
|
16931
|
+
var TargetSchema = object({
|
|
16932
|
+
id: string(),
|
|
16933
|
+
name: string(),
|
|
16934
|
+
kind: string(),
|
|
16935
|
+
addonId: string(),
|
|
16936
|
+
enabled: boolean(),
|
|
16937
|
+
config: record(string(), unknown())
|
|
16938
|
+
});
|
|
16939
|
+
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
16940
|
+
var DiscoveredTargetSchema = object({
|
|
16941
|
+
kind: string(),
|
|
16942
|
+
suggestedName: string(),
|
|
16943
|
+
config: record(string(), unknown())
|
|
16944
|
+
});
|
|
16945
|
+
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
16946
|
+
var RenderedAsSchema = object({
|
|
16947
|
+
level: string(),
|
|
16948
|
+
format: NotificationFormatSchema,
|
|
16949
|
+
attachmentsSent: number().int().nonnegative(),
|
|
16950
|
+
actionsSent: number().int().nonnegative(),
|
|
16951
|
+
truncated: boolean(),
|
|
16952
|
+
dropped: array(string())
|
|
16953
|
+
});
|
|
16954
|
+
var SendResultSchema = object({
|
|
16955
|
+
success: boolean(),
|
|
16956
|
+
error: string().optional(),
|
|
16957
|
+
renderedAs: RenderedAsSchema.optional()
|
|
16958
|
+
});
|
|
16959
|
+
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
16960
|
+
var TestResultSchema = SendResultSchema;
|
|
16961
|
+
/**
|
|
16962
|
+
* Pre-save credential probe against a partial (unsaved) target config.
|
|
16963
|
+
* `resolvedUrl` never includes secrets (token stripped from path).
|
|
16964
|
+
*/
|
|
16965
|
+
var ProbeTargetConfigResultSchema = object({
|
|
16966
|
+
ok: boolean(),
|
|
16967
|
+
resolvedUrl: string(),
|
|
16968
|
+
httpStatus: number().int().optional(),
|
|
16969
|
+
stateInstance: string().optional(),
|
|
16970
|
+
error: string().optional()
|
|
16971
|
+
});
|
|
16972
|
+
var notificationOutputCapability = {
|
|
16973
|
+
name: "notification-output",
|
|
16974
|
+
scope: "system",
|
|
16975
|
+
mode: "collection",
|
|
16976
|
+
methods: {
|
|
16977
|
+
listTargetKinds: method(object({}), array(TargetKindSchema)),
|
|
16978
|
+
listTargets: method(object({}), array(TargetSchema), { auth: "admin" }),
|
|
16979
|
+
/**
|
|
16980
|
+
* One target WITH secrets — admin edit / duplicate hydration only.
|
|
16981
|
+
* `listTargets` stays redacted; the editor loads this so password fields
|
|
16982
|
+
* are filled (masked in the input) and a save can round-trip the token
|
|
16983
|
+
* when a destination changes (Duplicate with a new chatId, etc.).
|
|
16984
|
+
*/
|
|
16985
|
+
getTarget: method(object({ targetId: string() }), TargetSchema, { auth: "admin" }),
|
|
16986
|
+
discoverTargets: method(object({
|
|
16987
|
+
kind: string(),
|
|
16988
|
+
config: record(string(), unknown()).optional(),
|
|
16989
|
+
/**
|
|
16990
|
+
* When editing / duplicating, the blank password field never round-trips
|
|
16991
|
+
* the stored token. Pass the source target id so the provider can restore
|
|
16992
|
+
* secrets server-side before dialling (same merge as upsert).
|
|
16993
|
+
*/
|
|
16994
|
+
targetId: string().optional()
|
|
16995
|
+
}), array(DiscoveredTargetSchema), { auth: "admin" }),
|
|
16996
|
+
probeTargetConfig: method(object({
|
|
16997
|
+
kind: string(),
|
|
16998
|
+
config: record(string(), unknown()),
|
|
16999
|
+
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
17000
|
+
targetId: string().optional()
|
|
17001
|
+
}), ProbeTargetConfigResultSchema, {
|
|
17002
|
+
kind: "mutation",
|
|
17003
|
+
auth: "admin"
|
|
17004
|
+
}),
|
|
17005
|
+
send: method(object({
|
|
17006
|
+
targetId: string(),
|
|
17007
|
+
notification: NotificationSchema
|
|
17008
|
+
}), SendResultSchema, {
|
|
17009
|
+
kind: "mutation",
|
|
17010
|
+
auth: "admin"
|
|
17011
|
+
}),
|
|
17012
|
+
testTarget: method(object({
|
|
17013
|
+
targetId: string(),
|
|
17014
|
+
sample: NotificationSchema.optional()
|
|
17015
|
+
}), TestResultSchema, {
|
|
17016
|
+
kind: "mutation",
|
|
17017
|
+
auth: "admin"
|
|
17018
|
+
}),
|
|
17019
|
+
upsertTarget: method(object({
|
|
17020
|
+
target: TargetSchema,
|
|
17021
|
+
/**
|
|
17022
|
+
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
17023
|
+
* existing row. The UI never holds the stored token; blank / redacted
|
|
17024
|
+
* password fields are restored from this source (D634 still applies if
|
|
17025
|
+
* destinations diverge without a re-typed secret).
|
|
17026
|
+
*/
|
|
17027
|
+
cloneSecretsFrom: string().optional()
|
|
17028
|
+
}), TargetSchema, {
|
|
17029
|
+
kind: "mutation",
|
|
17030
|
+
auth: "admin"
|
|
17031
|
+
}),
|
|
17032
|
+
deleteTarget: method(object({ targetId: string() }), _void(), {
|
|
17033
|
+
kind: "mutation",
|
|
17034
|
+
auth: "admin"
|
|
17035
|
+
}),
|
|
17036
|
+
setTargetEnabled: method(object({
|
|
17037
|
+
targetId: string(),
|
|
17038
|
+
enabled: boolean()
|
|
17039
|
+
}), _void(), {
|
|
17040
|
+
kind: "mutation",
|
|
17041
|
+
auth: "admin"
|
|
17042
|
+
})
|
|
17043
|
+
}
|
|
17044
|
+
};
|
|
17045
|
+
function errorText(err) {
|
|
17046
|
+
return err instanceof Error ? err.message : String(err);
|
|
17047
|
+
}
|
|
17048
|
+
/** `actions.run` flattens a refusal to `[code] text`; the code leads. */
|
|
17049
|
+
function refusalFromThrown(message) {
|
|
17050
|
+
if (message.startsWith("[busy]")) return "already-running";
|
|
17051
|
+
if (message.startsWith("[not-found]")) return "not-found";
|
|
17052
|
+
return null;
|
|
17053
|
+
}
|
|
17054
|
+
async function runExportedAction(api, log, input) {
|
|
17055
|
+
const { exporter, actionId } = input;
|
|
17056
|
+
const tags = { actionId };
|
|
17057
|
+
const refuse = (reason, detail) => {
|
|
17058
|
+
log.warn(`${exporter}: exported action not run — ${reason}`, {
|
|
17059
|
+
tags,
|
|
17060
|
+
meta: {
|
|
17061
|
+
reason,
|
|
17062
|
+
...detail ? { detail } : {}
|
|
17063
|
+
}
|
|
17064
|
+
});
|
|
17065
|
+
return {
|
|
17066
|
+
status: "refused",
|
|
17067
|
+
reason
|
|
17068
|
+
};
|
|
17069
|
+
};
|
|
17070
|
+
try {
|
|
17071
|
+
const result = await api.actions.run.mutate({ actionId });
|
|
17072
|
+
if (result.outcome === "skipped-overlap") return refuse("already-running", result.message);
|
|
17073
|
+
if (result.outcome === "skipped-cooldown") return refuse("cooldown", result.message);
|
|
17074
|
+
if (result.outcome === "failed" || result.outcome === "aborted") {
|
|
17075
|
+
const message = result.message ?? result.outcome;
|
|
17076
|
+
log.warn(`${exporter}: exported action failed`, {
|
|
17077
|
+
tags,
|
|
17078
|
+
meta: {
|
|
17079
|
+
outcome: result.outcome,
|
|
17080
|
+
message
|
|
17081
|
+
}
|
|
17082
|
+
});
|
|
17083
|
+
return {
|
|
17084
|
+
status: "failed",
|
|
17085
|
+
message
|
|
17086
|
+
};
|
|
17087
|
+
}
|
|
17088
|
+
log.info(`${exporter}: exported action ran`, {
|
|
17089
|
+
tags,
|
|
17090
|
+
meta: { outcome: result.outcome }
|
|
17091
|
+
});
|
|
17092
|
+
return { status: "started" };
|
|
17093
|
+
} catch (err) {
|
|
17094
|
+
const message = errorText(err);
|
|
17095
|
+
const reason = refusalFromThrown(message);
|
|
17096
|
+
if (reason !== null) return refuse(reason, message);
|
|
17097
|
+
log.warn(`${exporter}: exported action failed`, {
|
|
17098
|
+
tags,
|
|
17099
|
+
meta: { message }
|
|
17100
|
+
});
|
|
17101
|
+
return {
|
|
17102
|
+
status: "failed",
|
|
17103
|
+
message
|
|
17104
|
+
};
|
|
17105
|
+
}
|
|
17106
|
+
}
|
|
17107
|
+
/** Send through one CamStack notification target on behalf of an exporter. */
|
|
17108
|
+
async function sendToExportedTarget(api, log, input) {
|
|
17109
|
+
const { exporter, targetId } = input;
|
|
17110
|
+
const tags = { targetId };
|
|
17111
|
+
try {
|
|
17112
|
+
const target = (await api.notificationOutput.listTargets.query({})).find((t) => t.id === targetId);
|
|
17113
|
+
if (target === void 0) {
|
|
17114
|
+
log.warn(`${exporter}: exported notifier not used — target-gone`, {
|
|
17115
|
+
tags,
|
|
17116
|
+
meta: { reason: "target-gone" }
|
|
17117
|
+
});
|
|
17118
|
+
return {
|
|
17119
|
+
status: "refused",
|
|
17120
|
+
reason: "target-gone"
|
|
17121
|
+
};
|
|
17122
|
+
}
|
|
17123
|
+
if (!target.enabled) {
|
|
17124
|
+
log.warn(`${exporter}: exported notifier not used — target-disabled`, {
|
|
17125
|
+
tags,
|
|
17126
|
+
meta: { reason: "target-disabled" }
|
|
17127
|
+
});
|
|
17128
|
+
return {
|
|
17129
|
+
status: "refused",
|
|
17130
|
+
reason: "target-disabled"
|
|
17131
|
+
};
|
|
17132
|
+
}
|
|
17133
|
+
const result = await api.notificationOutput.send.mutate({
|
|
17134
|
+
targetId,
|
|
17135
|
+
addonId: target.addonId,
|
|
17136
|
+
notification: NotificationSchema.parse(input.notification)
|
|
17137
|
+
});
|
|
17138
|
+
if (!result.success) {
|
|
17139
|
+
const message = result.error ?? "send failed";
|
|
17140
|
+
log.warn(`${exporter}: exported notifier send failed`, {
|
|
17141
|
+
tags,
|
|
17142
|
+
meta: { message }
|
|
17143
|
+
});
|
|
17144
|
+
return {
|
|
17145
|
+
status: "failed",
|
|
17146
|
+
message
|
|
17147
|
+
};
|
|
17148
|
+
}
|
|
17149
|
+
return { status: "sent" };
|
|
17150
|
+
} catch (err) {
|
|
17151
|
+
const message = errorText(err);
|
|
17152
|
+
log.warn(`${exporter}: exported notifier send failed`, {
|
|
17153
|
+
tags,
|
|
17154
|
+
meta: { message }
|
|
17155
|
+
});
|
|
17156
|
+
return {
|
|
17157
|
+
status: "failed",
|
|
17158
|
+
message
|
|
17159
|
+
};
|
|
17160
|
+
}
|
|
17161
|
+
}
|
|
17162
|
+
/**
|
|
17163
|
+
* Split an exporter's selection into what still exists and what was deleted.
|
|
17164
|
+
* Devices are never judged here (their lifecycle is the tombstone, D865).
|
|
17165
|
+
* Callers pass ids from a query that ANSWERED — an unanswered list must not
|
|
17166
|
+
* reach this function (D49: a failed read changes nothing).
|
|
17167
|
+
*/
|
|
17168
|
+
function pruneDeletedEntities(selection, live, log, exporter) {
|
|
17169
|
+
const kept = [];
|
|
17170
|
+
const dropped = [];
|
|
17171
|
+
for (const ref of selection) if (ref.kind === "action" && !live.actionIds.has(ref.actionId)) {
|
|
17172
|
+
dropped.push(ref);
|
|
17173
|
+
log.warn(`${exporter}: exported action was deleted — dropped from the export`, { tags: { actionId: ref.actionId } });
|
|
17174
|
+
} else if (ref.kind === "notifier" && !live.targetIds.has(ref.targetId)) {
|
|
17175
|
+
dropped.push(ref);
|
|
17176
|
+
log.warn(`${exporter}: exported notifier target was deleted — dropped from the export`, { tags: { targetId: ref.targetId } });
|
|
17177
|
+
} else kept.push(ref);
|
|
17178
|
+
return {
|
|
17179
|
+
kept,
|
|
17180
|
+
dropped
|
|
17181
|
+
};
|
|
17182
|
+
}
|
|
17183
|
+
/**
|
|
16450
17184
|
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
16451
17185
|
* surface that admin-ui consumes through `useAddonPagesListPages()`.
|
|
16452
17186
|
*
|
|
@@ -16478,52 +17212,6 @@ var AddonPageInfoSchema = object({
|
|
|
16478
17212
|
bundleUrl: string()
|
|
16479
17213
|
});
|
|
16480
17214
|
method(_void(), array(AddonPageInfoSchema).readonly());
|
|
16481
|
-
/**
|
|
16482
|
-
* `addon-pages-source` — collection cap exposing per-provider raw page
|
|
16483
|
-
* declarations. Every addon that contributes a UI page registers a
|
|
16484
|
-
* provider here. The hub-side singleton aggregator (`addon-pages` cap,
|
|
16485
|
-
* see `addon-pages.cap.ts`) walks this collection, stamps versioned
|
|
16486
|
-
* `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
|
|
16487
|
-
* that admin-ui consumes.
|
|
16488
|
-
*
|
|
16489
|
-
* The split exists because the public listing has a different output
|
|
16490
|
-
* shape than the per-provider raw declarations, and we want both ends
|
|
16491
|
-
* to flow through codegen instead of relying on a hand-written wrapper.
|
|
16492
|
-
*/
|
|
16493
|
-
var AddonPageDeclarationSchema = object({
|
|
16494
|
-
id: string(),
|
|
16495
|
-
label: string(),
|
|
16496
|
-
icon: string(),
|
|
16497
|
-
path: string(),
|
|
16498
|
-
/**
|
|
16499
|
-
* Module Federation remote name — must match the `name` field on the
|
|
16500
|
-
* page addon's `federation()` plugin config. Used by admin-ui's
|
|
16501
|
-
* `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
|
|
16502
|
-
* Conventionally `addon_<id>_page` (snake_case; MF names cannot
|
|
16503
|
-
* contain hyphens).
|
|
16504
|
-
*/
|
|
16505
|
-
remoteName: string(),
|
|
16506
|
-
/**
|
|
16507
|
-
* Bundle filename inside the addon's `dist/` dir served at
|
|
16508
|
-
* `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
|
|
16509
|
-
* is always `'remoteEntry.js'`; the value is kept on the metadata so
|
|
16510
|
-
* the static-file route can compute an mtime-based cache-buster URL
|
|
16511
|
-
* without a separate filesystem stat.
|
|
16512
|
-
*/
|
|
16513
|
-
bundle: string(),
|
|
16514
|
-
/**
|
|
16515
|
-
* Sidebar section this page docks into. Well-known ids: `'detection'`,
|
|
16516
|
-
* `'cluster'`, `'administration'` — the page renders inside that group.
|
|
16517
|
-
* Any OTHER string creates (or joins) a custom section rendered after
|
|
16518
|
-
* the built-in groups; its label comes from `sectionLabel` (first
|
|
16519
|
-
* declaration wins), falling back to the id. Absent → the legacy
|
|
16520
|
-
* "Addon Pages" group.
|
|
16521
|
-
*/
|
|
16522
|
-
section: string().optional(),
|
|
16523
|
-
/** Display label for a CUSTOM `section` id (ignored for well-known ids). */
|
|
16524
|
-
sectionLabel: string().optional()
|
|
16525
|
-
});
|
|
16526
|
-
method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
|
|
16527
17215
|
var AddonHttpRouteSchema = object({
|
|
16528
17216
|
method: _enum([
|
|
16529
17217
|
"GET",
|
|
@@ -19240,7 +19928,11 @@ var CompositionProblemSchema = object({
|
|
|
19240
19928
|
"mixed-quantity",
|
|
19241
19929
|
"helper-mismatch",
|
|
19242
19930
|
"member-constraint",
|
|
19243
|
-
"retention"
|
|
19931
|
+
"retention",
|
|
19932
|
+
"command-method-unknown",
|
|
19933
|
+
"command-source-unknown",
|
|
19934
|
+
"command-input-unknown",
|
|
19935
|
+
"command-input-missing"
|
|
19244
19936
|
]),
|
|
19245
19937
|
path: string(),
|
|
19246
19938
|
message: string()
|
|
@@ -19279,7 +19971,9 @@ var CompositionFieldRoleSchema = _enum([
|
|
|
19279
19971
|
var CompositionFieldItemSchema = object({
|
|
19280
19972
|
arrayPath: string(),
|
|
19281
19973
|
key: string(),
|
|
19282
|
-
label: string()
|
|
19974
|
+
label: string(),
|
|
19975
|
+
/** True when the item carries a `reset` forward: the composer writes `resettable` from this (D967). */
|
|
19976
|
+
resettable: boolean().optional()
|
|
19283
19977
|
});
|
|
19284
19978
|
var CompositionFieldPlanSchema = object({
|
|
19285
19979
|
cap: string(),
|
|
@@ -19484,7 +20178,7 @@ Object.freeze({
|
|
|
19484
20178
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
19485
20179
|
* Data, not a form default written into the block.
|
|
19486
20180
|
*/
|
|
19487
|
-
var NOW$
|
|
20181
|
+
var NOW$3 = {
|
|
19488
20182
|
fieldPath: "lastFetchedAt",
|
|
19489
20183
|
expr: "now"
|
|
19490
20184
|
};
|
|
@@ -19536,7 +20230,7 @@ var GROUP_TEMPLATES = [
|
|
|
19536
20230
|
quantity: "temperature",
|
|
19537
20231
|
defaultAggregate: "mean",
|
|
19538
20232
|
empty: "null",
|
|
19539
|
-
companions: [NOW$
|
|
20233
|
+
companions: [NOW$3],
|
|
19540
20234
|
alsoFrom: {
|
|
19541
20235
|
deviceTypes: CLIMATE_DEVICES,
|
|
19542
20236
|
accept: {
|
|
@@ -19554,7 +20248,7 @@ var GROUP_TEMPLATES = [
|
|
|
19554
20248
|
quantity: "humidity",
|
|
19555
20249
|
defaultAggregate: "mean",
|
|
19556
20250
|
empty: "null",
|
|
19557
|
-
companions: [NOW$
|
|
20251
|
+
companions: [NOW$3],
|
|
19558
20252
|
alsoFrom: {
|
|
19559
20253
|
deviceTypes: CLIMATE_DEVICES,
|
|
19560
20254
|
accept: {
|
|
@@ -19572,7 +20266,7 @@ var GROUP_TEMPLATES = [
|
|
|
19572
20266
|
quantity: "pressure",
|
|
19573
20267
|
defaultAggregate: "mean",
|
|
19574
20268
|
empty: "null",
|
|
19575
|
-
companions: [NOW$
|
|
20269
|
+
companions: [NOW$3]
|
|
19576
20270
|
}),
|
|
19577
20271
|
numeric({
|
|
19578
20272
|
id: "illuminance",
|
|
@@ -19583,7 +20277,7 @@ var GROUP_TEMPLATES = [
|
|
|
19583
20277
|
quantity: "illuminance",
|
|
19584
20278
|
defaultAggregate: "mean",
|
|
19585
20279
|
empty: "null",
|
|
19586
|
-
companions: [NOW$
|
|
20280
|
+
companions: [NOW$3]
|
|
19587
20281
|
}),
|
|
19588
20282
|
numeric({
|
|
19589
20283
|
id: "power",
|
|
@@ -19594,7 +20288,7 @@ var GROUP_TEMPLATES = [
|
|
|
19594
20288
|
quantity: "power",
|
|
19595
20289
|
defaultAggregate: "sum",
|
|
19596
20290
|
empty: "omit",
|
|
19597
|
-
companions: [NOW$
|
|
20291
|
+
companions: [NOW$3]
|
|
19598
20292
|
}),
|
|
19599
20293
|
numeric({
|
|
19600
20294
|
id: "energy",
|
|
@@ -19605,7 +20299,7 @@ var GROUP_TEMPLATES = [
|
|
|
19605
20299
|
quantity: "energy",
|
|
19606
20300
|
defaultAggregate: "sum",
|
|
19607
20301
|
empty: "omit",
|
|
19608
|
-
companions: [NOW$
|
|
20302
|
+
companions: [NOW$3]
|
|
19609
20303
|
}),
|
|
19610
20304
|
numeric({
|
|
19611
20305
|
id: "battery",
|
|
@@ -19640,7 +20334,7 @@ var GROUP_TEMPLATES = [
|
|
|
19640
20334
|
quantity: "device",
|
|
19641
20335
|
defaultAggregate: "mean",
|
|
19642
20336
|
empty: "null",
|
|
19643
|
-
companions: [NOW$
|
|
20337
|
+
companions: [NOW$3]
|
|
19644
20338
|
}),
|
|
19645
20339
|
binary({
|
|
19646
20340
|
id: "motion",
|
|
@@ -22335,6 +23029,36 @@ var ExposeInputSchema = object({
|
|
|
22335
23029
|
capabilities: array(string()).optional()
|
|
22336
23030
|
});
|
|
22337
23031
|
var UnexposeInputSchema = object({ deviceId: string() });
|
|
23032
|
+
/**
|
|
23033
|
+
* What an exporter may expose besides devices (D968). An action and a
|
|
23034
|
+
* notification target are LIST ENTRIES, never devices (D954): they enter an
|
|
23035
|
+
* exporter's own selection, not the device registry.
|
|
23036
|
+
*/
|
|
23037
|
+
var ExportEntityKindSchema = _enum([
|
|
23038
|
+
"device",
|
|
23039
|
+
"action",
|
|
23040
|
+
"notifier"
|
|
23041
|
+
]);
|
|
23042
|
+
var ExportEntityRefSchema = discriminatedUnion("kind", [
|
|
23043
|
+
object({
|
|
23044
|
+
kind: literal("device"),
|
|
23045
|
+
deviceId: string()
|
|
23046
|
+
}),
|
|
23047
|
+
object({
|
|
23048
|
+
kind: literal("action"),
|
|
23049
|
+
actionId: string().min(1)
|
|
23050
|
+
}),
|
|
23051
|
+
object({
|
|
23052
|
+
kind: literal("notifier"),
|
|
23053
|
+
targetId: string().min(1)
|
|
23054
|
+
})
|
|
23055
|
+
]);
|
|
23056
|
+
var ExposedEntitySchema = object({
|
|
23057
|
+
entity: ExportEntityRefSchema,
|
|
23058
|
+
exposedAs: string().optional()
|
|
23059
|
+
});
|
|
23060
|
+
var ExposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
|
|
23061
|
+
var UnexposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
|
|
22338
23062
|
var deviceExportCapability = {
|
|
22339
23063
|
name: "device-export",
|
|
22340
23064
|
scope: "system",
|
|
@@ -22364,6 +23088,16 @@ var deviceExportCapability = {
|
|
|
22364
23088
|
unexposeDevice: method(UnexposeInputSchema, _void(), {
|
|
22365
23089
|
kind: "mutation",
|
|
22366
23090
|
auth: "admin"
|
|
23091
|
+
}),
|
|
23092
|
+
listSupportedEntityKinds: optionalMethod(_void(), array(ExportEntityKindSchema)),
|
|
23093
|
+
listExposedEntities: optionalMethod(_void(), array(ExposedEntitySchema)),
|
|
23094
|
+
exposeEntity: optionalMethod(ExposeEntityInputSchema, _void(), {
|
|
23095
|
+
kind: "mutation",
|
|
23096
|
+
auth: "admin"
|
|
23097
|
+
}),
|
|
23098
|
+
unexposeEntity: optionalMethod(UnexposeEntityInputSchema, _void(), {
|
|
23099
|
+
kind: "mutation",
|
|
23100
|
+
auth: "admin"
|
|
22367
23101
|
})
|
|
22368
23102
|
}
|
|
22369
23103
|
};
|
|
@@ -43587,6 +44321,14 @@ method(object({
|
|
|
43587
44321
|
kind: "mutation",
|
|
43588
44322
|
auth: "admin"
|
|
43589
44323
|
});
|
|
44324
|
+
method(object({
|
|
44325
|
+
nodeId: string().min(1),
|
|
44326
|
+
/** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
|
|
44327
|
+
removedAt: number()
|
|
44328
|
+
}), object({ counts: record(string(), number().int().nonnegative()) }), {
|
|
44329
|
+
kind: "mutation",
|
|
44330
|
+
auth: "admin"
|
|
44331
|
+
});
|
|
43590
44332
|
/**
|
|
43591
44333
|
* `device-removal-hook` — the reliable way an addon learns a device was DELETED,
|
|
43592
44334
|
* so it can delete what it keeps for that device (D865).
|
|
@@ -44307,7 +45049,7 @@ var LlmProviderWidgetSchema = object({
|
|
|
44307
45049
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
44308
45050
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
44309
45051
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
44310
|
-
var ConfigSchemaPassthrough$
|
|
45052
|
+
var ConfigSchemaPassthrough$2 = unknown();
|
|
44311
45053
|
var LlmProfileKindDescriptorSchema = object({
|
|
44312
45054
|
kind: LlmProfileKindSchema,
|
|
44313
45055
|
label: string(),
|
|
@@ -44316,9 +45058,9 @@ var LlmProfileKindDescriptorSchema = object({
|
|
|
44316
45058
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
44317
45059
|
addonId: string(),
|
|
44318
45060
|
/** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
|
|
44319
|
-
connectionSchema: ConfigSchemaPassthrough$
|
|
45061
|
+
connectionSchema: ConfigSchemaPassthrough$2,
|
|
44320
45062
|
/** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
|
|
44321
|
-
advancedSchema: ConfigSchemaPassthrough$
|
|
45063
|
+
advancedSchema: ConfigSchemaPassthrough$2.optional(),
|
|
44322
45064
|
/** The kind's defaults; a model from `listModels` may override any of them. */
|
|
44323
45065
|
features: LlmModelFeaturesSchema,
|
|
44324
45066
|
/** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
|
|
@@ -44844,10 +45586,10 @@ var CpuBreakdownSchema = object({
|
|
|
44844
45586
|
cores: number()
|
|
44845
45587
|
});
|
|
44846
45588
|
var MemoryInfoSchema = object({
|
|
44847
|
-
percent: number(),
|
|
45589
|
+
percent: number().nullable(),
|
|
44848
45590
|
totalBytes: number(),
|
|
44849
|
-
usedBytes: number(),
|
|
44850
|
-
availableBytes: number(),
|
|
45591
|
+
usedBytes: number().nullable(),
|
|
45592
|
+
availableBytes: number().nullable(),
|
|
44851
45593
|
swapUsedBytes: number(),
|
|
44852
45594
|
swapTotalBytes: number()
|
|
44853
45595
|
});
|
|
@@ -45151,8 +45893,9 @@ var GetLoadSeriesInputSchema = object({
|
|
|
45151
45893
|
});
|
|
45152
45894
|
var SystemMetricsSchema = object({
|
|
45153
45895
|
cpuPercent: number(),
|
|
45154
|
-
|
|
45155
|
-
|
|
45896
|
+
/** `null` when the memory sample failed or was not taken yet (D393). */
|
|
45897
|
+
memoryPercent: number().nullable(),
|
|
45898
|
+
memoryUsedMB: number().nullable(),
|
|
45156
45899
|
memoryTotalMB: number(),
|
|
45157
45900
|
diskPercent: number().optional(),
|
|
45158
45901
|
temperature: number().optional(),
|
|
@@ -45251,586 +45994,143 @@ var BrokerKindSchema = _enum(["external", "embedded"]);
|
|
|
45251
45994
|
*
|
|
45252
45995
|
* - `connected` — last probe completed a clean CONNACK
|
|
45253
45996
|
* - `disconnected` — no probe has run yet (cold cache)
|
|
45254
|
-
* - `auth-failed` — CONNACK refused with auth error (RC 4 / 5)
|
|
45255
|
-
* - `unreachable` — TCP connect timed out / refused
|
|
45256
|
-
* - `tls-error` — TLS handshake failed (cert / SNI / cipher)
|
|
45257
|
-
*/
|
|
45258
|
-
var BrokerStatusSchema$1 = _enum([
|
|
45259
|
-
"connected",
|
|
45260
|
-
"disconnected",
|
|
45261
|
-
"auth-failed",
|
|
45262
|
-
"unreachable",
|
|
45263
|
-
"tls-error"
|
|
45264
|
-
]);
|
|
45265
|
-
var BrokerInfoSchema$1 = object({
|
|
45266
|
-
id: string(),
|
|
45267
|
-
name: string(),
|
|
45268
|
-
url: string(),
|
|
45269
|
-
kind: BrokerKindSchema,
|
|
45270
|
-
status: BrokerStatusSchema$1,
|
|
45271
|
-
latencyMs: number().nullable(),
|
|
45272
|
-
error: string().optional(),
|
|
45273
|
-
/** Embedded brokers only: number of MQTT clients currently connected. */
|
|
45274
|
-
connectedClients: number().int().nonnegative().optional(),
|
|
45275
|
-
/** Epoch ms of the last live probe (external) or aedes snapshot (embedded). */
|
|
45276
|
-
lastCheckedAt: number().optional()
|
|
45277
|
-
});
|
|
45278
|
-
/**
|
|
45279
|
-
* Connection details — what a consumer needs to call
|
|
45280
|
-
* `mqtt.connect(url, options)`. We split URL + credentials so the
|
|
45281
|
-
* consumer can pass them as `mqtt.connect(url, { username, password })`
|
|
45282
|
-
* instead of stuffing creds into the URL (which leaks them into logs).
|
|
45283
|
-
*/
|
|
45284
|
-
var BrokerConnectionDetailsSchema = object({
|
|
45285
|
-
url: string(),
|
|
45286
|
-
username: string().optional(),
|
|
45287
|
-
password: string().optional(),
|
|
45288
|
-
/**
|
|
45289
|
-
* Suggested prefix for `clientId`. Each consumer should suffix this
|
|
45290
|
-
* with its own discriminator (addon id, instance id) so reconnects
|
|
45291
|
-
* don't kick each other off (MQTT spec: clientId must be unique per
|
|
45292
|
-
* broker).
|
|
45293
|
-
*/
|
|
45294
|
-
clientIdPrefix: string().optional()
|
|
45295
|
-
});
|
|
45296
|
-
var AddBrokerInputSchema = object({
|
|
45297
|
-
name: string().min(1),
|
|
45298
|
-
url: string().regex(/^(mqtt|mqtts|ws|wss):\/\//, "URL must start with mqtt(s):// or ws(s)://"),
|
|
45299
|
-
username: string().optional(),
|
|
45300
|
-
password: string().optional(),
|
|
45301
|
-
clientIdPrefix: string().optional()
|
|
45302
|
-
});
|
|
45303
|
-
var AddBrokerResultSchema = object({ id: string() });
|
|
45304
|
-
var IdInputSchema = object({ id: string() });
|
|
45305
|
-
var TestResultSchema$1 = discriminatedUnion("ok", [object({
|
|
45306
|
-
ok: literal(true),
|
|
45307
|
-
latencyMs: number()
|
|
45308
|
-
}), object({
|
|
45309
|
-
ok: literal(false),
|
|
45310
|
-
error: string()
|
|
45311
|
-
})]);
|
|
45312
|
-
var StartEmbeddedInputSchema = object({
|
|
45313
|
-
port: number().int().min(1).max(65535).default(1883),
|
|
45314
|
-
/** Allow anonymous connect (no username/password). Default: false. */
|
|
45315
|
-
allowAnonymous: boolean().default(false),
|
|
45316
|
-
/** Optional shared username/password for clients. */
|
|
45317
|
-
username: string().optional(),
|
|
45318
|
-
password: string().optional()
|
|
45319
|
-
});
|
|
45320
|
-
var StartEmbeddedResultSchema = object({
|
|
45321
|
-
id: string(),
|
|
45322
|
-
url: string()
|
|
45323
|
-
});
|
|
45324
|
-
var StatusSchema = object({
|
|
45325
|
-
brokerCount: number(),
|
|
45326
|
-
embeddedRunning: boolean()
|
|
45327
|
-
});
|
|
45328
|
-
method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
|
|
45329
|
-
kind: "mutation",
|
|
45330
|
-
auth: "admin"
|
|
45331
|
-
}), method(IdInputSchema, _void(), {
|
|
45332
|
-
kind: "mutation",
|
|
45333
|
-
auth: "admin"
|
|
45334
|
-
}), method(IdInputSchema, TestResultSchema$1, {
|
|
45335
|
-
kind: "mutation",
|
|
45336
|
-
auth: "admin"
|
|
45337
|
-
}), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, {
|
|
45338
|
-
kind: "mutation",
|
|
45339
|
-
auth: "admin"
|
|
45340
|
-
}), method(IdInputSchema, _void(), {
|
|
45341
|
-
kind: "mutation",
|
|
45342
|
-
auth: "admin"
|
|
45343
|
-
}), method(_void(), StatusSchema);
|
|
45344
|
-
var NetworkEndpointSchema = object({
|
|
45345
|
-
url: string(),
|
|
45346
|
-
hostname: string(),
|
|
45347
|
-
port: number(),
|
|
45348
|
-
protocol: _enum(["http", "https"]),
|
|
45349
|
-
/**
|
|
45350
|
-
* Whether this address survives a restart of its provider. `false` = it is
|
|
45351
|
-
* re-issued every time (a Quick tunnel's random `*.trycloudflare.com`); the
|
|
45352
|
-
* hub never selects such an address AUTOMATICALLY for a role (D860), though
|
|
45353
|
-
* it stays visible and an operator may still flag it. Absent = stable: every
|
|
45354
|
-
* provider except a Quick tunnel owns a name that does not change.
|
|
45355
|
-
*/
|
|
45356
|
-
stable: boolean().optional()
|
|
45357
|
-
});
|
|
45358
|
-
object({
|
|
45359
|
-
url: string(),
|
|
45360
|
-
/** Mirrors {@link NetworkEndpointSchema} `stable`; absent = stable. */
|
|
45361
|
-
stable: boolean().optional(),
|
|
45362
|
-
updated: boolean().optional()
|
|
45363
|
-
});
|
|
45364
|
-
var NetworkAccessStatusSchema = object({
|
|
45365
|
-
connected: boolean(),
|
|
45366
|
-
endpoint: NetworkEndpointSchema.nullable(),
|
|
45367
|
-
error: string().optional()
|
|
45368
|
-
});
|
|
45369
|
-
/**
|
|
45370
|
-
* Optional, richer endpoint shape returned by providers that expose
|
|
45371
|
-
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
45372
|
-
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
45373
|
-
* the originating provider config (mode + sourcePort) so the
|
|
45374
|
-
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
45375
|
-
* one endpoint just omit `listEndpoints` from their provider impl.
|
|
45376
|
-
*/
|
|
45377
|
-
var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
|
|
45378
|
-
/**
|
|
45379
|
-
* Stable id within the provider — typically `<mode>-<sourcePort>` so
|
|
45380
|
-
* the orchestrator can dedupe across `listEndpoints` polls.
|
|
45381
|
-
*/
|
|
45382
|
-
id: string(),
|
|
45383
|
-
/** Operator-facing label (mirrors `MeshEndpoint.label`). */
|
|
45384
|
-
label: string(),
|
|
45385
|
-
/** Optional provider-specific mode tag, used for icon/colour in admin UI. */
|
|
45386
|
-
mode: string().optional(),
|
|
45387
|
-
/** Originating local port the ingress fronts (informational). */
|
|
45388
|
-
sourcePort: number().optional()
|
|
45389
|
-
});
|
|
45390
|
-
method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
|
|
45391
|
-
/**
|
|
45392
|
-
* notification-output — canonical, capability-gated notification delivery.
|
|
45393
|
-
*
|
|
45394
|
-
* Apprise-derived model (see
|
|
45395
|
-
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
45396
|
-
* callers emit ONE canonical `Notification`; each provider declares a
|
|
45397
|
-
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
45398
|
-
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
45399
|
-
* message to what the kind supports — callers never special-case a service.
|
|
45400
|
-
*
|
|
45401
|
-
* DESIGN DECISIONS (locked):
|
|
45402
|
-
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
45403
|
-
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
45404
|
-
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
45405
|
-
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
45406
|
-
* alternative would fork the UI per addon and cannot host the
|
|
45407
|
-
* discovery→adopt flow.
|
|
45408
|
-
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
45409
|
-
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
45410
|
-
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
45411
|
-
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
45412
|
-
* `addonId` the generated collection router extracts from the call input.
|
|
45413
|
-
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
45414
|
-
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
45415
|
-
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
45416
|
-
* base64 fallback needed.
|
|
45417
|
-
*
|
|
45418
|
-
* TODO (deferred, closed-set change — separate decision): add
|
|
45419
|
-
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
45420
|
-
* admin "Integrations" page.
|
|
45421
|
-
*/
|
|
45422
|
-
/**
|
|
45423
|
-
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
45424
|
-
* adapter picks what it supports and the degrade engine filters the rest.
|
|
45425
|
-
*/
|
|
45426
|
-
var AttachmentMediaTypeSchema = _enum([
|
|
45427
|
-
"image",
|
|
45428
|
-
"video",
|
|
45429
|
-
"gif",
|
|
45430
|
-
"audio",
|
|
45431
|
-
"icon"
|
|
45432
|
-
]);
|
|
45433
|
-
/**
|
|
45434
|
-
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
45435
|
-
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
45436
|
-
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
45437
|
-
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
45438
|
-
*/
|
|
45439
|
-
var AttachmentSchema = object({
|
|
45440
|
-
mediaType: AttachmentMediaTypeSchema,
|
|
45441
|
-
url: string().optional(),
|
|
45442
|
-
bytes: _instanceof(Uint8Array).optional(),
|
|
45443
|
-
mime: string().optional(),
|
|
45444
|
-
name: string().optional()
|
|
45445
|
-
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
45446
|
-
var NotificationFormatSchema = _enum([
|
|
45447
|
-
"text",
|
|
45448
|
-
"markdown",
|
|
45449
|
-
"html"
|
|
45450
|
-
]);
|
|
45451
|
-
/**
|
|
45452
|
-
* The CLOSED icon vocabulary an action button may use.
|
|
45453
|
-
*
|
|
45454
|
-
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
45455
|
-
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
45456
|
-
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
45457
|
-
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
45458
|
-
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
45459
|
-
*
|
|
45460
|
-
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
45461
|
-
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
45462
|
-
* differently.
|
|
45463
|
-
*
|
|
45464
|
-
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
45465
|
-
*
|
|
45466
|
-
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
45467
|
-
* the whole set onto its own renderer's vocabulary through a
|
|
45468
|
-
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
45469
|
-
* fallback, so adding a member here fails every adapter's build until someone
|
|
45470
|
-
* decides its glyph, which is the only place that decision can be made
|
|
45471
|
-
* honestly.
|
|
45472
|
-
*
|
|
45473
|
-
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
45474
|
-
* `disarm` straight through; iOS feeds that string to
|
|
45475
|
-
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
45476
|
-
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
45477
|
-
* mapping, and "the field is documented" is not "the value renders".
|
|
45478
|
-
*
|
|
45479
|
-
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
45480
|
-
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
45481
|
-
* so an addon that emits a token the running hub does not know does not lose an
|
|
45482
|
-
* icon — its whole `send` fails Zod validation and the notification never
|
|
45483
|
-
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
45484
|
-
*/
|
|
45485
|
-
var NotificationActionIconSchema = _enum([
|
|
45486
|
-
"acknowledge",
|
|
45487
|
-
"dismiss",
|
|
45488
|
-
"silence",
|
|
45489
|
-
"snooze",
|
|
45490
|
-
"view",
|
|
45491
|
-
"play",
|
|
45492
|
-
"open",
|
|
45493
|
-
"close",
|
|
45494
|
-
"lock",
|
|
45495
|
-
"unlock",
|
|
45496
|
-
"arm",
|
|
45497
|
-
"arm-home",
|
|
45498
|
-
"arm-away",
|
|
45499
|
-
"arm-night",
|
|
45500
|
-
"disarm",
|
|
45501
|
-
"light",
|
|
45502
|
-
"alert",
|
|
45503
|
-
"camera"
|
|
45504
|
-
]);
|
|
45505
|
-
/** A single tap-through action button. */
|
|
45506
|
-
var NotificationActionSchema = object({
|
|
45507
|
-
id: string(),
|
|
45508
|
-
label: string(),
|
|
45509
|
-
url: string().optional(),
|
|
45510
|
-
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
45511
|
-
icon: NotificationActionIconSchema.optional(),
|
|
45512
|
-
/**
|
|
45513
|
-
* Renders in a warning style where the notifier supports it.
|
|
45514
|
-
*
|
|
45515
|
-
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
45516
|
-
* else — see `notification-center/action-token.ts` for what that does and
|
|
45517
|
-
* does not buy.
|
|
45518
|
-
*/
|
|
45519
|
-
destructive: boolean().optional(),
|
|
45520
|
-
/**
|
|
45521
|
-
* How the tap should REACH the url.
|
|
45522
|
-
*
|
|
45523
|
-
* `navigate` (absent, and every button authored before this field) opens it:
|
|
45524
|
-
* the phone leaves the notification and shows whatever the callback returns.
|
|
45525
|
-
* That is right for a button whose answer the operator wants to read.
|
|
45526
|
-
*
|
|
45527
|
-
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
45528
|
-
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
45529
|
-
* an answer to the notification, and being thrown into a browser tab to
|
|
45530
|
-
* confirm it costs more attention than the notification did. A backend that
|
|
45531
|
-
* cannot do a background call renders it as an ordinary link (the adapters
|
|
45532
|
-
* fall back rather than dropping the button), so this is a preference, never
|
|
45533
|
-
* a requirement.
|
|
45534
|
-
*/
|
|
45535
|
-
mode: _enum(["navigate", "background"]).optional()
|
|
45536
|
-
});
|
|
45537
|
-
/** A delivery parameter value: what a notifier declared it takes. */
|
|
45538
|
-
var DeliveryParamValueSchema = union([
|
|
45539
|
-
string(),
|
|
45540
|
-
number(),
|
|
45541
|
-
boolean()
|
|
45542
|
-
]);
|
|
45543
|
-
/**
|
|
45544
|
-
* One extra delivery parameter a notifier KIND declares (D938). A rule that
|
|
45545
|
-
* addresses a target of that kind may set it; a kind that declares nothing is
|
|
45546
|
-
* never handed one. The admin UI derives its control from this descriptor —
|
|
45547
|
-
* no editor knows a service by name.
|
|
45548
|
-
*
|
|
45549
|
-
* Optional fields are `.optional()`, never `.default()` (a Zod default does not
|
|
45550
|
-
* run on the addon cap path).
|
|
45551
|
-
*/
|
|
45552
|
-
var DeliveryParamSchema = object({
|
|
45553
|
-
key: string().min(1),
|
|
45554
|
-
label: string(),
|
|
45555
|
-
/** i18n key of the label, resolved by the declaring addon's namespace. */
|
|
45556
|
-
labelKey: string().optional(),
|
|
45557
|
-
type: _enum([
|
|
45558
|
-
"enum",
|
|
45559
|
-
"number",
|
|
45560
|
-
"boolean",
|
|
45561
|
-
"string"
|
|
45562
|
-
]),
|
|
45563
|
-
/** `enum` only: the closed set of accepted values. */
|
|
45564
|
-
options: array(object({
|
|
45565
|
-
value: string(),
|
|
45566
|
-
label: string()
|
|
45567
|
-
})).optional(),
|
|
45568
|
-
/** `number` only. */
|
|
45569
|
-
min: number().optional(),
|
|
45570
|
-
max: number().optional(),
|
|
45571
|
-
/** What the notifier does when the rule sets nothing; shown as the placeholder. */
|
|
45572
|
-
default: DeliveryParamValueSchema.optional()
|
|
45573
|
-
});
|
|
45574
|
-
/**
|
|
45575
|
-
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
45576
|
-
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
45577
|
-
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
45578
|
-
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
45579
|
-
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
45580
|
-
* `priority` for that one target.
|
|
45581
|
-
*/
|
|
45582
|
-
var NotificationSchema = object({
|
|
45583
|
-
body: string(),
|
|
45584
|
-
title: string().optional(),
|
|
45585
|
-
format: NotificationFormatSchema.default("text"),
|
|
45586
|
-
priority: number().int().min(1).max(5).default(3),
|
|
45587
|
-
level: string().optional(),
|
|
45588
|
-
attachments: array(AttachmentSchema).optional(),
|
|
45589
|
-
clickUrl: string().optional(),
|
|
45590
|
-
actions: array(NotificationActionSchema).optional(),
|
|
45591
|
-
sound: string().optional(),
|
|
45592
|
-
ttl: number().optional(),
|
|
45593
|
-
tag: string().optional(),
|
|
45594
|
-
deviceId: number().optional(),
|
|
45595
|
-
eventId: string().optional(),
|
|
45596
|
-
metadata: record(string(), unknown()).optional(),
|
|
45597
|
-
/**
|
|
45598
|
-
* Values for the parameters the target's kind DECLARES in
|
|
45599
|
-
* {@link TargetKindSchema} `deliveryParams`. Already validated against the
|
|
45600
|
-
* declaration by the sender; a notifier reads only the keys it declared.
|
|
45601
|
-
*/
|
|
45602
|
-
delivery: record(string(), DeliveryParamValueSchema).optional()
|
|
45603
|
-
});
|
|
45604
|
-
/** One declared native severity/priority level for a kind. */
|
|
45605
|
-
var TargetKindLevelSchema = object({
|
|
45606
|
-
id: string(),
|
|
45607
|
-
label: string(),
|
|
45608
|
-
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
45609
|
-
ordinal: number().int().min(1).max(5).nullable(),
|
|
45610
|
-
flags: object({
|
|
45611
|
-
critical: boolean().optional(),
|
|
45612
|
-
silent: boolean().optional(),
|
|
45613
|
-
noPush: boolean().optional()
|
|
45614
|
-
}).optional(),
|
|
45615
|
-
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
45616
|
-
requires: array(string()).optional(),
|
|
45617
|
-
description: string().optional()
|
|
45618
|
-
});
|
|
45619
|
-
/** The full capability block consulted before dispatch. */
|
|
45620
|
-
var TargetKindCapsSchema = object({
|
|
45621
|
-
attachments: object({
|
|
45622
|
-
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
45623
|
-
mode: _enum([
|
|
45624
|
-
"url",
|
|
45625
|
-
"bytes",
|
|
45626
|
-
"both"
|
|
45627
|
-
]),
|
|
45628
|
-
max: number().int().nonnegative(),
|
|
45629
|
-
maxBytes: number().int().positive().optional()
|
|
45630
|
-
}),
|
|
45631
|
-
/** Max action buttons (0 = none). */
|
|
45632
|
-
actions: number().int().nonnegative(),
|
|
45633
|
-
/**
|
|
45634
|
-
* Whether this kind renders a per-action ICON.
|
|
45635
|
-
*
|
|
45636
|
-
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
45637
|
-
* run on the addon cap path — three production failures in one day taught
|
|
45638
|
-
* this repo that once. Absent is read as false by the degrade engine, which
|
|
45639
|
-
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
45640
|
-
* assumed and dropped costs the operator's trust in the field.
|
|
45641
|
-
*/
|
|
45642
|
-
actionIcons: boolean().optional(),
|
|
45643
|
-
levels: array(TargetKindLevelSchema),
|
|
45644
|
-
format: array(NotificationFormatSchema),
|
|
45645
|
-
clickUrl: boolean(),
|
|
45646
|
-
sound: boolean(),
|
|
45647
|
-
ttl: boolean(),
|
|
45648
|
-
bodyMaxLen: number().int().positive()
|
|
45997
|
+
* - `auth-failed` — CONNACK refused with auth error (RC 4 / 5)
|
|
45998
|
+
* - `unreachable` — TCP connect timed out / refused
|
|
45999
|
+
* - `tls-error` — TLS handshake failed (cert / SNI / cipher)
|
|
46000
|
+
*/
|
|
46001
|
+
var BrokerStatusSchema$1 = _enum([
|
|
46002
|
+
"connected",
|
|
46003
|
+
"disconnected",
|
|
46004
|
+
"auth-failed",
|
|
46005
|
+
"unreachable",
|
|
46006
|
+
"tls-error"
|
|
46007
|
+
]);
|
|
46008
|
+
var BrokerInfoSchema$1 = object({
|
|
46009
|
+
id: string(),
|
|
46010
|
+
name: string(),
|
|
46011
|
+
url: string(),
|
|
46012
|
+
kind: BrokerKindSchema,
|
|
46013
|
+
status: BrokerStatusSchema$1,
|
|
46014
|
+
latencyMs: number().nullable(),
|
|
46015
|
+
error: string().optional(),
|
|
46016
|
+
/** Embedded brokers only: number of MQTT clients currently connected. */
|
|
46017
|
+
connectedClients: number().int().nonnegative().optional(),
|
|
46018
|
+
/** Epoch ms of the last live probe (external) or aedes snapshot (embedded). */
|
|
46019
|
+
lastCheckedAt: number().optional()
|
|
45649
46020
|
});
|
|
45650
46021
|
/**
|
|
45651
|
-
*
|
|
45652
|
-
*
|
|
45653
|
-
* `
|
|
45654
|
-
*
|
|
45655
|
-
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
46022
|
+
* Connection details — what a consumer needs to call
|
|
46023
|
+
* `mqtt.connect(url, options)`. We split URL + credentials so the
|
|
46024
|
+
* consumer can pass them as `mqtt.connect(url, { username, password })`
|
|
46025
|
+
* instead of stuffing creds into the URL (which leaks them into logs).
|
|
45656
46026
|
*/
|
|
45657
|
-
var
|
|
45658
|
-
|
|
45659
|
-
|
|
45660
|
-
|
|
45661
|
-
icon: string(),
|
|
45662
|
-
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
45663
|
-
addonId: string(),
|
|
45664
|
-
/**
|
|
45665
|
-
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
45666
|
-
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
45667
|
-
* when the addon bundles no icon for that kind — the client then falls back
|
|
45668
|
-
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
45669
|
-
*
|
|
45670
|
-
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
45671
|
-
* client, and a native client joins it onto its own hub base.
|
|
45672
|
-
*
|
|
45673
|
-
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
45674
|
-
* field that survived only because the runtime cap-router forwards provider
|
|
45675
|
-
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
45676
|
-
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
45677
|
-
* broken silently the moment output validation was tightened anywhere.
|
|
45678
|
-
*/
|
|
45679
|
-
iconUrl: string().optional(),
|
|
45680
|
-
/**
|
|
45681
|
-
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
45682
|
-
*
|
|
45683
|
-
* The server knows this and therefore says it, because the client cannot
|
|
45684
|
-
* safely guess: a React-Native client renders SVG and raster through two
|
|
45685
|
-
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
45686
|
-
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
45687
|
-
* placeholder glyph for every vector icon while the web build looked fine.
|
|
45688
|
-
*
|
|
45689
|
-
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
45690
|
-
* not been updated — a client that cannot determine the type should prefer
|
|
45691
|
-
* its raster path, which is the safe default for an unknown image.
|
|
45692
|
-
*/
|
|
45693
|
-
iconMediaType: string().optional(),
|
|
45694
|
-
configSchema: ConfigSchemaPassthrough$2,
|
|
45695
|
-
/**
|
|
45696
|
-
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
45697
|
-
* base URL, instance, chat, recipient). A save that changes any of them
|
|
45698
|
-
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
45699
|
-
* editor uses this to ask for the secret on exactly those edits instead of
|
|
45700
|
-
* on every edit.
|
|
45701
|
-
*
|
|
45702
|
-
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
45703
|
-
* kinds) and from a provider older than this field — an editor that finds
|
|
45704
|
-
* it absent must assume ANY edit may need the secret.
|
|
45705
|
-
*/
|
|
45706
|
-
destinationKeys: array(string()).optional(),
|
|
45707
|
-
supportsDiscovery: boolean(),
|
|
46027
|
+
var BrokerConnectionDetailsSchema = object({
|
|
46028
|
+
url: string(),
|
|
46029
|
+
username: string().optional(),
|
|
46030
|
+
password: string().optional(),
|
|
45708
46031
|
/**
|
|
45709
|
-
*
|
|
45710
|
-
*
|
|
46032
|
+
* Suggested prefix for `clientId`. Each consumer should suffix this
|
|
46033
|
+
* with its own discriminator (addon id, instance id) so reconnects
|
|
46034
|
+
* don't kick each other off (MQTT spec: clientId must be unique per
|
|
46035
|
+
* broker).
|
|
45711
46036
|
*/
|
|
45712
|
-
|
|
45713
|
-
caps: TargetKindCapsSchema
|
|
46037
|
+
clientIdPrefix: string().optional()
|
|
45714
46038
|
});
|
|
45715
|
-
|
|
45716
|
-
|
|
45717
|
-
|
|
45718
|
-
|
|
45719
|
-
|
|
45720
|
-
|
|
46039
|
+
var AddBrokerInputSchema = object({
|
|
46040
|
+
name: string().min(1),
|
|
46041
|
+
url: string().regex(/^(mqtt|mqtts|ws|wss):\/\//, "URL must start with mqtt(s):// or ws(s)://"),
|
|
46042
|
+
username: string().optional(),
|
|
46043
|
+
password: string().optional(),
|
|
46044
|
+
clientIdPrefix: string().optional()
|
|
46045
|
+
});
|
|
46046
|
+
var AddBrokerResultSchema = object({ id: string() });
|
|
46047
|
+
var IdInputSchema = object({ id: string() });
|
|
46048
|
+
var TestResultSchema$1 = discriminatedUnion("ok", [object({
|
|
46049
|
+
ok: literal(true),
|
|
46050
|
+
latencyMs: number()
|
|
46051
|
+
}), object({
|
|
46052
|
+
ok: literal(false),
|
|
46053
|
+
error: string()
|
|
46054
|
+
})]);
|
|
46055
|
+
var StartEmbeddedInputSchema = object({
|
|
46056
|
+
port: number().int().min(1).max(65535).default(1883),
|
|
46057
|
+
/** Allow anonymous connect (no username/password). Default: false. */
|
|
46058
|
+
allowAnonymous: boolean().default(false),
|
|
46059
|
+
/** Optional shared username/password for clients. */
|
|
46060
|
+
username: string().optional(),
|
|
46061
|
+
password: string().optional()
|
|
46062
|
+
});
|
|
46063
|
+
var StartEmbeddedResultSchema = object({
|
|
45721
46064
|
id: string(),
|
|
45722
|
-
|
|
45723
|
-
kind: string(),
|
|
45724
|
-
addonId: string(),
|
|
45725
|
-
enabled: boolean(),
|
|
45726
|
-
config: record(string(), unknown())
|
|
46065
|
+
url: string()
|
|
45727
46066
|
});
|
|
45728
|
-
|
|
45729
|
-
|
|
45730
|
-
|
|
45731
|
-
suggestedName: string(),
|
|
45732
|
-
config: record(string(), unknown())
|
|
46067
|
+
var StatusSchema = object({
|
|
46068
|
+
brokerCount: number(),
|
|
46069
|
+
embeddedRunning: boolean()
|
|
45733
46070
|
});
|
|
45734
|
-
|
|
45735
|
-
|
|
45736
|
-
|
|
45737
|
-
|
|
45738
|
-
|
|
45739
|
-
|
|
45740
|
-
|
|
45741
|
-
|
|
46071
|
+
method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
|
|
46072
|
+
kind: "mutation",
|
|
46073
|
+
auth: "admin"
|
|
46074
|
+
}), method(IdInputSchema, _void(), {
|
|
46075
|
+
kind: "mutation",
|
|
46076
|
+
auth: "admin"
|
|
46077
|
+
}), method(IdInputSchema, TestResultSchema$1, {
|
|
46078
|
+
kind: "mutation",
|
|
46079
|
+
auth: "admin"
|
|
46080
|
+
}), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, {
|
|
46081
|
+
kind: "mutation",
|
|
46082
|
+
auth: "admin"
|
|
46083
|
+
}), method(IdInputSchema, _void(), {
|
|
46084
|
+
kind: "mutation",
|
|
46085
|
+
auth: "admin"
|
|
46086
|
+
}), method(_void(), StatusSchema);
|
|
46087
|
+
var NetworkEndpointSchema = object({
|
|
46088
|
+
url: string(),
|
|
46089
|
+
hostname: string(),
|
|
46090
|
+
port: number(),
|
|
46091
|
+
protocol: _enum(["http", "https"]),
|
|
46092
|
+
/**
|
|
46093
|
+
* Whether this address survives a restart of its provider. `false` = it is
|
|
46094
|
+
* re-issued every time (a Quick tunnel's random `*.trycloudflare.com`); the
|
|
46095
|
+
* hub never selects such an address AUTOMATICALLY for a role (D860), though
|
|
46096
|
+
* it stays visible and an operator may still flag it. Absent = stable: every
|
|
46097
|
+
* provider except a Quick tunnel owns a name that does not change.
|
|
46098
|
+
*/
|
|
46099
|
+
stable: boolean().optional()
|
|
45742
46100
|
});
|
|
45743
|
-
|
|
45744
|
-
|
|
45745
|
-
|
|
45746
|
-
|
|
46101
|
+
object({
|
|
46102
|
+
url: string(),
|
|
46103
|
+
/** Mirrors {@link NetworkEndpointSchema} `stable`; absent = stable. */
|
|
46104
|
+
stable: boolean().optional(),
|
|
46105
|
+
updated: boolean().optional()
|
|
46106
|
+
});
|
|
46107
|
+
var NetworkAccessStatusSchema = object({
|
|
46108
|
+
connected: boolean(),
|
|
46109
|
+
endpoint: NetworkEndpointSchema.nullable(),
|
|
46110
|
+
error: string().optional()
|
|
45747
46111
|
});
|
|
45748
|
-
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
45749
|
-
var TestResultSchema = SendResultSchema;
|
|
45750
46112
|
/**
|
|
45751
|
-
*
|
|
45752
|
-
*
|
|
46113
|
+
* Optional, richer endpoint shape returned by providers that expose
|
|
46114
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
46115
|
+
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
46116
|
+
* the originating provider config (mode + sourcePort) so the
|
|
46117
|
+
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
46118
|
+
* one endpoint just omit `listEndpoints` from their provider impl.
|
|
45753
46119
|
*/
|
|
45754
|
-
var
|
|
45755
|
-
|
|
45756
|
-
|
|
45757
|
-
|
|
45758
|
-
|
|
45759
|
-
|
|
46120
|
+
var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
|
|
46121
|
+
/**
|
|
46122
|
+
* Stable id within the provider — typically `<mode>-<sourcePort>` so
|
|
46123
|
+
* the orchestrator can dedupe across `listEndpoints` polls.
|
|
46124
|
+
*/
|
|
46125
|
+
id: string(),
|
|
46126
|
+
/** Operator-facing label (mirrors `MeshEndpoint.label`). */
|
|
46127
|
+
label: string(),
|
|
46128
|
+
/** Optional provider-specific mode tag, used for icon/colour in admin UI. */
|
|
46129
|
+
mode: string().optional(),
|
|
46130
|
+
/** Originating local port the ingress fronts (informational). */
|
|
46131
|
+
sourcePort: number().optional()
|
|
45760
46132
|
});
|
|
45761
|
-
|
|
45762
|
-
name: "notification-output",
|
|
45763
|
-
scope: "system",
|
|
45764
|
-
mode: "collection",
|
|
45765
|
-
methods: {
|
|
45766
|
-
listTargetKinds: method(object({}), array(TargetKindSchema)),
|
|
45767
|
-
listTargets: method(object({}), array(TargetSchema), { auth: "admin" }),
|
|
45768
|
-
/**
|
|
45769
|
-
* One target WITH secrets — admin edit / duplicate hydration only.
|
|
45770
|
-
* `listTargets` stays redacted; the editor loads this so password fields
|
|
45771
|
-
* are filled (masked in the input) and a save can round-trip the token
|
|
45772
|
-
* when a destination changes (Duplicate with a new chatId, etc.).
|
|
45773
|
-
*/
|
|
45774
|
-
getTarget: method(object({ targetId: string() }), TargetSchema, { auth: "admin" }),
|
|
45775
|
-
discoverTargets: method(object({
|
|
45776
|
-
kind: string(),
|
|
45777
|
-
config: record(string(), unknown()).optional(),
|
|
45778
|
-
/**
|
|
45779
|
-
* When editing / duplicating, the blank password field never round-trips
|
|
45780
|
-
* the stored token. Pass the source target id so the provider can restore
|
|
45781
|
-
* secrets server-side before dialling (same merge as upsert).
|
|
45782
|
-
*/
|
|
45783
|
-
targetId: string().optional()
|
|
45784
|
-
}), array(DiscoveredTargetSchema), { auth: "admin" }),
|
|
45785
|
-
probeTargetConfig: method(object({
|
|
45786
|
-
kind: string(),
|
|
45787
|
-
config: record(string(), unknown()),
|
|
45788
|
-
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
45789
|
-
targetId: string().optional()
|
|
45790
|
-
}), ProbeTargetConfigResultSchema, {
|
|
45791
|
-
kind: "mutation",
|
|
45792
|
-
auth: "admin"
|
|
45793
|
-
}),
|
|
45794
|
-
send: method(object({
|
|
45795
|
-
targetId: string(),
|
|
45796
|
-
notification: NotificationSchema
|
|
45797
|
-
}), SendResultSchema, {
|
|
45798
|
-
kind: "mutation",
|
|
45799
|
-
auth: "admin"
|
|
45800
|
-
}),
|
|
45801
|
-
testTarget: method(object({
|
|
45802
|
-
targetId: string(),
|
|
45803
|
-
sample: NotificationSchema.optional()
|
|
45804
|
-
}), TestResultSchema, {
|
|
45805
|
-
kind: "mutation",
|
|
45806
|
-
auth: "admin"
|
|
45807
|
-
}),
|
|
45808
|
-
upsertTarget: method(object({
|
|
45809
|
-
target: TargetSchema,
|
|
45810
|
-
/**
|
|
45811
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
45812
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
45813
|
-
* password fields are restored from this source (D634 still applies if
|
|
45814
|
-
* destinations diverge without a re-typed secret).
|
|
45815
|
-
*/
|
|
45816
|
-
cloneSecretsFrom: string().optional()
|
|
45817
|
-
}), TargetSchema, {
|
|
45818
|
-
kind: "mutation",
|
|
45819
|
-
auth: "admin"
|
|
45820
|
-
}),
|
|
45821
|
-
deleteTarget: method(object({ targetId: string() }), _void(), {
|
|
45822
|
-
kind: "mutation",
|
|
45823
|
-
auth: "admin"
|
|
45824
|
-
}),
|
|
45825
|
-
setTargetEnabled: method(object({
|
|
45826
|
-
targetId: string(),
|
|
45827
|
-
enabled: boolean()
|
|
45828
|
-
}), _void(), {
|
|
45829
|
-
kind: "mutation",
|
|
45830
|
-
auth: "admin"
|
|
45831
|
-
})
|
|
45832
|
-
}
|
|
45833
|
-
};
|
|
46133
|
+
method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
|
|
45834
46134
|
var AUDIO_MACRO_LABELS = [
|
|
45835
46135
|
{
|
|
45836
46136
|
id: "speech",
|
|
@@ -52291,14 +52591,6 @@ var EngineProvisioningSchema = object({
|
|
|
52291
52591
|
*/
|
|
52292
52592
|
configIssues: array(string()).optional()
|
|
52293
52593
|
});
|
|
52294
|
-
var PipelineStepInputSchema = lazy(() => object({
|
|
52295
|
-
addonId: string(),
|
|
52296
|
-
modelId: string().optional(),
|
|
52297
|
-
enabled: boolean().default(true),
|
|
52298
|
-
children: array(PipelineStepInputSchema).optional(),
|
|
52299
|
-
settings: record(string(), unknown()).optional(),
|
|
52300
|
-
jumpDeviceKey: string().optional()
|
|
52301
|
-
}));
|
|
52302
52594
|
var ModelSubstitutionSchema = object({
|
|
52303
52595
|
addonId: string(),
|
|
52304
52596
|
chosen: string(),
|
|
@@ -52377,7 +52669,6 @@ var ConfigUISchemaBridge = custom();
|
|
|
52377
52669
|
var ConfigUISchemaNullableBridge = custom();
|
|
52378
52670
|
var InferenceCapabilitiesBridge = custom();
|
|
52379
52671
|
var ModelAvailabilityListBridge = custom();
|
|
52380
|
-
var PipelineRunResultBridge = custom();
|
|
52381
52672
|
method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
|
|
52382
52673
|
modelId: string(),
|
|
52383
52674
|
settings: record(string(), unknown()).readonly()
|
|
@@ -55583,6 +55874,12 @@ var ServerUpdateActionResultSchema = object({
|
|
|
55583
55874
|
*/
|
|
55584
55875
|
blockers: array(UpdateBlockerSchema).optional()
|
|
55585
55876
|
});
|
|
55877
|
+
var LaunchOverridesSchema = object({
|
|
55878
|
+
/** The persisted heap ceiling in MB, or `null` when none is set. */
|
|
55879
|
+
heapMb: number().int().nullable() });
|
|
55880
|
+
var SetLaunchOverridesResultSchema = LaunchOverridesSchema.extend({
|
|
55881
|
+
/** Always true: the override is read at launch, not by the running process. */
|
|
55882
|
+
restartRequired: boolean() });
|
|
55586
55883
|
method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
|
|
55587
55884
|
kind: "mutation",
|
|
55588
55885
|
auth: "admin"
|
|
@@ -55605,6 +55902,9 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
|
|
|
55605
55902
|
}), method(_void(), ServerUpdateActionResultSchema, {
|
|
55606
55903
|
kind: "mutation",
|
|
55607
55904
|
auth: "admin"
|
|
55905
|
+
}), method(_void(), LaunchOverridesSchema, { auth: "admin" }), method(object({ heapMb: number().int().min(512).max(65536).nullable() }), SetLaunchOverridesResultSchema, {
|
|
55906
|
+
kind: "mutation",
|
|
55907
|
+
auth: "admin"
|
|
55608
55908
|
});
|
|
55609
55909
|
/**
|
|
55610
55910
|
* `smtp-provider` — pluggable email delivery surface.
|
|
@@ -60609,14 +60909,13 @@ reset: method(object({
|
|
|
60609
60909
|
auth: "admin"
|
|
60610
60910
|
}) },
|
|
60611
60911
|
/**
|
|
60612
|
-
* A
|
|
60613
|
-
*
|
|
60614
|
-
*
|
|
60615
|
-
* commands (`forward`) arrive in slice 3 (D663).
|
|
60912
|
+
* A consumable ADDED to an existing device (a graft) refuses `reset` by
|
|
60913
|
+
* name: only a composed device of a NEW type forwards an item's reset to a
|
|
60914
|
+
* source button, and marks that item `resettable` (D967).
|
|
60616
60915
|
*/
|
|
60617
60916
|
composedMethods: { reset: {
|
|
60618
60917
|
kind: "refuse",
|
|
60619
|
-
reason: "a
|
|
60918
|
+
reason: "a consumable added to an existing device is not resettable — only a new composed device forwards a reset"
|
|
60620
60919
|
} },
|
|
60621
60920
|
status: {
|
|
60622
60921
|
schema: ConsumablesStatusSchema,
|
|
@@ -61210,7 +61509,7 @@ var DishwasherStatusSchema = object({
|
|
|
61210
61509
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
61211
61510
|
lastFetchedAt: number()
|
|
61212
61511
|
});
|
|
61213
|
-
var DeviceIdSchema = number().int().nonnegative();
|
|
61512
|
+
var DeviceIdSchema$1 = number().int().nonnegative();
|
|
61214
61513
|
/** A value a program option or setting can take. */
|
|
61215
61514
|
var DishwasherValueSchema = union([
|
|
61216
61515
|
boolean(),
|
|
@@ -61285,77 +61584,77 @@ var dishwasherCapability = {
|
|
|
61285
61584
|
* option feature keys; each is checked against the program's constraints.
|
|
61286
61585
|
*/
|
|
61287
61586
|
start: method(object({
|
|
61288
|
-
deviceId: DeviceIdSchema,
|
|
61587
|
+
deviceId: DeviceIdSchema$1,
|
|
61289
61588
|
program: string().min(1).optional(),
|
|
61290
61589
|
options: record(string(), DishwasherValueSchema).optional()
|
|
61291
61590
|
}), _void(), {
|
|
61292
61591
|
kind: "mutation",
|
|
61293
61592
|
auth: "protected"
|
|
61294
61593
|
}),
|
|
61295
|
-
pause: method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
61594
|
+
pause: method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
61296
61595
|
kind: "mutation",
|
|
61297
61596
|
auth: "protected"
|
|
61298
61597
|
}),
|
|
61299
|
-
resume: method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
61598
|
+
resume: method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
61300
61599
|
kind: "mutation",
|
|
61301
61600
|
auth: "protected"
|
|
61302
61601
|
}),
|
|
61303
|
-
stop: method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
61602
|
+
stop: method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
61304
61603
|
kind: "mutation",
|
|
61305
61604
|
auth: "protected"
|
|
61306
61605
|
}),
|
|
61307
61606
|
/** Select (not start) a program; refused when the appliance does not offer it. */
|
|
61308
61607
|
setProgram: method(object({
|
|
61309
|
-
deviceId: DeviceIdSchema,
|
|
61608
|
+
deviceId: DeviceIdSchema$1,
|
|
61310
61609
|
program: string().min(1)
|
|
61311
61610
|
}), _void(), {
|
|
61312
61611
|
kind: "mutation",
|
|
61313
61612
|
auth: "protected"
|
|
61314
61613
|
}),
|
|
61315
61614
|
setPower: method(object({
|
|
61316
|
-
deviceId: DeviceIdSchema,
|
|
61615
|
+
deviceId: DeviceIdSchema$1,
|
|
61317
61616
|
on: boolean()
|
|
61318
61617
|
}), _void(), {
|
|
61319
61618
|
kind: "mutation",
|
|
61320
61619
|
auth: "protected"
|
|
61321
61620
|
}),
|
|
61322
61621
|
setHalfLoad: method(object({
|
|
61323
|
-
deviceId: DeviceIdSchema,
|
|
61622
|
+
deviceId: DeviceIdSchema$1,
|
|
61324
61623
|
on: boolean()
|
|
61325
61624
|
}), _void(), {
|
|
61326
61625
|
kind: "mutation",
|
|
61327
61626
|
auth: "protected"
|
|
61328
61627
|
}),
|
|
61329
61628
|
setHygienePlus: method(object({
|
|
61330
|
-
deviceId: DeviceIdSchema,
|
|
61629
|
+
deviceId: DeviceIdSchema$1,
|
|
61331
61630
|
on: boolean()
|
|
61332
61631
|
}), _void(), {
|
|
61333
61632
|
kind: "mutation",
|
|
61334
61633
|
auth: "protected"
|
|
61335
61634
|
}),
|
|
61336
61635
|
setExtraDry: method(object({
|
|
61337
|
-
deviceId: DeviceIdSchema,
|
|
61636
|
+
deviceId: DeviceIdSchema$1,
|
|
61338
61637
|
on: boolean()
|
|
61339
61638
|
}), _void(), {
|
|
61340
61639
|
kind: "mutation",
|
|
61341
61640
|
auth: "protected"
|
|
61342
61641
|
}),
|
|
61343
61642
|
setIntensiveZone: method(object({
|
|
61344
|
-
deviceId: DeviceIdSchema,
|
|
61643
|
+
deviceId: DeviceIdSchema$1,
|
|
61345
61644
|
on: boolean()
|
|
61346
61645
|
}), _void(), {
|
|
61347
61646
|
kind: "mutation",
|
|
61348
61647
|
auth: "protected"
|
|
61349
61648
|
}),
|
|
61350
61649
|
setVarioSpeedPlus: method(object({
|
|
61351
|
-
deviceId: DeviceIdSchema,
|
|
61650
|
+
deviceId: DeviceIdSchema$1,
|
|
61352
61651
|
on: boolean()
|
|
61353
61652
|
}), _void(), {
|
|
61354
61653
|
kind: "mutation",
|
|
61355
61654
|
auth: "protected"
|
|
61356
61655
|
}),
|
|
61357
61656
|
setChildLock: method(object({
|
|
61358
|
-
deviceId: DeviceIdSchema,
|
|
61657
|
+
deviceId: DeviceIdSchema$1,
|
|
61359
61658
|
on: boolean()
|
|
61360
61659
|
}), _void(), {
|
|
61361
61660
|
kind: "mutation",
|
|
@@ -61367,7 +61666,7 @@ var dishwasherCapability = {
|
|
|
61367
61666
|
* its declared range / members.
|
|
61368
61667
|
*/
|
|
61369
61668
|
setOption: method(object({
|
|
61370
|
-
deviceId: DeviceIdSchema,
|
|
61669
|
+
deviceId: DeviceIdSchema$1,
|
|
61371
61670
|
key: string().min(1),
|
|
61372
61671
|
value: DishwasherValueSchema
|
|
61373
61672
|
}), _void(), {
|
|
@@ -61375,7 +61674,7 @@ var dishwasherCapability = {
|
|
|
61375
61674
|
auth: "protected"
|
|
61376
61675
|
}),
|
|
61377
61676
|
/** What this appliance can do, from its own description — the UI is derived from it. */
|
|
61378
|
-
getOptions: method(object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" })
|
|
61677
|
+
getOptions: method(object({ deviceId: DeviceIdSchema$1 }), DishwasherOptionsSchema, { auth: "protected" })
|
|
61379
61678
|
},
|
|
61380
61679
|
status: {
|
|
61381
61680
|
schema: DishwasherStatusSchema,
|
|
@@ -65076,7 +65375,8 @@ var TopologyNodeSchema = object({
|
|
|
65076
65375
|
isHub: boolean(),
|
|
65077
65376
|
isOnline: boolean(),
|
|
65078
65377
|
cpuPercent: number(),
|
|
65079
|
-
|
|
65378
|
+
/** `null` when the node's memory read failed or it has not reported one (D393). */
|
|
65379
|
+
memoryPercent: number().nullable(),
|
|
65080
65380
|
uptime: number(),
|
|
65081
65381
|
lastSeen: string(),
|
|
65082
65382
|
localIps: array(string()).readonly(),
|
|
@@ -65694,9 +65994,10 @@ var PetFeederStatusSchema = object({
|
|
|
65694
65994
|
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
65695
65995
|
* the device has not reported yet. */
|
|
65696
65996
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
65697
|
-
/** Food in the BOWL
|
|
65698
|
-
* feeder
|
|
65699
|
-
|
|
65997
|
+
/** Food in the BOWL, a mass (stored in kilograms, D944). Reported only by a
|
|
65998
|
+
* feeder with a bowl scale; null when the model has none or its reading is
|
|
65999
|
+
* of an unproven unit. */
|
|
66000
|
+
bowlFood: number().nullable().meta(readingMeta("mass")),
|
|
65700
66001
|
/** True while the pet is eating (`state.eating`). */
|
|
65701
66002
|
eating: boolean(),
|
|
65702
66003
|
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
@@ -65880,6 +66181,137 @@ var petFeederCapability = {
|
|
|
65880
66181
|
}
|
|
65881
66182
|
} }
|
|
65882
66183
|
};
|
|
66184
|
+
/**
|
|
66185
|
+
* Pet water-fountain cap. Models the telemetry and the control surface of a
|
|
66186
|
+
* circulating drinking fountain: how much water is in the tank, how much the
|
|
66187
|
+
* pet drank, whether the pump runs and on what duty cycle.
|
|
66188
|
+
*
|
|
66189
|
+
* It has no native vendor provider of its own: a fountain reaches CamStack as
|
|
66190
|
+
* a container of loose entities (a pump switch, two number boxes, a "filter
|
|
66191
|
+
* changed" button), and an operator BUILDS the device from them — a composed
|
|
66192
|
+
* device of this type (D659, D967). Every method is therefore a forward to ONE
|
|
66193
|
+
* method of ONE source entity, and every reading is a field derived from a
|
|
66194
|
+
* source entity; the provider-shaped interface below exists so the composed
|
|
66195
|
+
* device and any future native provider answer the same contract.
|
|
66196
|
+
*
|
|
66197
|
+
* Quantities are stored in the base unit of their kind (D944): volumes in
|
|
66198
|
+
* LITRES, durations in SECONDS, the tank level in percent. A source that
|
|
66199
|
+
* reports millilitres or minutes is converted in the composition's expression,
|
|
66200
|
+
* never here. Every reading may be `null`: a source that is offline or does
|
|
66201
|
+
* not report is unknown, never zero.
|
|
66202
|
+
*
|
|
66203
|
+
* The filter is not a field of this cap: it is an item of the `consumables`
|
|
66204
|
+
* cap on the same device (kind `wear`), reset by the same forward mechanism.
|
|
66205
|
+
*/
|
|
66206
|
+
var PetFountainStatusSchema = object({
|
|
66207
|
+
/** Water in the tank, 0..100 (%). Null while the source has not reported. */
|
|
66208
|
+
waterLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
66209
|
+
/** Water in the tank, in litres. Null while the source has not reported. */
|
|
66210
|
+
waterVolume: number().min(0).nullable().meta(readingMeta("volume")),
|
|
66211
|
+
/** True when the tank is empty. Null while unknown. */
|
|
66212
|
+
empty: boolean().nullable(),
|
|
66213
|
+
/** True while the pump is running. Null while unknown. */
|
|
66214
|
+
pumpRunning: boolean().nullable(),
|
|
66215
|
+
/** True when the pump works on an on/off duty cycle rather than continuously. */
|
|
66216
|
+
pumpIntervalEnabled: boolean().nullable(),
|
|
66217
|
+
/** How long the pump runs per cycle, in seconds. */
|
|
66218
|
+
pumpOnTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
66219
|
+
/** How long the pump rests per cycle, in seconds. */
|
|
66220
|
+
pumpOffTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
66221
|
+
/** Water drunk in the last drink, in litres. */
|
|
66222
|
+
lastDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
66223
|
+
/** Water drunk in the current hour, in litres. */
|
|
66224
|
+
currentHourDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
66225
|
+
/** Water drunk over the last 24 hours (rolling), in litres. */
|
|
66226
|
+
last24hDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
66227
|
+
/** Water drunk since local midnight, in litres. */
|
|
66228
|
+
todayDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
66229
|
+
/** Decoded fault message. Null means healthy; a non-null string is an active fault. */
|
|
66230
|
+
error: string().nullable(),
|
|
66231
|
+
/** Ms epoch when the slice was last refreshed. */
|
|
66232
|
+
lastFetchedAt: number()
|
|
66233
|
+
});
|
|
66234
|
+
var DeviceIdSchema = number().int().nonnegative();
|
|
66235
|
+
var MinutesSchema = number().int().min(0);
|
|
66236
|
+
var petFountainCapability = {
|
|
66237
|
+
name: "pet-fountain",
|
|
66238
|
+
scope: "device",
|
|
66239
|
+
deviceNative: true,
|
|
66240
|
+
mode: "singleton",
|
|
66241
|
+
deviceTypes: [DeviceType.PetFountain],
|
|
66242
|
+
methods: {
|
|
66243
|
+
/** Start or stop the pump. */
|
|
66244
|
+
setPump: method(object({
|
|
66245
|
+
deviceId: DeviceIdSchema,
|
|
66246
|
+
on: boolean()
|
|
66247
|
+
}), _void(), {
|
|
66248
|
+
kind: "mutation",
|
|
66249
|
+
auth: "protected"
|
|
66250
|
+
}),
|
|
66251
|
+
/** Switch the on/off duty cycle on or off (off = the pump runs continuously). */
|
|
66252
|
+
setPumpInterval: method(object({
|
|
66253
|
+
deviceId: DeviceIdSchema,
|
|
66254
|
+
on: boolean()
|
|
66255
|
+
}), _void(), {
|
|
66256
|
+
kind: "mutation",
|
|
66257
|
+
auth: "protected"
|
|
66258
|
+
}),
|
|
66259
|
+
/** Set how many minutes the pump runs per cycle. */
|
|
66260
|
+
setPumpOnMinutes: method(object({
|
|
66261
|
+
deviceId: DeviceIdSchema,
|
|
66262
|
+
minutes: MinutesSchema
|
|
66263
|
+
}), _void(), {
|
|
66264
|
+
kind: "mutation",
|
|
66265
|
+
auth: "protected"
|
|
66266
|
+
}),
|
|
66267
|
+
/** Set how many minutes the pump rests per cycle. */
|
|
66268
|
+
setPumpOffMinutes: method(object({
|
|
66269
|
+
deviceId: DeviceIdSchema,
|
|
66270
|
+
minutes: MinutesSchema
|
|
66271
|
+
}), _void(), {
|
|
66272
|
+
kind: "mutation",
|
|
66273
|
+
auth: "protected"
|
|
66274
|
+
}),
|
|
66275
|
+
/** Clear the rolling daily consumption counters on the device. */
|
|
66276
|
+
resetDailyConsumption: method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
66277
|
+
kind: "mutation",
|
|
66278
|
+
auth: "protected"
|
|
66279
|
+
})
|
|
66280
|
+
},
|
|
66281
|
+
status: {
|
|
66282
|
+
schema: PetFountainStatusSchema,
|
|
66283
|
+
kind: "push"
|
|
66284
|
+
},
|
|
66285
|
+
/** Runtime-state slice — mirrored by the kernel; the hero card reads it whole. */
|
|
66286
|
+
runtimeState: PetFountainStatusSchema,
|
|
66287
|
+
/**
|
|
66288
|
+
* Runtime-state durability: **session** — live appliance state re-published on connect.
|
|
66289
|
+
*
|
|
66290
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
66291
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
66292
|
+
*/
|
|
66293
|
+
durability: "session",
|
|
66294
|
+
/** History (D850): recorded series. */
|
|
66295
|
+
history: { fields: {
|
|
66296
|
+
waterLevel: {
|
|
66297
|
+
kind: "gauge",
|
|
66298
|
+
unit: "%",
|
|
66299
|
+
deadband: 1
|
|
66300
|
+
},
|
|
66301
|
+
waterVolume: {
|
|
66302
|
+
kind: "gauge",
|
|
66303
|
+
unit: "L"
|
|
66304
|
+
},
|
|
66305
|
+
todayDrink: {
|
|
66306
|
+
kind: "gauge",
|
|
66307
|
+
unit: "L"
|
|
66308
|
+
},
|
|
66309
|
+
last24hDrink: {
|
|
66310
|
+
kind: "gauge",
|
|
66311
|
+
unit: "L"
|
|
66312
|
+
}
|
|
66313
|
+
} }
|
|
66314
|
+
};
|
|
65883
66315
|
var VehicleSchema = object({
|
|
65884
66316
|
id: string(),
|
|
65885
66317
|
name: string(),
|
|
@@ -72245,7 +72677,7 @@ function tryConvertDelta(value, from, to) {
|
|
|
72245
72677
|
return shifted - origin;
|
|
72246
72678
|
}
|
|
72247
72679
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
72248
|
-
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.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;
|
|
72680
|
+
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;
|
|
72249
72681
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
72250
72682
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
72251
72683
|
new Set(CAP_NAMES);
|
|
@@ -72596,6 +73028,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
72596
73028
|
notifier: notifierCapability,
|
|
72597
73029
|
numericSensor: numericSensorCapability,
|
|
72598
73030
|
petFeeder: petFeederCapability,
|
|
73031
|
+
petFountain: petFountainCapability,
|
|
72599
73032
|
powerMeter: powerMeterCapability,
|
|
72600
73033
|
presence: presenceCapability,
|
|
72601
73034
|
pressureSensor: pressureSensorCapability,
|
|
@@ -73846,7 +74279,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
73846
74279
|
return toDeviceSummary(device, this.addonId);
|
|
73847
74280
|
}
|
|
73848
74281
|
};
|
|
73849
|
-
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;
|
|
74282
|
+
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;
|
|
73850
74283
|
new Set(Object.values(DeviceType));
|
|
73851
74284
|
Object.freeze({
|
|
73852
74285
|
"accessories.getStatus": {
|
|
@@ -75781,6 +76214,13 @@ Object.freeze({
|
|
|
75781
76214
|
access: "create",
|
|
75782
76215
|
role: "admin"
|
|
75783
76216
|
},
|
|
76217
|
+
"deviceExport.exposeEntity": {
|
|
76218
|
+
capName: "device-export",
|
|
76219
|
+
capScope: "system",
|
|
76220
|
+
addonId: null,
|
|
76221
|
+
access: "create",
|
|
76222
|
+
role: "admin"
|
|
76223
|
+
},
|
|
75784
76224
|
"deviceExport.getDeviceLiveContribution": {
|
|
75785
76225
|
capName: "device-export",
|
|
75786
76226
|
capScope: "system",
|
|
@@ -75809,6 +76249,13 @@ Object.freeze({
|
|
|
75809
76249
|
access: "view",
|
|
75810
76250
|
role: "admin"
|
|
75811
76251
|
},
|
|
76252
|
+
"deviceExport.listExposedEntities": {
|
|
76253
|
+
capName: "device-export",
|
|
76254
|
+
capScope: "system",
|
|
76255
|
+
addonId: null,
|
|
76256
|
+
access: "view",
|
|
76257
|
+
role: "admin"
|
|
76258
|
+
},
|
|
75812
76259
|
"deviceExport.listSupportedDeviceKinds": {
|
|
75813
76260
|
capName: "device-export",
|
|
75814
76261
|
capScope: "system",
|
|
@@ -75816,6 +76263,13 @@ Object.freeze({
|
|
|
75816
76263
|
access: "view",
|
|
75817
76264
|
role: "admin"
|
|
75818
76265
|
},
|
|
76266
|
+
"deviceExport.listSupportedEntityKinds": {
|
|
76267
|
+
capName: "device-export",
|
|
76268
|
+
capScope: "system",
|
|
76269
|
+
addonId: null,
|
|
76270
|
+
access: "view",
|
|
76271
|
+
role: "admin"
|
|
76272
|
+
},
|
|
75819
76273
|
"deviceExport.unexposeDevice": {
|
|
75820
76274
|
capName: "device-export",
|
|
75821
76275
|
capScope: "system",
|
|
@@ -75823,6 +76277,13 @@ Object.freeze({
|
|
|
75823
76277
|
access: "create",
|
|
75824
76278
|
role: "admin"
|
|
75825
76279
|
},
|
|
76280
|
+
"deviceExport.unexposeEntity": {
|
|
76281
|
+
capName: "device-export",
|
|
76282
|
+
capScope: "system",
|
|
76283
|
+
addonId: null,
|
|
76284
|
+
access: "create",
|
|
76285
|
+
role: "admin"
|
|
76286
|
+
},
|
|
75826
76287
|
"deviceExtension.listCandidates": {
|
|
75827
76288
|
capName: "device-extension",
|
|
75828
76289
|
capScope: "device",
|
|
@@ -78644,6 +79105,13 @@ Object.freeze({
|
|
|
78644
79105
|
access: "create",
|
|
78645
79106
|
role: "admin"
|
|
78646
79107
|
},
|
|
79108
|
+
"nodeRemovalHook.onNodeRemoved": {
|
|
79109
|
+
capName: "node-removal-hook",
|
|
79110
|
+
capScope: "system",
|
|
79111
|
+
addonId: null,
|
|
79112
|
+
access: "create",
|
|
79113
|
+
role: "admin"
|
|
79114
|
+
},
|
|
78647
79115
|
"nodes.clusterAddonStatus": {
|
|
78648
79116
|
capName: "nodes",
|
|
78649
79117
|
capScope: "system",
|
|
@@ -79281,6 +79749,48 @@ Object.freeze({
|
|
|
79281
79749
|
access: "create",
|
|
79282
79750
|
role: "viewer"
|
|
79283
79751
|
},
|
|
79752
|
+
"petFountain.getStatus": {
|
|
79753
|
+
capName: "pet-fountain",
|
|
79754
|
+
capScope: "device",
|
|
79755
|
+
addonId: null,
|
|
79756
|
+
access: "view",
|
|
79757
|
+
role: "viewer"
|
|
79758
|
+
},
|
|
79759
|
+
"petFountain.resetDailyConsumption": {
|
|
79760
|
+
capName: "pet-fountain",
|
|
79761
|
+
capScope: "device",
|
|
79762
|
+
addonId: null,
|
|
79763
|
+
access: "delete",
|
|
79764
|
+
role: "viewer"
|
|
79765
|
+
},
|
|
79766
|
+
"petFountain.setPump": {
|
|
79767
|
+
capName: "pet-fountain",
|
|
79768
|
+
capScope: "device",
|
|
79769
|
+
addonId: null,
|
|
79770
|
+
access: "create",
|
|
79771
|
+
role: "viewer"
|
|
79772
|
+
},
|
|
79773
|
+
"petFountain.setPumpInterval": {
|
|
79774
|
+
capName: "pet-fountain",
|
|
79775
|
+
capScope: "device",
|
|
79776
|
+
addonId: null,
|
|
79777
|
+
access: "create",
|
|
79778
|
+
role: "viewer"
|
|
79779
|
+
},
|
|
79780
|
+
"petFountain.setPumpOffMinutes": {
|
|
79781
|
+
capName: "pet-fountain",
|
|
79782
|
+
capScope: "device",
|
|
79783
|
+
addonId: null,
|
|
79784
|
+
access: "create",
|
|
79785
|
+
role: "viewer"
|
|
79786
|
+
},
|
|
79787
|
+
"petFountain.setPumpOnMinutes": {
|
|
79788
|
+
capName: "pet-fountain",
|
|
79789
|
+
capScope: "device",
|
|
79790
|
+
addonId: null,
|
|
79791
|
+
access: "create",
|
|
79792
|
+
role: "viewer"
|
|
79793
|
+
},
|
|
79284
79794
|
"pipelineAnalytics.addRetrainFrameFromRecording": {
|
|
79285
79795
|
capName: "pipeline-analytics",
|
|
79286
79796
|
capScope: "device",
|
|
@@ -81682,6 +82192,13 @@ Object.freeze({
|
|
|
81682
82192
|
access: "create",
|
|
81683
82193
|
role: "admin"
|
|
81684
82194
|
},
|
|
82195
|
+
"serverManagement.getLaunchOverrides": {
|
|
82196
|
+
capName: "server-management",
|
|
82197
|
+
capScope: "system",
|
|
82198
|
+
addonId: null,
|
|
82199
|
+
access: "view",
|
|
82200
|
+
role: "admin"
|
|
82201
|
+
},
|
|
81685
82202
|
"serverManagement.getServerPackageStatus": {
|
|
81686
82203
|
capName: "server-management",
|
|
81687
82204
|
capScope: "system",
|
|
@@ -81703,6 +82220,13 @@ Object.freeze({
|
|
|
81703
82220
|
access: "create",
|
|
81704
82221
|
role: "admin"
|
|
81705
82222
|
},
|
|
82223
|
+
"serverManagement.setLaunchOverrides": {
|
|
82224
|
+
capName: "server-management",
|
|
82225
|
+
capScope: "system",
|
|
82226
|
+
addonId: null,
|
|
82227
|
+
access: "create",
|
|
82228
|
+
role: "admin"
|
|
82229
|
+
},
|
|
81706
82230
|
"settingsStore.aggregate": {
|
|
81707
82231
|
capName: "settings-store",
|
|
81708
82232
|
capScope: "system",
|
|
@@ -85763,6 +86287,100 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
85763
86287
|
}
|
|
85764
86288
|
]
|
|
85765
86289
|
},
|
|
86290
|
+
"pet-fountain": {
|
|
86291
|
+
scope: "device",
|
|
86292
|
+
noun: t("Pet fountain", "Fontana per animali"),
|
|
86293
|
+
actions: [
|
|
86294
|
+
{
|
|
86295
|
+
id: "petFountain.pumpOn",
|
|
86296
|
+
method: "setPump",
|
|
86297
|
+
label: t("Pump on", "Pompa accesa"),
|
|
86298
|
+
phrase: t("Start the pump of {device}", "Avvia la pompa di {device}"),
|
|
86299
|
+
icon: "play",
|
|
86300
|
+
args: { on: true }
|
|
86301
|
+
},
|
|
86302
|
+
{
|
|
86303
|
+
id: "petFountain.pumpOff",
|
|
86304
|
+
method: "setPump",
|
|
86305
|
+
label: t("Pump off", "Pompa spenta"),
|
|
86306
|
+
phrase: t("Stop the pump of {device}", "Ferma la pompa di {device}"),
|
|
86307
|
+
icon: "square",
|
|
86308
|
+
args: { on: false }
|
|
86309
|
+
},
|
|
86310
|
+
{
|
|
86311
|
+
id: "petFountain.intervalOn",
|
|
86312
|
+
method: "setPumpInterval",
|
|
86313
|
+
label: t("Pump cycle on", "Ciclo pompa attivo"),
|
|
86314
|
+
phrase: t("Run the pump of {device} on its on/off cycle", "Fai lavorare la pompa di {device} a ciclo acceso/spento"),
|
|
86315
|
+
icon: "timer",
|
|
86316
|
+
args: { on: true }
|
|
86317
|
+
},
|
|
86318
|
+
{
|
|
86319
|
+
id: "petFountain.intervalOff",
|
|
86320
|
+
method: "setPumpInterval",
|
|
86321
|
+
label: t("Pump cycle off", "Ciclo pompa spento"),
|
|
86322
|
+
phrase: t("Run the pump of {device} continuously", "Fai lavorare la pompa di {device} in continuo"),
|
|
86323
|
+
icon: "timer-off",
|
|
86324
|
+
args: { on: false }
|
|
86325
|
+
},
|
|
86326
|
+
{
|
|
86327
|
+
id: "petFountain.pumpOnMinutes",
|
|
86328
|
+
method: "setPumpOnMinutes",
|
|
86329
|
+
label: t("Set pump run time", "Imposta il tempo di lavoro della pompa"),
|
|
86330
|
+
phrase: t("Set the pump of {device} to run {minutes} minutes per cycle", "Imposta la pompa di {device} a lavorare {minutes} minuti per ciclo"),
|
|
86331
|
+
icon: "timer",
|
|
86332
|
+
openArgs: [{
|
|
86333
|
+
key: "minutes",
|
|
86334
|
+
label: t("Minutes", "Minuti"),
|
|
86335
|
+
offer: {
|
|
86336
|
+
kind: "number",
|
|
86337
|
+
min: 0,
|
|
86338
|
+
step: 1,
|
|
86339
|
+
unit: "min"
|
|
86340
|
+
}
|
|
86341
|
+
}]
|
|
86342
|
+
},
|
|
86343
|
+
{
|
|
86344
|
+
id: "petFountain.pumpOffMinutes",
|
|
86345
|
+
method: "setPumpOffMinutes",
|
|
86346
|
+
label: t("Set pump rest time", "Imposta il tempo di riposo della pompa"),
|
|
86347
|
+
phrase: t("Set the pump of {device} to rest {minutes} minutes per cycle", "Imposta la pompa di {device} a riposare {minutes} minuti per ciclo"),
|
|
86348
|
+
icon: "timer-off",
|
|
86349
|
+
openArgs: [{
|
|
86350
|
+
key: "minutes",
|
|
86351
|
+
label: t("Minutes", "Minuti"),
|
|
86352
|
+
offer: {
|
|
86353
|
+
kind: "number",
|
|
86354
|
+
min: 0,
|
|
86355
|
+
step: 1,
|
|
86356
|
+
unit: "min"
|
|
86357
|
+
}
|
|
86358
|
+
}]
|
|
86359
|
+
},
|
|
86360
|
+
{
|
|
86361
|
+
id: "petFountain.resetDailyConsumption",
|
|
86362
|
+
method: "resetDailyConsumption",
|
|
86363
|
+
label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
|
|
86364
|
+
phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
|
|
86365
|
+
icon: "rotate-ccw"
|
|
86366
|
+
}
|
|
86367
|
+
],
|
|
86368
|
+
state: [{
|
|
86369
|
+
kind: "boolean",
|
|
86370
|
+
field: "empty",
|
|
86371
|
+
label: t("Tank empty", "Serbatoio vuoto"),
|
|
86372
|
+
whenTrue: t("When the tank of {device} is empty", "Quando il serbatoio di {device} è vuoto"),
|
|
86373
|
+
whenFalse: t("When the tank of {device} is refilled", "Quando il serbatoio di {device} viene riempito"),
|
|
86374
|
+
icon: "droplet"
|
|
86375
|
+
}, {
|
|
86376
|
+
kind: "numeric",
|
|
86377
|
+
field: "waterLevel",
|
|
86378
|
+
quantity: t("the water level", "il livello dell’acqua"),
|
|
86379
|
+
label: t("Water level", "Livello dell’acqua"),
|
|
86380
|
+
unit: "%",
|
|
86381
|
+
icon: "droplets"
|
|
86382
|
+
}]
|
|
86383
|
+
},
|
|
85766
86384
|
"media-player": {
|
|
85767
86385
|
scope: "device",
|
|
85768
86386
|
noun: t("Media player", "Lettore multimediale"),
|
|
@@ -87587,6 +88205,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
87587
88205
|
"device-manager": system("the device registry itself — rename, move and remove are administration"),
|
|
87588
88206
|
"device-provider": system(INTERNAL),
|
|
87589
88207
|
"device-removal-hook": system(INTERNAL),
|
|
88208
|
+
"node-removal-hook": system(INTERNAL),
|
|
87590
88209
|
"device-state": system("the runtime-state mirror every trigger already reads through"),
|
|
87591
88210
|
"display-palette": system("the cluster display palette — one colour table for every client, not a device action"),
|
|
87592
88211
|
"embedding-encoder": system(ML),
|
|
@@ -87699,7 +88318,7 @@ function projectAction(cap, action) {
|
|
|
87699
88318
|
};
|
|
87700
88319
|
}
|
|
87701
88320
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
87702
|
-
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;
|
|
88321
|
+
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;
|
|
87703
88322
|
DeviceFeature.BatteryOperated;
|
|
87704
88323
|
/**
|
|
87705
88324
|
* How a washer, dryer or dishwasher leaves CamStack.
|
|
@@ -89261,6 +89880,36 @@ Object.freeze({
|
|
|
89261
89880
|
form: "single",
|
|
89262
89881
|
optional: false
|
|
89263
89882
|
}],
|
|
89883
|
+
"petFountain.getStatus": [{
|
|
89884
|
+
name: "deviceId",
|
|
89885
|
+
form: "single",
|
|
89886
|
+
optional: false
|
|
89887
|
+
}],
|
|
89888
|
+
"petFountain.resetDailyConsumption": [{
|
|
89889
|
+
name: "deviceId",
|
|
89890
|
+
form: "single",
|
|
89891
|
+
optional: false
|
|
89892
|
+
}],
|
|
89893
|
+
"petFountain.setPump": [{
|
|
89894
|
+
name: "deviceId",
|
|
89895
|
+
form: "single",
|
|
89896
|
+
optional: false
|
|
89897
|
+
}],
|
|
89898
|
+
"petFountain.setPumpInterval": [{
|
|
89899
|
+
name: "deviceId",
|
|
89900
|
+
form: "single",
|
|
89901
|
+
optional: false
|
|
89902
|
+
}],
|
|
89903
|
+
"petFountain.setPumpOffMinutes": [{
|
|
89904
|
+
name: "deviceId",
|
|
89905
|
+
form: "single",
|
|
89906
|
+
optional: false
|
|
89907
|
+
}],
|
|
89908
|
+
"petFountain.setPumpOnMinutes": [{
|
|
89909
|
+
name: "deviceId",
|
|
89910
|
+
form: "single",
|
|
89911
|
+
optional: false
|
|
89912
|
+
}],
|
|
89264
89913
|
"pipelineAnalytics.addRetrainFrameFromRecording": [{
|
|
89265
89914
|
name: "deviceId",
|
|
89266
89915
|
form: "single",
|
|
@@ -92268,4 +92917,4 @@ object({
|
|
|
92268
92917
|
commands: record(string(), unknown())
|
|
92269
92918
|
});
|
|
92270
92919
|
//#endregion
|
|
92271
|
-
export { lawnMowerControlCapability as $,
|
|
92920
|
+
export { lawnMowerControlCapability as $, object as $t, buttonCapability as A, valveCapability as At, coverCapability as B, baseUnitOf as Bt, batteryCapability as C, switchCapability as Ct, brokerCapability as D, tryConvertUnit as Dt, brightnessCapability as E, tryConvertDelta as Et, connectivityCapability as F, DeviceRole as Ft, eventEmitterCapability as G, isReadingQuantity as Gt, deviceAdoptionCapability as H, defineEntryKind as Ht, consumableFormOf as I, DeviceType as It, gasCapability as J, array as Jt, fanControlCapability as K, nodePin as Kt, consumableKindOf as L, errMsg as Lt, carbonMonoxideCapability as M, waterHeaterCapability as Mt, climateControlCapability as N, weatherCapability as Nt, buildAddonRouteProvider as O, updateCapability as Ot, colorCapability as P, DeviceFeature as Pt, ingestReading as Q, number as Qt, contactCapability as R, BaseAddon as Rt, automationControlCapability as S, smokeCapability as St, binaryCapability as T, temperatureSensorCapability as Tt, deviceExportCapability as U, defineRecordKinds as Ut, deviceAdminLinkCapability as V, createEvent as Vt, enumSensorCapability as W, entryBody as Wt, humiditySensorCapability as X, discriminatedUnion as Xt, humidifierCapability as Y, boolean as Yt, imageCapability as Z, literal as Zt, accessoriesCapability as _, runningFromMediaPlayer as _t, COCO_TO_MACRO as a, EventCategory as an, notifierCapability as at, alarmPanelCapability as b, scriptRunnerCapability as bt, ConsumableItemSchema as c, parseAdminBase as ct, FanDirectionSchema as d, presenceCapability as dt, record as en, lockControlCapability as et, HvacModeSchema as f, pressureSensorCapability as ft, TargetSchema as g, runningFromLawnMower as gt, RUNNING_STATE_COLD as h, runExportedAction as ht, CAMERA_SWITCH_ORDER as i, url as in, notificationOutputCapability as it, calendarSourceCapability as j, vibrationCapability as jt, buildDeviceAdminUrl as k, vacuumControlCapability as kt, ConsumableStatusSchema as l, powerMeterCapability as lt, PLUG_APPLIANCE_TYPES as m, quantityFromUnit as mt, BaseDevice as n, union as nn, motionCapability as nt, CameraSwitchIdSchema as o, numericSensorCapability as ot, MediaPlayerRepeatSchema as p, pruneDeletedEntities as pt, floodCapability as q, _enum as qt, BaseDeviceProvider as r, unknown as rn, normalizeUnit as rt, ConsumableFormSchema as s, oauthIntegrationCapability as st, AlarmArmModeSchema as t, string as tn, mediaPlayerCapability as tt, EnumSensorDateTimeFormatSchema as u, prepareNotification as ut, addonRoutesCapability as v, runningFromVacuumState as vt, bestLocationMatch as w, tamperCapability as wt, ambientLightSensorCapability as x, sendToExportedTarget as xt, airQualitySensorCapability as y, runningStateCapability as yt, controlCapability as z, RecordSecretUnavailableError as zt };
|