@camstack/addon-google-services 0.2.35 → 0.2.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/google-services.addon.js +973 -335
- package/dist/google-services.addon.mjs +973 -335
- package/i18n/en.json +4 -4
- package/i18n/it.json +4 -4
- package/package.json +2 -2
|
@@ -785,7 +785,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
785
785
|
return EventCategory;
|
|
786
786
|
}({});
|
|
787
787
|
//#endregion
|
|
788
|
-
//#region ../types/dist/device-type-
|
|
788
|
+
//#region ../types/dist/device-type-D1fH5kaC.mjs
|
|
789
789
|
/**
|
|
790
790
|
import { errMsg } from '@camstack/types'
|
|
791
791
|
* Extract a human-readable message from an unknown error value.
|
|
@@ -974,6 +974,9 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
|
974
974
|
DeviceFeature["CoverPositionable"] = "cover-positionable";
|
|
975
975
|
/** Cover exposes slat-tilt control. Gates the tilt slider UI. */
|
|
976
976
|
DeviceFeature["CoverTilt"] = "cover-tilt";
|
|
977
|
+
/** Cover has a real stop: `stop` halts the travel in place. Gates the stop
|
|
978
|
+
* control in exporters; a provider whose `stop` is a no-op never declares it. */
|
|
979
|
+
DeviceFeature["CoverStoppable"] = "cover-stoppable";
|
|
977
980
|
/** Valve exposes intermediate position control (0..100). Gates the
|
|
978
981
|
* position slider / drag surface UI. */
|
|
979
982
|
DeviceFeature["ValvePositionable"] = "valve-positionable";
|
|
@@ -1013,6 +1016,12 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
|
1013
1016
|
DeviceFeature["MediaPlayerPrevious"] = "media-player-previous";
|
|
1014
1017
|
/** Media player exposes stop distinct from pause. */
|
|
1015
1018
|
DeviceFeature["MediaPlayerStop"] = "media-player-stop";
|
|
1019
|
+
/** Media player exposes volume up / down in steps (an infrared TV has no level). */
|
|
1020
|
+
DeviceFeature["MediaPlayerVolumeStep"] = "media-player-volume-step";
|
|
1021
|
+
/** Media player exposes channel up / down in steps. */
|
|
1022
|
+
DeviceFeature["MediaPlayerChannelStep"] = "media-player-channel-step";
|
|
1023
|
+
/** Media player exposes power on / off. */
|
|
1024
|
+
DeviceFeature["MediaPlayerPower"] = "media-player-power";
|
|
1016
1025
|
/** Alarm panel requires a PIN code on arm/disarm. */
|
|
1017
1026
|
DeviceFeature["AlarmPinRequired"] = "alarm-pin-required";
|
|
1018
1027
|
/** Presence device carries GPS coordinates (lat/lng/accuracy) in
|
|
@@ -5972,7 +5981,7 @@ var ZodIssueCode = {
|
|
|
5972
5981
|
var ZodFirstPartyTypeKind;
|
|
5973
5982
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5974
5983
|
//#endregion
|
|
5975
|
-
//#region ../types/dist/sleep-
|
|
5984
|
+
//#region ../types/dist/sleep-BKClVk0K.mjs
|
|
5976
5985
|
/**
|
|
5977
5986
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5978
5987
|
* window to float samples (D455).
|
|
@@ -6051,6 +6060,22 @@ function buildAlawTable() {
|
|
|
6051
6060
|
}
|
|
6052
6061
|
buildUlawTable();
|
|
6053
6062
|
buildAlawTable();
|
|
6063
|
+
/**
|
|
6064
|
+
* An addon whose own migrations did not apply refuses to initialise (D987). The error is typed
|
|
6065
|
+
* so a runner hosting several addons can tell "this addon is blocked" from a crash and keep its
|
|
6066
|
+
* co-located siblings running. Recognised by NAME and shape, never `instanceof`: a forked addon
|
|
6067
|
+
* bundles its own copy of this module, so the class the runner sees is not the one that threw.
|
|
6068
|
+
*/
|
|
6069
|
+
var MIGRATIONS_BLOCKED_ERROR_NAME = "AddonMigrationsBlockedError";
|
|
6070
|
+
var AddonMigrationsBlockedError = class extends Error {
|
|
6071
|
+
/** The ledger ids that did not apply and block the owner. */
|
|
6072
|
+
migrationIds;
|
|
6073
|
+
constructor(migrationIds) {
|
|
6074
|
+
super(`migration(s) ${migrationIds.join(", ")} did not apply and block this addon — fix or retry them (migrations.retry), then restart it`);
|
|
6075
|
+
this.name = MIGRATIONS_BLOCKED_ERROR_NAME;
|
|
6076
|
+
this.migrationIds = migrationIds;
|
|
6077
|
+
}
|
|
6078
|
+
};
|
|
6054
6079
|
/** Is this field's stored value a credential? */
|
|
6055
6080
|
function isSecretConfigField(field) {
|
|
6056
6081
|
if (field.type === "password") return true;
|
|
@@ -6488,6 +6513,53 @@ function emitReadiness(bus, params) {
|
|
|
6488
6513
|
ts
|
|
6489
6514
|
}));
|
|
6490
6515
|
}
|
|
6516
|
+
/** Why a catalogue entry is not a runnable body of its declared kind. */
|
|
6517
|
+
var AddonMigrationDefinitionError = class extends Error {
|
|
6518
|
+
id;
|
|
6519
|
+
constructor(id, message) {
|
|
6520
|
+
super(`${id}: ${message}`);
|
|
6521
|
+
this.id = id;
|
|
6522
|
+
this.name = "AddonMigrationDefinitionError";
|
|
6523
|
+
}
|
|
6524
|
+
};
|
|
6525
|
+
/**
|
|
6526
|
+
* Bind a catalogue to the addon's ports. Throws {@link AddonMigrationDefinitionError} when
|
|
6527
|
+
* an entry's `bodyKind` does not match the body it carries, or is `sql` (a hub-only
|
|
6528
|
+
* body). A throwing body never escapes `execute`: it is reported as a failed result.
|
|
6529
|
+
*/
|
|
6530
|
+
function defineAddonMigrations(catalogue, ports) {
|
|
6531
|
+
return catalogue.map((entry) => {
|
|
6532
|
+
const { meta } = entry;
|
|
6533
|
+
if ("plan" in entry) {
|
|
6534
|
+
if (meta.bodyKind !== "plan") throw new AddonMigrationDefinitionError(meta.id, `carries a plan body but declares "${meta.bodyKind}"`);
|
|
6535
|
+
return {
|
|
6536
|
+
meta,
|
|
6537
|
+
execute: () => guarded(async () => ({
|
|
6538
|
+
kind: "plan",
|
|
6539
|
+
plan: await entry.plan(ports)
|
|
6540
|
+
}))
|
|
6541
|
+
};
|
|
6542
|
+
}
|
|
6543
|
+
if (meta.bodyKind !== "procedure") throw new AddonMigrationDefinitionError(meta.id, `carries a procedure body but declares "${meta.bodyKind}"`);
|
|
6544
|
+
return {
|
|
6545
|
+
meta,
|
|
6546
|
+
execute: () => guarded(async () => ({
|
|
6547
|
+
kind: "procedure",
|
|
6548
|
+
result: await entry.procedure(ports)
|
|
6549
|
+
}))
|
|
6550
|
+
};
|
|
6551
|
+
});
|
|
6552
|
+
}
|
|
6553
|
+
async function guarded(work) {
|
|
6554
|
+
try {
|
|
6555
|
+
return await work();
|
|
6556
|
+
} catch (err) {
|
|
6557
|
+
return {
|
|
6558
|
+
kind: "failed",
|
|
6559
|
+
error: errMsg(err)
|
|
6560
|
+
};
|
|
6561
|
+
}
|
|
6562
|
+
}
|
|
6491
6563
|
var NOT_RUN = {
|
|
6492
6564
|
declared: false,
|
|
6493
6565
|
snapshot: null,
|
|
@@ -7657,7 +7729,7 @@ var BaseAddon = class {
|
|
|
7657
7729
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7658
7730
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7659
7731
|
}, migrations, this.ctx.logger);
|
|
7660
|
-
if (report.blockOwner.length > 0) throw new
|
|
7732
|
+
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7661
7733
|
return report;
|
|
7662
7734
|
}
|
|
7663
7735
|
/**
|
|
@@ -9648,6 +9720,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9648
9720
|
"backup",
|
|
9649
9721
|
"battery",
|
|
9650
9722
|
"binary",
|
|
9723
|
+
"board-key-source",
|
|
9651
9724
|
"brightness",
|
|
9652
9725
|
"broker",
|
|
9653
9726
|
"button",
|
|
@@ -9769,6 +9842,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9769
9842
|
"privacy-mask",
|
|
9770
9843
|
"ptz",
|
|
9771
9844
|
"ptz-autotrack",
|
|
9845
|
+
"pump-cycle",
|
|
9772
9846
|
"reboot",
|
|
9773
9847
|
"record-entries",
|
|
9774
9848
|
"recording",
|
|
@@ -11061,18 +11135,6 @@ Object.freeze({
|
|
|
11061
11135
|
"path": "pumpRunning",
|
|
11062
11136
|
"valueType": "boolean"
|
|
11063
11137
|
},
|
|
11064
|
-
{
|
|
11065
|
-
"path": "pumpIntervalEnabled",
|
|
11066
|
-
"valueType": "boolean"
|
|
11067
|
-
},
|
|
11068
|
-
{
|
|
11069
|
-
"path": "pumpOnTime",
|
|
11070
|
-
"valueType": "number"
|
|
11071
|
-
},
|
|
11072
|
-
{
|
|
11073
|
-
"path": "pumpOffTime",
|
|
11074
|
-
"valueType": "number"
|
|
11075
|
-
},
|
|
11076
11138
|
{
|
|
11077
11139
|
"path": "lastDrink",
|
|
11078
11140
|
"valueType": "number"
|
|
@@ -11196,6 +11258,20 @@ Object.freeze({
|
|
|
11196
11258
|
"valueType": "number"
|
|
11197
11259
|
}
|
|
11198
11260
|
],
|
|
11261
|
+
"pump-cycle": [
|
|
11262
|
+
{
|
|
11263
|
+
"path": "enabled",
|
|
11264
|
+
"valueType": "boolean"
|
|
11265
|
+
},
|
|
11266
|
+
{
|
|
11267
|
+
"path": "onTime",
|
|
11268
|
+
"valueType": "number"
|
|
11269
|
+
},
|
|
11270
|
+
{
|
|
11271
|
+
"path": "offTime",
|
|
11272
|
+
"valueType": "number"
|
|
11273
|
+
}
|
|
11274
|
+
],
|
|
11199
11275
|
"recording-archive": [
|
|
11200
11276
|
{
|
|
11201
11277
|
"path": "deviceId",
|
|
@@ -11659,7 +11735,7 @@ function userError(code, params, englishFallback) {
|
|
|
11659
11735
|
return new CamstackError(code, params, englishFallback);
|
|
11660
11736
|
}
|
|
11661
11737
|
//#endregion
|
|
11662
|
-
//#region ../types/dist/composition-
|
|
11738
|
+
//#region ../types/dist/composition-BodkvOV0.mjs
|
|
11663
11739
|
/**
|
|
11664
11740
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11665
11741
|
*
|
|
@@ -12796,7 +12872,14 @@ var CompositionForwardCommandSchema = object({
|
|
|
12796
12872
|
method: string().min(1),
|
|
12797
12873
|
args: record(string().min(1), CompositionForwardArgSchema).optional()
|
|
12798
12874
|
});
|
|
12799
|
-
var CompositionCommandTargetSchema = discriminatedUnion("kind", [
|
|
12875
|
+
var CompositionCommandTargetSchema = discriminatedUnion("kind", [
|
|
12876
|
+
CompositionForwardCommandSchema,
|
|
12877
|
+
CompositionCodeSourceSchema,
|
|
12878
|
+
object({
|
|
12879
|
+
kind: literal("extension"),
|
|
12880
|
+
target: literal("remote")
|
|
12881
|
+
})
|
|
12882
|
+
]);
|
|
12800
12883
|
/** An item key inside an item-array cap (`consumables.items[<key>]`). */
|
|
12801
12884
|
var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
|
|
12802
12885
|
/**
|
|
@@ -12873,6 +12956,73 @@ var NodeColorIdSchema = _enum([
|
|
|
12873
12956
|
"node-g",
|
|
12874
12957
|
"node-h"
|
|
12875
12958
|
]);
|
|
12959
|
+
//#endregion
|
|
12960
|
+
//#region ../types/dist/index.mjs
|
|
12961
|
+
var SMTP_INBOUND_DEFAULT_PORT = 2525;
|
|
12962
|
+
var SMTP_INBOUND_DEFAULT_DOMAIN = "camstack.local";
|
|
12963
|
+
var SMTP_INBOUND_DEFAULT_MAX_MESSAGE_BYTES = 25 * 1024 * 1024;
|
|
12964
|
+
/** The text excerpt a message carries on the bus. */
|
|
12965
|
+
var SMTP_INBOUND_TEXT_MAX_CHARS = 4096;
|
|
12966
|
+
var PortSchema = number().int().min(1).max(65535);
|
|
12967
|
+
/** A recipient local-part: what stands before the `@`. */
|
|
12968
|
+
var SmtpRecipientSchema = string().min(1).max(64).regex(/^[A-Za-z0-9._+-]+$/);
|
|
12969
|
+
object({
|
|
12970
|
+
port: PortSchema.default(SMTP_INBOUND_DEFAULT_PORT),
|
|
12971
|
+
bindHost: string().min(1).default("0.0.0.0"),
|
|
12972
|
+
domain: string().min(1).default(SMTP_INBOUND_DEFAULT_DOMAIN),
|
|
12973
|
+
requireAuth: boolean().default(true),
|
|
12974
|
+
username: string().default(""),
|
|
12975
|
+
password: string().default("").meta({ secret: true }),
|
|
12976
|
+
starttls: boolean().default(false),
|
|
12977
|
+
maxMessageBytes: number().int().positive().default(SMTP_INBOUND_DEFAULT_MAX_MESSAGE_BYTES)
|
|
12978
|
+
});
|
|
12979
|
+
object({
|
|
12980
|
+
host: string().min(1).nullable(),
|
|
12981
|
+
hostReason: string().nullable(),
|
|
12982
|
+
port: PortSchema,
|
|
12983
|
+
domain: string().min(1),
|
|
12984
|
+
requireAuth: boolean(),
|
|
12985
|
+
username: string(),
|
|
12986
|
+
password: string(),
|
|
12987
|
+
starttls: boolean()
|
|
12988
|
+
});
|
|
12989
|
+
object({
|
|
12990
|
+
recipients: array(SmtpRecipientSchema).min(1),
|
|
12991
|
+
/**
|
|
12992
|
+
* Who is subscribing. A second `subscribe` with the same id REPLACES that subscriber's
|
|
12993
|
+
* recipients (the way a consumer that restarted, or lost a device, declares its set again);
|
|
12994
|
+
* a recipient held by another subscriber is refused with `smtp-inbound/recipient-claimed`.
|
|
12995
|
+
* Absent, the subscriber is anonymous and unique.
|
|
12996
|
+
*/
|
|
12997
|
+
subscriberId: string().min(1).max(64).optional()
|
|
12998
|
+
});
|
|
12999
|
+
var SmtpInboundAttachmentSchema = object({
|
|
13000
|
+
filename: string(),
|
|
13001
|
+
contentType: string(),
|
|
13002
|
+
bytes: number().int().nonnegative()
|
|
13003
|
+
});
|
|
13004
|
+
/** The payload of one `broker.message` (the key is the recipient local-part). */
|
|
13005
|
+
var SmtpInboundMessageSchema = object({
|
|
13006
|
+
/** Monotonic per mailbox; a gap tells a consumer it missed an event. */
|
|
13007
|
+
seq: number().int().positive(),
|
|
13008
|
+
messageId: string(),
|
|
13009
|
+
receivedAt: number().int().nonnegative(),
|
|
13010
|
+
from: string(),
|
|
13011
|
+
to: string(),
|
|
13012
|
+
subject: string(),
|
|
13013
|
+
text: string().max(SMTP_INBOUND_TEXT_MAX_CHARS),
|
|
13014
|
+
attachments: array(SmtpInboundAttachmentSchema)
|
|
13015
|
+
});
|
|
13016
|
+
object({
|
|
13017
|
+
/** The newest seq issued so far (0 before any mail). */
|
|
13018
|
+
lastSeq: number().int().nonnegative(),
|
|
13019
|
+
/** The oldest seq still retained; messages below it are gone. */
|
|
13020
|
+
oldestSeq: number().int().nonnegative(),
|
|
13021
|
+
messages: array(object({
|
|
13022
|
+
key: string(),
|
|
13023
|
+
message: SmtpInboundMessageSchema
|
|
13024
|
+
}))
|
|
13025
|
+
});
|
|
12876
13026
|
new Set([
|
|
12877
13027
|
"track",
|
|
12878
13028
|
"face",
|
|
@@ -14084,6 +14234,16 @@ var MigrationPlanSchema = object({ edits: array(discriminatedUnion("op", [
|
|
|
14084
14234
|
precondition: PreconditionSchema
|
|
14085
14235
|
})
|
|
14086
14236
|
])) });
|
|
14237
|
+
/** `JSON.stringify` with object keys sorted, so equal values spell the same. */
|
|
14238
|
+
function canonicalJson(value) {
|
|
14239
|
+
if (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
|
|
14240
|
+
if (typeof value === "object" && value !== null) return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
|
|
14241
|
+
return JSON.stringify(value) ?? "null";
|
|
14242
|
+
}
|
|
14243
|
+
/** The precondition string for a value a plan read (`null` when it read nothing). */
|
|
14244
|
+
function preconditionOf(value) {
|
|
14245
|
+
return canonicalJson(value ?? null);
|
|
14246
|
+
}
|
|
14087
14247
|
/**
|
|
14088
14248
|
* What a `procedure` body reports. `already-done` means the rerun observed the
|
|
14089
14249
|
* work finished (a crash between the work and the ledger row, or a manual fix)
|
|
@@ -19262,24 +19422,52 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
19262
19422
|
args: { on: false }
|
|
19263
19423
|
},
|
|
19264
19424
|
{
|
|
19265
|
-
id: "petFountain.
|
|
19266
|
-
method: "
|
|
19425
|
+
id: "petFountain.resetDailyConsumption",
|
|
19426
|
+
method: "resetDailyConsumption",
|
|
19427
|
+
label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
|
|
19428
|
+
phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
|
|
19429
|
+
icon: "rotate-ccw"
|
|
19430
|
+
}
|
|
19431
|
+
],
|
|
19432
|
+
state: [{
|
|
19433
|
+
kind: "boolean",
|
|
19434
|
+
field: "empty",
|
|
19435
|
+
label: t("Tank empty", "Serbatoio vuoto"),
|
|
19436
|
+
whenTrue: t("When the tank of {device} is empty", "Quando il serbatoio di {device} è vuoto"),
|
|
19437
|
+
whenFalse: t("When the tank of {device} is refilled", "Quando il serbatoio di {device} viene riempito"),
|
|
19438
|
+
icon: "droplet"
|
|
19439
|
+
}, {
|
|
19440
|
+
kind: "numeric",
|
|
19441
|
+
field: "waterLevel",
|
|
19442
|
+
quantity: t("the water level", "il livello dell’acqua"),
|
|
19443
|
+
label: t("Water level", "Livello dell’acqua"),
|
|
19444
|
+
unit: "%",
|
|
19445
|
+
icon: "droplets"
|
|
19446
|
+
}]
|
|
19447
|
+
},
|
|
19448
|
+
"pump-cycle": {
|
|
19449
|
+
scope: "device",
|
|
19450
|
+
noun: t("Pump cycle", "Ciclo della pompa"),
|
|
19451
|
+
actions: [
|
|
19452
|
+
{
|
|
19453
|
+
id: "pumpCycle.on",
|
|
19454
|
+
method: "setEnabled",
|
|
19267
19455
|
label: t("Pump cycle on", "Ciclo pompa attivo"),
|
|
19268
19456
|
phrase: t("Run the pump of {device} on its on/off cycle", "Fai lavorare la pompa di {device} a ciclo acceso/spento"),
|
|
19269
19457
|
icon: "timer",
|
|
19270
19458
|
args: { on: true }
|
|
19271
19459
|
},
|
|
19272
19460
|
{
|
|
19273
|
-
id: "
|
|
19274
|
-
method: "
|
|
19461
|
+
id: "pumpCycle.off",
|
|
19462
|
+
method: "setEnabled",
|
|
19275
19463
|
label: t("Pump cycle off", "Ciclo pompa spento"),
|
|
19276
19464
|
phrase: t("Run the pump of {device} continuously", "Fai lavorare la pompa di {device} in continuo"),
|
|
19277
19465
|
icon: "timer-off",
|
|
19278
19466
|
args: { on: false }
|
|
19279
19467
|
},
|
|
19280
19468
|
{
|
|
19281
|
-
id: "
|
|
19282
|
-
method: "
|
|
19469
|
+
id: "pumpCycle.onMinutes",
|
|
19470
|
+
method: "setOnMinutes",
|
|
19283
19471
|
label: t("Set pump run time", "Imposta il tempo di lavoro della pompa"),
|
|
19284
19472
|
phrase: t("Set the pump of {device} to run {minutes} minutes per cycle", "Imposta la pompa di {device} a lavorare {minutes} minuti per ciclo"),
|
|
19285
19473
|
icon: "timer",
|
|
@@ -19295,8 +19483,8 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
19295
19483
|
}]
|
|
19296
19484
|
},
|
|
19297
19485
|
{
|
|
19298
|
-
id: "
|
|
19299
|
-
method: "
|
|
19486
|
+
id: "pumpCycle.offMinutes",
|
|
19487
|
+
method: "setOffMinutes",
|
|
19300
19488
|
label: t("Set pump rest time", "Imposta il tempo di riposo della pompa"),
|
|
19301
19489
|
phrase: t("Set the pump of {device} to rest {minutes} minutes per cycle", "Imposta la pompa di {device} a riposare {minutes} minuti per ciclo"),
|
|
19302
19490
|
icon: "timer-off",
|
|
@@ -19310,35 +19498,63 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
19310
19498
|
unit: "min"
|
|
19311
19499
|
}
|
|
19312
19500
|
}]
|
|
19313
|
-
},
|
|
19314
|
-
{
|
|
19315
|
-
id: "petFountain.resetDailyConsumption",
|
|
19316
|
-
method: "resetDailyConsumption",
|
|
19317
|
-
label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
|
|
19318
|
-
phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
|
|
19319
|
-
icon: "rotate-ccw"
|
|
19320
19501
|
}
|
|
19321
19502
|
],
|
|
19322
19503
|
state: [{
|
|
19323
19504
|
kind: "boolean",
|
|
19324
|
-
field: "
|
|
19325
|
-
label: t("
|
|
19326
|
-
whenTrue: t("When
|
|
19327
|
-
whenFalse: t("When
|
|
19328
|
-
icon: "
|
|
19329
|
-
}, {
|
|
19330
|
-
kind: "numeric",
|
|
19331
|
-
field: "waterLevel",
|
|
19332
|
-
quantity: t("the water level", "il livello dell’acqua"),
|
|
19333
|
-
label: t("Water level", "Livello dell’acqua"),
|
|
19334
|
-
unit: "%",
|
|
19335
|
-
icon: "droplets"
|
|
19505
|
+
field: "enabled",
|
|
19506
|
+
label: t("Pump cycle", "Ciclo pompa"),
|
|
19507
|
+
whenTrue: t("When {device} starts running its pump on a cycle", "Quando {device} inizia a far lavorare la pompa a ciclo"),
|
|
19508
|
+
whenFalse: t("When {device} runs its pump continuously", "Quando {device} fa lavorare la pompa in continuo"),
|
|
19509
|
+
icon: "timer"
|
|
19336
19510
|
}]
|
|
19337
19511
|
},
|
|
19338
19512
|
"media-player": {
|
|
19339
19513
|
scope: "device",
|
|
19340
19514
|
noun: t("Media player", "Lettore multimediale"),
|
|
19341
19515
|
actions: [
|
|
19516
|
+
{
|
|
19517
|
+
id: "media.turnOn",
|
|
19518
|
+
method: "turnOn",
|
|
19519
|
+
label: t("Turn on", "Accendi"),
|
|
19520
|
+
phrase: t("Turn on {device}", "Accendi {device}"),
|
|
19521
|
+
icon: "power"
|
|
19522
|
+
},
|
|
19523
|
+
{
|
|
19524
|
+
id: "media.turnOff",
|
|
19525
|
+
method: "turnOff",
|
|
19526
|
+
label: t("Turn off", "Spegni"),
|
|
19527
|
+
phrase: t("Turn off {device}", "Spegni {device}"),
|
|
19528
|
+
icon: "power"
|
|
19529
|
+
},
|
|
19530
|
+
{
|
|
19531
|
+
id: "media.volumeUp",
|
|
19532
|
+
method: "volumeUp",
|
|
19533
|
+
label: t("Volume up", "Alza il volume"),
|
|
19534
|
+
phrase: t("Raise the volume of {device}", "Alza il volume di {device}"),
|
|
19535
|
+
icon: "volume-2"
|
|
19536
|
+
},
|
|
19537
|
+
{
|
|
19538
|
+
id: "media.volumeDown",
|
|
19539
|
+
method: "volumeDown",
|
|
19540
|
+
label: t("Volume down", "Abbassa il volume"),
|
|
19541
|
+
phrase: t("Lower the volume of {device}", "Abbassa il volume di {device}"),
|
|
19542
|
+
icon: "volume-2"
|
|
19543
|
+
},
|
|
19544
|
+
{
|
|
19545
|
+
id: "media.channelUp",
|
|
19546
|
+
method: "channelUp",
|
|
19547
|
+
label: t("Channel up", "Canale successivo"),
|
|
19548
|
+
phrase: t("Go to the next channel on {device}", "Passa al canale successivo su {device}"),
|
|
19549
|
+
icon: "skip-forward"
|
|
19550
|
+
},
|
|
19551
|
+
{
|
|
19552
|
+
id: "media.channelDown",
|
|
19553
|
+
method: "channelDown",
|
|
19554
|
+
label: t("Channel down", "Canale precedente"),
|
|
19555
|
+
phrase: t("Go to the previous channel on {device}", "Torna al canale precedente su {device}"),
|
|
19556
|
+
icon: "skip-back"
|
|
19557
|
+
},
|
|
19342
19558
|
{
|
|
19343
19559
|
id: "media.play",
|
|
19344
19560
|
method: "play",
|
|
@@ -21225,6 +21441,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
21225
21441
|
"audio-analyzer": system(PIPELINE),
|
|
21226
21442
|
"audio-codec": system(INTERNAL),
|
|
21227
21443
|
"auth-provider": system(IDENTITY),
|
|
21444
|
+
"board-key-source": system(INTERNAL),
|
|
21228
21445
|
backup: system("system backups — run and restored from the Backups page"),
|
|
21229
21446
|
broker: system("message-broker connections — configured, not actuated"),
|
|
21230
21447
|
"calendar-source": system(INTERNAL),
|
|
@@ -45687,6 +45904,49 @@ object({
|
|
|
45687
45904
|
lastChangedAt: number()
|
|
45688
45905
|
});
|
|
45689
45906
|
DeviceType.Sensor;
|
|
45907
|
+
/**
|
|
45908
|
+
* Why a board key could not be read. Typed so a caller reports the cause and
|
|
45909
|
+
* never treats "could not read" as "has no key" (D49).
|
|
45910
|
+
*/
|
|
45911
|
+
var BoardKeyFailureReasonSchema = _enum([
|
|
45912
|
+
"unknown-source",
|
|
45913
|
+
"not-admin",
|
|
45914
|
+
"no-builder",
|
|
45915
|
+
"no-ingress-url",
|
|
45916
|
+
"unreachable",
|
|
45917
|
+
"unknown-board",
|
|
45918
|
+
"no-encryption-key",
|
|
45919
|
+
"unreadable-config"
|
|
45920
|
+
]);
|
|
45921
|
+
var BoardKeySourceSchema = object({
|
|
45922
|
+
sourceId: string(),
|
|
45923
|
+
name: string()
|
|
45924
|
+
});
|
|
45925
|
+
var BoardKeyBoardSchema = object({
|
|
45926
|
+
/** The builder's file name, e.g. `kitchen.yaml`: the handle `getBoardKey` takes. */
|
|
45927
|
+
configuration: string(),
|
|
45928
|
+
/** The node name the builder gives the board. */
|
|
45929
|
+
name: string(),
|
|
45930
|
+
/** The address the builder knows the board by, when it has one. */
|
|
45931
|
+
address: string().nullable()
|
|
45932
|
+
});
|
|
45933
|
+
var BoardKeyFailureSchema = object({
|
|
45934
|
+
ok: literal(false),
|
|
45935
|
+
reason: BoardKeyFailureReasonSchema,
|
|
45936
|
+
detail: string().optional()
|
|
45937
|
+
});
|
|
45938
|
+
var ListBoardsResultSchema = discriminatedUnion("ok", [object({
|
|
45939
|
+
ok: literal(true),
|
|
45940
|
+
boards: array(BoardKeyBoardSchema).readonly()
|
|
45941
|
+
}), BoardKeyFailureSchema]);
|
|
45942
|
+
var GetBoardKeyResultSchema = discriminatedUnion("ok", [object({
|
|
45943
|
+
ok: literal(true),
|
|
45944
|
+
key: string()
|
|
45945
|
+
}), BoardKeyFailureSchema]);
|
|
45946
|
+
method(_void(), array(BoardKeySourceSchema).readonly(), { auth: "admin" }), method(object({ sourceId: string().min(1) }), ListBoardsResultSchema, { auth: "admin" }), method(object({
|
|
45947
|
+
sourceId: string().min(1),
|
|
45948
|
+
configuration: string().min(1)
|
|
45949
|
+
}), GetBoardKeyResultSchema, { auth: "admin" });
|
|
45690
45950
|
object({
|
|
45691
45951
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
45692
45952
|
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
@@ -47823,6 +48083,188 @@ DeviceType.Control, method(object({
|
|
|
47823
48083
|
kind: "mutation",
|
|
47824
48084
|
auth: "protected"
|
|
47825
48085
|
});
|
|
48086
|
+
/**
|
|
48087
|
+
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
48088
|
+
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
48089
|
+
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
48090
|
+
* surface — those use `DeviceType.Camera` + the broker pipeline.
|
|
48091
|
+
*
|
|
48092
|
+
* The cap declares a wide method surface; not every backend supports
|
|
48093
|
+
* every method. Drivers signal sub-capability via the
|
|
48094
|
+
* `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
|
|
48095
|
+
* corresponding method) so the UI gates buttons without a slice
|
|
48096
|
+
* fetch — same pattern as `PanTiltZoom` for cameras.
|
|
48097
|
+
*/
|
|
48098
|
+
var MediaPlayerStateSchema = _enum([
|
|
48099
|
+
"off",
|
|
48100
|
+
"on",
|
|
48101
|
+
"idle",
|
|
48102
|
+
"playing",
|
|
48103
|
+
"paused",
|
|
48104
|
+
"buffering",
|
|
48105
|
+
"standby"
|
|
48106
|
+
]);
|
|
48107
|
+
var MediaPlayerRepeatSchema = _enum([
|
|
48108
|
+
"off",
|
|
48109
|
+
"all",
|
|
48110
|
+
"one"
|
|
48111
|
+
]);
|
|
48112
|
+
var MediaInfoSchema = object({
|
|
48113
|
+
/** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
|
|
48114
|
+
* `podcast`, …). */
|
|
48115
|
+
type: string(),
|
|
48116
|
+
/** Human-readable title. */
|
|
48117
|
+
title: string(),
|
|
48118
|
+
/** Optional artist / channel / station label. */
|
|
48119
|
+
artist: string().optional(),
|
|
48120
|
+
/** Optional album / season / show name. */
|
|
48121
|
+
album: string().optional(),
|
|
48122
|
+
/** Optional cover-art / thumbnail URL. */
|
|
48123
|
+
imageUrl: string().optional()
|
|
48124
|
+
});
|
|
48125
|
+
/**
|
|
48126
|
+
* Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
|
|
48127
|
+
* the device said so. `assumed`: the driver has no feedback (infrared) and
|
|
48128
|
+
* tracks what it last SENT; it moves only after a send was accepted, and a
|
|
48129
|
+
* human at the real remote makes it wrong. A UI shows an assumed value as a
|
|
48130
|
+
* guess, never as a reading.
|
|
48131
|
+
*/
|
|
48132
|
+
var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
|
|
48133
|
+
/** The fields of a media-player status a driver can only guess. */
|
|
48134
|
+
var MediaPlayerAssumedFieldSchema = _enum([
|
|
48135
|
+
"state",
|
|
48136
|
+
"volumeLevel",
|
|
48137
|
+
"isMuted",
|
|
48138
|
+
"source"
|
|
48139
|
+
]);
|
|
48140
|
+
var MediaPlayerStatusSchema = object({
|
|
48141
|
+
/** Playback lifecycle state. */
|
|
48142
|
+
state: MediaPlayerStateSchema,
|
|
48143
|
+
/**
|
|
48144
|
+
* Whether the state fields are readings or the driver's own bookkeeping.
|
|
48145
|
+
* A slice written before this field existed parses as `reported`.
|
|
48146
|
+
*/
|
|
48147
|
+
stateSource: MediaPlayerStateSourceSchema.default("reported"),
|
|
48148
|
+
/**
|
|
48149
|
+
* Which fields are the driver's own guess. `stateSource` is `assumed` when
|
|
48150
|
+
* this is not empty; a field not listed here is a reading even then (power
|
|
48151
|
+
* from a running-state source, mute guessed from the last send).
|
|
48152
|
+
*/
|
|
48153
|
+
assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
|
|
48154
|
+
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
48155
|
+
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
48156
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
48157
|
+
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
48158
|
+
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
48159
|
+
isMuted: boolean().nullable(),
|
|
48160
|
+
/** Active source / input. Empty when no source surface. */
|
|
48161
|
+
source: string(),
|
|
48162
|
+
/** Selectable sources. Empty when no source surface.
|
|
48163
|
+
* Pair with `DeviceFeature.MediaPlayerSelectSource`. */
|
|
48164
|
+
availableSources: array(string()),
|
|
48165
|
+
/** Currently-playing media info. Null when nothing is playing. */
|
|
48166
|
+
currentMedia: MediaInfoSchema.nullable(),
|
|
48167
|
+
/** Current playback position in ms. Null when not seekable or
|
|
48168
|
+
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
48169
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
48170
|
+
/** Total duration of the current media in ms. Null when unknown
|
|
48171
|
+
* (live stream) or nothing is playing. */
|
|
48172
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
48173
|
+
/** Shuffle toggle. Null when no shuffle surface.
|
|
48174
|
+
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
48175
|
+
shuffle: boolean().nullable(),
|
|
48176
|
+
/** Repeat mode. Null when no repeat surface.
|
|
48177
|
+
* Pair with `DeviceFeature.MediaPlayerRepeat`. */
|
|
48178
|
+
repeat: MediaPlayerRepeatSchema.nullable(),
|
|
48179
|
+
/** Ms epoch when the slice was last updated. */
|
|
48180
|
+
lastChangedAt: number()
|
|
48181
|
+
});
|
|
48182
|
+
var stepInput = object({
|
|
48183
|
+
deviceId: number().int().nonnegative(),
|
|
48184
|
+
/** How many presses: the caller's count (Alexa `volumeSteps`, `channelCount`). */
|
|
48185
|
+
steps: number().int().min(1).max(50).default(1)
|
|
48186
|
+
});
|
|
48187
|
+
var deviceOnly = object({ deviceId: number().int().nonnegative() });
|
|
48188
|
+
DeviceType.MediaPlayer, method(deviceOnly, _void(), {
|
|
48189
|
+
kind: "mutation",
|
|
48190
|
+
auth: "protected"
|
|
48191
|
+
}), method(deviceOnly, _void(), {
|
|
48192
|
+
kind: "mutation",
|
|
48193
|
+
auth: "protected"
|
|
48194
|
+
}), method(stepInput, _void(), {
|
|
48195
|
+
kind: "mutation",
|
|
48196
|
+
auth: "protected"
|
|
48197
|
+
}), method(stepInput, _void(), {
|
|
48198
|
+
kind: "mutation",
|
|
48199
|
+
auth: "protected"
|
|
48200
|
+
}), method(stepInput, _void(), {
|
|
48201
|
+
kind: "mutation",
|
|
48202
|
+
auth: "protected"
|
|
48203
|
+
}), method(stepInput, _void(), {
|
|
48204
|
+
kind: "mutation",
|
|
48205
|
+
auth: "protected"
|
|
48206
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48207
|
+
kind: "mutation",
|
|
48208
|
+
auth: "protected"
|
|
48209
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48210
|
+
kind: "mutation",
|
|
48211
|
+
auth: "protected"
|
|
48212
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48213
|
+
kind: "mutation",
|
|
48214
|
+
auth: "protected"
|
|
48215
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48216
|
+
kind: "mutation",
|
|
48217
|
+
auth: "protected"
|
|
48218
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48219
|
+
kind: "mutation",
|
|
48220
|
+
auth: "protected"
|
|
48221
|
+
}), method(object({
|
|
48222
|
+
deviceId: number().int().nonnegative(),
|
|
48223
|
+
position: number().int().nonnegative()
|
|
48224
|
+
}), _void(), {
|
|
48225
|
+
kind: "mutation",
|
|
48226
|
+
auth: "protected"
|
|
48227
|
+
}), method(object({
|
|
48228
|
+
deviceId: number().int().nonnegative(),
|
|
48229
|
+
volumeLevel: number().min(0).max(100)
|
|
48230
|
+
}), _void(), {
|
|
48231
|
+
kind: "mutation",
|
|
48232
|
+
auth: "protected"
|
|
48233
|
+
}), method(object({
|
|
48234
|
+
deviceId: number().int().nonnegative(),
|
|
48235
|
+
muted: boolean()
|
|
48236
|
+
}), _void(), {
|
|
48237
|
+
kind: "mutation",
|
|
48238
|
+
auth: "protected"
|
|
48239
|
+
}), method(object({
|
|
48240
|
+
deviceId: number().int().nonnegative(),
|
|
48241
|
+
shuffle: boolean()
|
|
48242
|
+
}), _void(), {
|
|
48243
|
+
kind: "mutation",
|
|
48244
|
+
auth: "protected"
|
|
48245
|
+
}), method(object({
|
|
48246
|
+
deviceId: number().int().nonnegative(),
|
|
48247
|
+
repeat: MediaPlayerRepeatSchema
|
|
48248
|
+
}), _void(), {
|
|
48249
|
+
kind: "mutation",
|
|
48250
|
+
auth: "protected"
|
|
48251
|
+
}), method(object({
|
|
48252
|
+
deviceId: number().int().nonnegative(),
|
|
48253
|
+
source: string().min(1)
|
|
48254
|
+
}), _void(), {
|
|
48255
|
+
kind: "mutation",
|
|
48256
|
+
auth: "protected"
|
|
48257
|
+
}), method(object({
|
|
48258
|
+
deviceId: number().int().nonnegative(),
|
|
48259
|
+
/** Media identifier / URL. */
|
|
48260
|
+
mediaId: string().min(1),
|
|
48261
|
+
/** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
|
|
48262
|
+
* passes it through to the upstream service. */
|
|
48263
|
+
mediaType: string().min(1)
|
|
48264
|
+
}), _void(), {
|
|
48265
|
+
kind: "mutation",
|
|
48266
|
+
auth: "protected"
|
|
48267
|
+
});
|
|
47826
48268
|
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.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, 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.Heater, 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.Heater, 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.Heater, 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.Heater, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, 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, DeviceType.Heater;
|
|
47827
48269
|
var NUMBER = {
|
|
47828
48270
|
kind: "fixed",
|
|
@@ -51408,7 +51850,7 @@ object({
|
|
|
51408
51850
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
51409
51851
|
lastFetchedAt: number()
|
|
51410
51852
|
});
|
|
51411
|
-
var DeviceIdSchema$
|
|
51853
|
+
var DeviceIdSchema$2 = number().int().nonnegative();
|
|
51412
51854
|
/** A value a program option or setting can take. */
|
|
51413
51855
|
var DishwasherValueSchema = union([
|
|
51414
51856
|
boolean(),
|
|
@@ -51472,77 +51914,77 @@ var DishwasherOptionsSchema = object({
|
|
|
51472
51914
|
options: array(DishwasherOptionDescriptorSchema)
|
|
51473
51915
|
});
|
|
51474
51916
|
DeviceType.Dishwasher, method(object({
|
|
51475
|
-
deviceId: DeviceIdSchema$
|
|
51917
|
+
deviceId: DeviceIdSchema$2,
|
|
51476
51918
|
program: string().min(1).optional(),
|
|
51477
51919
|
options: record(string(), DishwasherValueSchema).optional()
|
|
51478
51920
|
}), _void(), {
|
|
51479
51921
|
kind: "mutation",
|
|
51480
51922
|
auth: "protected"
|
|
51481
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
51923
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), _void(), {
|
|
51482
51924
|
kind: "mutation",
|
|
51483
51925
|
auth: "protected"
|
|
51484
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
51926
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), _void(), {
|
|
51485
51927
|
kind: "mutation",
|
|
51486
51928
|
auth: "protected"
|
|
51487
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
51929
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), _void(), {
|
|
51488
51930
|
kind: "mutation",
|
|
51489
51931
|
auth: "protected"
|
|
51490
51932
|
}), method(object({
|
|
51491
|
-
deviceId: DeviceIdSchema$
|
|
51933
|
+
deviceId: DeviceIdSchema$2,
|
|
51492
51934
|
program: string().min(1)
|
|
51493
51935
|
}), _void(), {
|
|
51494
51936
|
kind: "mutation",
|
|
51495
51937
|
auth: "protected"
|
|
51496
51938
|
}), method(object({
|
|
51497
|
-
deviceId: DeviceIdSchema$
|
|
51939
|
+
deviceId: DeviceIdSchema$2,
|
|
51498
51940
|
on: boolean()
|
|
51499
51941
|
}), _void(), {
|
|
51500
51942
|
kind: "mutation",
|
|
51501
51943
|
auth: "protected"
|
|
51502
51944
|
}), method(object({
|
|
51503
|
-
deviceId: DeviceIdSchema$
|
|
51945
|
+
deviceId: DeviceIdSchema$2,
|
|
51504
51946
|
on: boolean()
|
|
51505
51947
|
}), _void(), {
|
|
51506
51948
|
kind: "mutation",
|
|
51507
51949
|
auth: "protected"
|
|
51508
51950
|
}), method(object({
|
|
51509
|
-
deviceId: DeviceIdSchema$
|
|
51951
|
+
deviceId: DeviceIdSchema$2,
|
|
51510
51952
|
on: boolean()
|
|
51511
51953
|
}), _void(), {
|
|
51512
51954
|
kind: "mutation",
|
|
51513
51955
|
auth: "protected"
|
|
51514
51956
|
}), method(object({
|
|
51515
|
-
deviceId: DeviceIdSchema$
|
|
51957
|
+
deviceId: DeviceIdSchema$2,
|
|
51516
51958
|
on: boolean()
|
|
51517
51959
|
}), _void(), {
|
|
51518
51960
|
kind: "mutation",
|
|
51519
51961
|
auth: "protected"
|
|
51520
51962
|
}), method(object({
|
|
51521
|
-
deviceId: DeviceIdSchema$
|
|
51963
|
+
deviceId: DeviceIdSchema$2,
|
|
51522
51964
|
on: boolean()
|
|
51523
51965
|
}), _void(), {
|
|
51524
51966
|
kind: "mutation",
|
|
51525
51967
|
auth: "protected"
|
|
51526
51968
|
}), method(object({
|
|
51527
|
-
deviceId: DeviceIdSchema$
|
|
51969
|
+
deviceId: DeviceIdSchema$2,
|
|
51528
51970
|
on: boolean()
|
|
51529
51971
|
}), _void(), {
|
|
51530
51972
|
kind: "mutation",
|
|
51531
51973
|
auth: "protected"
|
|
51532
51974
|
}), method(object({
|
|
51533
|
-
deviceId: DeviceIdSchema$
|
|
51975
|
+
deviceId: DeviceIdSchema$2,
|
|
51534
51976
|
on: boolean()
|
|
51535
51977
|
}), _void(), {
|
|
51536
51978
|
kind: "mutation",
|
|
51537
51979
|
auth: "protected"
|
|
51538
51980
|
}), method(object({
|
|
51539
|
-
deviceId: DeviceIdSchema$
|
|
51981
|
+
deviceId: DeviceIdSchema$2,
|
|
51540
51982
|
key: string().min(1),
|
|
51541
51983
|
value: DishwasherValueSchema
|
|
51542
51984
|
}), _void(), {
|
|
51543
51985
|
kind: "mutation",
|
|
51544
51986
|
auth: "protected"
|
|
51545
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
51987
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), DishwasherOptionsSchema, { auth: "protected" });
|
|
51546
51988
|
/**
|
|
51547
51989
|
* Palette entry ids (D939). Audio is the one gradient; `audio-*` leaves are
|
|
51548
51990
|
* not entries. `heatmapScrub` is a mark, not an event kind.
|
|
@@ -53283,6 +53725,11 @@ var IntegrationInstanceSchema = object({
|
|
|
53283
53725
|
name: string(),
|
|
53284
53726
|
/** The addon's own rule (an URL is present, a client id is present...). */
|
|
53285
53727
|
configured: boolean(),
|
|
53728
|
+
/**
|
|
53729
|
+
* The instance's own on/off switch (D996): always present, derived from the record body
|
|
53730
|
+
* (`enabled !== false`, so a row written before the field existed is on).
|
|
53731
|
+
*/
|
|
53732
|
+
enabled: boolean(),
|
|
53286
53733
|
/** Values for the instance form; a secret is a `SecretFieldView`, never text. */
|
|
53287
53734
|
formValues: record(string(), unknown()),
|
|
53288
53735
|
status: IntegrationInstanceStatusSchema.optional()
|
|
@@ -53302,6 +53749,12 @@ method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method
|
|
|
53302
53749
|
}), method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), {
|
|
53303
53750
|
kind: "mutation",
|
|
53304
53751
|
auth: "admin"
|
|
53752
|
+
}), method(object({
|
|
53753
|
+
id: IntegrationInstanceIdSchema,
|
|
53754
|
+
enabled: boolean()
|
|
53755
|
+
}), object({ ok: literal(true) }), {
|
|
53756
|
+
kind: "mutation",
|
|
53757
|
+
auth: "admin"
|
|
53305
53758
|
}), method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), {
|
|
53306
53759
|
kind: "mutation",
|
|
53307
53760
|
auth: "admin"
|
|
@@ -55294,164 +55747,6 @@ var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
|
55294
55747
|
]);
|
|
55295
55748
|
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
55296
55749
|
/**
|
|
55297
|
-
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
55298
|
-
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
55299
|
-
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
55300
|
-
* surface — those use `DeviceType.Camera` + the broker pipeline.
|
|
55301
|
-
*
|
|
55302
|
-
* The cap declares a wide method surface; not every backend supports
|
|
55303
|
-
* every method. Drivers signal sub-capability via the
|
|
55304
|
-
* `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
|
|
55305
|
-
* corresponding method) so the UI gates buttons without a slice
|
|
55306
|
-
* fetch — same pattern as `PanTiltZoom` for cameras.
|
|
55307
|
-
*/
|
|
55308
|
-
var MediaPlayerStateSchema = _enum([
|
|
55309
|
-
"off",
|
|
55310
|
-
"on",
|
|
55311
|
-
"idle",
|
|
55312
|
-
"playing",
|
|
55313
|
-
"paused",
|
|
55314
|
-
"buffering",
|
|
55315
|
-
"standby"
|
|
55316
|
-
]);
|
|
55317
|
-
var MediaPlayerRepeatSchema = _enum([
|
|
55318
|
-
"off",
|
|
55319
|
-
"all",
|
|
55320
|
-
"one"
|
|
55321
|
-
]);
|
|
55322
|
-
var MediaInfoSchema = object({
|
|
55323
|
-
/** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
|
|
55324
|
-
* `podcast`, …). */
|
|
55325
|
-
type: string(),
|
|
55326
|
-
/** Human-readable title. */
|
|
55327
|
-
title: string(),
|
|
55328
|
-
/** Optional artist / channel / station label. */
|
|
55329
|
-
artist: string().optional(),
|
|
55330
|
-
/** Optional album / season / show name. */
|
|
55331
|
-
album: string().optional(),
|
|
55332
|
-
/** Optional cover-art / thumbnail URL. */
|
|
55333
|
-
imageUrl: string().optional()
|
|
55334
|
-
});
|
|
55335
|
-
/**
|
|
55336
|
-
* Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
|
|
55337
|
-
* the device said so. `assumed`: the driver has no feedback (infrared) and
|
|
55338
|
-
* tracks what it last SENT; it moves only after a send was accepted, and a
|
|
55339
|
-
* human at the real remote makes it wrong. A UI shows an assumed value as a
|
|
55340
|
-
* guess, never as a reading.
|
|
55341
|
-
*/
|
|
55342
|
-
var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
|
|
55343
|
-
/** The fields of a media-player status a driver can only guess. */
|
|
55344
|
-
var MediaPlayerAssumedFieldSchema = _enum([
|
|
55345
|
-
"state",
|
|
55346
|
-
"volumeLevel",
|
|
55347
|
-
"isMuted",
|
|
55348
|
-
"source"
|
|
55349
|
-
]);
|
|
55350
|
-
object({
|
|
55351
|
-
/** Playback lifecycle state. */
|
|
55352
|
-
state: MediaPlayerStateSchema,
|
|
55353
|
-
/**
|
|
55354
|
-
* Whether the state fields are readings or the driver's own bookkeeping.
|
|
55355
|
-
* A slice written before this field existed parses as `reported`.
|
|
55356
|
-
*/
|
|
55357
|
-
stateSource: MediaPlayerStateSourceSchema.default("reported"),
|
|
55358
|
-
/**
|
|
55359
|
-
* Which fields are the driver's own guess. `stateSource` is `assumed` when
|
|
55360
|
-
* this is not empty; a field not listed here is a reading even then (power
|
|
55361
|
-
* from a running-state source, mute guessed from the last send).
|
|
55362
|
-
*/
|
|
55363
|
-
assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
|
|
55364
|
-
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
55365
|
-
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
55366
|
-
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
55367
|
-
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
55368
|
-
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
55369
|
-
isMuted: boolean().nullable(),
|
|
55370
|
-
/** Active source / input. Empty when no source surface. */
|
|
55371
|
-
source: string(),
|
|
55372
|
-
/** Selectable sources. Empty when no source surface.
|
|
55373
|
-
* Pair with `DeviceFeature.MediaPlayerSelectSource`. */
|
|
55374
|
-
availableSources: array(string()),
|
|
55375
|
-
/** Currently-playing media info. Null when nothing is playing. */
|
|
55376
|
-
currentMedia: MediaInfoSchema.nullable(),
|
|
55377
|
-
/** Current playback position in ms. Null when not seekable or
|
|
55378
|
-
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
55379
|
-
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
55380
|
-
/** Total duration of the current media in ms. Null when unknown
|
|
55381
|
-
* (live stream) or nothing is playing. */
|
|
55382
|
-
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
55383
|
-
/** Shuffle toggle. Null when no shuffle surface.
|
|
55384
|
-
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
55385
|
-
shuffle: boolean().nullable(),
|
|
55386
|
-
/** Repeat mode. Null when no repeat surface.
|
|
55387
|
-
* Pair with `DeviceFeature.MediaPlayerRepeat`. */
|
|
55388
|
-
repeat: MediaPlayerRepeatSchema.nullable(),
|
|
55389
|
-
/** Ms epoch when the slice was last updated. */
|
|
55390
|
-
lastChangedAt: number()
|
|
55391
|
-
});
|
|
55392
|
-
DeviceType.MediaPlayer, method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55393
|
-
kind: "mutation",
|
|
55394
|
-
auth: "protected"
|
|
55395
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55396
|
-
kind: "mutation",
|
|
55397
|
-
auth: "protected"
|
|
55398
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55399
|
-
kind: "mutation",
|
|
55400
|
-
auth: "protected"
|
|
55401
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55402
|
-
kind: "mutation",
|
|
55403
|
-
auth: "protected"
|
|
55404
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55405
|
-
kind: "mutation",
|
|
55406
|
-
auth: "protected"
|
|
55407
|
-
}), method(object({
|
|
55408
|
-
deviceId: number().int().nonnegative(),
|
|
55409
|
-
position: number().int().nonnegative()
|
|
55410
|
-
}), _void(), {
|
|
55411
|
-
kind: "mutation",
|
|
55412
|
-
auth: "protected"
|
|
55413
|
-
}), method(object({
|
|
55414
|
-
deviceId: number().int().nonnegative(),
|
|
55415
|
-
volumeLevel: number().min(0).max(100)
|
|
55416
|
-
}), _void(), {
|
|
55417
|
-
kind: "mutation",
|
|
55418
|
-
auth: "protected"
|
|
55419
|
-
}), method(object({
|
|
55420
|
-
deviceId: number().int().nonnegative(),
|
|
55421
|
-
muted: boolean()
|
|
55422
|
-
}), _void(), {
|
|
55423
|
-
kind: "mutation",
|
|
55424
|
-
auth: "protected"
|
|
55425
|
-
}), method(object({
|
|
55426
|
-
deviceId: number().int().nonnegative(),
|
|
55427
|
-
shuffle: boolean()
|
|
55428
|
-
}), _void(), {
|
|
55429
|
-
kind: "mutation",
|
|
55430
|
-
auth: "protected"
|
|
55431
|
-
}), method(object({
|
|
55432
|
-
deviceId: number().int().nonnegative(),
|
|
55433
|
-
repeat: MediaPlayerRepeatSchema
|
|
55434
|
-
}), _void(), {
|
|
55435
|
-
kind: "mutation",
|
|
55436
|
-
auth: "protected"
|
|
55437
|
-
}), method(object({
|
|
55438
|
-
deviceId: number().int().nonnegative(),
|
|
55439
|
-
source: string().min(1)
|
|
55440
|
-
}), _void(), {
|
|
55441
|
-
kind: "mutation",
|
|
55442
|
-
auth: "protected"
|
|
55443
|
-
}), method(object({
|
|
55444
|
-
deviceId: number().int().nonnegative(),
|
|
55445
|
-
/** Media identifier / URL. */
|
|
55446
|
-
mediaId: string().min(1),
|
|
55447
|
-
/** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
|
|
55448
|
-
* passes it through to the upstream service. */
|
|
55449
|
-
mediaType: string().min(1)
|
|
55450
|
-
}), _void(), {
|
|
55451
|
-
kind: "mutation",
|
|
55452
|
-
auth: "protected"
|
|
55453
|
-
});
|
|
55454
|
-
/**
|
|
55455
55750
|
* mesh-network — collection cap for mesh-VPN providers.
|
|
55456
55751
|
*
|
|
55457
55752
|
* Different shape from `network-access` (public tunnels):
|
|
@@ -63345,7 +63640,14 @@ var NcMediaPolicySchema = object({
|
|
|
63345
63640
|
*/
|
|
63346
63641
|
profile: CamProfileSchema.optional(),
|
|
63347
63642
|
/** See {@link NcLinkedCameraModeSchema}. */
|
|
63348
|
-
linkedCameras: NcLinkedCameraModeSchema.optional()
|
|
63643
|
+
linkedCameras: NcLinkedCameraModeSchema.optional(),
|
|
63644
|
+
/**
|
|
63645
|
+
* Sensor rules only: may this rule wake a SLEEPING battery camera to take
|
|
63646
|
+
* the linked still it attaches (D998)? Absent means yes, an explicit `false`
|
|
63647
|
+
* opts out. Deliberately not a Zod default: a rule that never chose keeps
|
|
63648
|
+
* following the hub's. One bounded wake per event; nothing is attached later.
|
|
63649
|
+
*/
|
|
63650
|
+
wakeSleepingCamera: boolean().optional()
|
|
63349
63651
|
});
|
|
63350
63652
|
/**
|
|
63351
63653
|
* Cooldown GRANULARITY over the subject's class — how much a fired
|
|
@@ -63737,6 +64039,21 @@ var NcTemplateVarKindSchema = _enum([
|
|
|
63737
64039
|
"sound",
|
|
63738
64040
|
"system"
|
|
63739
64041
|
]);
|
|
64042
|
+
/**
|
|
64043
|
+
* The default title/body a rule of one SITUATION is delivered with when the
|
|
64044
|
+
* operator wrote no template — served so an editor can pre-fill it (and the
|
|
64045
|
+
* operator can rebuild it from placeholders) instead of starting from a blank
|
|
64046
|
+
* box that, once saved, silently replaces it. `id` names the situation inside
|
|
64047
|
+
* a rule `kind`: `appliance-cycle` is a `sensor` rule whose device is an
|
|
64048
|
+
* appliance finishing a cycle. Texts are raw templates (`{{var}}` intact), in
|
|
64049
|
+
* the hub's current language and with its overrides applied.
|
|
64050
|
+
*/
|
|
64051
|
+
var NcTemplateDefaultSchema = object({
|
|
64052
|
+
id: _enum(["appliance-cycle"]),
|
|
64053
|
+
kind: NcRuleKindSchema,
|
|
64054
|
+
title: string(),
|
|
64055
|
+
body: string()
|
|
64056
|
+
});
|
|
63740
64057
|
/** Which producer renders a template: an ordinary rule, a timelapse, a digest. */
|
|
63741
64058
|
var NcTemplateFamilySchema = _enum([
|
|
63742
64059
|
"rule",
|
|
@@ -64592,7 +64909,11 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
64592
64909
|
}), method(object({}), object({
|
|
64593
64910
|
catalog: array(NcConditionDescriptorSchema),
|
|
64594
64911
|
taxonomy: NcTaxonomySchema.optional()
|
|
64595
|
-
})), method(object({}), object({
|
|
64912
|
+
})), method(object({}), object({
|
|
64913
|
+
vars: array(NcTemplateVarDescriptorSchema),
|
|
64914
|
+
/** Per-situation default texts; absent on a hub that predates them. */
|
|
64915
|
+
defaults: array(NcTemplateDefaultSchema)
|
|
64916
|
+
})), method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
|
|
64596
64917
|
kind: "mutation",
|
|
64597
64918
|
access: "view"
|
|
64598
64919
|
}), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({}), NcRuleLastRunsSchema, { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
|
|
@@ -65340,52 +65661,27 @@ object({
|
|
|
65340
65661
|
empty: boolean().nullable(),
|
|
65341
65662
|
/** True while the pump is running. Null while unknown. */
|
|
65342
65663
|
pumpRunning: boolean().nullable(),
|
|
65343
|
-
/** True when the pump works on an on/off duty cycle rather than continuously. */
|
|
65344
|
-
pumpIntervalEnabled: boolean().nullable(),
|
|
65345
|
-
/** How long the pump runs per cycle, in seconds. */
|
|
65346
|
-
pumpOnTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
65347
|
-
/** How long the pump rests per cycle, in seconds. */
|
|
65348
|
-
pumpOffTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
65349
65664
|
/** Water drunk in the last drink, in litres. */
|
|
65350
65665
|
lastDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65351
65666
|
/** Water drunk in the current hour, in litres. */
|
|
65352
65667
|
currentHourDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65353
65668
|
/** Water drunk over the last 24 hours (rolling), in litres. */
|
|
65354
65669
|
last24hDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65355
|
-
/** Water drunk since local midnight, in litres. */
|
|
65670
|
+
/** Water drunk since local midnight, in litres. Null when the fountain has no such sensor. */
|
|
65356
65671
|
todayDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65357
65672
|
/** Decoded fault message. Null means healthy; a non-null string is an active fault. */
|
|
65358
65673
|
error: string().nullable(),
|
|
65359
65674
|
/** Ms epoch when the slice was last refreshed. */
|
|
65360
65675
|
lastFetchedAt: number()
|
|
65361
65676
|
});
|
|
65362
|
-
var DeviceIdSchema = number().int().nonnegative();
|
|
65363
|
-
var MinutesSchema = number().int().min(0);
|
|
65677
|
+
var DeviceIdSchema$1 = number().int().nonnegative();
|
|
65364
65678
|
DeviceType.PetFountain, method(object({
|
|
65365
|
-
deviceId: DeviceIdSchema,
|
|
65366
|
-
on: boolean()
|
|
65367
|
-
}), _void(), {
|
|
65368
|
-
kind: "mutation",
|
|
65369
|
-
auth: "protected"
|
|
65370
|
-
}), method(object({
|
|
65371
|
-
deviceId: DeviceIdSchema,
|
|
65679
|
+
deviceId: DeviceIdSchema$1,
|
|
65372
65680
|
on: boolean()
|
|
65373
65681
|
}), _void(), {
|
|
65374
65682
|
kind: "mutation",
|
|
65375
65683
|
auth: "protected"
|
|
65376
|
-
}), method(object({
|
|
65377
|
-
deviceId: DeviceIdSchema,
|
|
65378
|
-
minutes: MinutesSchema
|
|
65379
|
-
}), _void(), {
|
|
65380
|
-
kind: "mutation",
|
|
65381
|
-
auth: "protected"
|
|
65382
|
-
}), method(object({
|
|
65383
|
-
deviceId: DeviceIdSchema,
|
|
65384
|
-
minutes: MinutesSchema
|
|
65385
|
-
}), _void(), {
|
|
65386
|
-
kind: "mutation",
|
|
65387
|
-
auth: "protected"
|
|
65388
|
-
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
65684
|
+
}), method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
65389
65685
|
kind: "mutation",
|
|
65390
65686
|
auth: "protected"
|
|
65391
65687
|
});
|
|
@@ -67345,6 +67641,37 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
|
|
|
67345
67641
|
deviceId: number(),
|
|
67346
67642
|
status: PtzAutotrackStatusSchema
|
|
67347
67643
|
});
|
|
67644
|
+
object({
|
|
67645
|
+
/** True when the pump works on the on/off cycle, false when it runs continuously. Null while unknown. */
|
|
67646
|
+
enabled: boolean().nullable(),
|
|
67647
|
+
/** How long the pump runs per cycle, in seconds. Null while unknown. */
|
|
67648
|
+
onTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
67649
|
+
/** How long the pump rests per cycle, in seconds. Null while unknown. */
|
|
67650
|
+
offTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
67651
|
+
/** Ms epoch when the slice was last refreshed. */
|
|
67652
|
+
lastFetchedAt: number()
|
|
67653
|
+
});
|
|
67654
|
+
var DeviceIdSchema = number().int().nonnegative();
|
|
67655
|
+
var MinutesSchema = number().int().min(0);
|
|
67656
|
+
DeviceType.PetFountain, method(object({
|
|
67657
|
+
deviceId: DeviceIdSchema,
|
|
67658
|
+
on: boolean()
|
|
67659
|
+
}), _void(), {
|
|
67660
|
+
kind: "mutation",
|
|
67661
|
+
auth: "protected"
|
|
67662
|
+
}), method(object({
|
|
67663
|
+
deviceId: DeviceIdSchema,
|
|
67664
|
+
minutes: MinutesSchema
|
|
67665
|
+
}), _void(), {
|
|
67666
|
+
kind: "mutation",
|
|
67667
|
+
auth: "protected"
|
|
67668
|
+
}), method(object({
|
|
67669
|
+
deviceId: DeviceIdSchema,
|
|
67670
|
+
minutes: MinutesSchema
|
|
67671
|
+
}), _void(), {
|
|
67672
|
+
kind: "mutation",
|
|
67673
|
+
auth: "protected"
|
|
67674
|
+
});
|
|
67348
67675
|
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
67349
67676
|
kind: "mutation",
|
|
67350
67677
|
auth: "admin"
|
|
@@ -70058,7 +70385,13 @@ var RemoteMediaCommandSchema = _enum([
|
|
|
70058
70385
|
"next",
|
|
70059
70386
|
"previous",
|
|
70060
70387
|
"setMute",
|
|
70061
|
-
"selectSource"
|
|
70388
|
+
"selectSource",
|
|
70389
|
+
"turnOn",
|
|
70390
|
+
"turnOff",
|
|
70391
|
+
"volumeUp",
|
|
70392
|
+
"volumeDown",
|
|
70393
|
+
"channelUp",
|
|
70394
|
+
"channelDown"
|
|
70062
70395
|
]);
|
|
70063
70396
|
/** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
|
|
70064
70397
|
var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
|
|
@@ -70079,7 +70412,7 @@ method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.e
|
|
|
70079
70412
|
}), RemoteSendResultSchema, {
|
|
70080
70413
|
kind: "mutation",
|
|
70081
70414
|
auth: "protected"
|
|
70082
|
-
}), method(deviceInput.extend({
|
|
70415
|
+
}), method(deviceInput, MediaPlayerStatusSchema.nullable(), { auth: "protected" }), method(deviceInput.extend({
|
|
70083
70416
|
command: RemoteMediaCommandSchema,
|
|
70084
70417
|
muted: boolean().optional(),
|
|
70085
70418
|
source: string().optional(),
|
|
@@ -71107,10 +71440,12 @@ DeviceType.Camera, method(object({
|
|
|
71107
71440
|
* even when the cache is well within the device's
|
|
71108
71441
|
* `snapshotMaxAgeS` window.
|
|
71109
71442
|
*
|
|
71110
|
-
* **`force` is
|
|
71443
|
+
* **`force` is a demand with a named principal, not a freshness
|
|
71444
|
+
* preference** — an operator button, the scheduled refresh (D847), or a
|
|
71445
|
+
* notification rule that left the camera's wake allowed (D998). On a
|
|
71111
71446
|
* battery camera it is the one thing that walks past the wrapper's
|
|
71112
|
-
* sleep gate and wakes the camera, so a
|
|
71113
|
-
*
|
|
71447
|
+
* sleep gate and wakes the camera, so a poller, a thumbnail or a
|
|
71448
|
+
* prestream must NEVER set it. Every such caller
|
|
71114
71449
|
* gets the cached frame, which on a sleeping battery camera is the
|
|
71115
71450
|
* correct answer: stale but honest beats woken.
|
|
71116
71451
|
*/
|
|
@@ -71918,7 +72253,11 @@ object({
|
|
|
71918
72253
|
});
|
|
71919
72254
|
Object.values(DeviceType), method(_void(), array(StreamSignalSchema).readonly());
|
|
71920
72255
|
object({
|
|
71921
|
-
|
|
72256
|
+
/**
|
|
72257
|
+
* `null` = the device has never reported its state (a measurement that
|
|
72258
|
+
* has not happened is unknown, never `false`).
|
|
72259
|
+
*/
|
|
72260
|
+
on: boolean().nullable().meta({ label: "On" }),
|
|
71922
72261
|
/** Ms epoch of the last state change. Useful for UI "X minutes ago". */
|
|
71923
72262
|
lastChangedAt: number()
|
|
71924
72263
|
});
|
|
@@ -71930,7 +72269,7 @@ DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceTyp
|
|
|
71930
72269
|
auth: "protected"
|
|
71931
72270
|
}), object({
|
|
71932
72271
|
deviceId: number(),
|
|
71933
|
-
on: boolean(),
|
|
72272
|
+
on: boolean().nullable(),
|
|
71934
72273
|
lastChangedAt: number()
|
|
71935
72274
|
});
|
|
71936
72275
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
@@ -74942,6 +75281,7 @@ function buildDescriptor(capName, kind) {
|
|
|
74942
75281
|
* contributes one entry per bound cap present in this map.
|
|
74943
75282
|
*/
|
|
74944
75283
|
var EVENT_KIND_BY_CAP = Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
|
|
75284
|
+
DeviceFeature.MediaPlayerPower, DeviceFeature.MediaPlayerVolumeStep, DeviceFeature.MediaPlayerChannelStep, DeviceFeature.MediaPlayerMute, DeviceFeature.MediaPlayerStop, DeviceFeature.MediaPlayerNext, DeviceFeature.MediaPlayerPrevious;
|
|
74945
75285
|
var btn = (idSuffix, row, col, init) => ({
|
|
74946
75286
|
idSuffix,
|
|
74947
75287
|
row,
|
|
@@ -75068,7 +75408,7 @@ discriminatedUnion("kind", [object({
|
|
|
75068
75408
|
kind: literal("pronto"),
|
|
75069
75409
|
code: string()
|
|
75070
75410
|
})]);
|
|
75071
|
-
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
75411
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
75072
75412
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
75073
75413
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
75074
75414
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
@@ -76049,6 +76389,27 @@ Object.freeze({
|
|
|
76049
76389
|
access: "view",
|
|
76050
76390
|
role: "viewer"
|
|
76051
76391
|
},
|
|
76392
|
+
"boardKeySource.getBoardKey": {
|
|
76393
|
+
capName: "board-key-source",
|
|
76394
|
+
capScope: "system",
|
|
76395
|
+
addonId: null,
|
|
76396
|
+
access: "view",
|
|
76397
|
+
role: "admin"
|
|
76398
|
+
},
|
|
76399
|
+
"boardKeySource.listBoards": {
|
|
76400
|
+
capName: "board-key-source",
|
|
76401
|
+
capScope: "system",
|
|
76402
|
+
addonId: null,
|
|
76403
|
+
access: "view",
|
|
76404
|
+
role: "admin"
|
|
76405
|
+
},
|
|
76406
|
+
"boardKeySource.listSources": {
|
|
76407
|
+
capName: "board-key-source",
|
|
76408
|
+
capScope: "system",
|
|
76409
|
+
addonId: null,
|
|
76410
|
+
access: "view",
|
|
76411
|
+
role: "admin"
|
|
76412
|
+
},
|
|
76052
76413
|
"brightness.getStatus": {
|
|
76053
76414
|
capName: "brightness",
|
|
76054
76415
|
capScope: "device",
|
|
@@ -78730,6 +79091,13 @@ Object.freeze({
|
|
|
78730
79091
|
access: "view",
|
|
78731
79092
|
role: "admin"
|
|
78732
79093
|
},
|
|
79094
|
+
"integrationInstances.setInstanceEnabled": {
|
|
79095
|
+
capName: "integration-instances",
|
|
79096
|
+
capScope: "system",
|
|
79097
|
+
addonId: null,
|
|
79098
|
+
access: "create",
|
|
79099
|
+
role: "admin"
|
|
79100
|
+
},
|
|
78733
79101
|
"integrationInstances.testInstance": {
|
|
78734
79102
|
capName: "integration-instances",
|
|
78735
79103
|
capScope: "system",
|
|
@@ -79528,6 +79896,20 @@ Object.freeze({
|
|
|
79528
79896
|
access: "view",
|
|
79529
79897
|
role: "admin"
|
|
79530
79898
|
},
|
|
79899
|
+
"mediaPlayer.channelDown": {
|
|
79900
|
+
capName: "media-player",
|
|
79901
|
+
capScope: "device",
|
|
79902
|
+
addonId: null,
|
|
79903
|
+
access: "create",
|
|
79904
|
+
role: "viewer"
|
|
79905
|
+
},
|
|
79906
|
+
"mediaPlayer.channelUp": {
|
|
79907
|
+
capName: "media-player",
|
|
79908
|
+
capScope: "device",
|
|
79909
|
+
addonId: null,
|
|
79910
|
+
access: "create",
|
|
79911
|
+
role: "viewer"
|
|
79912
|
+
},
|
|
79531
79913
|
"mediaPlayer.getStatus": {
|
|
79532
79914
|
capName: "media-player",
|
|
79533
79915
|
capScope: "device",
|
|
@@ -79619,6 +80001,34 @@ Object.freeze({
|
|
|
79619
80001
|
access: "create",
|
|
79620
80002
|
role: "viewer"
|
|
79621
80003
|
},
|
|
80004
|
+
"mediaPlayer.turnOff": {
|
|
80005
|
+
capName: "media-player",
|
|
80006
|
+
capScope: "device",
|
|
80007
|
+
addonId: null,
|
|
80008
|
+
access: "create",
|
|
80009
|
+
role: "viewer"
|
|
80010
|
+
},
|
|
80011
|
+
"mediaPlayer.turnOn": {
|
|
80012
|
+
capName: "media-player",
|
|
80013
|
+
capScope: "device",
|
|
80014
|
+
addonId: null,
|
|
80015
|
+
access: "create",
|
|
80016
|
+
role: "viewer"
|
|
80017
|
+
},
|
|
80018
|
+
"mediaPlayer.volumeDown": {
|
|
80019
|
+
capName: "media-player",
|
|
80020
|
+
capScope: "device",
|
|
80021
|
+
addonId: null,
|
|
80022
|
+
access: "create",
|
|
80023
|
+
role: "viewer"
|
|
80024
|
+
},
|
|
80025
|
+
"mediaPlayer.volumeUp": {
|
|
80026
|
+
capName: "media-player",
|
|
80027
|
+
capScope: "device",
|
|
80028
|
+
addonId: null,
|
|
80029
|
+
access: "create",
|
|
80030
|
+
role: "viewer"
|
|
80031
|
+
},
|
|
79622
80032
|
"meshNetwork.getStatus": {
|
|
79623
80033
|
capName: "mesh-network",
|
|
79624
80034
|
capScope: "system",
|
|
@@ -80865,27 +81275,6 @@ Object.freeze({
|
|
|
80865
81275
|
access: "create",
|
|
80866
81276
|
role: "viewer"
|
|
80867
81277
|
},
|
|
80868
|
-
"petFountain.setPumpInterval": {
|
|
80869
|
-
capName: "pet-fountain",
|
|
80870
|
-
capScope: "device",
|
|
80871
|
-
addonId: null,
|
|
80872
|
-
access: "create",
|
|
80873
|
-
role: "viewer"
|
|
80874
|
-
},
|
|
80875
|
-
"petFountain.setPumpOffMinutes": {
|
|
80876
|
-
capName: "pet-fountain",
|
|
80877
|
-
capScope: "device",
|
|
80878
|
-
addonId: null,
|
|
80879
|
-
access: "create",
|
|
80880
|
-
role: "viewer"
|
|
80881
|
-
},
|
|
80882
|
-
"petFountain.setPumpOnMinutes": {
|
|
80883
|
-
capName: "pet-fountain",
|
|
80884
|
-
capScope: "device",
|
|
80885
|
-
addonId: null,
|
|
80886
|
-
access: "create",
|
|
80887
|
-
role: "viewer"
|
|
80888
|
-
},
|
|
80889
81278
|
"pipelineAnalytics.addRetrainFrameFromRecording": {
|
|
80890
81279
|
capName: "pipeline-analytics",
|
|
80891
81280
|
capScope: "device",
|
|
@@ -82664,6 +83053,34 @@ Object.freeze({
|
|
|
82664
83053
|
access: "create",
|
|
82665
83054
|
role: "admin"
|
|
82666
83055
|
},
|
|
83056
|
+
"pumpCycle.getStatus": {
|
|
83057
|
+
capName: "pump-cycle",
|
|
83058
|
+
capScope: "device",
|
|
83059
|
+
addonId: null,
|
|
83060
|
+
access: "view",
|
|
83061
|
+
role: "viewer"
|
|
83062
|
+
},
|
|
83063
|
+
"pumpCycle.setEnabled": {
|
|
83064
|
+
capName: "pump-cycle",
|
|
83065
|
+
capScope: "device",
|
|
83066
|
+
addonId: null,
|
|
83067
|
+
access: "create",
|
|
83068
|
+
role: "viewer"
|
|
83069
|
+
},
|
|
83070
|
+
"pumpCycle.setOffMinutes": {
|
|
83071
|
+
capName: "pump-cycle",
|
|
83072
|
+
capScope: "device",
|
|
83073
|
+
addonId: null,
|
|
83074
|
+
access: "create",
|
|
83075
|
+
role: "viewer"
|
|
83076
|
+
},
|
|
83077
|
+
"pumpCycle.setOnMinutes": {
|
|
83078
|
+
capName: "pump-cycle",
|
|
83079
|
+
capScope: "device",
|
|
83080
|
+
addonId: null,
|
|
83081
|
+
access: "create",
|
|
83082
|
+
role: "viewer"
|
|
83083
|
+
},
|
|
82667
83084
|
"reboot.reboot": {
|
|
82668
83085
|
capName: "reboot",
|
|
82669
83086
|
capScope: "device",
|
|
@@ -83168,6 +83585,13 @@ Object.freeze({
|
|
|
83168
83585
|
access: "create",
|
|
83169
83586
|
role: "admin"
|
|
83170
83587
|
},
|
|
83588
|
+
"remote.getAssumedStatus": {
|
|
83589
|
+
capName: "remote",
|
|
83590
|
+
capScope: "system",
|
|
83591
|
+
addonId: null,
|
|
83592
|
+
access: "view",
|
|
83593
|
+
role: "admin"
|
|
83594
|
+
},
|
|
83171
83595
|
"remote.getConfig": {
|
|
83172
83596
|
capName: "remote",
|
|
83173
83597
|
capScope: "system",
|
|
@@ -86834,6 +87258,16 @@ Object.freeze({
|
|
|
86834
87258
|
form: "single",
|
|
86835
87259
|
optional: true
|
|
86836
87260
|
}],
|
|
87261
|
+
"mediaPlayer.channelDown": [{
|
|
87262
|
+
name: "deviceId",
|
|
87263
|
+
form: "single",
|
|
87264
|
+
optional: false
|
|
87265
|
+
}],
|
|
87266
|
+
"mediaPlayer.channelUp": [{
|
|
87267
|
+
name: "deviceId",
|
|
87268
|
+
form: "single",
|
|
87269
|
+
optional: false
|
|
87270
|
+
}],
|
|
86837
87271
|
"mediaPlayer.getStatus": [{
|
|
86838
87272
|
name: "deviceId",
|
|
86839
87273
|
form: "single",
|
|
@@ -86899,6 +87333,26 @@ Object.freeze({
|
|
|
86899
87333
|
form: "single",
|
|
86900
87334
|
optional: false
|
|
86901
87335
|
}],
|
|
87336
|
+
"mediaPlayer.turnOff": [{
|
|
87337
|
+
name: "deviceId",
|
|
87338
|
+
form: "single",
|
|
87339
|
+
optional: false
|
|
87340
|
+
}],
|
|
87341
|
+
"mediaPlayer.turnOn": [{
|
|
87342
|
+
name: "deviceId",
|
|
87343
|
+
form: "single",
|
|
87344
|
+
optional: false
|
|
87345
|
+
}],
|
|
87346
|
+
"mediaPlayer.volumeDown": [{
|
|
87347
|
+
name: "deviceId",
|
|
87348
|
+
form: "single",
|
|
87349
|
+
optional: false
|
|
87350
|
+
}],
|
|
87351
|
+
"mediaPlayer.volumeUp": [{
|
|
87352
|
+
name: "deviceId",
|
|
87353
|
+
form: "single",
|
|
87354
|
+
optional: false
|
|
87355
|
+
}],
|
|
86902
87356
|
"meter.getStatus": [{
|
|
86903
87357
|
name: "deviceId",
|
|
86904
87358
|
form: "single",
|
|
@@ -87208,21 +87662,6 @@ Object.freeze({
|
|
|
87208
87662
|
form: "single",
|
|
87209
87663
|
optional: false
|
|
87210
87664
|
}],
|
|
87211
|
-
"petFountain.setPumpInterval": [{
|
|
87212
|
-
name: "deviceId",
|
|
87213
|
-
form: "single",
|
|
87214
|
-
optional: false
|
|
87215
|
-
}],
|
|
87216
|
-
"petFountain.setPumpOffMinutes": [{
|
|
87217
|
-
name: "deviceId",
|
|
87218
|
-
form: "single",
|
|
87219
|
-
optional: false
|
|
87220
|
-
}],
|
|
87221
|
-
"petFountain.setPumpOnMinutes": [{
|
|
87222
|
-
name: "deviceId",
|
|
87223
|
-
form: "single",
|
|
87224
|
-
optional: false
|
|
87225
|
-
}],
|
|
87226
87665
|
"pipelineAnalytics.addRetrainFrameFromRecording": [{
|
|
87227
87666
|
name: "deviceId",
|
|
87228
87667
|
form: "single",
|
|
@@ -87858,6 +88297,26 @@ Object.freeze({
|
|
|
87858
88297
|
form: "single",
|
|
87859
88298
|
optional: false
|
|
87860
88299
|
}],
|
|
88300
|
+
"pumpCycle.getStatus": [{
|
|
88301
|
+
name: "deviceId",
|
|
88302
|
+
form: "single",
|
|
88303
|
+
optional: false
|
|
88304
|
+
}],
|
|
88305
|
+
"pumpCycle.setEnabled": [{
|
|
88306
|
+
name: "deviceId",
|
|
88307
|
+
form: "single",
|
|
88308
|
+
optional: false
|
|
88309
|
+
}],
|
|
88310
|
+
"pumpCycle.setOffMinutes": [{
|
|
88311
|
+
name: "deviceId",
|
|
88312
|
+
form: "single",
|
|
88313
|
+
optional: false
|
|
88314
|
+
}],
|
|
88315
|
+
"pumpCycle.setOnMinutes": [{
|
|
88316
|
+
name: "deviceId",
|
|
88317
|
+
form: "single",
|
|
88318
|
+
optional: false
|
|
88319
|
+
}],
|
|
87861
88320
|
"reboot.reboot": [{
|
|
87862
88321
|
name: "deviceId",
|
|
87863
88322
|
form: "single",
|
|
@@ -88059,6 +88518,11 @@ Object.freeze({
|
|
|
88059
88518
|
form: "single",
|
|
88060
88519
|
optional: true
|
|
88061
88520
|
}],
|
|
88521
|
+
"remote.getAssumedStatus": [{
|
|
88522
|
+
name: "deviceId",
|
|
88523
|
+
form: "single",
|
|
88524
|
+
optional: false
|
|
88525
|
+
}],
|
|
88062
88526
|
"remote.getConfig": [{
|
|
88063
88527
|
name: "deviceId",
|
|
88064
88528
|
form: "single",
|
|
@@ -89314,6 +89778,7 @@ var STATE_SUMMARY_VARS = [
|
|
|
89314
89778
|
[...RULE_TEXT],
|
|
89315
89779
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
89316
89780
|
([...RULE_TEXT], [...COMBINED_ALARM]),
|
|
89781
|
+
([...RULE_TEXT], [...COMBINED_ALARM]),
|
|
89317
89782
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
89318
89783
|
([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
|
|
89319
89784
|
([...RULE_TEXT], [...CONSUMABLE]),
|
|
@@ -90075,13 +90540,20 @@ var GoogleBrokerEntrySchema = object({
|
|
|
90075
90540
|
calendarFilter: string().default(""),
|
|
90076
90541
|
/** Default From for Gmail send — usually the connected account email. */
|
|
90077
90542
|
fromAddress: string().default(""),
|
|
90078
|
-
fromName: string().default("")
|
|
90543
|
+
fromName: string().default(""),
|
|
90544
|
+
/**
|
|
90545
|
+
* The stamped calendar ids (`<brokerId>:<googleCalendarId>`) this broker imports. `undefined`
|
|
90546
|
+
* means "not moved here yet": the reader falls back to the shared `calendarIds` setting of
|
|
90547
|
+
* the addon until the migration copied it. Empty = import none.
|
|
90548
|
+
*/
|
|
90549
|
+
calendarIds: array(string()).optional()
|
|
90079
90550
|
});
|
|
90080
90551
|
var GoogleServicesConfigSchema = object({
|
|
90081
90552
|
brokers: array(GoogleBrokerEntrySchema).default([]),
|
|
90082
90553
|
/**
|
|
90083
|
-
*
|
|
90084
|
-
*
|
|
90554
|
+
* @deprecated The shared set (`brokerId:googleCalendarId`). Each broker owns its own
|
|
90555
|
+
* `calendarIds` now; this key is read only while a broker has none, and the migration
|
|
90556
|
+
* `google-services/2026-11-03-drop-shared-calendar-ids` deletes it.
|
|
90085
90557
|
*/
|
|
90086
90558
|
calendarIds: array(string()).default([]),
|
|
90087
90559
|
/** @deprecated Pre-broker singleton — cleared after migrateLegacyToBrokers. */
|
|
@@ -90159,14 +90631,15 @@ function settingsToBrokerFields(settings) {
|
|
|
90159
90631
|
clientSecret: parsed.clientSecret,
|
|
90160
90632
|
calendarFilter: calendarIdsToFilter(parsed),
|
|
90161
90633
|
fromAddress: parsed.fromAddress ?? "",
|
|
90162
|
-
fromName: parsed.fromName ?? ""
|
|
90634
|
+
fromName: parsed.fromName ?? "",
|
|
90635
|
+
...parsed.calendarIds !== void 0 ? { calendarIds: [...parsed.calendarIds] } : {}
|
|
90163
90636
|
};
|
|
90164
90637
|
}
|
|
90165
90638
|
function brokerEntryToSettings(entry) {
|
|
90166
90639
|
return {
|
|
90167
90640
|
clientId: entry.clientId,
|
|
90168
90641
|
clientSecret: entry.clientSecret,
|
|
90169
|
-
calendarIds: [...parseCalendarFilter(entry.calendarFilter)],
|
|
90642
|
+
calendarIds: [...entry.calendarIds ?? parseCalendarFilter(entry.calendarFilter)],
|
|
90170
90643
|
fromAddress: entry.fromAddress,
|
|
90171
90644
|
fromName: entry.fromName
|
|
90172
90645
|
};
|
|
@@ -90217,6 +90690,13 @@ function migrateLegacyToBrokers(rawConfig, legacyOauth) {
|
|
|
90217
90690
|
didMigrate: true
|
|
90218
90691
|
};
|
|
90219
90692
|
}
|
|
90693
|
+
/**
|
|
90694
|
+
* The stamped calendar ids one broker imports: its own set, or — while the migration has not
|
|
90695
|
+
* reached it — the shared setting older versions wrote, so the move never changes what is imported.
|
|
90696
|
+
*/
|
|
90697
|
+
function calendarIdsOfEntry(entry, shared) {
|
|
90698
|
+
return new Set(entry?.calendarIds ?? shared);
|
|
90699
|
+
}
|
|
90220
90700
|
function parseCalendarFilter(raw) {
|
|
90221
90701
|
const trimmed = raw.trim();
|
|
90222
90702
|
if (!trimmed) return /* @__PURE__ */ new Set();
|
|
@@ -90303,6 +90783,7 @@ var GoogleBrokerRegistry = class {
|
|
|
90303
90783
|
const entry = GoogleBrokerEntrySchema.parse({
|
|
90304
90784
|
id,
|
|
90305
90785
|
name,
|
|
90786
|
+
calendarIds: [],
|
|
90306
90787
|
...fields
|
|
90307
90788
|
});
|
|
90308
90789
|
this.entries = [...this.entries, entry];
|
|
@@ -90316,6 +90797,7 @@ var GoogleBrokerRegistry = class {
|
|
|
90316
90797
|
const next = GoogleBrokerEntrySchema.parse({
|
|
90317
90798
|
id: prev.id,
|
|
90318
90799
|
name: prev.name,
|
|
90800
|
+
...prev.calendarIds !== void 0 ? { calendarIds: prev.calendarIds } : {},
|
|
90319
90801
|
...fields
|
|
90320
90802
|
});
|
|
90321
90803
|
this.entries = this.entries.map((e, i) => i === idx ? next : e);
|
|
@@ -90521,6 +91003,24 @@ function buildGoogleBrokerSettingsSchema(publicOrigin) {
|
|
|
90521
91003
|
showToggle: true
|
|
90522
91004
|
}]
|
|
90523
91005
|
},
|
|
91006
|
+
{
|
|
91007
|
+
id: "google-calendars-import",
|
|
91008
|
+
title: "Calendars to import",
|
|
91009
|
+
titleKey: "google-services:googleBrokerSettings.s_google-calendars-import.title",
|
|
91010
|
+
description: "Nothing selected = no calendars imported from this account. Options appear once the account is signed in.",
|
|
91011
|
+
descriptionKey: "google-services:googleBrokerSettings.s_google-calendars-import.description",
|
|
91012
|
+
columns: 1,
|
|
91013
|
+
fields: [{
|
|
91014
|
+
type: "multiselect",
|
|
91015
|
+
key: "calendarIds",
|
|
91016
|
+
label: "Calendars",
|
|
91017
|
+
labelKey: "google-services:googleBrokerSettings.s_google-calendars-import.calendarIds.label",
|
|
91018
|
+
options: [],
|
|
91019
|
+
optionsFrom: "_calendarCatalog",
|
|
91020
|
+
description: "The calendars of this Google account that CamStack imports.",
|
|
91021
|
+
descriptionKey: "google-services:googleBrokerSettings.s_google-calendars-import.calendarIds.description"
|
|
91022
|
+
}]
|
|
91023
|
+
},
|
|
90524
91024
|
{
|
|
90525
91025
|
id: "gmail-send",
|
|
90526
91026
|
title: "Gmail send",
|
|
@@ -90544,7 +91044,7 @@ function buildGoogleBrokerSettingsSchema(publicOrigin) {
|
|
|
90544
91044
|
}
|
|
90545
91045
|
] };
|
|
90546
91046
|
}
|
|
90547
|
-
/** Connectivity Calendars / Email pages — calendar import picker
|
|
91047
|
+
/** Connectivity Calendars / Email pages — the calendar import picker is on each broker. */
|
|
90548
91048
|
function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
|
|
90549
91049
|
const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
|
|
90550
91050
|
if (cap === "calendar-source") return { sections: [{
|
|
@@ -90560,27 +91060,10 @@ function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
|
|
|
90560
91060
|
format: "html",
|
|
90561
91061
|
content: [
|
|
90562
91062
|
"<p>Link a Google account under Connectivity → <strong>Brokers</strong> ",
|
|
90563
|
-
"(Client ID / secret + Sign in).
|
|
91063
|
+
"(Client ID / secret + Sign in). Pick the calendars to import on each broker. ",
|
|
90564
91064
|
`OAuth redirect URI: <code>${redirect}</code>.</p>`
|
|
90565
91065
|
].join("")
|
|
90566
91066
|
}]
|
|
90567
|
-
}, {
|
|
90568
|
-
id: "google-calendars-import",
|
|
90569
|
-
title: "Calendars to import",
|
|
90570
|
-
titleKey: "google-services:googleFeatureSettings.s_google-calendars-import.title",
|
|
90571
|
-
description: "Nothing selected = no calendars imported. Options appear after at least one Google broker is signed in.",
|
|
90572
|
-
descriptionKey: "google-services:googleFeatureSettings.s_google-calendars-import.description",
|
|
90573
|
-
columns: 1,
|
|
90574
|
-
fields: [{
|
|
90575
|
-
type: "multiselect",
|
|
90576
|
-
key: "calendarIds",
|
|
90577
|
-
label: "Calendars",
|
|
90578
|
-
labelKey: "google-services:googleFeatureSettings.s_google-calendars-import.calendarIds.label",
|
|
90579
|
-
options: [],
|
|
90580
|
-
optionsFrom: "_calendarCatalog",
|
|
90581
|
-
description: "Ids are stamped as brokerId:googleCalendarId across every Google broker.",
|
|
90582
|
-
descriptionKey: "google-services:googleFeatureSettings.s_google-calendars-import.calendarIds.description"
|
|
90583
|
-
}]
|
|
90584
91067
|
}] };
|
|
90585
91068
|
return { sections: [{
|
|
90586
91069
|
id: "setup-intro",
|
|
@@ -90670,11 +91153,11 @@ function createGoogleCalendarApi(getAccessToken, http = { fetch }) {
|
|
|
90670
91153
|
function createGoogleCalendarSource(deps) {
|
|
90671
91154
|
return {
|
|
90672
91155
|
listCalendars: async () => {
|
|
90673
|
-
const selected = deps.selectedCalendarIds();
|
|
90674
|
-
if (selected.size === 0) return [];
|
|
90675
91156
|
const out = [];
|
|
90676
91157
|
for (const account of deps.accounts()) {
|
|
90677
91158
|
if (!account.connected) continue;
|
|
91159
|
+
const selected = deps.selectedCalendarIds(account.brokerId);
|
|
91160
|
+
if (selected.size === 0) continue;
|
|
90678
91161
|
try {
|
|
90679
91162
|
const list = await account.api.listCalendars();
|
|
90680
91163
|
for (const c of list) {
|
|
@@ -90697,20 +91180,18 @@ function createGoogleCalendarSource(deps) {
|
|
|
90697
91180
|
return out;
|
|
90698
91181
|
},
|
|
90699
91182
|
listEvents: async (input) => {
|
|
90700
|
-
const selected = deps.selectedCalendarIds();
|
|
90701
|
-
if (selected.size === 0) return [];
|
|
90702
91183
|
const timeMin = new Date(input.startMs).toISOString();
|
|
90703
91184
|
const timeMax = new Date(input.endMs).toISOString();
|
|
90704
91185
|
const accounts = deps.accounts().filter((a) => a.connected);
|
|
90705
91186
|
const byBroker = new Map(accounts.map((a) => [a.brokerId, a]));
|
|
90706
|
-
const requestIds = input.calendarIds !== void 0 && input.calendarIds.length > 0 ? input.calendarIds : [...
|
|
91187
|
+
const requestIds = input.calendarIds !== void 0 && input.calendarIds.length > 0 ? input.calendarIds : [...new Set(deps.accounts().flatMap((account) => [...deps.selectedCalendarIds(account.brokerId)].filter((stamped) => parseStampedCalendarId(stamped)?.brokerId === account.brokerId)))];
|
|
90707
91188
|
/** brokerId → google calendar ids to fetch */
|
|
90708
91189
|
const wanted = /* @__PURE__ */ new Map();
|
|
90709
91190
|
for (const stamped of requestIds) {
|
|
90710
|
-
if (!selected.has(stamped)) continue;
|
|
90711
91191
|
const parsed = parseStampedCalendarId(stamped);
|
|
90712
91192
|
if (parsed === null) continue;
|
|
90713
91193
|
if (!byBroker.has(parsed.brokerId)) continue;
|
|
91194
|
+
if (!deps.selectedCalendarIds(parsed.brokerId).has(stamped)) continue;
|
|
90714
91195
|
const list = wanted.get(parsed.brokerId) ?? [];
|
|
90715
91196
|
list.push(parsed.googleCalendarId);
|
|
90716
91197
|
wanted.set(parsed.brokerId, list);
|
|
@@ -91059,11 +91540,152 @@ var googleBrokersEntryKind = defineEntryKind("brokers", {
|
|
|
91059
91540
|
clientSecret: GoogleBrokerEntrySchema.shape.clientSecret,
|
|
91060
91541
|
calendarFilter: GoogleBrokerEntrySchema.shape.calendarFilter,
|
|
91061
91542
|
fromAddress: GoogleBrokerEntrySchema.shape.fromAddress,
|
|
91062
|
-
fromName: GoogleBrokerEntrySchema.shape.fromName
|
|
91543
|
+
fromName: GoogleBrokerEntrySchema.shape.fromName,
|
|
91544
|
+
calendarIds: GoogleBrokerEntrySchema.shape.calendarIds
|
|
91063
91545
|
}),
|
|
91064
91546
|
writeTrigger: "an operator adds or edits a Google OAuth broker"
|
|
91065
91547
|
});
|
|
91066
91548
|
//#endregion
|
|
91549
|
+
//#region src/migrations/calendar-ids-to-brokers.ts
|
|
91550
|
+
/**
|
|
91551
|
+
* The calendars a Google account imports are a fact of THAT account (D985): each broker record
|
|
91552
|
+
* carries its own `calendarIds`, and the addon-level `calendarIds` setting (one shared set of
|
|
91553
|
+
* `<brokerId>:<googleCalendarId>` ids) goes away.
|
|
91554
|
+
*
|
|
91555
|
+
* Two migrations, in this order:
|
|
91556
|
+
*
|
|
91557
|
+
* 1. `calendar-ids-to-brokers` (procedure) copies the shared set to EVERY broker record that has
|
|
91558
|
+
* no `calendarIds` yet. A record that already has one is never overwritten; a broker deleted
|
|
91559
|
+
* meanwhile is skipped, never recreated; a stored value that is not a list of strings is an
|
|
91560
|
+
* ambiguity and throws (nothing is guessed).
|
|
91561
|
+
* 2. `drop-shared-calendar-ids` (plan) deletes the shared setting, but only after a read-back:
|
|
91562
|
+
* every live broker must show its own `calendarIds`. When one of them holds a set that differs
|
|
91563
|
+
* from the shared one (an operator choice made before the copy), the shared value is the
|
|
91564
|
+
* loser and stays PARKED where it is, never deleted.
|
|
91565
|
+
*
|
|
91566
|
+
* Both are idempotent: with the copy landed the first reports `already-done`, and with the row
|
|
91567
|
+
* gone the second plans nothing.
|
|
91568
|
+
*/
|
|
91569
|
+
var GOOGLE_ADDON_ID = "google-services";
|
|
91570
|
+
/** The canonical settings table: one row per key, id `<addonId>.<key>`. */
|
|
91571
|
+
var ADDON_SETTINGS_TABLE = "addon-settings";
|
|
91572
|
+
/** The legacy shared set. */
|
|
91573
|
+
var SHARED_CALENDAR_IDS_KEY = "calendarIds";
|
|
91574
|
+
async function readSharedSet(ports) {
|
|
91575
|
+
const answer = await ports.readStored(SHARED_CALENDAR_IDS_KEY);
|
|
91576
|
+
if (answer.kind === "unreadable") throw new Error(`google-services: the shared calendarIds cannot be read (${answer.reason})`);
|
|
91577
|
+
if (answer.kind === "absent") return null;
|
|
91578
|
+
const value = answer.value;
|
|
91579
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) throw new Error("google-services: the shared calendarIds is not a list of ids — nothing copied");
|
|
91580
|
+
return value;
|
|
91581
|
+
}
|
|
91582
|
+
async function copyCalendarIdsToBrokers(ports) {
|
|
91583
|
+
const shared = await readSharedSet(ports);
|
|
91584
|
+
if (shared === null) return {
|
|
91585
|
+
outcome: "already-done",
|
|
91586
|
+
summary: { reason: "no-shared-set" }
|
|
91587
|
+
};
|
|
91588
|
+
let copied = 0;
|
|
91589
|
+
let kept = 0;
|
|
91590
|
+
let gone = 0;
|
|
91591
|
+
for (const broker of await ports.listBrokers()) {
|
|
91592
|
+
if (broker.calendarIds !== void 0) {
|
|
91593
|
+
kept += 1;
|
|
91594
|
+
continue;
|
|
91595
|
+
}
|
|
91596
|
+
if (await ports.setBrokerCalendarIds(broker.id, shared)) copied += 1;
|
|
91597
|
+
else gone += 1;
|
|
91598
|
+
}
|
|
91599
|
+
return {
|
|
91600
|
+
outcome: copied === 0 ? "already-done" : "done",
|
|
91601
|
+
summary: {
|
|
91602
|
+
copied,
|
|
91603
|
+
kept,
|
|
91604
|
+
gone
|
|
91605
|
+
}
|
|
91606
|
+
};
|
|
91607
|
+
}
|
|
91608
|
+
var sameSet = (a, b) => {
|
|
91609
|
+
const left = new Set(a);
|
|
91610
|
+
const right = new Set(b);
|
|
91611
|
+
return left.size === right.size && [...left].every((id) => right.has(id));
|
|
91612
|
+
};
|
|
91613
|
+
async function planDropSharedCalendarIds(ports) {
|
|
91614
|
+
const shared = await readSharedSet(ports);
|
|
91615
|
+
if (shared === null) return { edits: [] };
|
|
91616
|
+
const brokers = await ports.listBrokers();
|
|
91617
|
+
if (brokers.length === 0) return { edits: [] };
|
|
91618
|
+
const missing = brokers.filter((broker) => broker.calendarIds === void 0);
|
|
91619
|
+
if (missing.length > 0) throw new Error(`google-services: the shared calendarIds is not dropped, ${missing.map((b) => b.id).join(", ")} has no set of its own`);
|
|
91620
|
+
if (!brokers.every((broker) => sameSet(broker.calendarIds ?? [], shared))) return { edits: [] };
|
|
91621
|
+
const rowId = `${GOOGLE_ADDON_ID}.${SHARED_CALENDAR_IDS_KEY}`;
|
|
91622
|
+
return { edits: [{
|
|
91623
|
+
op: "deleteKeys",
|
|
91624
|
+
scope: ADDON_SETTINGS_TABLE,
|
|
91625
|
+
keys: [rowId],
|
|
91626
|
+
precondition: preconditionOf({ [rowId]: {
|
|
91627
|
+
addonId: GOOGLE_ADDON_ID,
|
|
91628
|
+
key: SHARED_CALENDAR_IDS_KEY,
|
|
91629
|
+
value: shared
|
|
91630
|
+
} })
|
|
91631
|
+
}] };
|
|
91632
|
+
}
|
|
91633
|
+
//#endregion
|
|
91634
|
+
//#region src/migrations/index.ts
|
|
91635
|
+
/** The `google-services` catalogue (D987): offered to the engine when the addon initialises. */
|
|
91636
|
+
var ADDON_MIGRATIONS = [{
|
|
91637
|
+
meta: {
|
|
91638
|
+
id: "google-services/2026-11-03-calendar-ids-to-brokers",
|
|
91639
|
+
owner: "google-services",
|
|
91640
|
+
level: "addon-state",
|
|
91641
|
+
seq: 1,
|
|
91642
|
+
dependsOn: [],
|
|
91643
|
+
store: "settings",
|
|
91644
|
+
scope: "cluster",
|
|
91645
|
+
onFailure: "tolerate",
|
|
91646
|
+
bodyKind: "procedure",
|
|
91647
|
+
description: "Copy the shared calendar set to every Google broker record that has none of its own",
|
|
91648
|
+
fixtureMark: "the client secret marker of a broker record the migration does not know"
|
|
91649
|
+
},
|
|
91650
|
+
procedure: copyCalendarIdsToBrokers
|
|
91651
|
+
}, {
|
|
91652
|
+
meta: {
|
|
91653
|
+
id: "google-services/2026-11-03-drop-shared-calendar-ids",
|
|
91654
|
+
owner: "google-services",
|
|
91655
|
+
level: "addon-state",
|
|
91656
|
+
seq: 2,
|
|
91657
|
+
dependsOn: ["google-services/2026-11-03-calendar-ids-to-brokers"],
|
|
91658
|
+
store: "settings",
|
|
91659
|
+
scope: "cluster",
|
|
91660
|
+
onFailure: "tolerate",
|
|
91661
|
+
bodyKind: "plan",
|
|
91662
|
+
description: "Delete the shared calendar set once every broker shows its own",
|
|
91663
|
+
fixtureMark: "the shared set parked when a broker holds a different one"
|
|
91664
|
+
},
|
|
91665
|
+
plan: planDropSharedCalendarIds
|
|
91666
|
+
}];
|
|
91667
|
+
//#endregion
|
|
91668
|
+
//#region src/migrations/ports.ts
|
|
91669
|
+
/** The migration's ports over the `brokers` record kind and the addon's raw settings read. */
|
|
91670
|
+
function calendarIdsPortsOver(kind, readStored) {
|
|
91671
|
+
return {
|
|
91672
|
+
readStored,
|
|
91673
|
+
listBrokers: async () => {
|
|
91674
|
+
return (await kind.query({ limit: 1e3 })).records.map((record) => ({
|
|
91675
|
+
id: record.id,
|
|
91676
|
+
calendarIds: record.data.calendarIds
|
|
91677
|
+
}));
|
|
91678
|
+
},
|
|
91679
|
+
setBrokerCalendarIds: async (id, ids) => {
|
|
91680
|
+
const row = await kind.get(id);
|
|
91681
|
+
if (row === null) return false;
|
|
91682
|
+
if (row.data.calendarIds !== void 0) return true;
|
|
91683
|
+
await kind.patch(id, { calendarIds: [...ids] });
|
|
91684
|
+
return true;
|
|
91685
|
+
}
|
|
91686
|
+
};
|
|
91687
|
+
}
|
|
91688
|
+
//#endregion
|
|
91067
91689
|
//#region src/google-broker-store.ts
|
|
91068
91690
|
function rowToEntry(id, data) {
|
|
91069
91691
|
const parsed = GoogleBrokerEntrySchema.safeParse({
|
|
@@ -91101,14 +91723,15 @@ var GoogleBrokerStore = class GoogleBrokerStore {
|
|
|
91101
91723
|
const nextIds = new Set(entries.map((e) => e.id));
|
|
91102
91724
|
for (const row of page.records) if (!nextIds.has(row.id)) await this.kind.delete(row.id);
|
|
91103
91725
|
for (const entry of entries) {
|
|
91104
|
-
const { id, name, clientId, clientSecret, calendarFilter, fromAddress, fromName } = entry;
|
|
91726
|
+
const { id, name, clientId, clientSecret, calendarFilter, fromAddress, fromName, calendarIds } = entry;
|
|
91105
91727
|
const body = {
|
|
91106
91728
|
name,
|
|
91107
91729
|
clientId,
|
|
91108
91730
|
clientSecret,
|
|
91109
91731
|
calendarFilter,
|
|
91110
91732
|
fromAddress,
|
|
91111
|
-
fromName
|
|
91733
|
+
fromName,
|
|
91734
|
+
...calendarIds !== void 0 ? { calendarIds } : {}
|
|
91112
91735
|
};
|
|
91113
91736
|
const parsed = googleBrokersEntryKind.schema.safeParse(body);
|
|
91114
91737
|
if (!parsed.success) throw new Error(`invalid Google broker '${id}': ${parsed.error.message}`);
|
|
@@ -91205,6 +91828,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91205
91828
|
await this.brokerStore.replaceAll(this.config.brokers);
|
|
91206
91829
|
this.activeBrokers = await this.loadPersistedBrokers();
|
|
91207
91830
|
}
|
|
91831
|
+
await this.migrateCalendarIds(records);
|
|
91208
91832
|
this.registry.seedFrom(this.activeBrokers, oauthAccounts.accounts);
|
|
91209
91833
|
await this.refreshPublicOrigin();
|
|
91210
91834
|
const calendarProvider = createGoogleCalendarSource({
|
|
@@ -91214,7 +91838,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91214
91838
|
connected: this.registry.isConnected(entry.id),
|
|
91215
91839
|
api: createGoogleCalendarApi(() => this.ensureAccessToken(entry.id))
|
|
91216
91840
|
})),
|
|
91217
|
-
selectedCalendarIds: () =>
|
|
91841
|
+
selectedCalendarIds: (brokerId) => this.calendarIdsOf(brokerId)
|
|
91218
91842
|
});
|
|
91219
91843
|
const smtpProvider = createGmailSmtpProvider({ accounts: () => this.registry.listEntries().map((entry) => {
|
|
91220
91844
|
const oauth = this.registry.getOauth(entry.id);
|
|
@@ -91385,7 +92009,10 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91385
92009
|
getSettings: async ({ id }) => {
|
|
91386
92010
|
const entry = this.registry.getEntry(id);
|
|
91387
92011
|
if (entry === null) return null;
|
|
91388
|
-
return
|
|
92012
|
+
return {
|
|
92013
|
+
...brokerEntryToSettings(entry),
|
|
92014
|
+
_calendarCatalog: await this.loadStampedCalendarCatalog(id)
|
|
92015
|
+
};
|
|
91389
92016
|
},
|
|
91390
92017
|
setSettings: async ({ id, settings }) => {
|
|
91391
92018
|
if (!owns(id)) return;
|
|
@@ -91398,6 +92025,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91398
92025
|
const oauth = this.registry.getOauth(id);
|
|
91399
92026
|
return {
|
|
91400
92027
|
...brokerEntryToSettings(entry),
|
|
92028
|
+
_calendarCatalog: await this.loadStampedCalendarCatalog(id),
|
|
91401
92029
|
accountEmail: oauth?.accountEmail ?? "",
|
|
91402
92030
|
connected: this.registry.isConnected(id)
|
|
91403
92031
|
};
|
|
@@ -91603,6 +92231,13 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91603
92231
|
const cutoff = Date.now() - 10 * 6e4;
|
|
91604
92232
|
for (const [k, v] of this.pendingStates) if (v.issuedAt < cutoff) this.pendingStates.delete(k);
|
|
91605
92233
|
}
|
|
92234
|
+
/**
|
|
92235
|
+
* The shared calendar set becomes each broker's own (D985, D987). Brokers are reloaded once the
|
|
92236
|
+
* copy landed, so the registry is seeded with the sets the migration wrote.
|
|
92237
|
+
*/
|
|
92238
|
+
async migrateCalendarIds(records) {
|
|
92239
|
+
if ((await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, calendarIdsPortsOver(records.kind(googleBrokersEntryKind), (key) => this.readStoredGlobalKey(key))))).applied.length > 0) this.activeBrokers = await this.loadPersistedBrokers();
|
|
92240
|
+
}
|
|
91606
92241
|
async loadPersistedBrokers() {
|
|
91607
92242
|
const store = this.brokerStore;
|
|
91608
92243
|
if (store !== null) {
|
|
@@ -91626,9 +92261,10 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91626
92261
|
await this.oauthAccountsHandle.set({ accounts: [...this.registry.listOauthAccounts()] });
|
|
91627
92262
|
}
|
|
91628
92263
|
/** Stamped catalog for Connectivity → Calendars (`optionsFrom`). */
|
|
91629
|
-
async loadStampedCalendarCatalog() {
|
|
92264
|
+
async loadStampedCalendarCatalog(onlyBrokerId) {
|
|
91630
92265
|
const out = [];
|
|
91631
92266
|
for (const entry of this.registry.listEntries()) {
|
|
92267
|
+
if (onlyBrokerId !== void 0 && entry.id !== onlyBrokerId) continue;
|
|
91632
92268
|
if (!this.registry.isConnected(entry.id)) continue;
|
|
91633
92269
|
try {
|
|
91634
92270
|
const list = await createGoogleCalendarApi(() => this.ensureAccessToken(entry.id)).listCalendars();
|
|
@@ -91768,13 +92404,15 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91768
92404
|
}
|
|
91769
92405
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
91770
92406
|
await this.refreshPublicOrigin();
|
|
91771
|
-
|
|
91772
|
-
|
|
91773
|
-
|
|
91774
|
-
|
|
91775
|
-
|
|
91776
|
-
|
|
91777
|
-
|
|
92407
|
+
return super.getGlobalSettings(overlay, cap, nodeId);
|
|
92408
|
+
}
|
|
92409
|
+
/**
|
|
92410
|
+
* The stamped calendar ids ONE broker imports. A broker the migration has not reached yet
|
|
92411
|
+
* (no `calendarIds` of its own) reads the shared setting older versions wrote, so the move
|
|
92412
|
+
* never changes what is imported mid-way.
|
|
92413
|
+
*/
|
|
92414
|
+
calendarIdsOf(brokerId) {
|
|
92415
|
+
return calendarIdsOfEntry(this.registry.getEntry(brokerId), this.config.calendarIds ?? []);
|
|
91778
92416
|
}
|
|
91779
92417
|
};
|
|
91780
92418
|
function asSettingsCap(cap) {
|