@camstack/addon-import-alexa 0.2.166 → 0.2.168
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 +12 -0
- package/dist/addon.js +1272 -663
- package/dist/addon.mjs +1272 -663
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -5248,6 +5248,14 @@ function object(shape, params) {
|
|
|
5248
5248
|
...normalizeParams(params)
|
|
5249
5249
|
});
|
|
5250
5250
|
}
|
|
5251
|
+
function strictObject(shape, params) {
|
|
5252
|
+
return new ZodObject({
|
|
5253
|
+
type: "object",
|
|
5254
|
+
shape,
|
|
5255
|
+
catchall: never(),
|
|
5256
|
+
...normalizeParams(params)
|
|
5257
|
+
});
|
|
5258
|
+
}
|
|
5251
5259
|
function looseObject(shape, params) {
|
|
5252
5260
|
return new ZodObject({
|
|
5253
5261
|
type: "object",
|
|
@@ -5627,7 +5635,7 @@ var ZodIssueCode = {
|
|
|
5627
5635
|
var ZodFirstPartyTypeKind;
|
|
5628
5636
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5629
5637
|
//#endregion
|
|
5630
|
-
//#region ../types/dist/sleep-
|
|
5638
|
+
//#region ../types/dist/sleep-6eG9C8Xl.mjs
|
|
5631
5639
|
/**
|
|
5632
5640
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5633
5641
|
* window to float samples (D455).
|
|
@@ -8862,9 +8870,76 @@ function optionalMethod(input, output, options) {
|
|
|
8862
8870
|
function event(data) {
|
|
8863
8871
|
return { data };
|
|
8864
8872
|
}
|
|
8873
|
+
/**
|
|
8874
|
+
* `addon-pages-source` — collection cap exposing per-provider raw page
|
|
8875
|
+
* declarations. Every addon that contributes a UI page registers a
|
|
8876
|
+
* provider here. The hub-side singleton aggregator (`addon-pages` cap,
|
|
8877
|
+
* see `addon-pages.cap.ts`) walks this collection, stamps versioned
|
|
8878
|
+
* `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
|
|
8879
|
+
* that admin-ui consumes.
|
|
8880
|
+
*
|
|
8881
|
+
* The split exists because the public listing has a different output
|
|
8882
|
+
* shape than the per-provider raw declarations, and we want both ends
|
|
8883
|
+
* to flow through codegen instead of relying on a hand-written wrapper.
|
|
8884
|
+
*/
|
|
8885
|
+
var AddonPageDeclarationSchema = object({
|
|
8886
|
+
id: string(),
|
|
8887
|
+
label: string(),
|
|
8888
|
+
icon: string(),
|
|
8889
|
+
path: string(),
|
|
8890
|
+
/**
|
|
8891
|
+
* Module Federation remote name — must match the `name` field on the
|
|
8892
|
+
* page addon's `federation()` plugin config. Used by admin-ui's
|
|
8893
|
+
* `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
|
|
8894
|
+
* Conventionally `addon_<id>_page` (snake_case; MF names cannot
|
|
8895
|
+
* contain hyphens).
|
|
8896
|
+
*/
|
|
8897
|
+
remoteName: string(),
|
|
8898
|
+
/**
|
|
8899
|
+
* Bundle filename inside the addon's `dist/` dir served at
|
|
8900
|
+
* `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
|
|
8901
|
+
* is always `'remoteEntry.js'`; the value is kept on the metadata so
|
|
8902
|
+
* the static-file route can compute an mtime-based cache-buster URL
|
|
8903
|
+
* without a separate filesystem stat.
|
|
8904
|
+
*/
|
|
8905
|
+
bundle: string(),
|
|
8906
|
+
/**
|
|
8907
|
+
* Sidebar section this page docks into. Well-known ids: `'detection'`,
|
|
8908
|
+
* `'cluster'`, `'administration'` — the page renders inside that group.
|
|
8909
|
+
* Any OTHER string creates (or joins) a custom section rendered after
|
|
8910
|
+
* the built-in groups; its label comes from `sectionLabel` (first
|
|
8911
|
+
* declaration wins), falling back to the id. Absent → the legacy
|
|
8912
|
+
* "Addon Pages" group.
|
|
8913
|
+
*/
|
|
8914
|
+
section: string().optional(),
|
|
8915
|
+
/** Display label for a CUSTOM `section` id (ignored for well-known ids). */
|
|
8916
|
+
sectionLabel: string().optional()
|
|
8917
|
+
});
|
|
8918
|
+
method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
|
|
8865
8919
|
var StaticDirOutputSchema$1 = object({ staticDir: string() });
|
|
8866
8920
|
var VersionOutputSchema$1 = object({ version: string() });
|
|
8867
8921
|
method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
|
|
8922
|
+
/**
|
|
8923
|
+
* The pipeline step-input shape and the run-result bridge, in a module that
|
|
8924
|
+
* imports nothing but zod and types.
|
|
8925
|
+
*
|
|
8926
|
+
* WHY A SEPARATE FILE (addon footprint, D28): both were declared in
|
|
8927
|
+
* `pipeline-executor.cap.ts`, whose import evaluates the whole executor schema
|
|
8928
|
+
* graph. An addon that only needs these two (the benchmark's `runPipelineTest`
|
|
8929
|
+
* action) paid ~130 MB of heap at load. `pipeline-executor.cap.ts` re-exports
|
|
8930
|
+
* them, so every existing import keeps working; addon server code takes them
|
|
8931
|
+
* from `@camstack/types/addon`.
|
|
8932
|
+
*/
|
|
8933
|
+
var PipelineStepInputSchema = lazy(() => object({
|
|
8934
|
+
addonId: string(),
|
|
8935
|
+
modelId: string().optional(),
|
|
8936
|
+
enabled: boolean().default(true),
|
|
8937
|
+
children: array(PipelineStepInputSchema).optional(),
|
|
8938
|
+
settings: record(string(), unknown()).optional(),
|
|
8939
|
+
jumpDeviceKey: string().optional()
|
|
8940
|
+
}));
|
|
8941
|
+
/** Typed bridge to the recursive `FrameResult` graph; validation is structural. */
|
|
8942
|
+
var PipelineRunResultBridge = custom();
|
|
8868
8943
|
var StaticDirOutputSchema = object({ staticDir: string() });
|
|
8869
8944
|
var VersionOutputSchema = object({ version: string() });
|
|
8870
8945
|
method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
|
|
@@ -9060,6 +9135,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9060
9135
|
"osd",
|
|
9061
9136
|
"osd-manager",
|
|
9062
9137
|
"pet-feeder",
|
|
9138
|
+
"pet-fountain",
|
|
9063
9139
|
"pipeline-analytics",
|
|
9064
9140
|
"pipeline-executor",
|
|
9065
9141
|
"pipeline-orchestrator",
|
|
@@ -10339,6 +10415,56 @@ Object.freeze({
|
|
|
10339
10415
|
"valueType": "number"
|
|
10340
10416
|
}
|
|
10341
10417
|
],
|
|
10418
|
+
"pet-fountain": [
|
|
10419
|
+
{
|
|
10420
|
+
"path": "waterLevel",
|
|
10421
|
+
"valueType": "number"
|
|
10422
|
+
},
|
|
10423
|
+
{
|
|
10424
|
+
"path": "waterVolume",
|
|
10425
|
+
"valueType": "number"
|
|
10426
|
+
},
|
|
10427
|
+
{
|
|
10428
|
+
"path": "empty",
|
|
10429
|
+
"valueType": "boolean"
|
|
10430
|
+
},
|
|
10431
|
+
{
|
|
10432
|
+
"path": "pumpRunning",
|
|
10433
|
+
"valueType": "boolean"
|
|
10434
|
+
},
|
|
10435
|
+
{
|
|
10436
|
+
"path": "pumpIntervalEnabled",
|
|
10437
|
+
"valueType": "boolean"
|
|
10438
|
+
},
|
|
10439
|
+
{
|
|
10440
|
+
"path": "pumpOnTime",
|
|
10441
|
+
"valueType": "number"
|
|
10442
|
+
},
|
|
10443
|
+
{
|
|
10444
|
+
"path": "pumpOffTime",
|
|
10445
|
+
"valueType": "number"
|
|
10446
|
+
},
|
|
10447
|
+
{
|
|
10448
|
+
"path": "lastDrink",
|
|
10449
|
+
"valueType": "number"
|
|
10450
|
+
},
|
|
10451
|
+
{
|
|
10452
|
+
"path": "currentHourDrink",
|
|
10453
|
+
"valueType": "number"
|
|
10454
|
+
},
|
|
10455
|
+
{
|
|
10456
|
+
"path": "last24hDrink",
|
|
10457
|
+
"valueType": "number"
|
|
10458
|
+
},
|
|
10459
|
+
{
|
|
10460
|
+
"path": "todayDrink",
|
|
10461
|
+
"valueType": "number"
|
|
10462
|
+
},
|
|
10463
|
+
{
|
|
10464
|
+
"path": "error",
|
|
10465
|
+
"valueType": "string"
|
|
10466
|
+
}
|
|
10467
|
+
],
|
|
10342
10468
|
"power-meter": [
|
|
10343
10469
|
{
|
|
10344
10470
|
"path": "power",
|
|
@@ -10876,7 +11002,7 @@ object({
|
|
|
10876
11002
|
params: record(string(), union([string(), number()])).optional()
|
|
10877
11003
|
});
|
|
10878
11004
|
//#endregion
|
|
10879
|
-
//#region ../types/dist/err-msg-
|
|
11005
|
+
//#region ../types/dist/err-msg-DWXWc0Di.mjs
|
|
10880
11006
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10881
11007
|
DeviceType["Camera"] = "camera";
|
|
10882
11008
|
DeviceType["Hub"] = "hub";
|
|
@@ -10968,6 +11094,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10968
11094
|
* native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
|
|
10969
11095
|
* integrations sharing the same food/desiccant/hopper surface. */
|
|
10970
11096
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
11097
|
+
/** Pet water fountain — tank level, water drunk, pump and its duty cycle.
|
|
11098
|
+
* Installed with the `pet-fountain` cap. Reaches CamStack as loose entities
|
|
11099
|
+
* of a container and is BUILT as a composed device (D967). */
|
|
11100
|
+
DeviceType["PetFountain"] = "pet-fountain";
|
|
10971
11101
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10972
11102
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10973
11103
|
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
@@ -11238,7 +11368,7 @@ function errMsg(err) {
|
|
|
11238
11368
|
return String(err);
|
|
11239
11369
|
}
|
|
11240
11370
|
//#endregion
|
|
11241
|
-
//#region ../types/dist/composition-
|
|
11371
|
+
//#region ../types/dist/composition-kSuOifuG.mjs
|
|
11242
11372
|
/**
|
|
11243
11373
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11244
11374
|
*
|
|
@@ -11286,11 +11416,11 @@ function errMsg(err) {
|
|
|
11286
11416
|
*/
|
|
11287
11417
|
/** The pinned jellyfin-ffmpeg release. Changing this is the version change. */
|
|
11288
11418
|
var FFMPEG_RELEASE = "8.1.2-5";
|
|
11289
|
-
var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFMPEG_RELEASE}`;
|
|
11419
|
+
var BASE$1 = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFMPEG_RELEASE}`;
|
|
11290
11420
|
function portable(target) {
|
|
11291
|
-
return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
|
|
11421
|
+
return `${BASE$1}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
|
|
11292
11422
|
}
|
|
11293
|
-
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64"), `${BASE}${FFMPEG_RELEASE}`;
|
|
11423
|
+
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64"), `${BASE$1}${FFMPEG_RELEASE}`;
|
|
11294
11424
|
/**
|
|
11295
11425
|
* THE table "which boolean of this device type counts as HIGH", and the
|
|
11296
11426
|
* pure evaluator of its EDGE.
|
|
@@ -12323,12 +12453,28 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
12323
12453
|
])
|
|
12324
12454
|
})
|
|
12325
12455
|
]);
|
|
12326
|
-
var
|
|
12456
|
+
var forwardArgUnion = union([
|
|
12457
|
+
string(),
|
|
12458
|
+
number(),
|
|
12459
|
+
boolean(),
|
|
12460
|
+
strictObject({ ["$input"]: string().min(1) }),
|
|
12461
|
+
record(string().min(1), lazy(() => forwardArgUnion))
|
|
12462
|
+
]);
|
|
12463
|
+
var CompositionForwardArgSchema = forwardArgUnion;
|
|
12464
|
+
/**
|
|
12465
|
+
* `forward` (D659, amended by D967): one method of a BUILT capability is one
|
|
12466
|
+
* call of `cap.method` on `source`. Without `args` the built method's input
|
|
12467
|
+
* passes through minus `deviceId`; with `args` the template IS the source's
|
|
12468
|
+
* whole input. `deviceId` is always the source's own, never an argument.
|
|
12469
|
+
*/
|
|
12470
|
+
var CompositionForwardCommandSchema = object({
|
|
12327
12471
|
kind: literal("forward"),
|
|
12328
12472
|
source: CompositionSourceRefSchema,
|
|
12329
12473
|
cap: string().min(1),
|
|
12330
|
-
method: string().min(1)
|
|
12331
|
-
|
|
12474
|
+
method: string().min(1),
|
|
12475
|
+
args: record(string().min(1), CompositionForwardArgSchema).optional()
|
|
12476
|
+
});
|
|
12477
|
+
var CompositionCommandTargetSchema = discriminatedUnion("kind", [CompositionForwardCommandSchema, CompositionCodeSourceSchema]);
|
|
12332
12478
|
/** An item key inside an item-array cap (`consumables.items[<key>]`). */
|
|
12333
12479
|
var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
|
|
12334
12480
|
/**
|
|
@@ -12338,7 +12484,13 @@ var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
|
|
|
12338
12484
|
*/
|
|
12339
12485
|
var CompositionItemEntrySchema = object({
|
|
12340
12486
|
label: string().min(1).max(80),
|
|
12341
|
-
fields: record(string().min(1), CompositionFieldSourceSchema)
|
|
12487
|
+
fields: record(string().min(1), CompositionFieldSourceSchema),
|
|
12488
|
+
/**
|
|
12489
|
+
* What the cap's `reset` does for THIS item (consumables). Present = the item
|
|
12490
|
+
* is resettable and a `reset` call naming its key forwards here. The item key
|
|
12491
|
+
* selects the command, so it is never an argument: there is no pass-through.
|
|
12492
|
+
*/
|
|
12493
|
+
reset: CompositionForwardCommandSchema.optional()
|
|
12342
12494
|
});
|
|
12343
12495
|
var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
12344
12496
|
kind: literal("fields"),
|
|
@@ -12350,7 +12502,7 @@ var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
|
12350
12502
|
* owns the WHOLE array field: the composed value holds exactly these items.
|
|
12351
12503
|
*/
|
|
12352
12504
|
items: record(string().regex(COMPOSITION_ITEM_KEY_RE), CompositionItemEntrySchema).refine((items) => Object.keys(items).length <= 16, { message: `at most 16 items` }).optional(),
|
|
12353
|
-
/**
|
|
12505
|
+
/** Method name → what serves it. Only a BUILT capability takes commands (D967). */
|
|
12354
12506
|
commands: record(string().min(1), CompositionCommandTargetSchema).optional()
|
|
12355
12507
|
}), object({
|
|
12356
12508
|
kind: literal("passthrough"),
|
|
@@ -16407,6 +16559,449 @@ object({
|
|
|
16407
16559
|
lastError: string().nullable()
|
|
16408
16560
|
});
|
|
16409
16561
|
/**
|
|
16562
|
+
* notification-output — canonical, capability-gated notification delivery.
|
|
16563
|
+
*
|
|
16564
|
+
* Apprise-derived model (see
|
|
16565
|
+
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
16566
|
+
* callers emit ONE canonical `Notification`; each provider declares a
|
|
16567
|
+
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
16568
|
+
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
16569
|
+
* message to what the kind supports — callers never special-case a service.
|
|
16570
|
+
*
|
|
16571
|
+
* DESIGN DECISIONS (locked):
|
|
16572
|
+
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
16573
|
+
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
16574
|
+
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
16575
|
+
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
16576
|
+
* alternative would fork the UI per addon and cannot host the
|
|
16577
|
+
* discovery→adopt flow.
|
|
16578
|
+
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
16579
|
+
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
16580
|
+
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
16581
|
+
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
16582
|
+
* `addonId` the generated collection router extracts from the call input.
|
|
16583
|
+
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
16584
|
+
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
16585
|
+
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
16586
|
+
* base64 fallback needed.
|
|
16587
|
+
*
|
|
16588
|
+
* TODO (deferred, closed-set change — separate decision): add
|
|
16589
|
+
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
16590
|
+
* admin "Integrations" page.
|
|
16591
|
+
*/
|
|
16592
|
+
/**
|
|
16593
|
+
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
16594
|
+
* adapter picks what it supports and the degrade engine filters the rest.
|
|
16595
|
+
*/
|
|
16596
|
+
var AttachmentMediaTypeSchema = _enum([
|
|
16597
|
+
"image",
|
|
16598
|
+
"video",
|
|
16599
|
+
"gif",
|
|
16600
|
+
"audio",
|
|
16601
|
+
"icon"
|
|
16602
|
+
]);
|
|
16603
|
+
/**
|
|
16604
|
+
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
16605
|
+
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
16606
|
+
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
16607
|
+
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
16608
|
+
*/
|
|
16609
|
+
var AttachmentSchema = object({
|
|
16610
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
16611
|
+
url: string().optional(),
|
|
16612
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
16613
|
+
mime: string().optional(),
|
|
16614
|
+
name: string().optional()
|
|
16615
|
+
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
16616
|
+
var NotificationFormatSchema = _enum([
|
|
16617
|
+
"text",
|
|
16618
|
+
"markdown",
|
|
16619
|
+
"html"
|
|
16620
|
+
]);
|
|
16621
|
+
/**
|
|
16622
|
+
* The CLOSED icon vocabulary an action button may use.
|
|
16623
|
+
*
|
|
16624
|
+
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
16625
|
+
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
16626
|
+
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
16627
|
+
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
16628
|
+
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
16629
|
+
*
|
|
16630
|
+
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
16631
|
+
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
16632
|
+
* differently.
|
|
16633
|
+
*
|
|
16634
|
+
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
16635
|
+
*
|
|
16636
|
+
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
16637
|
+
* the whole set onto its own renderer's vocabulary through a
|
|
16638
|
+
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
16639
|
+
* fallback, so adding a member here fails every adapter's build until someone
|
|
16640
|
+
* decides its glyph, which is the only place that decision can be made
|
|
16641
|
+
* honestly.
|
|
16642
|
+
*
|
|
16643
|
+
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
16644
|
+
* `disarm` straight through; iOS feeds that string to
|
|
16645
|
+
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
16646
|
+
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
16647
|
+
* mapping, and "the field is documented" is not "the value renders".
|
|
16648
|
+
*
|
|
16649
|
+
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
16650
|
+
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
16651
|
+
* so an addon that emits a token the running hub does not know does not lose an
|
|
16652
|
+
* icon — its whole `send` fails Zod validation and the notification never
|
|
16653
|
+
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
16654
|
+
*/
|
|
16655
|
+
var NotificationActionIconSchema = _enum([
|
|
16656
|
+
"acknowledge",
|
|
16657
|
+
"dismiss",
|
|
16658
|
+
"silence",
|
|
16659
|
+
"snooze",
|
|
16660
|
+
"view",
|
|
16661
|
+
"play",
|
|
16662
|
+
"open",
|
|
16663
|
+
"close",
|
|
16664
|
+
"lock",
|
|
16665
|
+
"unlock",
|
|
16666
|
+
"arm",
|
|
16667
|
+
"arm-home",
|
|
16668
|
+
"arm-away",
|
|
16669
|
+
"arm-night",
|
|
16670
|
+
"disarm",
|
|
16671
|
+
"light",
|
|
16672
|
+
"alert",
|
|
16673
|
+
"camera"
|
|
16674
|
+
]);
|
|
16675
|
+
/** A single tap-through action button. */
|
|
16676
|
+
var NotificationActionSchema = object({
|
|
16677
|
+
id: string(),
|
|
16678
|
+
label: string(),
|
|
16679
|
+
url: string().optional(),
|
|
16680
|
+
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
16681
|
+
icon: NotificationActionIconSchema.optional(),
|
|
16682
|
+
/**
|
|
16683
|
+
* Renders in a warning style where the notifier supports it.
|
|
16684
|
+
*
|
|
16685
|
+
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
16686
|
+
* else — see `notification-center/action-token.ts` for what that does and
|
|
16687
|
+
* does not buy.
|
|
16688
|
+
*/
|
|
16689
|
+
destructive: boolean().optional(),
|
|
16690
|
+
/**
|
|
16691
|
+
* How the tap should REACH the url.
|
|
16692
|
+
*
|
|
16693
|
+
* `navigate` (absent, and every button authored before this field) opens it:
|
|
16694
|
+
* the phone leaves the notification and shows whatever the callback returns.
|
|
16695
|
+
* That is right for a button whose answer the operator wants to read.
|
|
16696
|
+
*
|
|
16697
|
+
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
16698
|
+
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
16699
|
+
* an answer to the notification, and being thrown into a browser tab to
|
|
16700
|
+
* confirm it costs more attention than the notification did. A backend that
|
|
16701
|
+
* cannot do a background call renders it as an ordinary link (the adapters
|
|
16702
|
+
* fall back rather than dropping the button), so this is a preference, never
|
|
16703
|
+
* a requirement.
|
|
16704
|
+
*/
|
|
16705
|
+
mode: _enum(["navigate", "background"]).optional()
|
|
16706
|
+
});
|
|
16707
|
+
/** A delivery parameter value: what a notifier declared it takes. */
|
|
16708
|
+
var DeliveryParamValueSchema = union([
|
|
16709
|
+
string(),
|
|
16710
|
+
number(),
|
|
16711
|
+
boolean()
|
|
16712
|
+
]);
|
|
16713
|
+
/**
|
|
16714
|
+
* One extra delivery parameter a notifier KIND declares (D938). A rule that
|
|
16715
|
+
* addresses a target of that kind may set it; a kind that declares nothing is
|
|
16716
|
+
* never handed one. The admin UI derives its control from this descriptor —
|
|
16717
|
+
* no editor knows a service by name.
|
|
16718
|
+
*
|
|
16719
|
+
* Optional fields are `.optional()`, never `.default()` (a Zod default does not
|
|
16720
|
+
* run on the addon cap path).
|
|
16721
|
+
*/
|
|
16722
|
+
var DeliveryParamSchema = object({
|
|
16723
|
+
key: string().min(1),
|
|
16724
|
+
label: string(),
|
|
16725
|
+
/** i18n key of the label, resolved by the declaring addon's namespace. */
|
|
16726
|
+
labelKey: string().optional(),
|
|
16727
|
+
type: _enum([
|
|
16728
|
+
"enum",
|
|
16729
|
+
"number",
|
|
16730
|
+
"boolean",
|
|
16731
|
+
"string"
|
|
16732
|
+
]),
|
|
16733
|
+
/** `enum` only: the closed set of accepted values. */
|
|
16734
|
+
options: array(object({
|
|
16735
|
+
value: string(),
|
|
16736
|
+
label: string()
|
|
16737
|
+
})).optional(),
|
|
16738
|
+
/** `number` only. */
|
|
16739
|
+
min: number().optional(),
|
|
16740
|
+
max: number().optional(),
|
|
16741
|
+
/** What the notifier does when the rule sets nothing; shown as the placeholder. */
|
|
16742
|
+
default: DeliveryParamValueSchema.optional()
|
|
16743
|
+
});
|
|
16744
|
+
/**
|
|
16745
|
+
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
16746
|
+
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
16747
|
+
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
16748
|
+
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
16749
|
+
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
16750
|
+
* `priority` for that one target.
|
|
16751
|
+
*/
|
|
16752
|
+
var NotificationSchema = object({
|
|
16753
|
+
body: string(),
|
|
16754
|
+
title: string().optional(),
|
|
16755
|
+
format: NotificationFormatSchema.default("text"),
|
|
16756
|
+
priority: number().int().min(1).max(5).default(3),
|
|
16757
|
+
level: string().optional(),
|
|
16758
|
+
attachments: array(AttachmentSchema).optional(),
|
|
16759
|
+
clickUrl: string().optional(),
|
|
16760
|
+
actions: array(NotificationActionSchema).optional(),
|
|
16761
|
+
sound: string().optional(),
|
|
16762
|
+
ttl: number().optional(),
|
|
16763
|
+
tag: string().optional(),
|
|
16764
|
+
deviceId: number().optional(),
|
|
16765
|
+
eventId: string().optional(),
|
|
16766
|
+
metadata: record(string(), unknown()).optional(),
|
|
16767
|
+
/**
|
|
16768
|
+
* Values for the parameters the target's kind DECLARES in
|
|
16769
|
+
* {@link TargetKindSchema} `deliveryParams`. Already validated against the
|
|
16770
|
+
* declaration by the sender; a notifier reads only the keys it declared.
|
|
16771
|
+
*/
|
|
16772
|
+
delivery: record(string(), DeliveryParamValueSchema).optional()
|
|
16773
|
+
});
|
|
16774
|
+
/** One declared native severity/priority level for a kind. */
|
|
16775
|
+
var TargetKindLevelSchema = object({
|
|
16776
|
+
id: string(),
|
|
16777
|
+
label: string(),
|
|
16778
|
+
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
16779
|
+
ordinal: number().int().min(1).max(5).nullable(),
|
|
16780
|
+
flags: object({
|
|
16781
|
+
critical: boolean().optional(),
|
|
16782
|
+
silent: boolean().optional(),
|
|
16783
|
+
noPush: boolean().optional()
|
|
16784
|
+
}).optional(),
|
|
16785
|
+
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
16786
|
+
requires: array(string()).optional(),
|
|
16787
|
+
description: string().optional()
|
|
16788
|
+
});
|
|
16789
|
+
/** The full capability block consulted before dispatch. */
|
|
16790
|
+
var TargetKindCapsSchema = object({
|
|
16791
|
+
attachments: object({
|
|
16792
|
+
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
16793
|
+
mode: _enum([
|
|
16794
|
+
"url",
|
|
16795
|
+
"bytes",
|
|
16796
|
+
"both"
|
|
16797
|
+
]),
|
|
16798
|
+
max: number().int().nonnegative(),
|
|
16799
|
+
maxBytes: number().int().positive().optional()
|
|
16800
|
+
}),
|
|
16801
|
+
/** Max action buttons (0 = none). */
|
|
16802
|
+
actions: number().int().nonnegative(),
|
|
16803
|
+
/**
|
|
16804
|
+
* Whether this kind renders a per-action ICON.
|
|
16805
|
+
*
|
|
16806
|
+
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
16807
|
+
* run on the addon cap path — three production failures in one day taught
|
|
16808
|
+
* this repo that once. Absent is read as false by the degrade engine, which
|
|
16809
|
+
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
16810
|
+
* assumed and dropped costs the operator's trust in the field.
|
|
16811
|
+
*/
|
|
16812
|
+
actionIcons: boolean().optional(),
|
|
16813
|
+
levels: array(TargetKindLevelSchema),
|
|
16814
|
+
format: array(NotificationFormatSchema),
|
|
16815
|
+
clickUrl: boolean(),
|
|
16816
|
+
sound: boolean(),
|
|
16817
|
+
ttl: boolean(),
|
|
16818
|
+
bodyMaxLen: number().int().positive()
|
|
16819
|
+
});
|
|
16820
|
+
/**
|
|
16821
|
+
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
16822
|
+
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
16823
|
+
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
16824
|
+
* the union is large and not meant for runtime validation here; the exported
|
|
16825
|
+
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
16826
|
+
*/
|
|
16827
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
16828
|
+
var TargetKindSchema = object({
|
|
16829
|
+
kind: string(),
|
|
16830
|
+
label: string(),
|
|
16831
|
+
icon: string(),
|
|
16832
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
16833
|
+
addonId: string(),
|
|
16834
|
+
/**
|
|
16835
|
+
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
16836
|
+
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
16837
|
+
* when the addon bundles no icon for that kind — the client then falls back
|
|
16838
|
+
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
16839
|
+
*
|
|
16840
|
+
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
16841
|
+
* client, and a native client joins it onto its own hub base.
|
|
16842
|
+
*
|
|
16843
|
+
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
16844
|
+
* field that survived only because the runtime cap-router forwards provider
|
|
16845
|
+
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
16846
|
+
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
16847
|
+
* broken silently the moment output validation was tightened anywhere.
|
|
16848
|
+
*/
|
|
16849
|
+
iconUrl: string().optional(),
|
|
16850
|
+
/**
|
|
16851
|
+
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
16852
|
+
*
|
|
16853
|
+
* The server knows this and therefore says it, because the client cannot
|
|
16854
|
+
* safely guess: a React-Native client renders SVG and raster through two
|
|
16855
|
+
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
16856
|
+
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
16857
|
+
* placeholder glyph for every vector icon while the web build looked fine.
|
|
16858
|
+
*
|
|
16859
|
+
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
16860
|
+
* not been updated — a client that cannot determine the type should prefer
|
|
16861
|
+
* its raster path, which is the safe default for an unknown image.
|
|
16862
|
+
*/
|
|
16863
|
+
iconMediaType: string().optional(),
|
|
16864
|
+
configSchema: ConfigSchemaPassthrough$3,
|
|
16865
|
+
/**
|
|
16866
|
+
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
16867
|
+
* base URL, instance, chat, recipient). A save that changes any of them
|
|
16868
|
+
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
16869
|
+
* editor uses this to ask for the secret on exactly those edits instead of
|
|
16870
|
+
* on every edit.
|
|
16871
|
+
*
|
|
16872
|
+
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
16873
|
+
* kinds) and from a provider older than this field — an editor that finds
|
|
16874
|
+
* it absent must assume ANY edit may need the secret.
|
|
16875
|
+
*/
|
|
16876
|
+
destinationKeys: array(string()).optional(),
|
|
16877
|
+
supportsDiscovery: boolean(),
|
|
16878
|
+
/**
|
|
16879
|
+
* Extra delivery parameters a rule may set for a target of this kind (D938).
|
|
16880
|
+
* Absent = none; the rule editor then offers nothing.
|
|
16881
|
+
*/
|
|
16882
|
+
deliveryParams: array(DeliveryParamSchema).optional(),
|
|
16883
|
+
caps: TargetKindCapsSchema
|
|
16884
|
+
});
|
|
16885
|
+
/**
|
|
16886
|
+
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
16887
|
+
* (return a presence marker only) when serving `listTargets` — never
|
|
16888
|
+
* round-trip a stored secret to the UI.
|
|
16889
|
+
*/
|
|
16890
|
+
var TargetSchema = object({
|
|
16891
|
+
id: string(),
|
|
16892
|
+
name: string(),
|
|
16893
|
+
kind: string(),
|
|
16894
|
+
addonId: string(),
|
|
16895
|
+
enabled: boolean(),
|
|
16896
|
+
config: record(string(), unknown())
|
|
16897
|
+
});
|
|
16898
|
+
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
16899
|
+
var DiscoveredTargetSchema = object({
|
|
16900
|
+
kind: string(),
|
|
16901
|
+
suggestedName: string(),
|
|
16902
|
+
config: record(string(), unknown())
|
|
16903
|
+
});
|
|
16904
|
+
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
16905
|
+
var RenderedAsSchema = object({
|
|
16906
|
+
level: string(),
|
|
16907
|
+
format: NotificationFormatSchema,
|
|
16908
|
+
attachmentsSent: number().int().nonnegative(),
|
|
16909
|
+
actionsSent: number().int().nonnegative(),
|
|
16910
|
+
truncated: boolean(),
|
|
16911
|
+
dropped: array(string())
|
|
16912
|
+
});
|
|
16913
|
+
var SendResultSchema = object({
|
|
16914
|
+
success: boolean(),
|
|
16915
|
+
error: string().optional(),
|
|
16916
|
+
renderedAs: RenderedAsSchema.optional()
|
|
16917
|
+
});
|
|
16918
|
+
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
16919
|
+
var TestResultSchema = SendResultSchema;
|
|
16920
|
+
/**
|
|
16921
|
+
* Pre-save credential probe against a partial (unsaved) target config.
|
|
16922
|
+
* `resolvedUrl` never includes secrets (token stripped from path).
|
|
16923
|
+
*/
|
|
16924
|
+
var ProbeTargetConfigResultSchema = object({
|
|
16925
|
+
ok: boolean(),
|
|
16926
|
+
resolvedUrl: string(),
|
|
16927
|
+
httpStatus: number().int().optional(),
|
|
16928
|
+
stateInstance: string().optional(),
|
|
16929
|
+
error: string().optional()
|
|
16930
|
+
});
|
|
16931
|
+
var notificationOutputCapability = {
|
|
16932
|
+
name: "notification-output",
|
|
16933
|
+
scope: "system",
|
|
16934
|
+
mode: "collection",
|
|
16935
|
+
methods: {
|
|
16936
|
+
listTargetKinds: method(object({}), array(TargetKindSchema)),
|
|
16937
|
+
listTargets: method(object({}), array(TargetSchema), { auth: "admin" }),
|
|
16938
|
+
/**
|
|
16939
|
+
* One target WITH secrets — admin edit / duplicate hydration only.
|
|
16940
|
+
* `listTargets` stays redacted; the editor loads this so password fields
|
|
16941
|
+
* are filled (masked in the input) and a save can round-trip the token
|
|
16942
|
+
* when a destination changes (Duplicate with a new chatId, etc.).
|
|
16943
|
+
*/
|
|
16944
|
+
getTarget: method(object({ targetId: string() }), TargetSchema, { auth: "admin" }),
|
|
16945
|
+
discoverTargets: method(object({
|
|
16946
|
+
kind: string(),
|
|
16947
|
+
config: record(string(), unknown()).optional(),
|
|
16948
|
+
/**
|
|
16949
|
+
* When editing / duplicating, the blank password field never round-trips
|
|
16950
|
+
* the stored token. Pass the source target id so the provider can restore
|
|
16951
|
+
* secrets server-side before dialling (same merge as upsert).
|
|
16952
|
+
*/
|
|
16953
|
+
targetId: string().optional()
|
|
16954
|
+
}), array(DiscoveredTargetSchema), { auth: "admin" }),
|
|
16955
|
+
probeTargetConfig: method(object({
|
|
16956
|
+
kind: string(),
|
|
16957
|
+
config: record(string(), unknown()),
|
|
16958
|
+
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
16959
|
+
targetId: string().optional()
|
|
16960
|
+
}), ProbeTargetConfigResultSchema, {
|
|
16961
|
+
kind: "mutation",
|
|
16962
|
+
auth: "admin"
|
|
16963
|
+
}),
|
|
16964
|
+
send: method(object({
|
|
16965
|
+
targetId: string(),
|
|
16966
|
+
notification: NotificationSchema
|
|
16967
|
+
}), SendResultSchema, {
|
|
16968
|
+
kind: "mutation",
|
|
16969
|
+
auth: "admin"
|
|
16970
|
+
}),
|
|
16971
|
+
testTarget: method(object({
|
|
16972
|
+
targetId: string(),
|
|
16973
|
+
sample: NotificationSchema.optional()
|
|
16974
|
+
}), TestResultSchema, {
|
|
16975
|
+
kind: "mutation",
|
|
16976
|
+
auth: "admin"
|
|
16977
|
+
}),
|
|
16978
|
+
upsertTarget: method(object({
|
|
16979
|
+
target: TargetSchema,
|
|
16980
|
+
/**
|
|
16981
|
+
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
16982
|
+
* existing row. The UI never holds the stored token; blank / redacted
|
|
16983
|
+
* password fields are restored from this source (D634 still applies if
|
|
16984
|
+
* destinations diverge without a re-typed secret).
|
|
16985
|
+
*/
|
|
16986
|
+
cloneSecretsFrom: string().optional()
|
|
16987
|
+
}), TargetSchema, {
|
|
16988
|
+
kind: "mutation",
|
|
16989
|
+
auth: "admin"
|
|
16990
|
+
}),
|
|
16991
|
+
deleteTarget: method(object({ targetId: string() }), _void(), {
|
|
16992
|
+
kind: "mutation",
|
|
16993
|
+
auth: "admin"
|
|
16994
|
+
}),
|
|
16995
|
+
setTargetEnabled: method(object({
|
|
16996
|
+
targetId: string(),
|
|
16997
|
+
enabled: boolean()
|
|
16998
|
+
}), _void(), {
|
|
16999
|
+
kind: "mutation",
|
|
17000
|
+
auth: "admin"
|
|
17001
|
+
})
|
|
17002
|
+
}
|
|
17003
|
+
};
|
|
17004
|
+
/**
|
|
16410
17005
|
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
16411
17006
|
* surface that admin-ui consumes through `useAddonPagesListPages()`.
|
|
16412
17007
|
*
|
|
@@ -16438,52 +17033,6 @@ var AddonPageInfoSchema = object({
|
|
|
16438
17033
|
bundleUrl: string()
|
|
16439
17034
|
});
|
|
16440
17035
|
method(_void(), array(AddonPageInfoSchema).readonly());
|
|
16441
|
-
/**
|
|
16442
|
-
* `addon-pages-source` — collection cap exposing per-provider raw page
|
|
16443
|
-
* declarations. Every addon that contributes a UI page registers a
|
|
16444
|
-
* provider here. The hub-side singleton aggregator (`addon-pages` cap,
|
|
16445
|
-
* see `addon-pages.cap.ts`) walks this collection, stamps versioned
|
|
16446
|
-
* `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
|
|
16447
|
-
* that admin-ui consumes.
|
|
16448
|
-
*
|
|
16449
|
-
* The split exists because the public listing has a different output
|
|
16450
|
-
* shape than the per-provider raw declarations, and we want both ends
|
|
16451
|
-
* to flow through codegen instead of relying on a hand-written wrapper.
|
|
16452
|
-
*/
|
|
16453
|
-
var AddonPageDeclarationSchema = object({
|
|
16454
|
-
id: string(),
|
|
16455
|
-
label: string(),
|
|
16456
|
-
icon: string(),
|
|
16457
|
-
path: string(),
|
|
16458
|
-
/**
|
|
16459
|
-
* Module Federation remote name — must match the `name` field on the
|
|
16460
|
-
* page addon's `federation()` plugin config. Used by admin-ui's
|
|
16461
|
-
* `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
|
|
16462
|
-
* Conventionally `addon_<id>_page` (snake_case; MF names cannot
|
|
16463
|
-
* contain hyphens).
|
|
16464
|
-
*/
|
|
16465
|
-
remoteName: string(),
|
|
16466
|
-
/**
|
|
16467
|
-
* Bundle filename inside the addon's `dist/` dir served at
|
|
16468
|
-
* `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
|
|
16469
|
-
* is always `'remoteEntry.js'`; the value is kept on the metadata so
|
|
16470
|
-
* the static-file route can compute an mtime-based cache-buster URL
|
|
16471
|
-
* without a separate filesystem stat.
|
|
16472
|
-
*/
|
|
16473
|
-
bundle: string(),
|
|
16474
|
-
/**
|
|
16475
|
-
* Sidebar section this page docks into. Well-known ids: `'detection'`,
|
|
16476
|
-
* `'cluster'`, `'administration'` — the page renders inside that group.
|
|
16477
|
-
* Any OTHER string creates (or joins) a custom section rendered after
|
|
16478
|
-
* the built-in groups; its label comes from `sectionLabel` (first
|
|
16479
|
-
* declaration wins), falling back to the id. Absent → the legacy
|
|
16480
|
-
* "Addon Pages" group.
|
|
16481
|
-
*/
|
|
16482
|
-
section: string().optional(),
|
|
16483
|
-
/** Display label for a CUSTOM `section` id (ignored for well-known ids). */
|
|
16484
|
-
sectionLabel: string().optional()
|
|
16485
|
-
});
|
|
16486
|
-
method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
|
|
16487
17036
|
var AddonHttpRouteSchema = object({
|
|
16488
17037
|
method: _enum([
|
|
16489
17038
|
"GET",
|
|
@@ -19200,7 +19749,11 @@ var CompositionProblemSchema = object({
|
|
|
19200
19749
|
"mixed-quantity",
|
|
19201
19750
|
"helper-mismatch",
|
|
19202
19751
|
"member-constraint",
|
|
19203
|
-
"retention"
|
|
19752
|
+
"retention",
|
|
19753
|
+
"command-method-unknown",
|
|
19754
|
+
"command-source-unknown",
|
|
19755
|
+
"command-input-unknown",
|
|
19756
|
+
"command-input-missing"
|
|
19204
19757
|
]),
|
|
19205
19758
|
path: string(),
|
|
19206
19759
|
message: string()
|
|
@@ -19239,7 +19792,9 @@ var CompositionFieldRoleSchema = _enum([
|
|
|
19239
19792
|
var CompositionFieldItemSchema = object({
|
|
19240
19793
|
arrayPath: string(),
|
|
19241
19794
|
key: string(),
|
|
19242
|
-
label: string()
|
|
19795
|
+
label: string(),
|
|
19796
|
+
/** True when the item carries a `reset` forward: the composer writes `resettable` from this (D967). */
|
|
19797
|
+
resettable: boolean().optional()
|
|
19243
19798
|
});
|
|
19244
19799
|
var CompositionFieldPlanSchema = object({
|
|
19245
19800
|
cap: string(),
|
|
@@ -19444,7 +19999,7 @@ Object.freeze({
|
|
|
19444
19999
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
19445
20000
|
* Data, not a form default written into the block.
|
|
19446
20001
|
*/
|
|
19447
|
-
var NOW$
|
|
20002
|
+
var NOW$3 = {
|
|
19448
20003
|
fieldPath: "lastFetchedAt",
|
|
19449
20004
|
expr: "now"
|
|
19450
20005
|
};
|
|
@@ -19496,7 +20051,7 @@ var GROUP_TEMPLATES = [
|
|
|
19496
20051
|
quantity: "temperature",
|
|
19497
20052
|
defaultAggregate: "mean",
|
|
19498
20053
|
empty: "null",
|
|
19499
|
-
companions: [NOW$
|
|
20054
|
+
companions: [NOW$3],
|
|
19500
20055
|
alsoFrom: {
|
|
19501
20056
|
deviceTypes: CLIMATE_DEVICES,
|
|
19502
20057
|
accept: {
|
|
@@ -19514,7 +20069,7 @@ var GROUP_TEMPLATES = [
|
|
|
19514
20069
|
quantity: "humidity",
|
|
19515
20070
|
defaultAggregate: "mean",
|
|
19516
20071
|
empty: "null",
|
|
19517
|
-
companions: [NOW$
|
|
20072
|
+
companions: [NOW$3],
|
|
19518
20073
|
alsoFrom: {
|
|
19519
20074
|
deviceTypes: CLIMATE_DEVICES,
|
|
19520
20075
|
accept: {
|
|
@@ -19532,7 +20087,7 @@ var GROUP_TEMPLATES = [
|
|
|
19532
20087
|
quantity: "pressure",
|
|
19533
20088
|
defaultAggregate: "mean",
|
|
19534
20089
|
empty: "null",
|
|
19535
|
-
companions: [NOW$
|
|
20090
|
+
companions: [NOW$3]
|
|
19536
20091
|
}),
|
|
19537
20092
|
numeric({
|
|
19538
20093
|
id: "illuminance",
|
|
@@ -19543,7 +20098,7 @@ var GROUP_TEMPLATES = [
|
|
|
19543
20098
|
quantity: "illuminance",
|
|
19544
20099
|
defaultAggregate: "mean",
|
|
19545
20100
|
empty: "null",
|
|
19546
|
-
companions: [NOW$
|
|
20101
|
+
companions: [NOW$3]
|
|
19547
20102
|
}),
|
|
19548
20103
|
numeric({
|
|
19549
20104
|
id: "power",
|
|
@@ -19554,7 +20109,7 @@ var GROUP_TEMPLATES = [
|
|
|
19554
20109
|
quantity: "power",
|
|
19555
20110
|
defaultAggregate: "sum",
|
|
19556
20111
|
empty: "omit",
|
|
19557
|
-
companions: [NOW$
|
|
20112
|
+
companions: [NOW$3]
|
|
19558
20113
|
}),
|
|
19559
20114
|
numeric({
|
|
19560
20115
|
id: "energy",
|
|
@@ -19565,7 +20120,7 @@ var GROUP_TEMPLATES = [
|
|
|
19565
20120
|
quantity: "energy",
|
|
19566
20121
|
defaultAggregate: "sum",
|
|
19567
20122
|
empty: "omit",
|
|
19568
|
-
companions: [NOW$
|
|
20123
|
+
companions: [NOW$3]
|
|
19569
20124
|
}),
|
|
19570
20125
|
numeric({
|
|
19571
20126
|
id: "battery",
|
|
@@ -19600,7 +20155,7 @@ var GROUP_TEMPLATES = [
|
|
|
19600
20155
|
quantity: "device",
|
|
19601
20156
|
defaultAggregate: "mean",
|
|
19602
20157
|
empty: "null",
|
|
19603
|
-
companions: [NOW$
|
|
20158
|
+
companions: [NOW$3]
|
|
19604
20159
|
}),
|
|
19605
20160
|
binary({
|
|
19606
20161
|
id: "motion",
|
|
@@ -22074,12 +22629,48 @@ var ExposeInputSchema = object({
|
|
|
22074
22629
|
capabilities: array(string()).optional()
|
|
22075
22630
|
});
|
|
22076
22631
|
var UnexposeInputSchema = object({ deviceId: string() });
|
|
22632
|
+
/**
|
|
22633
|
+
* What an exporter may expose besides devices (D968). An action and a
|
|
22634
|
+
* notification target are LIST ENTRIES, never devices (D954): they enter an
|
|
22635
|
+
* exporter's own selection, not the device registry.
|
|
22636
|
+
*/
|
|
22637
|
+
var ExportEntityKindSchema = _enum([
|
|
22638
|
+
"device",
|
|
22639
|
+
"action",
|
|
22640
|
+
"notifier"
|
|
22641
|
+
]);
|
|
22642
|
+
var ExportEntityRefSchema = discriminatedUnion("kind", [
|
|
22643
|
+
object({
|
|
22644
|
+
kind: literal("device"),
|
|
22645
|
+
deviceId: string()
|
|
22646
|
+
}),
|
|
22647
|
+
object({
|
|
22648
|
+
kind: literal("action"),
|
|
22649
|
+
actionId: string().min(1)
|
|
22650
|
+
}),
|
|
22651
|
+
object({
|
|
22652
|
+
kind: literal("notifier"),
|
|
22653
|
+
targetId: string().min(1)
|
|
22654
|
+
})
|
|
22655
|
+
]);
|
|
22656
|
+
var ExposedEntitySchema = object({
|
|
22657
|
+
entity: ExportEntityRefSchema,
|
|
22658
|
+
exposedAs: string().optional()
|
|
22659
|
+
});
|
|
22660
|
+
var ExposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
|
|
22661
|
+
var UnexposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
|
|
22077
22662
|
method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchema)), method(_void(), array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
|
|
22078
22663
|
kind: "mutation",
|
|
22079
22664
|
auth: "admin"
|
|
22080
22665
|
}), method(UnexposeInputSchema, _void(), {
|
|
22081
22666
|
kind: "mutation",
|
|
22082
22667
|
auth: "admin"
|
|
22668
|
+
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(_void(), array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
|
|
22669
|
+
kind: "mutation",
|
|
22670
|
+
auth: "admin"
|
|
22671
|
+
}), optionalMethod(UnexposeEntityInputSchema, _void(), {
|
|
22672
|
+
kind: "mutation",
|
|
22673
|
+
auth: "admin"
|
|
22083
22674
|
});
|
|
22084
22675
|
/** Lucide category ids, alphabetical. `ICON_META` indexes into this. */
|
|
22085
22676
|
var ICON_CATEGORY_IDS = [
|
|
@@ -44029,7 +44620,7 @@ var LlmProviderWidgetSchema = object({
|
|
|
44029
44620
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
44030
44621
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
44031
44622
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
44032
|
-
var ConfigSchemaPassthrough$
|
|
44623
|
+
var ConfigSchemaPassthrough$2 = unknown();
|
|
44033
44624
|
var LlmProfileKindDescriptorSchema = object({
|
|
44034
44625
|
kind: LlmProfileKindSchema,
|
|
44035
44626
|
label: string(),
|
|
@@ -44038,9 +44629,9 @@ var LlmProfileKindDescriptorSchema = object({
|
|
|
44038
44629
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
44039
44630
|
addonId: string(),
|
|
44040
44631
|
/** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
|
|
44041
|
-
connectionSchema: ConfigSchemaPassthrough$
|
|
44632
|
+
connectionSchema: ConfigSchemaPassthrough$2,
|
|
44042
44633
|
/** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
|
|
44043
|
-
advancedSchema: ConfigSchemaPassthrough$
|
|
44634
|
+
advancedSchema: ConfigSchemaPassthrough$2.optional(),
|
|
44044
44635
|
/** The kind's defaults; a model from `listModels` may override any of them. */
|
|
44045
44636
|
features: LlmModelFeaturesSchema,
|
|
44046
44637
|
/** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
|
|
@@ -44566,10 +45157,10 @@ var CpuBreakdownSchema = object({
|
|
|
44566
45157
|
cores: number()
|
|
44567
45158
|
});
|
|
44568
45159
|
var MemoryInfoSchema = object({
|
|
44569
|
-
percent: number(),
|
|
45160
|
+
percent: number().nullable(),
|
|
44570
45161
|
totalBytes: number(),
|
|
44571
|
-
usedBytes: number(),
|
|
44572
|
-
availableBytes: number(),
|
|
45162
|
+
usedBytes: number().nullable(),
|
|
45163
|
+
availableBytes: number().nullable(),
|
|
44573
45164
|
swapUsedBytes: number(),
|
|
44574
45165
|
swapTotalBytes: number()
|
|
44575
45166
|
});
|
|
@@ -44873,8 +45464,9 @@ var GetLoadSeriesInputSchema = object({
|
|
|
44873
45464
|
});
|
|
44874
45465
|
var SystemMetricsSchema = object({
|
|
44875
45466
|
cpuPercent: number(),
|
|
44876
|
-
|
|
44877
|
-
|
|
45467
|
+
/** `null` when the memory sample failed or was not taken yet (D393). */
|
|
45468
|
+
memoryPercent: number().nullable(),
|
|
45469
|
+
memoryUsedMB: number().nullable(),
|
|
44878
45470
|
memoryTotalMB: number(),
|
|
44879
45471
|
diskPercent: number().optional(),
|
|
44880
45472
|
temperature: number().optional(),
|
|
@@ -45003,556 +45595,113 @@ var BrokerInfoSchema$1 = object({
|
|
|
45003
45595
|
* consumer can pass them as `mqtt.connect(url, { username, password })`
|
|
45004
45596
|
* instead of stuffing creds into the URL (which leaks them into logs).
|
|
45005
45597
|
*/
|
|
45006
|
-
var BrokerConnectionDetailsSchema = object({
|
|
45007
|
-
url: string(),
|
|
45008
|
-
username: string().optional(),
|
|
45009
|
-
password: string().optional(),
|
|
45010
|
-
/**
|
|
45011
|
-
* Suggested prefix for `clientId`. Each consumer should suffix this
|
|
45012
|
-
* with its own discriminator (addon id, instance id) so reconnects
|
|
45013
|
-
* don't kick each other off (MQTT spec: clientId must be unique per
|
|
45014
|
-
* broker).
|
|
45015
|
-
*/
|
|
45016
|
-
clientIdPrefix: string().optional()
|
|
45017
|
-
});
|
|
45018
|
-
var AddBrokerInputSchema = object({
|
|
45019
|
-
name: string().min(1),
|
|
45020
|
-
url: string().regex(/^(mqtt|mqtts|ws|wss):\/\//, "URL must start with mqtt(s):// or ws(s)://"),
|
|
45021
|
-
username: string().optional(),
|
|
45022
|
-
password: string().optional(),
|
|
45023
|
-
clientIdPrefix: string().optional()
|
|
45024
|
-
});
|
|
45025
|
-
var AddBrokerResultSchema = object({ id: string() });
|
|
45026
|
-
var IdInputSchema = object({ id: string() });
|
|
45027
|
-
var TestResultSchema$1 = discriminatedUnion("ok", [object({
|
|
45028
|
-
ok: literal(true),
|
|
45029
|
-
latencyMs: number()
|
|
45030
|
-
}), object({
|
|
45031
|
-
ok: literal(false),
|
|
45032
|
-
error: string()
|
|
45033
|
-
})]);
|
|
45034
|
-
var StartEmbeddedInputSchema = object({
|
|
45035
|
-
port: number().int().min(1).max(65535).default(1883),
|
|
45036
|
-
/** Allow anonymous connect (no username/password). Default: false. */
|
|
45037
|
-
allowAnonymous: boolean().default(false),
|
|
45038
|
-
/** Optional shared username/password for clients. */
|
|
45039
|
-
username: string().optional(),
|
|
45040
|
-
password: string().optional()
|
|
45041
|
-
});
|
|
45042
|
-
var StartEmbeddedResultSchema = object({
|
|
45043
|
-
id: string(),
|
|
45044
|
-
url: string()
|
|
45045
|
-
});
|
|
45046
|
-
var StatusSchema = object({
|
|
45047
|
-
brokerCount: number(),
|
|
45048
|
-
embeddedRunning: boolean()
|
|
45049
|
-
});
|
|
45050
|
-
method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
|
|
45051
|
-
kind: "mutation",
|
|
45052
|
-
auth: "admin"
|
|
45053
|
-
}), method(IdInputSchema, _void(), {
|
|
45054
|
-
kind: "mutation",
|
|
45055
|
-
auth: "admin"
|
|
45056
|
-
}), method(IdInputSchema, TestResultSchema$1, {
|
|
45057
|
-
kind: "mutation",
|
|
45058
|
-
auth: "admin"
|
|
45059
|
-
}), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, {
|
|
45060
|
-
kind: "mutation",
|
|
45061
|
-
auth: "admin"
|
|
45062
|
-
}), method(IdInputSchema, _void(), {
|
|
45063
|
-
kind: "mutation",
|
|
45064
|
-
auth: "admin"
|
|
45065
|
-
}), method(_void(), StatusSchema);
|
|
45066
|
-
var NetworkEndpointSchema = object({
|
|
45067
|
-
url: string(),
|
|
45068
|
-
hostname: string(),
|
|
45069
|
-
port: number(),
|
|
45070
|
-
protocol: _enum(["http", "https"]),
|
|
45071
|
-
/**
|
|
45072
|
-
* Whether this address survives a restart of its provider. `false` = it is
|
|
45073
|
-
* re-issued every time (a Quick tunnel's random `*.trycloudflare.com`); the
|
|
45074
|
-
* hub never selects such an address AUTOMATICALLY for a role (D860), though
|
|
45075
|
-
* it stays visible and an operator may still flag it. Absent = stable: every
|
|
45076
|
-
* provider except a Quick tunnel owns a name that does not change.
|
|
45077
|
-
*/
|
|
45078
|
-
stable: boolean().optional()
|
|
45079
|
-
});
|
|
45080
|
-
object({
|
|
45081
|
-
url: string(),
|
|
45082
|
-
/** Mirrors {@link NetworkEndpointSchema} `stable`; absent = stable. */
|
|
45083
|
-
stable: boolean().optional(),
|
|
45084
|
-
updated: boolean().optional()
|
|
45085
|
-
});
|
|
45086
|
-
var NetworkAccessStatusSchema = object({
|
|
45087
|
-
connected: boolean(),
|
|
45088
|
-
endpoint: NetworkEndpointSchema.nullable(),
|
|
45089
|
-
error: string().optional()
|
|
45090
|
-
});
|
|
45091
|
-
/**
|
|
45092
|
-
* Optional, richer endpoint shape returned by providers that expose
|
|
45093
|
-
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
45094
|
-
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
45095
|
-
* the originating provider config (mode + sourcePort) so the
|
|
45096
|
-
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
45097
|
-
* one endpoint just omit `listEndpoints` from their provider impl.
|
|
45098
|
-
*/
|
|
45099
|
-
var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
|
|
45100
|
-
/**
|
|
45101
|
-
* Stable id within the provider — typically `<mode>-<sourcePort>` so
|
|
45102
|
-
* the orchestrator can dedupe across `listEndpoints` polls.
|
|
45103
|
-
*/
|
|
45104
|
-
id: string(),
|
|
45105
|
-
/** Operator-facing label (mirrors `MeshEndpoint.label`). */
|
|
45106
|
-
label: string(),
|
|
45107
|
-
/** Optional provider-specific mode tag, used for icon/colour in admin UI. */
|
|
45108
|
-
mode: string().optional(),
|
|
45109
|
-
/** Originating local port the ingress fronts (informational). */
|
|
45110
|
-
sourcePort: number().optional()
|
|
45111
|
-
});
|
|
45112
|
-
method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
|
|
45113
|
-
/**
|
|
45114
|
-
* notification-output — canonical, capability-gated notification delivery.
|
|
45115
|
-
*
|
|
45116
|
-
* Apprise-derived model (see
|
|
45117
|
-
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
45118
|
-
* callers emit ONE canonical `Notification`; each provider declares a
|
|
45119
|
-
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
45120
|
-
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
45121
|
-
* message to what the kind supports — callers never special-case a service.
|
|
45122
|
-
*
|
|
45123
|
-
* DESIGN DECISIONS (locked):
|
|
45124
|
-
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
45125
|
-
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
45126
|
-
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
45127
|
-
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
45128
|
-
* alternative would fork the UI per addon and cannot host the
|
|
45129
|
-
* discovery→adopt flow.
|
|
45130
|
-
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
45131
|
-
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
45132
|
-
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
45133
|
-
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
45134
|
-
* `addonId` the generated collection router extracts from the call input.
|
|
45135
|
-
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
45136
|
-
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
45137
|
-
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
45138
|
-
* base64 fallback needed.
|
|
45139
|
-
*
|
|
45140
|
-
* TODO (deferred, closed-set change — separate decision): add
|
|
45141
|
-
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
45142
|
-
* admin "Integrations" page.
|
|
45143
|
-
*/
|
|
45144
|
-
/**
|
|
45145
|
-
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
45146
|
-
* adapter picks what it supports and the degrade engine filters the rest.
|
|
45147
|
-
*/
|
|
45148
|
-
var AttachmentMediaTypeSchema = _enum([
|
|
45149
|
-
"image",
|
|
45150
|
-
"video",
|
|
45151
|
-
"gif",
|
|
45152
|
-
"audio",
|
|
45153
|
-
"icon"
|
|
45154
|
-
]);
|
|
45155
|
-
/**
|
|
45156
|
-
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
45157
|
-
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
45158
|
-
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
45159
|
-
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
45160
|
-
*/
|
|
45161
|
-
var AttachmentSchema = object({
|
|
45162
|
-
mediaType: AttachmentMediaTypeSchema,
|
|
45163
|
-
url: string().optional(),
|
|
45164
|
-
bytes: _instanceof(Uint8Array).optional(),
|
|
45165
|
-
mime: string().optional(),
|
|
45166
|
-
name: string().optional()
|
|
45167
|
-
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
45168
|
-
var NotificationFormatSchema = _enum([
|
|
45169
|
-
"text",
|
|
45170
|
-
"markdown",
|
|
45171
|
-
"html"
|
|
45172
|
-
]);
|
|
45173
|
-
/**
|
|
45174
|
-
* The CLOSED icon vocabulary an action button may use.
|
|
45175
|
-
*
|
|
45176
|
-
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
45177
|
-
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
45178
|
-
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
45179
|
-
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
45180
|
-
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
45181
|
-
*
|
|
45182
|
-
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
45183
|
-
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
45184
|
-
* differently.
|
|
45185
|
-
*
|
|
45186
|
-
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
45187
|
-
*
|
|
45188
|
-
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
45189
|
-
* the whole set onto its own renderer's vocabulary through a
|
|
45190
|
-
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
45191
|
-
* fallback, so adding a member here fails every adapter's build until someone
|
|
45192
|
-
* decides its glyph, which is the only place that decision can be made
|
|
45193
|
-
* honestly.
|
|
45194
|
-
*
|
|
45195
|
-
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
45196
|
-
* `disarm` straight through; iOS feeds that string to
|
|
45197
|
-
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
45198
|
-
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
45199
|
-
* mapping, and "the field is documented" is not "the value renders".
|
|
45200
|
-
*
|
|
45201
|
-
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
45202
|
-
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
45203
|
-
* so an addon that emits a token the running hub does not know does not lose an
|
|
45204
|
-
* icon — its whole `send` fails Zod validation and the notification never
|
|
45205
|
-
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
45206
|
-
*/
|
|
45207
|
-
var NotificationActionIconSchema = _enum([
|
|
45208
|
-
"acknowledge",
|
|
45209
|
-
"dismiss",
|
|
45210
|
-
"silence",
|
|
45211
|
-
"snooze",
|
|
45212
|
-
"view",
|
|
45213
|
-
"play",
|
|
45214
|
-
"open",
|
|
45215
|
-
"close",
|
|
45216
|
-
"lock",
|
|
45217
|
-
"unlock",
|
|
45218
|
-
"arm",
|
|
45219
|
-
"arm-home",
|
|
45220
|
-
"arm-away",
|
|
45221
|
-
"arm-night",
|
|
45222
|
-
"disarm",
|
|
45223
|
-
"light",
|
|
45224
|
-
"alert",
|
|
45225
|
-
"camera"
|
|
45226
|
-
]);
|
|
45227
|
-
/** A single tap-through action button. */
|
|
45228
|
-
var NotificationActionSchema = object({
|
|
45229
|
-
id: string(),
|
|
45230
|
-
label: string(),
|
|
45231
|
-
url: string().optional(),
|
|
45232
|
-
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
45233
|
-
icon: NotificationActionIconSchema.optional(),
|
|
45234
|
-
/**
|
|
45235
|
-
* Renders in a warning style where the notifier supports it.
|
|
45236
|
-
*
|
|
45237
|
-
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
45238
|
-
* else — see `notification-center/action-token.ts` for what that does and
|
|
45239
|
-
* does not buy.
|
|
45240
|
-
*/
|
|
45241
|
-
destructive: boolean().optional(),
|
|
45242
|
-
/**
|
|
45243
|
-
* How the tap should REACH the url.
|
|
45244
|
-
*
|
|
45245
|
-
* `navigate` (absent, and every button authored before this field) opens it:
|
|
45246
|
-
* the phone leaves the notification and shows whatever the callback returns.
|
|
45247
|
-
* That is right for a button whose answer the operator wants to read.
|
|
45248
|
-
*
|
|
45249
|
-
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
45250
|
-
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
45251
|
-
* an answer to the notification, and being thrown into a browser tab to
|
|
45252
|
-
* confirm it costs more attention than the notification did. A backend that
|
|
45253
|
-
* cannot do a background call renders it as an ordinary link (the adapters
|
|
45254
|
-
* fall back rather than dropping the button), so this is a preference, never
|
|
45255
|
-
* a requirement.
|
|
45256
|
-
*/
|
|
45257
|
-
mode: _enum(["navigate", "background"]).optional()
|
|
45258
|
-
});
|
|
45259
|
-
/** A delivery parameter value: what a notifier declared it takes. */
|
|
45260
|
-
var DeliveryParamValueSchema = union([
|
|
45261
|
-
string(),
|
|
45262
|
-
number(),
|
|
45263
|
-
boolean()
|
|
45264
|
-
]);
|
|
45265
|
-
/**
|
|
45266
|
-
* One extra delivery parameter a notifier KIND declares (D938). A rule that
|
|
45267
|
-
* addresses a target of that kind may set it; a kind that declares nothing is
|
|
45268
|
-
* never handed one. The admin UI derives its control from this descriptor —
|
|
45269
|
-
* no editor knows a service by name.
|
|
45270
|
-
*
|
|
45271
|
-
* Optional fields are `.optional()`, never `.default()` (a Zod default does not
|
|
45272
|
-
* run on the addon cap path).
|
|
45273
|
-
*/
|
|
45274
|
-
var DeliveryParamSchema = object({
|
|
45275
|
-
key: string().min(1),
|
|
45276
|
-
label: string(),
|
|
45277
|
-
/** i18n key of the label, resolved by the declaring addon's namespace. */
|
|
45278
|
-
labelKey: string().optional(),
|
|
45279
|
-
type: _enum([
|
|
45280
|
-
"enum",
|
|
45281
|
-
"number",
|
|
45282
|
-
"boolean",
|
|
45283
|
-
"string"
|
|
45284
|
-
]),
|
|
45285
|
-
/** `enum` only: the closed set of accepted values. */
|
|
45286
|
-
options: array(object({
|
|
45287
|
-
value: string(),
|
|
45288
|
-
label: string()
|
|
45289
|
-
})).optional(),
|
|
45290
|
-
/** `number` only. */
|
|
45291
|
-
min: number().optional(),
|
|
45292
|
-
max: number().optional(),
|
|
45293
|
-
/** What the notifier does when the rule sets nothing; shown as the placeholder. */
|
|
45294
|
-
default: DeliveryParamValueSchema.optional()
|
|
45295
|
-
});
|
|
45296
|
-
/**
|
|
45297
|
-
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
45298
|
-
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
45299
|
-
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
45300
|
-
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
45301
|
-
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
45302
|
-
* `priority` for that one target.
|
|
45303
|
-
*/
|
|
45304
|
-
var NotificationSchema = object({
|
|
45305
|
-
body: string(),
|
|
45306
|
-
title: string().optional(),
|
|
45307
|
-
format: NotificationFormatSchema.default("text"),
|
|
45308
|
-
priority: number().int().min(1).max(5).default(3),
|
|
45309
|
-
level: string().optional(),
|
|
45310
|
-
attachments: array(AttachmentSchema).optional(),
|
|
45311
|
-
clickUrl: string().optional(),
|
|
45312
|
-
actions: array(NotificationActionSchema).optional(),
|
|
45313
|
-
sound: string().optional(),
|
|
45314
|
-
ttl: number().optional(),
|
|
45315
|
-
tag: string().optional(),
|
|
45316
|
-
deviceId: number().optional(),
|
|
45317
|
-
eventId: string().optional(),
|
|
45318
|
-
metadata: record(string(), unknown()).optional(),
|
|
45319
|
-
/**
|
|
45320
|
-
* Values for the parameters the target's kind DECLARES in
|
|
45321
|
-
* {@link TargetKindSchema} `deliveryParams`. Already validated against the
|
|
45322
|
-
* declaration by the sender; a notifier reads only the keys it declared.
|
|
45323
|
-
*/
|
|
45324
|
-
delivery: record(string(), DeliveryParamValueSchema).optional()
|
|
45325
|
-
});
|
|
45326
|
-
/** One declared native severity/priority level for a kind. */
|
|
45327
|
-
var TargetKindLevelSchema = object({
|
|
45328
|
-
id: string(),
|
|
45329
|
-
label: string(),
|
|
45330
|
-
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
45331
|
-
ordinal: number().int().min(1).max(5).nullable(),
|
|
45332
|
-
flags: object({
|
|
45333
|
-
critical: boolean().optional(),
|
|
45334
|
-
silent: boolean().optional(),
|
|
45335
|
-
noPush: boolean().optional()
|
|
45336
|
-
}).optional(),
|
|
45337
|
-
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
45338
|
-
requires: array(string()).optional(),
|
|
45339
|
-
description: string().optional()
|
|
45340
|
-
});
|
|
45341
|
-
/** The full capability block consulted before dispatch. */
|
|
45342
|
-
var TargetKindCapsSchema = object({
|
|
45343
|
-
attachments: object({
|
|
45344
|
-
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
45345
|
-
mode: _enum([
|
|
45346
|
-
"url",
|
|
45347
|
-
"bytes",
|
|
45348
|
-
"both"
|
|
45349
|
-
]),
|
|
45350
|
-
max: number().int().nonnegative(),
|
|
45351
|
-
maxBytes: number().int().positive().optional()
|
|
45352
|
-
}),
|
|
45353
|
-
/** Max action buttons (0 = none). */
|
|
45354
|
-
actions: number().int().nonnegative(),
|
|
45355
|
-
/**
|
|
45356
|
-
* Whether this kind renders a per-action ICON.
|
|
45357
|
-
*
|
|
45358
|
-
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
45359
|
-
* run on the addon cap path — three production failures in one day taught
|
|
45360
|
-
* this repo that once. Absent is read as false by the degrade engine, which
|
|
45361
|
-
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
45362
|
-
* assumed and dropped costs the operator's trust in the field.
|
|
45363
|
-
*/
|
|
45364
|
-
actionIcons: boolean().optional(),
|
|
45365
|
-
levels: array(TargetKindLevelSchema),
|
|
45366
|
-
format: array(NotificationFormatSchema),
|
|
45367
|
-
clickUrl: boolean(),
|
|
45368
|
-
sound: boolean(),
|
|
45369
|
-
ttl: boolean(),
|
|
45370
|
-
bodyMaxLen: number().int().positive()
|
|
45371
|
-
});
|
|
45372
|
-
/**
|
|
45373
|
-
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
45374
|
-
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
45375
|
-
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
45376
|
-
* the union is large and not meant for runtime validation here; the exported
|
|
45377
|
-
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
45378
|
-
*/
|
|
45379
|
-
var ConfigSchemaPassthrough$2 = unknown();
|
|
45380
|
-
var TargetKindSchema = object({
|
|
45381
|
-
kind: string(),
|
|
45382
|
-
label: string(),
|
|
45383
|
-
icon: string(),
|
|
45384
|
-
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
45385
|
-
addonId: string(),
|
|
45386
|
-
/**
|
|
45387
|
-
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
45388
|
-
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
45389
|
-
* when the addon bundles no icon for that kind — the client then falls back
|
|
45390
|
-
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
45391
|
-
*
|
|
45392
|
-
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
45393
|
-
* client, and a native client joins it onto its own hub base.
|
|
45394
|
-
*
|
|
45395
|
-
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
45396
|
-
* field that survived only because the runtime cap-router forwards provider
|
|
45397
|
-
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
45398
|
-
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
45399
|
-
* broken silently the moment output validation was tightened anywhere.
|
|
45400
|
-
*/
|
|
45401
|
-
iconUrl: string().optional(),
|
|
45402
|
-
/**
|
|
45403
|
-
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
45404
|
-
*
|
|
45405
|
-
* The server knows this and therefore says it, because the client cannot
|
|
45406
|
-
* safely guess: a React-Native client renders SVG and raster through two
|
|
45407
|
-
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
45408
|
-
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
45409
|
-
* placeholder glyph for every vector icon while the web build looked fine.
|
|
45410
|
-
*
|
|
45411
|
-
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
45412
|
-
* not been updated — a client that cannot determine the type should prefer
|
|
45413
|
-
* its raster path, which is the safe default for an unknown image.
|
|
45414
|
-
*/
|
|
45415
|
-
iconMediaType: string().optional(),
|
|
45416
|
-
configSchema: ConfigSchemaPassthrough$2,
|
|
45417
|
-
/**
|
|
45418
|
-
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
45419
|
-
* base URL, instance, chat, recipient). A save that changes any of them
|
|
45420
|
-
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
45421
|
-
* editor uses this to ask for the secret on exactly those edits instead of
|
|
45422
|
-
* on every edit.
|
|
45423
|
-
*
|
|
45424
|
-
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
45425
|
-
* kinds) and from a provider older than this field — an editor that finds
|
|
45426
|
-
* it absent must assume ANY edit may need the secret.
|
|
45427
|
-
*/
|
|
45428
|
-
destinationKeys: array(string()).optional(),
|
|
45429
|
-
supportsDiscovery: boolean(),
|
|
45598
|
+
var BrokerConnectionDetailsSchema = object({
|
|
45599
|
+
url: string(),
|
|
45600
|
+
username: string().optional(),
|
|
45601
|
+
password: string().optional(),
|
|
45430
45602
|
/**
|
|
45431
|
-
*
|
|
45432
|
-
*
|
|
45603
|
+
* Suggested prefix for `clientId`. Each consumer should suffix this
|
|
45604
|
+
* with its own discriminator (addon id, instance id) so reconnects
|
|
45605
|
+
* don't kick each other off (MQTT spec: clientId must be unique per
|
|
45606
|
+
* broker).
|
|
45433
45607
|
*/
|
|
45434
|
-
|
|
45435
|
-
caps: TargetKindCapsSchema
|
|
45608
|
+
clientIdPrefix: string().optional()
|
|
45436
45609
|
});
|
|
45437
|
-
|
|
45438
|
-
|
|
45439
|
-
|
|
45440
|
-
|
|
45441
|
-
|
|
45442
|
-
|
|
45610
|
+
var AddBrokerInputSchema = object({
|
|
45611
|
+
name: string().min(1),
|
|
45612
|
+
url: string().regex(/^(mqtt|mqtts|ws|wss):\/\//, "URL must start with mqtt(s):// or ws(s)://"),
|
|
45613
|
+
username: string().optional(),
|
|
45614
|
+
password: string().optional(),
|
|
45615
|
+
clientIdPrefix: string().optional()
|
|
45616
|
+
});
|
|
45617
|
+
var AddBrokerResultSchema = object({ id: string() });
|
|
45618
|
+
var IdInputSchema = object({ id: string() });
|
|
45619
|
+
var TestResultSchema$1 = discriminatedUnion("ok", [object({
|
|
45620
|
+
ok: literal(true),
|
|
45621
|
+
latencyMs: number()
|
|
45622
|
+
}), object({
|
|
45623
|
+
ok: literal(false),
|
|
45624
|
+
error: string()
|
|
45625
|
+
})]);
|
|
45626
|
+
var StartEmbeddedInputSchema = object({
|
|
45627
|
+
port: number().int().min(1).max(65535).default(1883),
|
|
45628
|
+
/** Allow anonymous connect (no username/password). Default: false. */
|
|
45629
|
+
allowAnonymous: boolean().default(false),
|
|
45630
|
+
/** Optional shared username/password for clients. */
|
|
45631
|
+
username: string().optional(),
|
|
45632
|
+
password: string().optional()
|
|
45633
|
+
});
|
|
45634
|
+
var StartEmbeddedResultSchema = object({
|
|
45443
45635
|
id: string(),
|
|
45444
|
-
|
|
45445
|
-
kind: string(),
|
|
45446
|
-
addonId: string(),
|
|
45447
|
-
enabled: boolean(),
|
|
45448
|
-
config: record(string(), unknown())
|
|
45636
|
+
url: string()
|
|
45449
45637
|
});
|
|
45450
|
-
|
|
45451
|
-
|
|
45452
|
-
|
|
45453
|
-
suggestedName: string(),
|
|
45454
|
-
config: record(string(), unknown())
|
|
45638
|
+
var StatusSchema = object({
|
|
45639
|
+
brokerCount: number(),
|
|
45640
|
+
embeddedRunning: boolean()
|
|
45455
45641
|
});
|
|
45456
|
-
|
|
45457
|
-
|
|
45458
|
-
|
|
45459
|
-
|
|
45460
|
-
|
|
45461
|
-
|
|
45462
|
-
|
|
45463
|
-
|
|
45642
|
+
method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
|
|
45643
|
+
kind: "mutation",
|
|
45644
|
+
auth: "admin"
|
|
45645
|
+
}), method(IdInputSchema, _void(), {
|
|
45646
|
+
kind: "mutation",
|
|
45647
|
+
auth: "admin"
|
|
45648
|
+
}), method(IdInputSchema, TestResultSchema$1, {
|
|
45649
|
+
kind: "mutation",
|
|
45650
|
+
auth: "admin"
|
|
45651
|
+
}), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, {
|
|
45652
|
+
kind: "mutation",
|
|
45653
|
+
auth: "admin"
|
|
45654
|
+
}), method(IdInputSchema, _void(), {
|
|
45655
|
+
kind: "mutation",
|
|
45656
|
+
auth: "admin"
|
|
45657
|
+
}), method(_void(), StatusSchema);
|
|
45658
|
+
var NetworkEndpointSchema = object({
|
|
45659
|
+
url: string(),
|
|
45660
|
+
hostname: string(),
|
|
45661
|
+
port: number(),
|
|
45662
|
+
protocol: _enum(["http", "https"]),
|
|
45663
|
+
/**
|
|
45664
|
+
* Whether this address survives a restart of its provider. `false` = it is
|
|
45665
|
+
* re-issued every time (a Quick tunnel's random `*.trycloudflare.com`); the
|
|
45666
|
+
* hub never selects such an address AUTOMATICALLY for a role (D860), though
|
|
45667
|
+
* it stays visible and an operator may still flag it. Absent = stable: every
|
|
45668
|
+
* provider except a Quick tunnel owns a name that does not change.
|
|
45669
|
+
*/
|
|
45670
|
+
stable: boolean().optional()
|
|
45464
45671
|
});
|
|
45465
|
-
|
|
45466
|
-
|
|
45467
|
-
|
|
45468
|
-
|
|
45672
|
+
object({
|
|
45673
|
+
url: string(),
|
|
45674
|
+
/** Mirrors {@link NetworkEndpointSchema} `stable`; absent = stable. */
|
|
45675
|
+
stable: boolean().optional(),
|
|
45676
|
+
updated: boolean().optional()
|
|
45677
|
+
});
|
|
45678
|
+
var NetworkAccessStatusSchema = object({
|
|
45679
|
+
connected: boolean(),
|
|
45680
|
+
endpoint: NetworkEndpointSchema.nullable(),
|
|
45681
|
+
error: string().optional()
|
|
45469
45682
|
});
|
|
45470
|
-
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
45471
|
-
var TestResultSchema = SendResultSchema;
|
|
45472
45683
|
/**
|
|
45473
|
-
*
|
|
45474
|
-
*
|
|
45684
|
+
* Optional, richer endpoint shape returned by providers that expose
|
|
45685
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
45686
|
+
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
45687
|
+
* the originating provider config (mode + sourcePort) so the
|
|
45688
|
+
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
45689
|
+
* one endpoint just omit `listEndpoints` from their provider impl.
|
|
45475
45690
|
*/
|
|
45476
|
-
var
|
|
45477
|
-
|
|
45478
|
-
|
|
45479
|
-
|
|
45480
|
-
|
|
45481
|
-
|
|
45691
|
+
var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
|
|
45692
|
+
/**
|
|
45693
|
+
* Stable id within the provider — typically `<mode>-<sourcePort>` so
|
|
45694
|
+
* the orchestrator can dedupe across `listEndpoints` polls.
|
|
45695
|
+
*/
|
|
45696
|
+
id: string(),
|
|
45697
|
+
/** Operator-facing label (mirrors `MeshEndpoint.label`). */
|
|
45698
|
+
label: string(),
|
|
45699
|
+
/** Optional provider-specific mode tag, used for icon/colour in admin UI. */
|
|
45700
|
+
mode: string().optional(),
|
|
45701
|
+
/** Originating local port the ingress fronts (informational). */
|
|
45702
|
+
sourcePort: number().optional()
|
|
45482
45703
|
});
|
|
45483
|
-
|
|
45484
|
-
name: "notification-output",
|
|
45485
|
-
scope: "system",
|
|
45486
|
-
mode: "collection",
|
|
45487
|
-
methods: {
|
|
45488
|
-
listTargetKinds: method(object({}), array(TargetKindSchema)),
|
|
45489
|
-
listTargets: method(object({}), array(TargetSchema), { auth: "admin" }),
|
|
45490
|
-
/**
|
|
45491
|
-
* One target WITH secrets — admin edit / duplicate hydration only.
|
|
45492
|
-
* `listTargets` stays redacted; the editor loads this so password fields
|
|
45493
|
-
* are filled (masked in the input) and a save can round-trip the token
|
|
45494
|
-
* when a destination changes (Duplicate with a new chatId, etc.).
|
|
45495
|
-
*/
|
|
45496
|
-
getTarget: method(object({ targetId: string() }), TargetSchema, { auth: "admin" }),
|
|
45497
|
-
discoverTargets: method(object({
|
|
45498
|
-
kind: string(),
|
|
45499
|
-
config: record(string(), unknown()).optional(),
|
|
45500
|
-
/**
|
|
45501
|
-
* When editing / duplicating, the blank password field never round-trips
|
|
45502
|
-
* the stored token. Pass the source target id so the provider can restore
|
|
45503
|
-
* secrets server-side before dialling (same merge as upsert).
|
|
45504
|
-
*/
|
|
45505
|
-
targetId: string().optional()
|
|
45506
|
-
}), array(DiscoveredTargetSchema), { auth: "admin" }),
|
|
45507
|
-
probeTargetConfig: method(object({
|
|
45508
|
-
kind: string(),
|
|
45509
|
-
config: record(string(), unknown()),
|
|
45510
|
-
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
45511
|
-
targetId: string().optional()
|
|
45512
|
-
}), ProbeTargetConfigResultSchema, {
|
|
45513
|
-
kind: "mutation",
|
|
45514
|
-
auth: "admin"
|
|
45515
|
-
}),
|
|
45516
|
-
send: method(object({
|
|
45517
|
-
targetId: string(),
|
|
45518
|
-
notification: NotificationSchema
|
|
45519
|
-
}), SendResultSchema, {
|
|
45520
|
-
kind: "mutation",
|
|
45521
|
-
auth: "admin"
|
|
45522
|
-
}),
|
|
45523
|
-
testTarget: method(object({
|
|
45524
|
-
targetId: string(),
|
|
45525
|
-
sample: NotificationSchema.optional()
|
|
45526
|
-
}), TestResultSchema, {
|
|
45527
|
-
kind: "mutation",
|
|
45528
|
-
auth: "admin"
|
|
45529
|
-
}),
|
|
45530
|
-
upsertTarget: method(object({
|
|
45531
|
-
target: TargetSchema,
|
|
45532
|
-
/**
|
|
45533
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
45534
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
45535
|
-
* password fields are restored from this source (D634 still applies if
|
|
45536
|
-
* destinations diverge without a re-typed secret).
|
|
45537
|
-
*/
|
|
45538
|
-
cloneSecretsFrom: string().optional()
|
|
45539
|
-
}), TargetSchema, {
|
|
45540
|
-
kind: "mutation",
|
|
45541
|
-
auth: "admin"
|
|
45542
|
-
}),
|
|
45543
|
-
deleteTarget: method(object({ targetId: string() }), _void(), {
|
|
45544
|
-
kind: "mutation",
|
|
45545
|
-
auth: "admin"
|
|
45546
|
-
}),
|
|
45547
|
-
setTargetEnabled: method(object({
|
|
45548
|
-
targetId: string(),
|
|
45549
|
-
enabled: boolean()
|
|
45550
|
-
}), _void(), {
|
|
45551
|
-
kind: "mutation",
|
|
45552
|
-
auth: "admin"
|
|
45553
|
-
})
|
|
45554
|
-
}
|
|
45555
|
-
};
|
|
45704
|
+
method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
|
|
45556
45705
|
var AUDIO_MACRO_LABELS = [
|
|
45557
45706
|
{
|
|
45558
45707
|
id: "speech",
|
|
@@ -51997,14 +52146,6 @@ var EngineProvisioningSchema = object({
|
|
|
51997
52146
|
*/
|
|
51998
52147
|
configIssues: array(string()).optional()
|
|
51999
52148
|
});
|
|
52000
|
-
var PipelineStepInputSchema = lazy(() => object({
|
|
52001
|
-
addonId: string(),
|
|
52002
|
-
modelId: string().optional(),
|
|
52003
|
-
enabled: boolean().default(true),
|
|
52004
|
-
children: array(PipelineStepInputSchema).optional(),
|
|
52005
|
-
settings: record(string(), unknown()).optional(),
|
|
52006
|
-
jumpDeviceKey: string().optional()
|
|
52007
|
-
}));
|
|
52008
52149
|
var ModelSubstitutionSchema = object({
|
|
52009
52150
|
addonId: string(),
|
|
52010
52151
|
chosen: string(),
|
|
@@ -52083,7 +52224,6 @@ var ConfigUISchemaBridge = custom();
|
|
|
52083
52224
|
var ConfigUISchemaNullableBridge = custom();
|
|
52084
52225
|
var InferenceCapabilitiesBridge = custom();
|
|
52085
52226
|
var ModelAvailabilityListBridge = custom();
|
|
52086
|
-
var PipelineRunResultBridge = custom();
|
|
52087
52227
|
method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
|
|
52088
52228
|
modelId: string(),
|
|
52089
52229
|
settings: record(string(), unknown()).readonly()
|
|
@@ -55051,11 +55191,13 @@ var ImageContractSchema = object({
|
|
|
55051
55191
|
* The shell verdict (see {@link WrapperKindSchema}) with the proofs that
|
|
55052
55192
|
* produced it.
|
|
55053
55193
|
*
|
|
55054
|
-
* There is deliberately NO `canSelfUpdate`
|
|
55055
|
-
*
|
|
55056
|
-
*
|
|
55057
|
-
*
|
|
55058
|
-
*
|
|
55194
|
+
* There is deliberately NO `canSelfUpdate` boolean: a wrapper update is an
|
|
55195
|
+
* ANNOUNCEMENT. The notification body carries the exact command for a `docker`
|
|
55196
|
+
* shell and says the app restarts into the new build for `electron`, and that
|
|
55197
|
+
* sentence is chosen from `kind` alone — a boolean nothing reads is the defect
|
|
55198
|
+
* this repo keeps paying for. The one shell that CAN be acted on is the
|
|
55199
|
+
* Electron app, and only through `installDesktopUpdate` (see
|
|
55200
|
+
* {@link DesktopAppStatusSchema} for what the node reads from the app first).
|
|
55059
55201
|
*/
|
|
55060
55202
|
var WrapperIdentitySchema = object({
|
|
55061
55203
|
kind: _enum([
|
|
@@ -55103,6 +55245,49 @@ var ServerRollbackInfoSchema = object({
|
|
|
55103
55245
|
reason: string()
|
|
55104
55246
|
});
|
|
55105
55247
|
/**
|
|
55248
|
+
* What the native desktop app (Electron) that hosts this node says about
|
|
55249
|
+
* itself, read from `<dataDir>/server-root/desktop-app-state.json` (ADR-0971).
|
|
55250
|
+
* Absent on the status = no desktop app, or it never wrote the file.
|
|
55251
|
+
*
|
|
55252
|
+
* `stale` is the honest answer to an app that stopped writing (closed, hung,
|
|
55253
|
+
* crashed): the update facts are then the LAST ones known, not current ones,
|
|
55254
|
+
* and a consumer must not read `update.available: false` from a stale record
|
|
55255
|
+
* as "no update". `stateAtMs` is when the app last wrote.
|
|
55256
|
+
*/
|
|
55257
|
+
var DesktopAppStatusSchema = object({
|
|
55258
|
+
/** The Electron app's own version (not the node closure's). */
|
|
55259
|
+
version: string(),
|
|
55260
|
+
update: object({
|
|
55261
|
+
available: boolean(),
|
|
55262
|
+
/** The new build is downloaded and `installDesktopUpdate` can apply it. */
|
|
55263
|
+
downloaded: boolean(),
|
|
55264
|
+
version: string().nullable(),
|
|
55265
|
+
checking: boolean(),
|
|
55266
|
+
/** Why the app's last update check produced no answer; `null` when it did. */
|
|
55267
|
+
error: string().nullable()
|
|
55268
|
+
}),
|
|
55269
|
+
stateAtMs: number(),
|
|
55270
|
+
stale: boolean()
|
|
55271
|
+
});
|
|
55272
|
+
/**
|
|
55273
|
+
* Why `installDesktopUpdate` did nothing. Stable names a UI maps to a sentence:
|
|
55274
|
+
* - `not-electron` — this node does not run inside the desktop app.
|
|
55275
|
+
* - `no-app-state` — the app never published its state (or the file is unreadable).
|
|
55276
|
+
* - `app-state-stale` — the app stopped writing; it is probably not running.
|
|
55277
|
+
* - `not-downloaded` — no update is downloaded yet; nothing to install.
|
|
55278
|
+
*/
|
|
55279
|
+
var InstallDesktopUpdateReasonSchema = _enum([
|
|
55280
|
+
"not-electron",
|
|
55281
|
+
"no-app-state",
|
|
55282
|
+
"app-state-stale",
|
|
55283
|
+
"not-downloaded"
|
|
55284
|
+
]);
|
|
55285
|
+
var InstallDesktopUpdateResultSchema = object({
|
|
55286
|
+
accepted: boolean(),
|
|
55287
|
+
/** Non-null exactly when `accepted` is false. */
|
|
55288
|
+
reason: InstallDesktopUpdateReasonSchema.nullable()
|
|
55289
|
+
});
|
|
55290
|
+
/**
|
|
55106
55291
|
* The runner boot check (D877): the runners this node PLANNED (hub: one per
|
|
55107
55292
|
* `resolveRunnerId` over its forked addons; agent: the runners its process
|
|
55108
55293
|
* service holds, because only the hub plans) against the states the process
|
|
@@ -55206,7 +55391,12 @@ var ServerPackageStatusSchema = object({
|
|
|
55206
55391
|
* on a provider that predates it. Absent is "not checked yet", never "all
|
|
55207
55392
|
* well": a consumer that needs the answer treats it as pending.
|
|
55208
55393
|
*/
|
|
55209
|
-
runners: RunnerBootCheckSchema.optional()
|
|
55394
|
+
runners: RunnerBootCheckSchema.optional(),
|
|
55395
|
+
/**
|
|
55396
|
+
* The desktop app hosting this node (see {@link DesktopAppStatusSchema}).
|
|
55397
|
+
* Optional for version skew AND because most nodes have no desktop app.
|
|
55398
|
+
*/
|
|
55399
|
+
desktopApp: DesktopAppStatusSchema.optional()
|
|
55210
55400
|
});
|
|
55211
55401
|
var ServerUpdateCheckResultSchema = object({
|
|
55212
55402
|
packageName: string(),
|
|
@@ -55289,6 +55479,12 @@ var ServerUpdateActionResultSchema = object({
|
|
|
55289
55479
|
*/
|
|
55290
55480
|
blockers: array(UpdateBlockerSchema).optional()
|
|
55291
55481
|
});
|
|
55482
|
+
var LaunchOverridesSchema = object({
|
|
55483
|
+
/** The persisted heap ceiling in MB, or `null` when none is set. */
|
|
55484
|
+
heapMb: number().int().nullable() });
|
|
55485
|
+
var SetLaunchOverridesResultSchema = LaunchOverridesSchema.extend({
|
|
55486
|
+
/** Always true: the override is read at launch, not by the running process. */
|
|
55487
|
+
restartRequired: boolean() });
|
|
55292
55488
|
method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
|
|
55293
55489
|
kind: "mutation",
|
|
55294
55490
|
auth: "admin"
|
|
@@ -55305,12 +55501,18 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
|
|
|
55305
55501
|
kind: "mutation",
|
|
55306
55502
|
auth: "admin",
|
|
55307
55503
|
timeoutMs: 16 * 6e4
|
|
55504
|
+
}), method(_void(), InstallDesktopUpdateResultSchema, {
|
|
55505
|
+
kind: "mutation",
|
|
55506
|
+
auth: "admin"
|
|
55308
55507
|
}), method(_void(), ServerUpdateActionResultSchema, {
|
|
55309
55508
|
kind: "mutation",
|
|
55310
55509
|
auth: "admin"
|
|
55311
55510
|
}), method(_void(), ServerUpdateActionResultSchema, {
|
|
55312
55511
|
kind: "mutation",
|
|
55313
55512
|
auth: "admin"
|
|
55513
|
+
}), method(_void(), LaunchOverridesSchema, { auth: "admin" }), method(object({ heapMb: number().int().min(512).max(65536).nullable() }), SetLaunchOverridesResultSchema, {
|
|
55514
|
+
kind: "mutation",
|
|
55515
|
+
auth: "admin"
|
|
55314
55516
|
});
|
|
55315
55517
|
/**
|
|
55316
55518
|
* `smtp-provider` — pluggable email delivery surface.
|
|
@@ -60219,14 +60421,13 @@ reset: method(object({
|
|
|
60219
60421
|
auth: "admin"
|
|
60220
60422
|
}) },
|
|
60221
60423
|
/**
|
|
60222
|
-
* A
|
|
60223
|
-
*
|
|
60224
|
-
*
|
|
60225
|
-
* commands (`forward`) arrive in slice 3 (D663).
|
|
60424
|
+
* A consumable ADDED to an existing device (a graft) refuses `reset` by
|
|
60425
|
+
* name: only a composed device of a NEW type forwards an item's reset to a
|
|
60426
|
+
* source button, and marks that item `resettable` (D967).
|
|
60226
60427
|
*/
|
|
60227
60428
|
composedMethods: { reset: {
|
|
60228
60429
|
kind: "refuse",
|
|
60229
|
-
reason: "a
|
|
60430
|
+
reason: "a consumable added to an existing device is not resettable — only a new composed device forwards a reset"
|
|
60230
60431
|
} },
|
|
60231
60432
|
status: {
|
|
60232
60433
|
schema: ConsumablesStatusSchema,
|
|
@@ -60820,7 +61021,7 @@ var DishwasherStatusSchema = object({
|
|
|
60820
61021
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
60821
61022
|
lastFetchedAt: number()
|
|
60822
61023
|
});
|
|
60823
|
-
var DeviceIdSchema = number().int().nonnegative();
|
|
61024
|
+
var DeviceIdSchema$1 = number().int().nonnegative();
|
|
60824
61025
|
/** A value a program option or setting can take. */
|
|
60825
61026
|
var DishwasherValueSchema = union([
|
|
60826
61027
|
boolean(),
|
|
@@ -60895,77 +61096,77 @@ var dishwasherCapability = {
|
|
|
60895
61096
|
* option feature keys; each is checked against the program's constraints.
|
|
60896
61097
|
*/
|
|
60897
61098
|
start: method(object({
|
|
60898
|
-
deviceId: DeviceIdSchema,
|
|
61099
|
+
deviceId: DeviceIdSchema$1,
|
|
60899
61100
|
program: string().min(1).optional(),
|
|
60900
61101
|
options: record(string(), DishwasherValueSchema).optional()
|
|
60901
61102
|
}), _void(), {
|
|
60902
61103
|
kind: "mutation",
|
|
60903
61104
|
auth: "protected"
|
|
60904
61105
|
}),
|
|
60905
|
-
pause: method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
61106
|
+
pause: method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
60906
61107
|
kind: "mutation",
|
|
60907
61108
|
auth: "protected"
|
|
60908
61109
|
}),
|
|
60909
|
-
resume: method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
61110
|
+
resume: method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
60910
61111
|
kind: "mutation",
|
|
60911
61112
|
auth: "protected"
|
|
60912
61113
|
}),
|
|
60913
|
-
stop: method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
61114
|
+
stop: method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
60914
61115
|
kind: "mutation",
|
|
60915
61116
|
auth: "protected"
|
|
60916
61117
|
}),
|
|
60917
61118
|
/** Select (not start) a program; refused when the appliance does not offer it. */
|
|
60918
61119
|
setProgram: method(object({
|
|
60919
|
-
deviceId: DeviceIdSchema,
|
|
61120
|
+
deviceId: DeviceIdSchema$1,
|
|
60920
61121
|
program: string().min(1)
|
|
60921
61122
|
}), _void(), {
|
|
60922
61123
|
kind: "mutation",
|
|
60923
61124
|
auth: "protected"
|
|
60924
61125
|
}),
|
|
60925
61126
|
setPower: method(object({
|
|
60926
|
-
deviceId: DeviceIdSchema,
|
|
61127
|
+
deviceId: DeviceIdSchema$1,
|
|
60927
61128
|
on: boolean()
|
|
60928
61129
|
}), _void(), {
|
|
60929
61130
|
kind: "mutation",
|
|
60930
61131
|
auth: "protected"
|
|
60931
61132
|
}),
|
|
60932
61133
|
setHalfLoad: method(object({
|
|
60933
|
-
deviceId: DeviceIdSchema,
|
|
61134
|
+
deviceId: DeviceIdSchema$1,
|
|
60934
61135
|
on: boolean()
|
|
60935
61136
|
}), _void(), {
|
|
60936
61137
|
kind: "mutation",
|
|
60937
61138
|
auth: "protected"
|
|
60938
61139
|
}),
|
|
60939
61140
|
setHygienePlus: method(object({
|
|
60940
|
-
deviceId: DeviceIdSchema,
|
|
61141
|
+
deviceId: DeviceIdSchema$1,
|
|
60941
61142
|
on: boolean()
|
|
60942
61143
|
}), _void(), {
|
|
60943
61144
|
kind: "mutation",
|
|
60944
61145
|
auth: "protected"
|
|
60945
61146
|
}),
|
|
60946
61147
|
setExtraDry: method(object({
|
|
60947
|
-
deviceId: DeviceIdSchema,
|
|
61148
|
+
deviceId: DeviceIdSchema$1,
|
|
60948
61149
|
on: boolean()
|
|
60949
61150
|
}), _void(), {
|
|
60950
61151
|
kind: "mutation",
|
|
60951
61152
|
auth: "protected"
|
|
60952
61153
|
}),
|
|
60953
61154
|
setIntensiveZone: method(object({
|
|
60954
|
-
deviceId: DeviceIdSchema,
|
|
61155
|
+
deviceId: DeviceIdSchema$1,
|
|
60955
61156
|
on: boolean()
|
|
60956
61157
|
}), _void(), {
|
|
60957
61158
|
kind: "mutation",
|
|
60958
61159
|
auth: "protected"
|
|
60959
61160
|
}),
|
|
60960
61161
|
setVarioSpeedPlus: method(object({
|
|
60961
|
-
deviceId: DeviceIdSchema,
|
|
61162
|
+
deviceId: DeviceIdSchema$1,
|
|
60962
61163
|
on: boolean()
|
|
60963
61164
|
}), _void(), {
|
|
60964
61165
|
kind: "mutation",
|
|
60965
61166
|
auth: "protected"
|
|
60966
61167
|
}),
|
|
60967
61168
|
setChildLock: method(object({
|
|
60968
|
-
deviceId: DeviceIdSchema,
|
|
61169
|
+
deviceId: DeviceIdSchema$1,
|
|
60969
61170
|
on: boolean()
|
|
60970
61171
|
}), _void(), {
|
|
60971
61172
|
kind: "mutation",
|
|
@@ -60977,7 +61178,7 @@ var dishwasherCapability = {
|
|
|
60977
61178
|
* its declared range / members.
|
|
60978
61179
|
*/
|
|
60979
61180
|
setOption: method(object({
|
|
60980
|
-
deviceId: DeviceIdSchema,
|
|
61181
|
+
deviceId: DeviceIdSchema$1,
|
|
60981
61182
|
key: string().min(1),
|
|
60982
61183
|
value: DishwasherValueSchema
|
|
60983
61184
|
}), _void(), {
|
|
@@ -60985,7 +61186,7 @@ var dishwasherCapability = {
|
|
|
60985
61186
|
auth: "protected"
|
|
60986
61187
|
}),
|
|
60987
61188
|
/** What this appliance can do, from its own description — the UI is derived from it. */
|
|
60988
|
-
getOptions: method(object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" })
|
|
61189
|
+
getOptions: method(object({ deviceId: DeviceIdSchema$1 }), DishwasherOptionsSchema, { auth: "protected" })
|
|
60989
61190
|
},
|
|
60990
61191
|
status: {
|
|
60991
61192
|
schema: DishwasherStatusSchema,
|
|
@@ -64676,7 +64877,8 @@ var TopologyNodeSchema = object({
|
|
|
64676
64877
|
isHub: boolean(),
|
|
64677
64878
|
isOnline: boolean(),
|
|
64678
64879
|
cpuPercent: number(),
|
|
64679
|
-
|
|
64880
|
+
/** `null` when the node's memory read failed or it has not reported one (D393). */
|
|
64881
|
+
memoryPercent: number().nullable(),
|
|
64680
64882
|
uptime: number(),
|
|
64681
64883
|
lastSeen: string(),
|
|
64682
64884
|
localIps: array(string()).readonly(),
|
|
@@ -65294,9 +65496,10 @@ var PetFeederStatusSchema = object({
|
|
|
65294
65496
|
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
65295
65497
|
* the device has not reported yet. */
|
|
65296
65498
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
65297
|
-
/** Food in the BOWL
|
|
65298
|
-
* feeder
|
|
65299
|
-
|
|
65499
|
+
/** Food in the BOWL, a mass (stored in kilograms, D944). Reported only by a
|
|
65500
|
+
* feeder with a bowl scale; null when the model has none or its reading is
|
|
65501
|
+
* of an unproven unit. */
|
|
65502
|
+
bowlFood: number().nullable().meta(readingMeta("mass")),
|
|
65300
65503
|
/** True while the pet is eating (`state.eating`). */
|
|
65301
65504
|
eating: boolean(),
|
|
65302
65505
|
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
@@ -65480,6 +65683,137 @@ var petFeederCapability = {
|
|
|
65480
65683
|
}
|
|
65481
65684
|
} }
|
|
65482
65685
|
};
|
|
65686
|
+
/**
|
|
65687
|
+
* Pet water-fountain cap. Models the telemetry and the control surface of a
|
|
65688
|
+
* circulating drinking fountain: how much water is in the tank, how much the
|
|
65689
|
+
* pet drank, whether the pump runs and on what duty cycle.
|
|
65690
|
+
*
|
|
65691
|
+
* It has no native vendor provider of its own: a fountain reaches CamStack as
|
|
65692
|
+
* a container of loose entities (a pump switch, two number boxes, a "filter
|
|
65693
|
+
* changed" button), and an operator BUILDS the device from them — a composed
|
|
65694
|
+
* device of this type (D659, D967). Every method is therefore a forward to ONE
|
|
65695
|
+
* method of ONE source entity, and every reading is a field derived from a
|
|
65696
|
+
* source entity; the provider-shaped interface below exists so the composed
|
|
65697
|
+
* device and any future native provider answer the same contract.
|
|
65698
|
+
*
|
|
65699
|
+
* Quantities are stored in the base unit of their kind (D944): volumes in
|
|
65700
|
+
* LITRES, durations in SECONDS, the tank level in percent. A source that
|
|
65701
|
+
* reports millilitres or minutes is converted in the composition's expression,
|
|
65702
|
+
* never here. Every reading may be `null`: a source that is offline or does
|
|
65703
|
+
* not report is unknown, never zero.
|
|
65704
|
+
*
|
|
65705
|
+
* The filter is not a field of this cap: it is an item of the `consumables`
|
|
65706
|
+
* cap on the same device (kind `wear`), reset by the same forward mechanism.
|
|
65707
|
+
*/
|
|
65708
|
+
var PetFountainStatusSchema = object({
|
|
65709
|
+
/** Water in the tank, 0..100 (%). Null while the source has not reported. */
|
|
65710
|
+
waterLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
65711
|
+
/** Water in the tank, in litres. Null while the source has not reported. */
|
|
65712
|
+
waterVolume: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65713
|
+
/** True when the tank is empty. Null while unknown. */
|
|
65714
|
+
empty: boolean().nullable(),
|
|
65715
|
+
/** True while the pump is running. Null while unknown. */
|
|
65716
|
+
pumpRunning: boolean().nullable(),
|
|
65717
|
+
/** True when the pump works on an on/off duty cycle rather than continuously. */
|
|
65718
|
+
pumpIntervalEnabled: boolean().nullable(),
|
|
65719
|
+
/** How long the pump runs per cycle, in seconds. */
|
|
65720
|
+
pumpOnTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
65721
|
+
/** How long the pump rests per cycle, in seconds. */
|
|
65722
|
+
pumpOffTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
65723
|
+
/** Water drunk in the last drink, in litres. */
|
|
65724
|
+
lastDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65725
|
+
/** Water drunk in the current hour, in litres. */
|
|
65726
|
+
currentHourDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65727
|
+
/** Water drunk over the last 24 hours (rolling), in litres. */
|
|
65728
|
+
last24hDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65729
|
+
/** Water drunk since local midnight, in litres. */
|
|
65730
|
+
todayDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65731
|
+
/** Decoded fault message. Null means healthy; a non-null string is an active fault. */
|
|
65732
|
+
error: string().nullable(),
|
|
65733
|
+
/** Ms epoch when the slice was last refreshed. */
|
|
65734
|
+
lastFetchedAt: number()
|
|
65735
|
+
});
|
|
65736
|
+
var DeviceIdSchema = number().int().nonnegative();
|
|
65737
|
+
var MinutesSchema = number().int().min(0);
|
|
65738
|
+
var petFountainCapability = {
|
|
65739
|
+
name: "pet-fountain",
|
|
65740
|
+
scope: "device",
|
|
65741
|
+
deviceNative: true,
|
|
65742
|
+
mode: "singleton",
|
|
65743
|
+
deviceTypes: [DeviceType.PetFountain],
|
|
65744
|
+
methods: {
|
|
65745
|
+
/** Start or stop the pump. */
|
|
65746
|
+
setPump: method(object({
|
|
65747
|
+
deviceId: DeviceIdSchema,
|
|
65748
|
+
on: boolean()
|
|
65749
|
+
}), _void(), {
|
|
65750
|
+
kind: "mutation",
|
|
65751
|
+
auth: "protected"
|
|
65752
|
+
}),
|
|
65753
|
+
/** Switch the on/off duty cycle on or off (off = the pump runs continuously). */
|
|
65754
|
+
setPumpInterval: method(object({
|
|
65755
|
+
deviceId: DeviceIdSchema,
|
|
65756
|
+
on: boolean()
|
|
65757
|
+
}), _void(), {
|
|
65758
|
+
kind: "mutation",
|
|
65759
|
+
auth: "protected"
|
|
65760
|
+
}),
|
|
65761
|
+
/** Set how many minutes the pump runs per cycle. */
|
|
65762
|
+
setPumpOnMinutes: method(object({
|
|
65763
|
+
deviceId: DeviceIdSchema,
|
|
65764
|
+
minutes: MinutesSchema
|
|
65765
|
+
}), _void(), {
|
|
65766
|
+
kind: "mutation",
|
|
65767
|
+
auth: "protected"
|
|
65768
|
+
}),
|
|
65769
|
+
/** Set how many minutes the pump rests per cycle. */
|
|
65770
|
+
setPumpOffMinutes: method(object({
|
|
65771
|
+
deviceId: DeviceIdSchema,
|
|
65772
|
+
minutes: MinutesSchema
|
|
65773
|
+
}), _void(), {
|
|
65774
|
+
kind: "mutation",
|
|
65775
|
+
auth: "protected"
|
|
65776
|
+
}),
|
|
65777
|
+
/** Clear the rolling daily consumption counters on the device. */
|
|
65778
|
+
resetDailyConsumption: method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
65779
|
+
kind: "mutation",
|
|
65780
|
+
auth: "protected"
|
|
65781
|
+
})
|
|
65782
|
+
},
|
|
65783
|
+
status: {
|
|
65784
|
+
schema: PetFountainStatusSchema,
|
|
65785
|
+
kind: "push"
|
|
65786
|
+
},
|
|
65787
|
+
/** Runtime-state slice — mirrored by the kernel; the hero card reads it whole. */
|
|
65788
|
+
runtimeState: PetFountainStatusSchema,
|
|
65789
|
+
/**
|
|
65790
|
+
* Runtime-state durability: **session** — live appliance state re-published on connect.
|
|
65791
|
+
*
|
|
65792
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
65793
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
65794
|
+
*/
|
|
65795
|
+
durability: "session",
|
|
65796
|
+
/** History (D850): recorded series. */
|
|
65797
|
+
history: { fields: {
|
|
65798
|
+
waterLevel: {
|
|
65799
|
+
kind: "gauge",
|
|
65800
|
+
unit: "%",
|
|
65801
|
+
deadband: 1
|
|
65802
|
+
},
|
|
65803
|
+
waterVolume: {
|
|
65804
|
+
kind: "gauge",
|
|
65805
|
+
unit: "L"
|
|
65806
|
+
},
|
|
65807
|
+
todayDrink: {
|
|
65808
|
+
kind: "gauge",
|
|
65809
|
+
unit: "L"
|
|
65810
|
+
},
|
|
65811
|
+
last24hDrink: {
|
|
65812
|
+
kind: "gauge",
|
|
65813
|
+
unit: "L"
|
|
65814
|
+
}
|
|
65815
|
+
} }
|
|
65816
|
+
};
|
|
65483
65817
|
var VehicleSchema = object({
|
|
65484
65818
|
id: string(),
|
|
65485
65819
|
name: string(),
|
|
@@ -71402,7 +71736,7 @@ discriminatedUnion("kind", [object({
|
|
|
71402
71736
|
})]);
|
|
71403
71737
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
71404
71738
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
71405
|
-
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;
|
|
71739
|
+
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;
|
|
71406
71740
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
71407
71741
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
71408
71742
|
new Set(CAP_NAMES);
|
|
@@ -71753,6 +72087,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
71753
72087
|
notifier: notifierCapability,
|
|
71754
72088
|
numericSensor: numericSensorCapability,
|
|
71755
72089
|
petFeeder: petFeederCapability,
|
|
72090
|
+
petFountain: petFountainCapability,
|
|
71756
72091
|
powerMeter: powerMeterCapability,
|
|
71757
72092
|
presence: presenceCapability,
|
|
71758
72093
|
pressureSensor: pressureSensorCapability,
|
|
@@ -73003,7 +73338,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
73003
73338
|
return toDeviceSummary(device, this.addonId);
|
|
73004
73339
|
}
|
|
73005
73340
|
};
|
|
73006
|
-
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;
|
|
73341
|
+
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;
|
|
73007
73342
|
new Set(Object.values(DeviceType));
|
|
73008
73343
|
Object.freeze({
|
|
73009
73344
|
"accessories.getStatus": {
|
|
@@ -74938,6 +75273,13 @@ Object.freeze({
|
|
|
74938
75273
|
access: "create",
|
|
74939
75274
|
role: "admin"
|
|
74940
75275
|
},
|
|
75276
|
+
"deviceExport.exposeEntity": {
|
|
75277
|
+
capName: "device-export",
|
|
75278
|
+
capScope: "system",
|
|
75279
|
+
addonId: null,
|
|
75280
|
+
access: "create",
|
|
75281
|
+
role: "admin"
|
|
75282
|
+
},
|
|
74941
75283
|
"deviceExport.getDeviceLiveContribution": {
|
|
74942
75284
|
capName: "device-export",
|
|
74943
75285
|
capScope: "system",
|
|
@@ -74966,6 +75308,13 @@ Object.freeze({
|
|
|
74966
75308
|
access: "view",
|
|
74967
75309
|
role: "admin"
|
|
74968
75310
|
},
|
|
75311
|
+
"deviceExport.listExposedEntities": {
|
|
75312
|
+
capName: "device-export",
|
|
75313
|
+
capScope: "system",
|
|
75314
|
+
addonId: null,
|
|
75315
|
+
access: "view",
|
|
75316
|
+
role: "admin"
|
|
75317
|
+
},
|
|
74969
75318
|
"deviceExport.listSupportedDeviceKinds": {
|
|
74970
75319
|
capName: "device-export",
|
|
74971
75320
|
capScope: "system",
|
|
@@ -74973,6 +75322,13 @@ Object.freeze({
|
|
|
74973
75322
|
access: "view",
|
|
74974
75323
|
role: "admin"
|
|
74975
75324
|
},
|
|
75325
|
+
"deviceExport.listSupportedEntityKinds": {
|
|
75326
|
+
capName: "device-export",
|
|
75327
|
+
capScope: "system",
|
|
75328
|
+
addonId: null,
|
|
75329
|
+
access: "view",
|
|
75330
|
+
role: "admin"
|
|
75331
|
+
},
|
|
74976
75332
|
"deviceExport.unexposeDevice": {
|
|
74977
75333
|
capName: "device-export",
|
|
74978
75334
|
capScope: "system",
|
|
@@ -74980,6 +75336,13 @@ Object.freeze({
|
|
|
74980
75336
|
access: "create",
|
|
74981
75337
|
role: "admin"
|
|
74982
75338
|
},
|
|
75339
|
+
"deviceExport.unexposeEntity": {
|
|
75340
|
+
capName: "device-export",
|
|
75341
|
+
capScope: "system",
|
|
75342
|
+
addonId: null,
|
|
75343
|
+
access: "create",
|
|
75344
|
+
role: "admin"
|
|
75345
|
+
},
|
|
74983
75346
|
"deviceExtension.listCandidates": {
|
|
74984
75347
|
capName: "device-extension",
|
|
74985
75348
|
capScope: "device",
|
|
@@ -78445,6 +78808,48 @@ Object.freeze({
|
|
|
78445
78808
|
access: "create",
|
|
78446
78809
|
role: "viewer"
|
|
78447
78810
|
},
|
|
78811
|
+
"petFountain.getStatus": {
|
|
78812
|
+
capName: "pet-fountain",
|
|
78813
|
+
capScope: "device",
|
|
78814
|
+
addonId: null,
|
|
78815
|
+
access: "view",
|
|
78816
|
+
role: "viewer"
|
|
78817
|
+
},
|
|
78818
|
+
"petFountain.resetDailyConsumption": {
|
|
78819
|
+
capName: "pet-fountain",
|
|
78820
|
+
capScope: "device",
|
|
78821
|
+
addonId: null,
|
|
78822
|
+
access: "delete",
|
|
78823
|
+
role: "viewer"
|
|
78824
|
+
},
|
|
78825
|
+
"petFountain.setPump": {
|
|
78826
|
+
capName: "pet-fountain",
|
|
78827
|
+
capScope: "device",
|
|
78828
|
+
addonId: null,
|
|
78829
|
+
access: "create",
|
|
78830
|
+
role: "viewer"
|
|
78831
|
+
},
|
|
78832
|
+
"petFountain.setPumpInterval": {
|
|
78833
|
+
capName: "pet-fountain",
|
|
78834
|
+
capScope: "device",
|
|
78835
|
+
addonId: null,
|
|
78836
|
+
access: "create",
|
|
78837
|
+
role: "viewer"
|
|
78838
|
+
},
|
|
78839
|
+
"petFountain.setPumpOffMinutes": {
|
|
78840
|
+
capName: "pet-fountain",
|
|
78841
|
+
capScope: "device",
|
|
78842
|
+
addonId: null,
|
|
78843
|
+
access: "create",
|
|
78844
|
+
role: "viewer"
|
|
78845
|
+
},
|
|
78846
|
+
"petFountain.setPumpOnMinutes": {
|
|
78847
|
+
capName: "pet-fountain",
|
|
78848
|
+
capScope: "device",
|
|
78849
|
+
addonId: null,
|
|
78850
|
+
access: "create",
|
|
78851
|
+
role: "viewer"
|
|
78852
|
+
},
|
|
78448
78853
|
"pipelineAnalytics.addRetrainFrameFromRecording": {
|
|
78449
78854
|
capName: "pipeline-analytics",
|
|
78450
78855
|
capScope: "device",
|
|
@@ -80846,6 +81251,13 @@ Object.freeze({
|
|
|
80846
81251
|
access: "create",
|
|
80847
81252
|
role: "admin"
|
|
80848
81253
|
},
|
|
81254
|
+
"serverManagement.getLaunchOverrides": {
|
|
81255
|
+
capName: "server-management",
|
|
81256
|
+
capScope: "system",
|
|
81257
|
+
addonId: null,
|
|
81258
|
+
access: "view",
|
|
81259
|
+
role: "admin"
|
|
81260
|
+
},
|
|
80849
81261
|
"serverManagement.getServerPackageStatus": {
|
|
80850
81262
|
capName: "server-management",
|
|
80851
81263
|
capScope: "system",
|
|
@@ -80853,6 +81265,13 @@ Object.freeze({
|
|
|
80853
81265
|
access: "view",
|
|
80854
81266
|
role: "admin"
|
|
80855
81267
|
},
|
|
81268
|
+
"serverManagement.installDesktopUpdate": {
|
|
81269
|
+
capName: "server-management",
|
|
81270
|
+
capScope: "system",
|
|
81271
|
+
addonId: null,
|
|
81272
|
+
access: "create",
|
|
81273
|
+
role: "admin"
|
|
81274
|
+
},
|
|
80856
81275
|
"serverManagement.restartServer": {
|
|
80857
81276
|
capName: "server-management",
|
|
80858
81277
|
capScope: "system",
|
|
@@ -80867,6 +81286,13 @@ Object.freeze({
|
|
|
80867
81286
|
access: "create",
|
|
80868
81287
|
role: "admin"
|
|
80869
81288
|
},
|
|
81289
|
+
"serverManagement.setLaunchOverrides": {
|
|
81290
|
+
capName: "server-management",
|
|
81291
|
+
capScope: "system",
|
|
81292
|
+
addonId: null,
|
|
81293
|
+
access: "create",
|
|
81294
|
+
role: "admin"
|
|
81295
|
+
},
|
|
80870
81296
|
"settingsStore.aggregate": {
|
|
80871
81297
|
capName: "settings-store",
|
|
80872
81298
|
capScope: "system",
|
|
@@ -84927,6 +85353,100 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
84927
85353
|
}
|
|
84928
85354
|
]
|
|
84929
85355
|
},
|
|
85356
|
+
"pet-fountain": {
|
|
85357
|
+
scope: "device",
|
|
85358
|
+
noun: t("Pet fountain", "Fontana per animali"),
|
|
85359
|
+
actions: [
|
|
85360
|
+
{
|
|
85361
|
+
id: "petFountain.pumpOn",
|
|
85362
|
+
method: "setPump",
|
|
85363
|
+
label: t("Pump on", "Pompa accesa"),
|
|
85364
|
+
phrase: t("Start the pump of {device}", "Avvia la pompa di {device}"),
|
|
85365
|
+
icon: "play",
|
|
85366
|
+
args: { on: true }
|
|
85367
|
+
},
|
|
85368
|
+
{
|
|
85369
|
+
id: "petFountain.pumpOff",
|
|
85370
|
+
method: "setPump",
|
|
85371
|
+
label: t("Pump off", "Pompa spenta"),
|
|
85372
|
+
phrase: t("Stop the pump of {device}", "Ferma la pompa di {device}"),
|
|
85373
|
+
icon: "square",
|
|
85374
|
+
args: { on: false }
|
|
85375
|
+
},
|
|
85376
|
+
{
|
|
85377
|
+
id: "petFountain.intervalOn",
|
|
85378
|
+
method: "setPumpInterval",
|
|
85379
|
+
label: t("Pump cycle on", "Ciclo pompa attivo"),
|
|
85380
|
+
phrase: t("Run the pump of {device} on its on/off cycle", "Fai lavorare la pompa di {device} a ciclo acceso/spento"),
|
|
85381
|
+
icon: "timer",
|
|
85382
|
+
args: { on: true }
|
|
85383
|
+
},
|
|
85384
|
+
{
|
|
85385
|
+
id: "petFountain.intervalOff",
|
|
85386
|
+
method: "setPumpInterval",
|
|
85387
|
+
label: t("Pump cycle off", "Ciclo pompa spento"),
|
|
85388
|
+
phrase: t("Run the pump of {device} continuously", "Fai lavorare la pompa di {device} in continuo"),
|
|
85389
|
+
icon: "timer-off",
|
|
85390
|
+
args: { on: false }
|
|
85391
|
+
},
|
|
85392
|
+
{
|
|
85393
|
+
id: "petFountain.pumpOnMinutes",
|
|
85394
|
+
method: "setPumpOnMinutes",
|
|
85395
|
+
label: t("Set pump run time", "Imposta il tempo di lavoro della pompa"),
|
|
85396
|
+
phrase: t("Set the pump of {device} to run {minutes} minutes per cycle", "Imposta la pompa di {device} a lavorare {minutes} minuti per ciclo"),
|
|
85397
|
+
icon: "timer",
|
|
85398
|
+
openArgs: [{
|
|
85399
|
+
key: "minutes",
|
|
85400
|
+
label: t("Minutes", "Minuti"),
|
|
85401
|
+
offer: {
|
|
85402
|
+
kind: "number",
|
|
85403
|
+
min: 0,
|
|
85404
|
+
step: 1,
|
|
85405
|
+
unit: "min"
|
|
85406
|
+
}
|
|
85407
|
+
}]
|
|
85408
|
+
},
|
|
85409
|
+
{
|
|
85410
|
+
id: "petFountain.pumpOffMinutes",
|
|
85411
|
+
method: "setPumpOffMinutes",
|
|
85412
|
+
label: t("Set pump rest time", "Imposta il tempo di riposo della pompa"),
|
|
85413
|
+
phrase: t("Set the pump of {device} to rest {minutes} minutes per cycle", "Imposta la pompa di {device} a riposare {minutes} minuti per ciclo"),
|
|
85414
|
+
icon: "timer-off",
|
|
85415
|
+
openArgs: [{
|
|
85416
|
+
key: "minutes",
|
|
85417
|
+
label: t("Minutes", "Minuti"),
|
|
85418
|
+
offer: {
|
|
85419
|
+
kind: "number",
|
|
85420
|
+
min: 0,
|
|
85421
|
+
step: 1,
|
|
85422
|
+
unit: "min"
|
|
85423
|
+
}
|
|
85424
|
+
}]
|
|
85425
|
+
},
|
|
85426
|
+
{
|
|
85427
|
+
id: "petFountain.resetDailyConsumption",
|
|
85428
|
+
method: "resetDailyConsumption",
|
|
85429
|
+
label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
|
|
85430
|
+
phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
|
|
85431
|
+
icon: "rotate-ccw"
|
|
85432
|
+
}
|
|
85433
|
+
],
|
|
85434
|
+
state: [{
|
|
85435
|
+
kind: "boolean",
|
|
85436
|
+
field: "empty",
|
|
85437
|
+
label: t("Tank empty", "Serbatoio vuoto"),
|
|
85438
|
+
whenTrue: t("When the tank of {device} is empty", "Quando il serbatoio di {device} è vuoto"),
|
|
85439
|
+
whenFalse: t("When the tank of {device} is refilled", "Quando il serbatoio di {device} viene riempito"),
|
|
85440
|
+
icon: "droplet"
|
|
85441
|
+
}, {
|
|
85442
|
+
kind: "numeric",
|
|
85443
|
+
field: "waterLevel",
|
|
85444
|
+
quantity: t("the water level", "il livello dell’acqua"),
|
|
85445
|
+
label: t("Water level", "Livello dell’acqua"),
|
|
85446
|
+
unit: "%",
|
|
85447
|
+
icon: "droplets"
|
|
85448
|
+
}]
|
|
85449
|
+
},
|
|
84930
85450
|
"media-player": {
|
|
84931
85451
|
scope: "device",
|
|
84932
85452
|
noun: t("Media player", "Lettore multimediale"),
|
|
@@ -86864,7 +87384,7 @@ function projectAction(cap, action) {
|
|
|
86864
87384
|
};
|
|
86865
87385
|
}
|
|
86866
87386
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
86867
|
-
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;
|
|
87387
|
+
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;
|
|
86868
87388
|
DeviceFeature.BatteryOperated;
|
|
86869
87389
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
86870
87390
|
var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
|
|
@@ -88415,6 +88935,36 @@ Object.freeze({
|
|
|
88415
88935
|
form: "single",
|
|
88416
88936
|
optional: false
|
|
88417
88937
|
}],
|
|
88938
|
+
"petFountain.getStatus": [{
|
|
88939
|
+
name: "deviceId",
|
|
88940
|
+
form: "single",
|
|
88941
|
+
optional: false
|
|
88942
|
+
}],
|
|
88943
|
+
"petFountain.resetDailyConsumption": [{
|
|
88944
|
+
name: "deviceId",
|
|
88945
|
+
form: "single",
|
|
88946
|
+
optional: false
|
|
88947
|
+
}],
|
|
88948
|
+
"petFountain.setPump": [{
|
|
88949
|
+
name: "deviceId",
|
|
88950
|
+
form: "single",
|
|
88951
|
+
optional: false
|
|
88952
|
+
}],
|
|
88953
|
+
"petFountain.setPumpInterval": [{
|
|
88954
|
+
name: "deviceId",
|
|
88955
|
+
form: "single",
|
|
88956
|
+
optional: false
|
|
88957
|
+
}],
|
|
88958
|
+
"petFountain.setPumpOffMinutes": [{
|
|
88959
|
+
name: "deviceId",
|
|
88960
|
+
form: "single",
|
|
88961
|
+
optional: false
|
|
88962
|
+
}],
|
|
88963
|
+
"petFountain.setPumpOnMinutes": [{
|
|
88964
|
+
name: "deviceId",
|
|
88965
|
+
form: "single",
|
|
88966
|
+
optional: false
|
|
88967
|
+
}],
|
|
88418
88968
|
"pipelineAnalytics.addRetrainFrameFromRecording": [{
|
|
88419
88969
|
name: "deviceId",
|
|
88420
88970
|
form: "single",
|
|
@@ -133568,6 +134118,38 @@ function alexaRegionOptions(amazonDomain) {
|
|
|
133568
134118
|
};
|
|
133569
134119
|
}
|
|
133570
134120
|
//#endregion
|
|
134121
|
+
//#region src/alexa-url-params.ts
|
|
134122
|
+
var BASE = "http://placeholder.invalid";
|
|
134123
|
+
function describeUrlParams(url) {
|
|
134124
|
+
let parsed;
|
|
134125
|
+
try {
|
|
134126
|
+
parsed = new URL(url, BASE);
|
|
134127
|
+
} catch {
|
|
134128
|
+
return {
|
|
134129
|
+
path: "",
|
|
134130
|
+
names: [],
|
|
134131
|
+
oa2: false,
|
|
134132
|
+
returnTo: false,
|
|
134133
|
+
assocHandle: false,
|
|
134134
|
+
mode: false
|
|
134135
|
+
};
|
|
134136
|
+
}
|
|
134137
|
+
const names = [...new Set(parsed.searchParams.keys())].sort();
|
|
134138
|
+
return {
|
|
134139
|
+
path: parsed.pathname,
|
|
134140
|
+
names,
|
|
134141
|
+
oa2: names.some((n) => n.startsWith("openid.oa2.")),
|
|
134142
|
+
returnTo: names.includes("openid.return_to"),
|
|
134143
|
+
assocHandle: names.includes("openid.assoc_handle"),
|
|
134144
|
+
mode: names.includes("openid.mode")
|
|
134145
|
+
};
|
|
134146
|
+
}
|
|
134147
|
+
/** One-line, value-free rendering of {@link describeUrlParams}. */
|
|
134148
|
+
function formatUrlParams(url) {
|
|
134149
|
+
const d = describeUrlParams(url);
|
|
134150
|
+
return `names=[${d.names.join(",")}] oa2=${d.oa2} return_to=${d.returnTo} assoc_handle=${d.assocHandle} mode=${d.mode}`;
|
|
134151
|
+
}
|
|
134152
|
+
//#endregion
|
|
133571
134153
|
//#region src/alexa-proxy-trace.ts
|
|
133572
134154
|
/**
|
|
133573
134155
|
* Sanitised one-line trace of the alexa-cookie2 proxy's own log lines.
|
|
@@ -133578,7 +134160,9 @@ function alexaRegionOptions(amazonDomain) {
|
|
|
133578
134160
|
* redirect target — always WITHOUT a query string or fragment. Any line that is
|
|
133579
134161
|
* not one of the known step lines yields `null` and is never traced.
|
|
133580
134162
|
*/
|
|
133581
|
-
var ROUTER_RE = /^Router: (\S+) \/ ([A-Z]+)
|
|
134163
|
+
var ROUTER_RE = /^Router: (\S+) \/ ([A-Z]+) \/ (.*)$/;
|
|
134164
|
+
var INITIAL_REQUEST_RE = /^Alexa-Cookie: Initial Page Request: (\S+)$/;
|
|
134165
|
+
var USER_AGENT_RE = /"user-agent":"([^"]*)"/i;
|
|
133582
134166
|
var PROXY_REQUEST_RE = /^Alexa-Cookie: Proxy-Request: ([A-Z]+) (\S+)$/;
|
|
133583
134167
|
var PROXY_RESPONSE_RE = /^Alexa-Cookie: Proxy Response from Host: (\S+)$/;
|
|
133584
134168
|
var REDIRECT_ORIGINAL_RE = /^Redirect: Original Location ----> (\S+)$/;
|
|
@@ -133588,17 +134172,32 @@ function stripQuery(target) {
|
|
|
133588
134172
|
const cut = target.search(/[?#]/);
|
|
133589
134173
|
return cut === -1 ? target : target.slice(0, cut);
|
|
133590
134174
|
}
|
|
134175
|
+
/** The target without its query, plus the value-free parameter summary. */
|
|
134176
|
+
function withParams(target) {
|
|
134177
|
+
return `${stripQuery(target)} ${formatUrlParams(target)}`;
|
|
134178
|
+
}
|
|
134179
|
+
/** Browser family of the header block's user-agent; the string itself is never kept. */
|
|
134180
|
+
function userAgentFamily(headers) {
|
|
134181
|
+
const ua = USER_AGENT_RE.exec(headers)?.[1];
|
|
134182
|
+
if (ua === void 0) return "none";
|
|
134183
|
+
if (/CriOS|Chrome|Chromium|Edg\//.test(ua)) return "chrome";
|
|
134184
|
+
if (/Firefox|FxiOS/.test(ua)) return "firefox";
|
|
134185
|
+
if (/Safari/.test(ua)) return "safari";
|
|
134186
|
+
return "other";
|
|
134187
|
+
}
|
|
133591
134188
|
function sanitizeProxyTraceLine(line) {
|
|
133592
134189
|
const router = ROUTER_RE.exec(line);
|
|
133593
|
-
if (router !== null) return `router ${router[2]} ${
|
|
134190
|
+
if (router !== null) return `router ${router[2]} ${withParams(router[1] ?? "")} ua=${userAgentFamily(router[3] ?? "")}`;
|
|
134191
|
+
const initial = INITIAL_REQUEST_RE.exec(line);
|
|
134192
|
+
if (initial !== null) return `initial-request ${withParams(initial[1] ?? "")}`;
|
|
133594
134193
|
const request = PROXY_REQUEST_RE.exec(line);
|
|
133595
|
-
if (request !== null) return `proxy-request ${request[1]} ${
|
|
134194
|
+
if (request !== null) return `proxy-request ${request[1]} ${withParams(request[2] ?? "")}`;
|
|
133596
134195
|
const response = PROXY_RESPONSE_RE.exec(line);
|
|
133597
134196
|
if (response !== null) return `proxy-response host=${response[1]}`;
|
|
133598
134197
|
const original = REDIRECT_ORIGINAL_RE.exec(line);
|
|
133599
|
-
if (original !== null) return `redirect-original ${
|
|
134198
|
+
if (original !== null) return `redirect-original ${withParams(original[1] ?? "")}`;
|
|
133600
134199
|
const final = REDIRECT_FINAL_RE.exec(line);
|
|
133601
|
-
if (final !== null) return `redirect-final ${
|
|
134200
|
+
if (final !== null) return `redirect-final ${withParams(final[1] ?? "")}`;
|
|
133602
134201
|
return null;
|
|
133603
134202
|
}
|
|
133604
134203
|
//#endregion
|
|
@@ -133714,7 +134313,7 @@ var AlexaProxyLoginSession = class {
|
|
|
133714
134313
|
const line = args.map(String).join(" ");
|
|
133715
134314
|
const trace = sanitizeProxyTraceLine(line);
|
|
133716
134315
|
if (trace !== null) {
|
|
133717
|
-
this.#logger.info(`alexa
|
|
134316
|
+
this.#logger.info(`[alexa-login-trace] ${trace}`, { meta: { sessionId: this.#opts.sessionId } });
|
|
133718
134317
|
return;
|
|
133719
134318
|
}
|
|
133720
134319
|
if (SECRET_LINE_RE.test(line)) return;
|
|
@@ -133892,6 +134491,13 @@ var AlexaProxyLoginManager = class {
|
|
|
133892
134491
|
this.#getLoginProxy = deps.getLoginProxy;
|
|
133893
134492
|
this.#captureWaitMs = deps.captureWaitMs ?? CAPTURE_WAIT_MS;
|
|
133894
134493
|
}
|
|
134494
|
+
/** One value-free line per upstream hop the hub login proxy sees (names only). */
|
|
134495
|
+
#traceHop(sessionId, view) {
|
|
134496
|
+
const url = describeUrlParams(view.url);
|
|
134497
|
+
const landing = url.path.endsWith("/ap/maplanding") ? " maplanding" : "";
|
|
134498
|
+
const location = view.location === null ? "none" : `${describeUrlParams(view.location).path} ${formatUrlParams(view.location)}`;
|
|
134499
|
+
this.#logger.info(`[alexa-login-trace] hub-hop${landing} status=${view.status} url=${url.path} ${formatUrlParams(view.url)} location=${location}`, { meta: { sessionId } });
|
|
134500
|
+
}
|
|
133895
134501
|
/**
|
|
133896
134502
|
* Start a fresh proxy-login. Cancels any in-flight session first (single
|
|
133897
134503
|
* fixed port). Returns the `loginUrl` for the onboarding UI to open.
|
|
@@ -133910,7 +134516,10 @@ var AlexaProxyLoginManager = class {
|
|
|
133910
134516
|
host: upstreamHost,
|
|
133911
134517
|
scheme: "http"
|
|
133912
134518
|
}],
|
|
133913
|
-
terminal:
|
|
134519
|
+
terminal: (view) => {
|
|
134520
|
+
this.#traceHop(sessionId, view);
|
|
134521
|
+
return cookieSuccessTerminal(view);
|
|
134522
|
+
},
|
|
133914
134523
|
onDone: async (end) => {
|
|
133915
134524
|
if (end === "no_authorization_code") {
|
|
133916
134525
|
this.#logger.warn("alexa proxy-login: Amazon ended the login without an authorization code", { meta: { sessionId } });
|