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