@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
|
@@ -789,7 +789,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
789
789
|
return EventCategory;
|
|
790
790
|
}({});
|
|
791
791
|
//#endregion
|
|
792
|
-
//#region ../types/dist/device-type-
|
|
792
|
+
//#region ../types/dist/device-type-D1fH5kaC.mjs
|
|
793
793
|
/**
|
|
794
794
|
import { errMsg } from '@camstack/types'
|
|
795
795
|
* Extract a human-readable message from an unknown error value.
|
|
@@ -978,6 +978,9 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
|
978
978
|
DeviceFeature["CoverPositionable"] = "cover-positionable";
|
|
979
979
|
/** Cover exposes slat-tilt control. Gates the tilt slider UI. */
|
|
980
980
|
DeviceFeature["CoverTilt"] = "cover-tilt";
|
|
981
|
+
/** Cover has a real stop: `stop` halts the travel in place. Gates the stop
|
|
982
|
+
* control in exporters; a provider whose `stop` is a no-op never declares it. */
|
|
983
|
+
DeviceFeature["CoverStoppable"] = "cover-stoppable";
|
|
981
984
|
/** Valve exposes intermediate position control (0..100). Gates the
|
|
982
985
|
* position slider / drag surface UI. */
|
|
983
986
|
DeviceFeature["ValvePositionable"] = "valve-positionable";
|
|
@@ -1017,6 +1020,12 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
|
1017
1020
|
DeviceFeature["MediaPlayerPrevious"] = "media-player-previous";
|
|
1018
1021
|
/** Media player exposes stop distinct from pause. */
|
|
1019
1022
|
DeviceFeature["MediaPlayerStop"] = "media-player-stop";
|
|
1023
|
+
/** Media player exposes volume up / down in steps (an infrared TV has no level). */
|
|
1024
|
+
DeviceFeature["MediaPlayerVolumeStep"] = "media-player-volume-step";
|
|
1025
|
+
/** Media player exposes channel up / down in steps. */
|
|
1026
|
+
DeviceFeature["MediaPlayerChannelStep"] = "media-player-channel-step";
|
|
1027
|
+
/** Media player exposes power on / off. */
|
|
1028
|
+
DeviceFeature["MediaPlayerPower"] = "media-player-power";
|
|
1020
1029
|
/** Alarm panel requires a PIN code on arm/disarm. */
|
|
1021
1030
|
DeviceFeature["AlarmPinRequired"] = "alarm-pin-required";
|
|
1022
1031
|
/** Presence device carries GPS coordinates (lat/lng/accuracy) in
|
|
@@ -5976,7 +5985,7 @@ var ZodIssueCode = {
|
|
|
5976
5985
|
var ZodFirstPartyTypeKind;
|
|
5977
5986
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5978
5987
|
//#endregion
|
|
5979
|
-
//#region ../types/dist/sleep-
|
|
5988
|
+
//#region ../types/dist/sleep-BKClVk0K.mjs
|
|
5980
5989
|
/**
|
|
5981
5990
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5982
5991
|
* window to float samples (D455).
|
|
@@ -6055,6 +6064,22 @@ function buildAlawTable() {
|
|
|
6055
6064
|
}
|
|
6056
6065
|
buildUlawTable();
|
|
6057
6066
|
buildAlawTable();
|
|
6067
|
+
/**
|
|
6068
|
+
* An addon whose own migrations did not apply refuses to initialise (D987). The error is typed
|
|
6069
|
+
* so a runner hosting several addons can tell "this addon is blocked" from a crash and keep its
|
|
6070
|
+
* co-located siblings running. Recognised by NAME and shape, never `instanceof`: a forked addon
|
|
6071
|
+
* bundles its own copy of this module, so the class the runner sees is not the one that threw.
|
|
6072
|
+
*/
|
|
6073
|
+
var MIGRATIONS_BLOCKED_ERROR_NAME = "AddonMigrationsBlockedError";
|
|
6074
|
+
var AddonMigrationsBlockedError = class extends Error {
|
|
6075
|
+
/** The ledger ids that did not apply and block the owner. */
|
|
6076
|
+
migrationIds;
|
|
6077
|
+
constructor(migrationIds) {
|
|
6078
|
+
super(`migration(s) ${migrationIds.join(", ")} did not apply and block this addon — fix or retry them (migrations.retry), then restart it`);
|
|
6079
|
+
this.name = MIGRATIONS_BLOCKED_ERROR_NAME;
|
|
6080
|
+
this.migrationIds = migrationIds;
|
|
6081
|
+
}
|
|
6082
|
+
};
|
|
6058
6083
|
/** Is this field's stored value a credential? */
|
|
6059
6084
|
function isSecretConfigField(field) {
|
|
6060
6085
|
if (field.type === "password") return true;
|
|
@@ -6492,6 +6517,53 @@ function emitReadiness(bus, params) {
|
|
|
6492
6517
|
ts
|
|
6493
6518
|
}));
|
|
6494
6519
|
}
|
|
6520
|
+
/** Why a catalogue entry is not a runnable body of its declared kind. */
|
|
6521
|
+
var AddonMigrationDefinitionError = class extends Error {
|
|
6522
|
+
id;
|
|
6523
|
+
constructor(id, message) {
|
|
6524
|
+
super(`${id}: ${message}`);
|
|
6525
|
+
this.id = id;
|
|
6526
|
+
this.name = "AddonMigrationDefinitionError";
|
|
6527
|
+
}
|
|
6528
|
+
};
|
|
6529
|
+
/**
|
|
6530
|
+
* Bind a catalogue to the addon's ports. Throws {@link AddonMigrationDefinitionError} when
|
|
6531
|
+
* an entry's `bodyKind` does not match the body it carries, or is `sql` (a hub-only
|
|
6532
|
+
* body). A throwing body never escapes `execute`: it is reported as a failed result.
|
|
6533
|
+
*/
|
|
6534
|
+
function defineAddonMigrations(catalogue, ports) {
|
|
6535
|
+
return catalogue.map((entry) => {
|
|
6536
|
+
const { meta } = entry;
|
|
6537
|
+
if ("plan" in entry) {
|
|
6538
|
+
if (meta.bodyKind !== "plan") throw new AddonMigrationDefinitionError(meta.id, `carries a plan body but declares "${meta.bodyKind}"`);
|
|
6539
|
+
return {
|
|
6540
|
+
meta,
|
|
6541
|
+
execute: () => guarded(async () => ({
|
|
6542
|
+
kind: "plan",
|
|
6543
|
+
plan: await entry.plan(ports)
|
|
6544
|
+
}))
|
|
6545
|
+
};
|
|
6546
|
+
}
|
|
6547
|
+
if (meta.bodyKind !== "procedure") throw new AddonMigrationDefinitionError(meta.id, `carries a procedure body but declares "${meta.bodyKind}"`);
|
|
6548
|
+
return {
|
|
6549
|
+
meta,
|
|
6550
|
+
execute: () => guarded(async () => ({
|
|
6551
|
+
kind: "procedure",
|
|
6552
|
+
result: await entry.procedure(ports)
|
|
6553
|
+
}))
|
|
6554
|
+
};
|
|
6555
|
+
});
|
|
6556
|
+
}
|
|
6557
|
+
async function guarded(work) {
|
|
6558
|
+
try {
|
|
6559
|
+
return await work();
|
|
6560
|
+
} catch (err) {
|
|
6561
|
+
return {
|
|
6562
|
+
kind: "failed",
|
|
6563
|
+
error: errMsg(err)
|
|
6564
|
+
};
|
|
6565
|
+
}
|
|
6566
|
+
}
|
|
6495
6567
|
var NOT_RUN = {
|
|
6496
6568
|
declared: false,
|
|
6497
6569
|
snapshot: null,
|
|
@@ -7661,7 +7733,7 @@ var BaseAddon = class {
|
|
|
7661
7733
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7662
7734
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7663
7735
|
}, migrations, this.ctx.logger);
|
|
7664
|
-
if (report.blockOwner.length > 0) throw new
|
|
7736
|
+
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7665
7737
|
return report;
|
|
7666
7738
|
}
|
|
7667
7739
|
/**
|
|
@@ -9652,6 +9724,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9652
9724
|
"backup",
|
|
9653
9725
|
"battery",
|
|
9654
9726
|
"binary",
|
|
9727
|
+
"board-key-source",
|
|
9655
9728
|
"brightness",
|
|
9656
9729
|
"broker",
|
|
9657
9730
|
"button",
|
|
@@ -9773,6 +9846,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9773
9846
|
"privacy-mask",
|
|
9774
9847
|
"ptz",
|
|
9775
9848
|
"ptz-autotrack",
|
|
9849
|
+
"pump-cycle",
|
|
9776
9850
|
"reboot",
|
|
9777
9851
|
"record-entries",
|
|
9778
9852
|
"recording",
|
|
@@ -11065,18 +11139,6 @@ Object.freeze({
|
|
|
11065
11139
|
"path": "pumpRunning",
|
|
11066
11140
|
"valueType": "boolean"
|
|
11067
11141
|
},
|
|
11068
|
-
{
|
|
11069
|
-
"path": "pumpIntervalEnabled",
|
|
11070
|
-
"valueType": "boolean"
|
|
11071
|
-
},
|
|
11072
|
-
{
|
|
11073
|
-
"path": "pumpOnTime",
|
|
11074
|
-
"valueType": "number"
|
|
11075
|
-
},
|
|
11076
|
-
{
|
|
11077
|
-
"path": "pumpOffTime",
|
|
11078
|
-
"valueType": "number"
|
|
11079
|
-
},
|
|
11080
11142
|
{
|
|
11081
11143
|
"path": "lastDrink",
|
|
11082
11144
|
"valueType": "number"
|
|
@@ -11200,6 +11262,20 @@ Object.freeze({
|
|
|
11200
11262
|
"valueType": "number"
|
|
11201
11263
|
}
|
|
11202
11264
|
],
|
|
11265
|
+
"pump-cycle": [
|
|
11266
|
+
{
|
|
11267
|
+
"path": "enabled",
|
|
11268
|
+
"valueType": "boolean"
|
|
11269
|
+
},
|
|
11270
|
+
{
|
|
11271
|
+
"path": "onTime",
|
|
11272
|
+
"valueType": "number"
|
|
11273
|
+
},
|
|
11274
|
+
{
|
|
11275
|
+
"path": "offTime",
|
|
11276
|
+
"valueType": "number"
|
|
11277
|
+
}
|
|
11278
|
+
],
|
|
11203
11279
|
"recording-archive": [
|
|
11204
11280
|
{
|
|
11205
11281
|
"path": "deviceId",
|
|
@@ -11663,7 +11739,7 @@ function userError(code, params, englishFallback) {
|
|
|
11663
11739
|
return new CamstackError(code, params, englishFallback);
|
|
11664
11740
|
}
|
|
11665
11741
|
//#endregion
|
|
11666
|
-
//#region ../types/dist/composition-
|
|
11742
|
+
//#region ../types/dist/composition-BodkvOV0.mjs
|
|
11667
11743
|
/**
|
|
11668
11744
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11669
11745
|
*
|
|
@@ -12800,7 +12876,14 @@ var CompositionForwardCommandSchema = object({
|
|
|
12800
12876
|
method: string().min(1),
|
|
12801
12877
|
args: record(string().min(1), CompositionForwardArgSchema).optional()
|
|
12802
12878
|
});
|
|
12803
|
-
var CompositionCommandTargetSchema = discriminatedUnion("kind", [
|
|
12879
|
+
var CompositionCommandTargetSchema = discriminatedUnion("kind", [
|
|
12880
|
+
CompositionForwardCommandSchema,
|
|
12881
|
+
CompositionCodeSourceSchema,
|
|
12882
|
+
object({
|
|
12883
|
+
kind: literal("extension"),
|
|
12884
|
+
target: literal("remote")
|
|
12885
|
+
})
|
|
12886
|
+
]);
|
|
12804
12887
|
/** An item key inside an item-array cap (`consumables.items[<key>]`). */
|
|
12805
12888
|
var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
|
|
12806
12889
|
/**
|
|
@@ -12877,6 +12960,73 @@ var NodeColorIdSchema = _enum([
|
|
|
12877
12960
|
"node-g",
|
|
12878
12961
|
"node-h"
|
|
12879
12962
|
]);
|
|
12963
|
+
//#endregion
|
|
12964
|
+
//#region ../types/dist/index.mjs
|
|
12965
|
+
var SMTP_INBOUND_DEFAULT_PORT = 2525;
|
|
12966
|
+
var SMTP_INBOUND_DEFAULT_DOMAIN = "camstack.local";
|
|
12967
|
+
var SMTP_INBOUND_DEFAULT_MAX_MESSAGE_BYTES = 25 * 1024 * 1024;
|
|
12968
|
+
/** The text excerpt a message carries on the bus. */
|
|
12969
|
+
var SMTP_INBOUND_TEXT_MAX_CHARS = 4096;
|
|
12970
|
+
var PortSchema = number().int().min(1).max(65535);
|
|
12971
|
+
/** A recipient local-part: what stands before the `@`. */
|
|
12972
|
+
var SmtpRecipientSchema = string().min(1).max(64).regex(/^[A-Za-z0-9._+-]+$/);
|
|
12973
|
+
object({
|
|
12974
|
+
port: PortSchema.default(SMTP_INBOUND_DEFAULT_PORT),
|
|
12975
|
+
bindHost: string().min(1).default("0.0.0.0"),
|
|
12976
|
+
domain: string().min(1).default(SMTP_INBOUND_DEFAULT_DOMAIN),
|
|
12977
|
+
requireAuth: boolean().default(true),
|
|
12978
|
+
username: string().default(""),
|
|
12979
|
+
password: string().default("").meta({ secret: true }),
|
|
12980
|
+
starttls: boolean().default(false),
|
|
12981
|
+
maxMessageBytes: number().int().positive().default(SMTP_INBOUND_DEFAULT_MAX_MESSAGE_BYTES)
|
|
12982
|
+
});
|
|
12983
|
+
object({
|
|
12984
|
+
host: string().min(1).nullable(),
|
|
12985
|
+
hostReason: string().nullable(),
|
|
12986
|
+
port: PortSchema,
|
|
12987
|
+
domain: string().min(1),
|
|
12988
|
+
requireAuth: boolean(),
|
|
12989
|
+
username: string(),
|
|
12990
|
+
password: string(),
|
|
12991
|
+
starttls: boolean()
|
|
12992
|
+
});
|
|
12993
|
+
object({
|
|
12994
|
+
recipients: array(SmtpRecipientSchema).min(1),
|
|
12995
|
+
/**
|
|
12996
|
+
* Who is subscribing. A second `subscribe` with the same id REPLACES that subscriber's
|
|
12997
|
+
* recipients (the way a consumer that restarted, or lost a device, declares its set again);
|
|
12998
|
+
* a recipient held by another subscriber is refused with `smtp-inbound/recipient-claimed`.
|
|
12999
|
+
* Absent, the subscriber is anonymous and unique.
|
|
13000
|
+
*/
|
|
13001
|
+
subscriberId: string().min(1).max(64).optional()
|
|
13002
|
+
});
|
|
13003
|
+
var SmtpInboundAttachmentSchema = object({
|
|
13004
|
+
filename: string(),
|
|
13005
|
+
contentType: string(),
|
|
13006
|
+
bytes: number().int().nonnegative()
|
|
13007
|
+
});
|
|
13008
|
+
/** The payload of one `broker.message` (the key is the recipient local-part). */
|
|
13009
|
+
var SmtpInboundMessageSchema = object({
|
|
13010
|
+
/** Monotonic per mailbox; a gap tells a consumer it missed an event. */
|
|
13011
|
+
seq: number().int().positive(),
|
|
13012
|
+
messageId: string(),
|
|
13013
|
+
receivedAt: number().int().nonnegative(),
|
|
13014
|
+
from: string(),
|
|
13015
|
+
to: string(),
|
|
13016
|
+
subject: string(),
|
|
13017
|
+
text: string().max(SMTP_INBOUND_TEXT_MAX_CHARS),
|
|
13018
|
+
attachments: array(SmtpInboundAttachmentSchema)
|
|
13019
|
+
});
|
|
13020
|
+
object({
|
|
13021
|
+
/** The newest seq issued so far (0 before any mail). */
|
|
13022
|
+
lastSeq: number().int().nonnegative(),
|
|
13023
|
+
/** The oldest seq still retained; messages below it are gone. */
|
|
13024
|
+
oldestSeq: number().int().nonnegative(),
|
|
13025
|
+
messages: array(object({
|
|
13026
|
+
key: string(),
|
|
13027
|
+
message: SmtpInboundMessageSchema
|
|
13028
|
+
}))
|
|
13029
|
+
});
|
|
12880
13030
|
new Set([
|
|
12881
13031
|
"track",
|
|
12882
13032
|
"face",
|
|
@@ -14088,6 +14238,16 @@ var MigrationPlanSchema = object({ edits: array(discriminatedUnion("op", [
|
|
|
14088
14238
|
precondition: PreconditionSchema
|
|
14089
14239
|
})
|
|
14090
14240
|
])) });
|
|
14241
|
+
/** `JSON.stringify` with object keys sorted, so equal values spell the same. */
|
|
14242
|
+
function canonicalJson(value) {
|
|
14243
|
+
if (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
|
|
14244
|
+
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(",")}}`;
|
|
14245
|
+
return JSON.stringify(value) ?? "null";
|
|
14246
|
+
}
|
|
14247
|
+
/** The precondition string for a value a plan read (`null` when it read nothing). */
|
|
14248
|
+
function preconditionOf(value) {
|
|
14249
|
+
return canonicalJson(value ?? null);
|
|
14250
|
+
}
|
|
14091
14251
|
/**
|
|
14092
14252
|
* What a `procedure` body reports. `already-done` means the rerun observed the
|
|
14093
14253
|
* work finished (a crash between the work and the ledger row, or a manual fix)
|
|
@@ -19266,24 +19426,52 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
19266
19426
|
args: { on: false }
|
|
19267
19427
|
},
|
|
19268
19428
|
{
|
|
19269
|
-
id: "petFountain.
|
|
19270
|
-
method: "
|
|
19429
|
+
id: "petFountain.resetDailyConsumption",
|
|
19430
|
+
method: "resetDailyConsumption",
|
|
19431
|
+
label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
|
|
19432
|
+
phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
|
|
19433
|
+
icon: "rotate-ccw"
|
|
19434
|
+
}
|
|
19435
|
+
],
|
|
19436
|
+
state: [{
|
|
19437
|
+
kind: "boolean",
|
|
19438
|
+
field: "empty",
|
|
19439
|
+
label: t("Tank empty", "Serbatoio vuoto"),
|
|
19440
|
+
whenTrue: t("When the tank of {device} is empty", "Quando il serbatoio di {device} è vuoto"),
|
|
19441
|
+
whenFalse: t("When the tank of {device} is refilled", "Quando il serbatoio di {device} viene riempito"),
|
|
19442
|
+
icon: "droplet"
|
|
19443
|
+
}, {
|
|
19444
|
+
kind: "numeric",
|
|
19445
|
+
field: "waterLevel",
|
|
19446
|
+
quantity: t("the water level", "il livello dell’acqua"),
|
|
19447
|
+
label: t("Water level", "Livello dell’acqua"),
|
|
19448
|
+
unit: "%",
|
|
19449
|
+
icon: "droplets"
|
|
19450
|
+
}]
|
|
19451
|
+
},
|
|
19452
|
+
"pump-cycle": {
|
|
19453
|
+
scope: "device",
|
|
19454
|
+
noun: t("Pump cycle", "Ciclo della pompa"),
|
|
19455
|
+
actions: [
|
|
19456
|
+
{
|
|
19457
|
+
id: "pumpCycle.on",
|
|
19458
|
+
method: "setEnabled",
|
|
19271
19459
|
label: t("Pump cycle on", "Ciclo pompa attivo"),
|
|
19272
19460
|
phrase: t("Run the pump of {device} on its on/off cycle", "Fai lavorare la pompa di {device} a ciclo acceso/spento"),
|
|
19273
19461
|
icon: "timer",
|
|
19274
19462
|
args: { on: true }
|
|
19275
19463
|
},
|
|
19276
19464
|
{
|
|
19277
|
-
id: "
|
|
19278
|
-
method: "
|
|
19465
|
+
id: "pumpCycle.off",
|
|
19466
|
+
method: "setEnabled",
|
|
19279
19467
|
label: t("Pump cycle off", "Ciclo pompa spento"),
|
|
19280
19468
|
phrase: t("Run the pump of {device} continuously", "Fai lavorare la pompa di {device} in continuo"),
|
|
19281
19469
|
icon: "timer-off",
|
|
19282
19470
|
args: { on: false }
|
|
19283
19471
|
},
|
|
19284
19472
|
{
|
|
19285
|
-
id: "
|
|
19286
|
-
method: "
|
|
19473
|
+
id: "pumpCycle.onMinutes",
|
|
19474
|
+
method: "setOnMinutes",
|
|
19287
19475
|
label: t("Set pump run time", "Imposta il tempo di lavoro della pompa"),
|
|
19288
19476
|
phrase: t("Set the pump of {device} to run {minutes} minutes per cycle", "Imposta la pompa di {device} a lavorare {minutes} minuti per ciclo"),
|
|
19289
19477
|
icon: "timer",
|
|
@@ -19299,8 +19487,8 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
19299
19487
|
}]
|
|
19300
19488
|
},
|
|
19301
19489
|
{
|
|
19302
|
-
id: "
|
|
19303
|
-
method: "
|
|
19490
|
+
id: "pumpCycle.offMinutes",
|
|
19491
|
+
method: "setOffMinutes",
|
|
19304
19492
|
label: t("Set pump rest time", "Imposta il tempo di riposo della pompa"),
|
|
19305
19493
|
phrase: t("Set the pump of {device} to rest {minutes} minutes per cycle", "Imposta la pompa di {device} a riposare {minutes} minuti per ciclo"),
|
|
19306
19494
|
icon: "timer-off",
|
|
@@ -19314,35 +19502,63 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
19314
19502
|
unit: "min"
|
|
19315
19503
|
}
|
|
19316
19504
|
}]
|
|
19317
|
-
},
|
|
19318
|
-
{
|
|
19319
|
-
id: "petFountain.resetDailyConsumption",
|
|
19320
|
-
method: "resetDailyConsumption",
|
|
19321
|
-
label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
|
|
19322
|
-
phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
|
|
19323
|
-
icon: "rotate-ccw"
|
|
19324
19505
|
}
|
|
19325
19506
|
],
|
|
19326
19507
|
state: [{
|
|
19327
19508
|
kind: "boolean",
|
|
19328
|
-
field: "
|
|
19329
|
-
label: t("
|
|
19330
|
-
whenTrue: t("When
|
|
19331
|
-
whenFalse: t("When
|
|
19332
|
-
icon: "
|
|
19333
|
-
}, {
|
|
19334
|
-
kind: "numeric",
|
|
19335
|
-
field: "waterLevel",
|
|
19336
|
-
quantity: t("the water level", "il livello dell’acqua"),
|
|
19337
|
-
label: t("Water level", "Livello dell’acqua"),
|
|
19338
|
-
unit: "%",
|
|
19339
|
-
icon: "droplets"
|
|
19509
|
+
field: "enabled",
|
|
19510
|
+
label: t("Pump cycle", "Ciclo pompa"),
|
|
19511
|
+
whenTrue: t("When {device} starts running its pump on a cycle", "Quando {device} inizia a far lavorare la pompa a ciclo"),
|
|
19512
|
+
whenFalse: t("When {device} runs its pump continuously", "Quando {device} fa lavorare la pompa in continuo"),
|
|
19513
|
+
icon: "timer"
|
|
19340
19514
|
}]
|
|
19341
19515
|
},
|
|
19342
19516
|
"media-player": {
|
|
19343
19517
|
scope: "device",
|
|
19344
19518
|
noun: t("Media player", "Lettore multimediale"),
|
|
19345
19519
|
actions: [
|
|
19520
|
+
{
|
|
19521
|
+
id: "media.turnOn",
|
|
19522
|
+
method: "turnOn",
|
|
19523
|
+
label: t("Turn on", "Accendi"),
|
|
19524
|
+
phrase: t("Turn on {device}", "Accendi {device}"),
|
|
19525
|
+
icon: "power"
|
|
19526
|
+
},
|
|
19527
|
+
{
|
|
19528
|
+
id: "media.turnOff",
|
|
19529
|
+
method: "turnOff",
|
|
19530
|
+
label: t("Turn off", "Spegni"),
|
|
19531
|
+
phrase: t("Turn off {device}", "Spegni {device}"),
|
|
19532
|
+
icon: "power"
|
|
19533
|
+
},
|
|
19534
|
+
{
|
|
19535
|
+
id: "media.volumeUp",
|
|
19536
|
+
method: "volumeUp",
|
|
19537
|
+
label: t("Volume up", "Alza il volume"),
|
|
19538
|
+
phrase: t("Raise the volume of {device}", "Alza il volume di {device}"),
|
|
19539
|
+
icon: "volume-2"
|
|
19540
|
+
},
|
|
19541
|
+
{
|
|
19542
|
+
id: "media.volumeDown",
|
|
19543
|
+
method: "volumeDown",
|
|
19544
|
+
label: t("Volume down", "Abbassa il volume"),
|
|
19545
|
+
phrase: t("Lower the volume of {device}", "Abbassa il volume di {device}"),
|
|
19546
|
+
icon: "volume-2"
|
|
19547
|
+
},
|
|
19548
|
+
{
|
|
19549
|
+
id: "media.channelUp",
|
|
19550
|
+
method: "channelUp",
|
|
19551
|
+
label: t("Channel up", "Canale successivo"),
|
|
19552
|
+
phrase: t("Go to the next channel on {device}", "Passa al canale successivo su {device}"),
|
|
19553
|
+
icon: "skip-forward"
|
|
19554
|
+
},
|
|
19555
|
+
{
|
|
19556
|
+
id: "media.channelDown",
|
|
19557
|
+
method: "channelDown",
|
|
19558
|
+
label: t("Channel down", "Canale precedente"),
|
|
19559
|
+
phrase: t("Go to the previous channel on {device}", "Torna al canale precedente su {device}"),
|
|
19560
|
+
icon: "skip-back"
|
|
19561
|
+
},
|
|
19346
19562
|
{
|
|
19347
19563
|
id: "media.play",
|
|
19348
19564
|
method: "play",
|
|
@@ -21229,6 +21445,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
21229
21445
|
"audio-analyzer": system(PIPELINE),
|
|
21230
21446
|
"audio-codec": system(INTERNAL),
|
|
21231
21447
|
"auth-provider": system(IDENTITY),
|
|
21448
|
+
"board-key-source": system(INTERNAL),
|
|
21232
21449
|
backup: system("system backups — run and restored from the Backups page"),
|
|
21233
21450
|
broker: system("message-broker connections — configured, not actuated"),
|
|
21234
21451
|
"calendar-source": system(INTERNAL),
|
|
@@ -45691,6 +45908,49 @@ object({
|
|
|
45691
45908
|
lastChangedAt: number()
|
|
45692
45909
|
});
|
|
45693
45910
|
DeviceType.Sensor;
|
|
45911
|
+
/**
|
|
45912
|
+
* Why a board key could not be read. Typed so a caller reports the cause and
|
|
45913
|
+
* never treats "could not read" as "has no key" (D49).
|
|
45914
|
+
*/
|
|
45915
|
+
var BoardKeyFailureReasonSchema = _enum([
|
|
45916
|
+
"unknown-source",
|
|
45917
|
+
"not-admin",
|
|
45918
|
+
"no-builder",
|
|
45919
|
+
"no-ingress-url",
|
|
45920
|
+
"unreachable",
|
|
45921
|
+
"unknown-board",
|
|
45922
|
+
"no-encryption-key",
|
|
45923
|
+
"unreadable-config"
|
|
45924
|
+
]);
|
|
45925
|
+
var BoardKeySourceSchema = object({
|
|
45926
|
+
sourceId: string(),
|
|
45927
|
+
name: string()
|
|
45928
|
+
});
|
|
45929
|
+
var BoardKeyBoardSchema = object({
|
|
45930
|
+
/** The builder's file name, e.g. `kitchen.yaml`: the handle `getBoardKey` takes. */
|
|
45931
|
+
configuration: string(),
|
|
45932
|
+
/** The node name the builder gives the board. */
|
|
45933
|
+
name: string(),
|
|
45934
|
+
/** The address the builder knows the board by, when it has one. */
|
|
45935
|
+
address: string().nullable()
|
|
45936
|
+
});
|
|
45937
|
+
var BoardKeyFailureSchema = object({
|
|
45938
|
+
ok: literal(false),
|
|
45939
|
+
reason: BoardKeyFailureReasonSchema,
|
|
45940
|
+
detail: string().optional()
|
|
45941
|
+
});
|
|
45942
|
+
var ListBoardsResultSchema = discriminatedUnion("ok", [object({
|
|
45943
|
+
ok: literal(true),
|
|
45944
|
+
boards: array(BoardKeyBoardSchema).readonly()
|
|
45945
|
+
}), BoardKeyFailureSchema]);
|
|
45946
|
+
var GetBoardKeyResultSchema = discriminatedUnion("ok", [object({
|
|
45947
|
+
ok: literal(true),
|
|
45948
|
+
key: string()
|
|
45949
|
+
}), BoardKeyFailureSchema]);
|
|
45950
|
+
method(_void(), array(BoardKeySourceSchema).readonly(), { auth: "admin" }), method(object({ sourceId: string().min(1) }), ListBoardsResultSchema, { auth: "admin" }), method(object({
|
|
45951
|
+
sourceId: string().min(1),
|
|
45952
|
+
configuration: string().min(1)
|
|
45953
|
+
}), GetBoardKeyResultSchema, { auth: "admin" });
|
|
45694
45954
|
object({
|
|
45695
45955
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
45696
45956
|
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
@@ -47827,6 +48087,188 @@ DeviceType.Control, method(object({
|
|
|
47827
48087
|
kind: "mutation",
|
|
47828
48088
|
auth: "protected"
|
|
47829
48089
|
});
|
|
48090
|
+
/**
|
|
48091
|
+
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
48092
|
+
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
48093
|
+
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
48094
|
+
* surface — those use `DeviceType.Camera` + the broker pipeline.
|
|
48095
|
+
*
|
|
48096
|
+
* The cap declares a wide method surface; not every backend supports
|
|
48097
|
+
* every method. Drivers signal sub-capability via the
|
|
48098
|
+
* `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
|
|
48099
|
+
* corresponding method) so the UI gates buttons without a slice
|
|
48100
|
+
* fetch — same pattern as `PanTiltZoom` for cameras.
|
|
48101
|
+
*/
|
|
48102
|
+
var MediaPlayerStateSchema = _enum([
|
|
48103
|
+
"off",
|
|
48104
|
+
"on",
|
|
48105
|
+
"idle",
|
|
48106
|
+
"playing",
|
|
48107
|
+
"paused",
|
|
48108
|
+
"buffering",
|
|
48109
|
+
"standby"
|
|
48110
|
+
]);
|
|
48111
|
+
var MediaPlayerRepeatSchema = _enum([
|
|
48112
|
+
"off",
|
|
48113
|
+
"all",
|
|
48114
|
+
"one"
|
|
48115
|
+
]);
|
|
48116
|
+
var MediaInfoSchema = object({
|
|
48117
|
+
/** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
|
|
48118
|
+
* `podcast`, …). */
|
|
48119
|
+
type: string(),
|
|
48120
|
+
/** Human-readable title. */
|
|
48121
|
+
title: string(),
|
|
48122
|
+
/** Optional artist / channel / station label. */
|
|
48123
|
+
artist: string().optional(),
|
|
48124
|
+
/** Optional album / season / show name. */
|
|
48125
|
+
album: string().optional(),
|
|
48126
|
+
/** Optional cover-art / thumbnail URL. */
|
|
48127
|
+
imageUrl: string().optional()
|
|
48128
|
+
});
|
|
48129
|
+
/**
|
|
48130
|
+
* Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
|
|
48131
|
+
* the device said so. `assumed`: the driver has no feedback (infrared) and
|
|
48132
|
+
* tracks what it last SENT; it moves only after a send was accepted, and a
|
|
48133
|
+
* human at the real remote makes it wrong. A UI shows an assumed value as a
|
|
48134
|
+
* guess, never as a reading.
|
|
48135
|
+
*/
|
|
48136
|
+
var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
|
|
48137
|
+
/** The fields of a media-player status a driver can only guess. */
|
|
48138
|
+
var MediaPlayerAssumedFieldSchema = _enum([
|
|
48139
|
+
"state",
|
|
48140
|
+
"volumeLevel",
|
|
48141
|
+
"isMuted",
|
|
48142
|
+
"source"
|
|
48143
|
+
]);
|
|
48144
|
+
var MediaPlayerStatusSchema = object({
|
|
48145
|
+
/** Playback lifecycle state. */
|
|
48146
|
+
state: MediaPlayerStateSchema,
|
|
48147
|
+
/**
|
|
48148
|
+
* Whether the state fields are readings or the driver's own bookkeeping.
|
|
48149
|
+
* A slice written before this field existed parses as `reported`.
|
|
48150
|
+
*/
|
|
48151
|
+
stateSource: MediaPlayerStateSourceSchema.default("reported"),
|
|
48152
|
+
/**
|
|
48153
|
+
* Which fields are the driver's own guess. `stateSource` is `assumed` when
|
|
48154
|
+
* this is not empty; a field not listed here is a reading even then (power
|
|
48155
|
+
* from a running-state source, mute guessed from the last send).
|
|
48156
|
+
*/
|
|
48157
|
+
assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
|
|
48158
|
+
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
48159
|
+
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
48160
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
48161
|
+
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
48162
|
+
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
48163
|
+
isMuted: boolean().nullable(),
|
|
48164
|
+
/** Active source / input. Empty when no source surface. */
|
|
48165
|
+
source: string(),
|
|
48166
|
+
/** Selectable sources. Empty when no source surface.
|
|
48167
|
+
* Pair with `DeviceFeature.MediaPlayerSelectSource`. */
|
|
48168
|
+
availableSources: array(string()),
|
|
48169
|
+
/** Currently-playing media info. Null when nothing is playing. */
|
|
48170
|
+
currentMedia: MediaInfoSchema.nullable(),
|
|
48171
|
+
/** Current playback position in ms. Null when not seekable or
|
|
48172
|
+
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
48173
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
48174
|
+
/** Total duration of the current media in ms. Null when unknown
|
|
48175
|
+
* (live stream) or nothing is playing. */
|
|
48176
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
48177
|
+
/** Shuffle toggle. Null when no shuffle surface.
|
|
48178
|
+
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
48179
|
+
shuffle: boolean().nullable(),
|
|
48180
|
+
/** Repeat mode. Null when no repeat surface.
|
|
48181
|
+
* Pair with `DeviceFeature.MediaPlayerRepeat`. */
|
|
48182
|
+
repeat: MediaPlayerRepeatSchema.nullable(),
|
|
48183
|
+
/** Ms epoch when the slice was last updated. */
|
|
48184
|
+
lastChangedAt: number()
|
|
48185
|
+
});
|
|
48186
|
+
var stepInput = object({
|
|
48187
|
+
deviceId: number().int().nonnegative(),
|
|
48188
|
+
/** How many presses: the caller's count (Alexa `volumeSteps`, `channelCount`). */
|
|
48189
|
+
steps: number().int().min(1).max(50).default(1)
|
|
48190
|
+
});
|
|
48191
|
+
var deviceOnly = object({ deviceId: number().int().nonnegative() });
|
|
48192
|
+
DeviceType.MediaPlayer, method(deviceOnly, _void(), {
|
|
48193
|
+
kind: "mutation",
|
|
48194
|
+
auth: "protected"
|
|
48195
|
+
}), method(deviceOnly, _void(), {
|
|
48196
|
+
kind: "mutation",
|
|
48197
|
+
auth: "protected"
|
|
48198
|
+
}), method(stepInput, _void(), {
|
|
48199
|
+
kind: "mutation",
|
|
48200
|
+
auth: "protected"
|
|
48201
|
+
}), method(stepInput, _void(), {
|
|
48202
|
+
kind: "mutation",
|
|
48203
|
+
auth: "protected"
|
|
48204
|
+
}), method(stepInput, _void(), {
|
|
48205
|
+
kind: "mutation",
|
|
48206
|
+
auth: "protected"
|
|
48207
|
+
}), method(stepInput, _void(), {
|
|
48208
|
+
kind: "mutation",
|
|
48209
|
+
auth: "protected"
|
|
48210
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48211
|
+
kind: "mutation",
|
|
48212
|
+
auth: "protected"
|
|
48213
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48214
|
+
kind: "mutation",
|
|
48215
|
+
auth: "protected"
|
|
48216
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48217
|
+
kind: "mutation",
|
|
48218
|
+
auth: "protected"
|
|
48219
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48220
|
+
kind: "mutation",
|
|
48221
|
+
auth: "protected"
|
|
48222
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48223
|
+
kind: "mutation",
|
|
48224
|
+
auth: "protected"
|
|
48225
|
+
}), method(object({
|
|
48226
|
+
deviceId: number().int().nonnegative(),
|
|
48227
|
+
position: number().int().nonnegative()
|
|
48228
|
+
}), _void(), {
|
|
48229
|
+
kind: "mutation",
|
|
48230
|
+
auth: "protected"
|
|
48231
|
+
}), method(object({
|
|
48232
|
+
deviceId: number().int().nonnegative(),
|
|
48233
|
+
volumeLevel: number().min(0).max(100)
|
|
48234
|
+
}), _void(), {
|
|
48235
|
+
kind: "mutation",
|
|
48236
|
+
auth: "protected"
|
|
48237
|
+
}), method(object({
|
|
48238
|
+
deviceId: number().int().nonnegative(),
|
|
48239
|
+
muted: boolean()
|
|
48240
|
+
}), _void(), {
|
|
48241
|
+
kind: "mutation",
|
|
48242
|
+
auth: "protected"
|
|
48243
|
+
}), method(object({
|
|
48244
|
+
deviceId: number().int().nonnegative(),
|
|
48245
|
+
shuffle: boolean()
|
|
48246
|
+
}), _void(), {
|
|
48247
|
+
kind: "mutation",
|
|
48248
|
+
auth: "protected"
|
|
48249
|
+
}), method(object({
|
|
48250
|
+
deviceId: number().int().nonnegative(),
|
|
48251
|
+
repeat: MediaPlayerRepeatSchema
|
|
48252
|
+
}), _void(), {
|
|
48253
|
+
kind: "mutation",
|
|
48254
|
+
auth: "protected"
|
|
48255
|
+
}), method(object({
|
|
48256
|
+
deviceId: number().int().nonnegative(),
|
|
48257
|
+
source: string().min(1)
|
|
48258
|
+
}), _void(), {
|
|
48259
|
+
kind: "mutation",
|
|
48260
|
+
auth: "protected"
|
|
48261
|
+
}), method(object({
|
|
48262
|
+
deviceId: number().int().nonnegative(),
|
|
48263
|
+
/** Media identifier / URL. */
|
|
48264
|
+
mediaId: string().min(1),
|
|
48265
|
+
/** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
|
|
48266
|
+
* passes it through to the upstream service. */
|
|
48267
|
+
mediaType: string().min(1)
|
|
48268
|
+
}), _void(), {
|
|
48269
|
+
kind: "mutation",
|
|
48270
|
+
auth: "protected"
|
|
48271
|
+
});
|
|
47830
48272
|
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;
|
|
47831
48273
|
var NUMBER = {
|
|
47832
48274
|
kind: "fixed",
|
|
@@ -51412,7 +51854,7 @@ object({
|
|
|
51412
51854
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
51413
51855
|
lastFetchedAt: number()
|
|
51414
51856
|
});
|
|
51415
|
-
var DeviceIdSchema$
|
|
51857
|
+
var DeviceIdSchema$2 = number().int().nonnegative();
|
|
51416
51858
|
/** A value a program option or setting can take. */
|
|
51417
51859
|
var DishwasherValueSchema = union([
|
|
51418
51860
|
boolean(),
|
|
@@ -51476,77 +51918,77 @@ var DishwasherOptionsSchema = object({
|
|
|
51476
51918
|
options: array(DishwasherOptionDescriptorSchema)
|
|
51477
51919
|
});
|
|
51478
51920
|
DeviceType.Dishwasher, method(object({
|
|
51479
|
-
deviceId: DeviceIdSchema$
|
|
51921
|
+
deviceId: DeviceIdSchema$2,
|
|
51480
51922
|
program: string().min(1).optional(),
|
|
51481
51923
|
options: record(string(), DishwasherValueSchema).optional()
|
|
51482
51924
|
}), _void(), {
|
|
51483
51925
|
kind: "mutation",
|
|
51484
51926
|
auth: "protected"
|
|
51485
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
51927
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), _void(), {
|
|
51486
51928
|
kind: "mutation",
|
|
51487
51929
|
auth: "protected"
|
|
51488
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
51930
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), _void(), {
|
|
51489
51931
|
kind: "mutation",
|
|
51490
51932
|
auth: "protected"
|
|
51491
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
51933
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), _void(), {
|
|
51492
51934
|
kind: "mutation",
|
|
51493
51935
|
auth: "protected"
|
|
51494
51936
|
}), method(object({
|
|
51495
|
-
deviceId: DeviceIdSchema$
|
|
51937
|
+
deviceId: DeviceIdSchema$2,
|
|
51496
51938
|
program: string().min(1)
|
|
51497
51939
|
}), _void(), {
|
|
51498
51940
|
kind: "mutation",
|
|
51499
51941
|
auth: "protected"
|
|
51500
51942
|
}), method(object({
|
|
51501
|
-
deviceId: DeviceIdSchema$
|
|
51943
|
+
deviceId: DeviceIdSchema$2,
|
|
51502
51944
|
on: boolean()
|
|
51503
51945
|
}), _void(), {
|
|
51504
51946
|
kind: "mutation",
|
|
51505
51947
|
auth: "protected"
|
|
51506
51948
|
}), method(object({
|
|
51507
|
-
deviceId: DeviceIdSchema$
|
|
51949
|
+
deviceId: DeviceIdSchema$2,
|
|
51508
51950
|
on: boolean()
|
|
51509
51951
|
}), _void(), {
|
|
51510
51952
|
kind: "mutation",
|
|
51511
51953
|
auth: "protected"
|
|
51512
51954
|
}), method(object({
|
|
51513
|
-
deviceId: DeviceIdSchema$
|
|
51955
|
+
deviceId: DeviceIdSchema$2,
|
|
51514
51956
|
on: boolean()
|
|
51515
51957
|
}), _void(), {
|
|
51516
51958
|
kind: "mutation",
|
|
51517
51959
|
auth: "protected"
|
|
51518
51960
|
}), method(object({
|
|
51519
|
-
deviceId: DeviceIdSchema$
|
|
51961
|
+
deviceId: DeviceIdSchema$2,
|
|
51520
51962
|
on: boolean()
|
|
51521
51963
|
}), _void(), {
|
|
51522
51964
|
kind: "mutation",
|
|
51523
51965
|
auth: "protected"
|
|
51524
51966
|
}), method(object({
|
|
51525
|
-
deviceId: DeviceIdSchema$
|
|
51967
|
+
deviceId: DeviceIdSchema$2,
|
|
51526
51968
|
on: boolean()
|
|
51527
51969
|
}), _void(), {
|
|
51528
51970
|
kind: "mutation",
|
|
51529
51971
|
auth: "protected"
|
|
51530
51972
|
}), method(object({
|
|
51531
|
-
deviceId: DeviceIdSchema$
|
|
51973
|
+
deviceId: DeviceIdSchema$2,
|
|
51532
51974
|
on: boolean()
|
|
51533
51975
|
}), _void(), {
|
|
51534
51976
|
kind: "mutation",
|
|
51535
51977
|
auth: "protected"
|
|
51536
51978
|
}), method(object({
|
|
51537
|
-
deviceId: DeviceIdSchema$
|
|
51979
|
+
deviceId: DeviceIdSchema$2,
|
|
51538
51980
|
on: boolean()
|
|
51539
51981
|
}), _void(), {
|
|
51540
51982
|
kind: "mutation",
|
|
51541
51983
|
auth: "protected"
|
|
51542
51984
|
}), method(object({
|
|
51543
|
-
deviceId: DeviceIdSchema$
|
|
51985
|
+
deviceId: DeviceIdSchema$2,
|
|
51544
51986
|
key: string().min(1),
|
|
51545
51987
|
value: DishwasherValueSchema
|
|
51546
51988
|
}), _void(), {
|
|
51547
51989
|
kind: "mutation",
|
|
51548
51990
|
auth: "protected"
|
|
51549
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
51991
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), DishwasherOptionsSchema, { auth: "protected" });
|
|
51550
51992
|
/**
|
|
51551
51993
|
* Palette entry ids (D939). Audio is the one gradient; `audio-*` leaves are
|
|
51552
51994
|
* not entries. `heatmapScrub` is a mark, not an event kind.
|
|
@@ -53287,6 +53729,11 @@ var IntegrationInstanceSchema = object({
|
|
|
53287
53729
|
name: string(),
|
|
53288
53730
|
/** The addon's own rule (an URL is present, a client id is present...). */
|
|
53289
53731
|
configured: boolean(),
|
|
53732
|
+
/**
|
|
53733
|
+
* The instance's own on/off switch (D996): always present, derived from the record body
|
|
53734
|
+
* (`enabled !== false`, so a row written before the field existed is on).
|
|
53735
|
+
*/
|
|
53736
|
+
enabled: boolean(),
|
|
53290
53737
|
/** Values for the instance form; a secret is a `SecretFieldView`, never text. */
|
|
53291
53738
|
formValues: record(string(), unknown()),
|
|
53292
53739
|
status: IntegrationInstanceStatusSchema.optional()
|
|
@@ -53306,6 +53753,12 @@ method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method
|
|
|
53306
53753
|
}), method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), {
|
|
53307
53754
|
kind: "mutation",
|
|
53308
53755
|
auth: "admin"
|
|
53756
|
+
}), method(object({
|
|
53757
|
+
id: IntegrationInstanceIdSchema,
|
|
53758
|
+
enabled: boolean()
|
|
53759
|
+
}), object({ ok: literal(true) }), {
|
|
53760
|
+
kind: "mutation",
|
|
53761
|
+
auth: "admin"
|
|
53309
53762
|
}), method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), {
|
|
53310
53763
|
kind: "mutation",
|
|
53311
53764
|
auth: "admin"
|
|
@@ -55298,164 +55751,6 @@ var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
|
55298
55751
|
]);
|
|
55299
55752
|
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
55300
55753
|
/**
|
|
55301
|
-
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
55302
|
-
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
55303
|
-
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
55304
|
-
* surface — those use `DeviceType.Camera` + the broker pipeline.
|
|
55305
|
-
*
|
|
55306
|
-
* The cap declares a wide method surface; not every backend supports
|
|
55307
|
-
* every method. Drivers signal sub-capability via the
|
|
55308
|
-
* `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
|
|
55309
|
-
* corresponding method) so the UI gates buttons without a slice
|
|
55310
|
-
* fetch — same pattern as `PanTiltZoom` for cameras.
|
|
55311
|
-
*/
|
|
55312
|
-
var MediaPlayerStateSchema = _enum([
|
|
55313
|
-
"off",
|
|
55314
|
-
"on",
|
|
55315
|
-
"idle",
|
|
55316
|
-
"playing",
|
|
55317
|
-
"paused",
|
|
55318
|
-
"buffering",
|
|
55319
|
-
"standby"
|
|
55320
|
-
]);
|
|
55321
|
-
var MediaPlayerRepeatSchema = _enum([
|
|
55322
|
-
"off",
|
|
55323
|
-
"all",
|
|
55324
|
-
"one"
|
|
55325
|
-
]);
|
|
55326
|
-
var MediaInfoSchema = object({
|
|
55327
|
-
/** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
|
|
55328
|
-
* `podcast`, …). */
|
|
55329
|
-
type: string(),
|
|
55330
|
-
/** Human-readable title. */
|
|
55331
|
-
title: string(),
|
|
55332
|
-
/** Optional artist / channel / station label. */
|
|
55333
|
-
artist: string().optional(),
|
|
55334
|
-
/** Optional album / season / show name. */
|
|
55335
|
-
album: string().optional(),
|
|
55336
|
-
/** Optional cover-art / thumbnail URL. */
|
|
55337
|
-
imageUrl: string().optional()
|
|
55338
|
-
});
|
|
55339
|
-
/**
|
|
55340
|
-
* Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
|
|
55341
|
-
* the device said so. `assumed`: the driver has no feedback (infrared) and
|
|
55342
|
-
* tracks what it last SENT; it moves only after a send was accepted, and a
|
|
55343
|
-
* human at the real remote makes it wrong. A UI shows an assumed value as a
|
|
55344
|
-
* guess, never as a reading.
|
|
55345
|
-
*/
|
|
55346
|
-
var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
|
|
55347
|
-
/** The fields of a media-player status a driver can only guess. */
|
|
55348
|
-
var MediaPlayerAssumedFieldSchema = _enum([
|
|
55349
|
-
"state",
|
|
55350
|
-
"volumeLevel",
|
|
55351
|
-
"isMuted",
|
|
55352
|
-
"source"
|
|
55353
|
-
]);
|
|
55354
|
-
object({
|
|
55355
|
-
/** Playback lifecycle state. */
|
|
55356
|
-
state: MediaPlayerStateSchema,
|
|
55357
|
-
/**
|
|
55358
|
-
* Whether the state fields are readings or the driver's own bookkeeping.
|
|
55359
|
-
* A slice written before this field existed parses as `reported`.
|
|
55360
|
-
*/
|
|
55361
|
-
stateSource: MediaPlayerStateSourceSchema.default("reported"),
|
|
55362
|
-
/**
|
|
55363
|
-
* Which fields are the driver's own guess. `stateSource` is `assumed` when
|
|
55364
|
-
* this is not empty; a field not listed here is a reading even then (power
|
|
55365
|
-
* from a running-state source, mute guessed from the last send).
|
|
55366
|
-
*/
|
|
55367
|
-
assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
|
|
55368
|
-
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
55369
|
-
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
55370
|
-
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
55371
|
-
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
55372
|
-
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
55373
|
-
isMuted: boolean().nullable(),
|
|
55374
|
-
/** Active source / input. Empty when no source surface. */
|
|
55375
|
-
source: string(),
|
|
55376
|
-
/** Selectable sources. Empty when no source surface.
|
|
55377
|
-
* Pair with `DeviceFeature.MediaPlayerSelectSource`. */
|
|
55378
|
-
availableSources: array(string()),
|
|
55379
|
-
/** Currently-playing media info. Null when nothing is playing. */
|
|
55380
|
-
currentMedia: MediaInfoSchema.nullable(),
|
|
55381
|
-
/** Current playback position in ms. Null when not seekable or
|
|
55382
|
-
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
55383
|
-
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
55384
|
-
/** Total duration of the current media in ms. Null when unknown
|
|
55385
|
-
* (live stream) or nothing is playing. */
|
|
55386
|
-
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
55387
|
-
/** Shuffle toggle. Null when no shuffle surface.
|
|
55388
|
-
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
55389
|
-
shuffle: boolean().nullable(),
|
|
55390
|
-
/** Repeat mode. Null when no repeat surface.
|
|
55391
|
-
* Pair with `DeviceFeature.MediaPlayerRepeat`. */
|
|
55392
|
-
repeat: MediaPlayerRepeatSchema.nullable(),
|
|
55393
|
-
/** Ms epoch when the slice was last updated. */
|
|
55394
|
-
lastChangedAt: number()
|
|
55395
|
-
});
|
|
55396
|
-
DeviceType.MediaPlayer, method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55397
|
-
kind: "mutation",
|
|
55398
|
-
auth: "protected"
|
|
55399
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55400
|
-
kind: "mutation",
|
|
55401
|
-
auth: "protected"
|
|
55402
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55403
|
-
kind: "mutation",
|
|
55404
|
-
auth: "protected"
|
|
55405
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55406
|
-
kind: "mutation",
|
|
55407
|
-
auth: "protected"
|
|
55408
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55409
|
-
kind: "mutation",
|
|
55410
|
-
auth: "protected"
|
|
55411
|
-
}), method(object({
|
|
55412
|
-
deviceId: number().int().nonnegative(),
|
|
55413
|
-
position: number().int().nonnegative()
|
|
55414
|
-
}), _void(), {
|
|
55415
|
-
kind: "mutation",
|
|
55416
|
-
auth: "protected"
|
|
55417
|
-
}), method(object({
|
|
55418
|
-
deviceId: number().int().nonnegative(),
|
|
55419
|
-
volumeLevel: number().min(0).max(100)
|
|
55420
|
-
}), _void(), {
|
|
55421
|
-
kind: "mutation",
|
|
55422
|
-
auth: "protected"
|
|
55423
|
-
}), method(object({
|
|
55424
|
-
deviceId: number().int().nonnegative(),
|
|
55425
|
-
muted: boolean()
|
|
55426
|
-
}), _void(), {
|
|
55427
|
-
kind: "mutation",
|
|
55428
|
-
auth: "protected"
|
|
55429
|
-
}), method(object({
|
|
55430
|
-
deviceId: number().int().nonnegative(),
|
|
55431
|
-
shuffle: boolean()
|
|
55432
|
-
}), _void(), {
|
|
55433
|
-
kind: "mutation",
|
|
55434
|
-
auth: "protected"
|
|
55435
|
-
}), method(object({
|
|
55436
|
-
deviceId: number().int().nonnegative(),
|
|
55437
|
-
repeat: MediaPlayerRepeatSchema
|
|
55438
|
-
}), _void(), {
|
|
55439
|
-
kind: "mutation",
|
|
55440
|
-
auth: "protected"
|
|
55441
|
-
}), method(object({
|
|
55442
|
-
deviceId: number().int().nonnegative(),
|
|
55443
|
-
source: string().min(1)
|
|
55444
|
-
}), _void(), {
|
|
55445
|
-
kind: "mutation",
|
|
55446
|
-
auth: "protected"
|
|
55447
|
-
}), method(object({
|
|
55448
|
-
deviceId: number().int().nonnegative(),
|
|
55449
|
-
/** Media identifier / URL. */
|
|
55450
|
-
mediaId: string().min(1),
|
|
55451
|
-
/** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
|
|
55452
|
-
* passes it through to the upstream service. */
|
|
55453
|
-
mediaType: string().min(1)
|
|
55454
|
-
}), _void(), {
|
|
55455
|
-
kind: "mutation",
|
|
55456
|
-
auth: "protected"
|
|
55457
|
-
});
|
|
55458
|
-
/**
|
|
55459
55754
|
* mesh-network — collection cap for mesh-VPN providers.
|
|
55460
55755
|
*
|
|
55461
55756
|
* Different shape from `network-access` (public tunnels):
|
|
@@ -63349,7 +63644,14 @@ var NcMediaPolicySchema = object({
|
|
|
63349
63644
|
*/
|
|
63350
63645
|
profile: CamProfileSchema.optional(),
|
|
63351
63646
|
/** See {@link NcLinkedCameraModeSchema}. */
|
|
63352
|
-
linkedCameras: NcLinkedCameraModeSchema.optional()
|
|
63647
|
+
linkedCameras: NcLinkedCameraModeSchema.optional(),
|
|
63648
|
+
/**
|
|
63649
|
+
* Sensor rules only: may this rule wake a SLEEPING battery camera to take
|
|
63650
|
+
* the linked still it attaches (D998)? Absent means yes, an explicit `false`
|
|
63651
|
+
* opts out. Deliberately not a Zod default: a rule that never chose keeps
|
|
63652
|
+
* following the hub's. One bounded wake per event; nothing is attached later.
|
|
63653
|
+
*/
|
|
63654
|
+
wakeSleepingCamera: boolean().optional()
|
|
63353
63655
|
});
|
|
63354
63656
|
/**
|
|
63355
63657
|
* Cooldown GRANULARITY over the subject's class — how much a fired
|
|
@@ -63741,6 +64043,21 @@ var NcTemplateVarKindSchema = _enum([
|
|
|
63741
64043
|
"sound",
|
|
63742
64044
|
"system"
|
|
63743
64045
|
]);
|
|
64046
|
+
/**
|
|
64047
|
+
* The default title/body a rule of one SITUATION is delivered with when the
|
|
64048
|
+
* operator wrote no template — served so an editor can pre-fill it (and the
|
|
64049
|
+
* operator can rebuild it from placeholders) instead of starting from a blank
|
|
64050
|
+
* box that, once saved, silently replaces it. `id` names the situation inside
|
|
64051
|
+
* a rule `kind`: `appliance-cycle` is a `sensor` rule whose device is an
|
|
64052
|
+
* appliance finishing a cycle. Texts are raw templates (`{{var}}` intact), in
|
|
64053
|
+
* the hub's current language and with its overrides applied.
|
|
64054
|
+
*/
|
|
64055
|
+
var NcTemplateDefaultSchema = object({
|
|
64056
|
+
id: _enum(["appliance-cycle"]),
|
|
64057
|
+
kind: NcRuleKindSchema,
|
|
64058
|
+
title: string(),
|
|
64059
|
+
body: string()
|
|
64060
|
+
});
|
|
63744
64061
|
/** Which producer renders a template: an ordinary rule, a timelapse, a digest. */
|
|
63745
64062
|
var NcTemplateFamilySchema = _enum([
|
|
63746
64063
|
"rule",
|
|
@@ -64596,7 +64913,11 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
64596
64913
|
}), method(object({}), object({
|
|
64597
64914
|
catalog: array(NcConditionDescriptorSchema),
|
|
64598
64915
|
taxonomy: NcTaxonomySchema.optional()
|
|
64599
|
-
})), method(object({}), object({
|
|
64916
|
+
})), method(object({}), object({
|
|
64917
|
+
vars: array(NcTemplateVarDescriptorSchema),
|
|
64918
|
+
/** Per-situation default texts; absent on a hub that predates them. */
|
|
64919
|
+
defaults: array(NcTemplateDefaultSchema)
|
|
64920
|
+
})), method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
|
|
64600
64921
|
kind: "mutation",
|
|
64601
64922
|
access: "view"
|
|
64602
64923
|
}), 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 }), {
|
|
@@ -65344,52 +65665,27 @@ object({
|
|
|
65344
65665
|
empty: boolean().nullable(),
|
|
65345
65666
|
/** True while the pump is running. Null while unknown. */
|
|
65346
65667
|
pumpRunning: boolean().nullable(),
|
|
65347
|
-
/** True when the pump works on an on/off duty cycle rather than continuously. */
|
|
65348
|
-
pumpIntervalEnabled: boolean().nullable(),
|
|
65349
|
-
/** How long the pump runs per cycle, in seconds. */
|
|
65350
|
-
pumpOnTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
65351
|
-
/** How long the pump rests per cycle, in seconds. */
|
|
65352
|
-
pumpOffTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
65353
65668
|
/** Water drunk in the last drink, in litres. */
|
|
65354
65669
|
lastDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65355
65670
|
/** Water drunk in the current hour, in litres. */
|
|
65356
65671
|
currentHourDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65357
65672
|
/** Water drunk over the last 24 hours (rolling), in litres. */
|
|
65358
65673
|
last24hDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65359
|
-
/** Water drunk since local midnight, in litres. */
|
|
65674
|
+
/** Water drunk since local midnight, in litres. Null when the fountain has no such sensor. */
|
|
65360
65675
|
todayDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65361
65676
|
/** Decoded fault message. Null means healthy; a non-null string is an active fault. */
|
|
65362
65677
|
error: string().nullable(),
|
|
65363
65678
|
/** Ms epoch when the slice was last refreshed. */
|
|
65364
65679
|
lastFetchedAt: number()
|
|
65365
65680
|
});
|
|
65366
|
-
var DeviceIdSchema = number().int().nonnegative();
|
|
65367
|
-
var MinutesSchema = number().int().min(0);
|
|
65681
|
+
var DeviceIdSchema$1 = number().int().nonnegative();
|
|
65368
65682
|
DeviceType.PetFountain, method(object({
|
|
65369
|
-
deviceId: DeviceIdSchema,
|
|
65370
|
-
on: boolean()
|
|
65371
|
-
}), _void(), {
|
|
65372
|
-
kind: "mutation",
|
|
65373
|
-
auth: "protected"
|
|
65374
|
-
}), method(object({
|
|
65375
|
-
deviceId: DeviceIdSchema,
|
|
65683
|
+
deviceId: DeviceIdSchema$1,
|
|
65376
65684
|
on: boolean()
|
|
65377
65685
|
}), _void(), {
|
|
65378
65686
|
kind: "mutation",
|
|
65379
65687
|
auth: "protected"
|
|
65380
|
-
}), method(object({
|
|
65381
|
-
deviceId: DeviceIdSchema,
|
|
65382
|
-
minutes: MinutesSchema
|
|
65383
|
-
}), _void(), {
|
|
65384
|
-
kind: "mutation",
|
|
65385
|
-
auth: "protected"
|
|
65386
|
-
}), method(object({
|
|
65387
|
-
deviceId: DeviceIdSchema,
|
|
65388
|
-
minutes: MinutesSchema
|
|
65389
|
-
}), _void(), {
|
|
65390
|
-
kind: "mutation",
|
|
65391
|
-
auth: "protected"
|
|
65392
|
-
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
65688
|
+
}), method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
65393
65689
|
kind: "mutation",
|
|
65394
65690
|
auth: "protected"
|
|
65395
65691
|
});
|
|
@@ -67349,6 +67645,37 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
|
|
|
67349
67645
|
deviceId: number(),
|
|
67350
67646
|
status: PtzAutotrackStatusSchema
|
|
67351
67647
|
});
|
|
67648
|
+
object({
|
|
67649
|
+
/** True when the pump works on the on/off cycle, false when it runs continuously. Null while unknown. */
|
|
67650
|
+
enabled: boolean().nullable(),
|
|
67651
|
+
/** How long the pump runs per cycle, in seconds. Null while unknown. */
|
|
67652
|
+
onTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
67653
|
+
/** How long the pump rests per cycle, in seconds. Null while unknown. */
|
|
67654
|
+
offTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
67655
|
+
/** Ms epoch when the slice was last refreshed. */
|
|
67656
|
+
lastFetchedAt: number()
|
|
67657
|
+
});
|
|
67658
|
+
var DeviceIdSchema = number().int().nonnegative();
|
|
67659
|
+
var MinutesSchema = number().int().min(0);
|
|
67660
|
+
DeviceType.PetFountain, method(object({
|
|
67661
|
+
deviceId: DeviceIdSchema,
|
|
67662
|
+
on: boolean()
|
|
67663
|
+
}), _void(), {
|
|
67664
|
+
kind: "mutation",
|
|
67665
|
+
auth: "protected"
|
|
67666
|
+
}), method(object({
|
|
67667
|
+
deviceId: DeviceIdSchema,
|
|
67668
|
+
minutes: MinutesSchema
|
|
67669
|
+
}), _void(), {
|
|
67670
|
+
kind: "mutation",
|
|
67671
|
+
auth: "protected"
|
|
67672
|
+
}), method(object({
|
|
67673
|
+
deviceId: DeviceIdSchema,
|
|
67674
|
+
minutes: MinutesSchema
|
|
67675
|
+
}), _void(), {
|
|
67676
|
+
kind: "mutation",
|
|
67677
|
+
auth: "protected"
|
|
67678
|
+
});
|
|
67352
67679
|
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
67353
67680
|
kind: "mutation",
|
|
67354
67681
|
auth: "admin"
|
|
@@ -70062,7 +70389,13 @@ var RemoteMediaCommandSchema = _enum([
|
|
|
70062
70389
|
"next",
|
|
70063
70390
|
"previous",
|
|
70064
70391
|
"setMute",
|
|
70065
|
-
"selectSource"
|
|
70392
|
+
"selectSource",
|
|
70393
|
+
"turnOn",
|
|
70394
|
+
"turnOff",
|
|
70395
|
+
"volumeUp",
|
|
70396
|
+
"volumeDown",
|
|
70397
|
+
"channelUp",
|
|
70398
|
+
"channelDown"
|
|
70066
70399
|
]);
|
|
70067
70400
|
/** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
|
|
70068
70401
|
var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
|
|
@@ -70083,7 +70416,7 @@ method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.e
|
|
|
70083
70416
|
}), RemoteSendResultSchema, {
|
|
70084
70417
|
kind: "mutation",
|
|
70085
70418
|
auth: "protected"
|
|
70086
|
-
}), method(deviceInput.extend({
|
|
70419
|
+
}), method(deviceInput, MediaPlayerStatusSchema.nullable(), { auth: "protected" }), method(deviceInput.extend({
|
|
70087
70420
|
command: RemoteMediaCommandSchema,
|
|
70088
70421
|
muted: boolean().optional(),
|
|
70089
70422
|
source: string().optional(),
|
|
@@ -71111,10 +71444,12 @@ DeviceType.Camera, method(object({
|
|
|
71111
71444
|
* even when the cache is well within the device's
|
|
71112
71445
|
* `snapshotMaxAgeS` window.
|
|
71113
71446
|
*
|
|
71114
|
-
* **`force` is
|
|
71447
|
+
* **`force` is a demand with a named principal, not a freshness
|
|
71448
|
+
* preference** — an operator button, the scheduled refresh (D847), or a
|
|
71449
|
+
* notification rule that left the camera's wake allowed (D998). On a
|
|
71115
71450
|
* battery camera it is the one thing that walks past the wrapper's
|
|
71116
|
-
* sleep gate and wakes the camera, so a
|
|
71117
|
-
*
|
|
71451
|
+
* sleep gate and wakes the camera, so a poller, a thumbnail or a
|
|
71452
|
+
* prestream must NEVER set it. Every such caller
|
|
71118
71453
|
* gets the cached frame, which on a sleeping battery camera is the
|
|
71119
71454
|
* correct answer: stale but honest beats woken.
|
|
71120
71455
|
*/
|
|
@@ -71922,7 +72257,11 @@ object({
|
|
|
71922
72257
|
});
|
|
71923
72258
|
Object.values(DeviceType), method(_void(), array(StreamSignalSchema).readonly());
|
|
71924
72259
|
object({
|
|
71925
|
-
|
|
72260
|
+
/**
|
|
72261
|
+
* `null` = the device has never reported its state (a measurement that
|
|
72262
|
+
* has not happened is unknown, never `false`).
|
|
72263
|
+
*/
|
|
72264
|
+
on: boolean().nullable().meta({ label: "On" }),
|
|
71926
72265
|
/** Ms epoch of the last state change. Useful for UI "X minutes ago". */
|
|
71927
72266
|
lastChangedAt: number()
|
|
71928
72267
|
});
|
|
@@ -71934,7 +72273,7 @@ DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceTyp
|
|
|
71934
72273
|
auth: "protected"
|
|
71935
72274
|
}), object({
|
|
71936
72275
|
deviceId: number(),
|
|
71937
|
-
on: boolean(),
|
|
72276
|
+
on: boolean().nullable(),
|
|
71938
72277
|
lastChangedAt: number()
|
|
71939
72278
|
});
|
|
71940
72279
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
@@ -74946,6 +75285,7 @@ function buildDescriptor(capName, kind) {
|
|
|
74946
75285
|
* contributes one entry per bound cap present in this map.
|
|
74947
75286
|
*/
|
|
74948
75287
|
var EVENT_KIND_BY_CAP = Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
|
|
75288
|
+
DeviceFeature.MediaPlayerPower, DeviceFeature.MediaPlayerVolumeStep, DeviceFeature.MediaPlayerChannelStep, DeviceFeature.MediaPlayerMute, DeviceFeature.MediaPlayerStop, DeviceFeature.MediaPlayerNext, DeviceFeature.MediaPlayerPrevious;
|
|
74949
75289
|
var btn = (idSuffix, row, col, init) => ({
|
|
74950
75290
|
idSuffix,
|
|
74951
75291
|
row,
|
|
@@ -75072,7 +75412,7 @@ discriminatedUnion("kind", [object({
|
|
|
75072
75412
|
kind: literal("pronto"),
|
|
75073
75413
|
code: string()
|
|
75074
75414
|
})]);
|
|
75075
|
-
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
75415
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
75076
75416
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
75077
75417
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
75078
75418
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
@@ -76053,6 +76393,27 @@ Object.freeze({
|
|
|
76053
76393
|
access: "view",
|
|
76054
76394
|
role: "viewer"
|
|
76055
76395
|
},
|
|
76396
|
+
"boardKeySource.getBoardKey": {
|
|
76397
|
+
capName: "board-key-source",
|
|
76398
|
+
capScope: "system",
|
|
76399
|
+
addonId: null,
|
|
76400
|
+
access: "view",
|
|
76401
|
+
role: "admin"
|
|
76402
|
+
},
|
|
76403
|
+
"boardKeySource.listBoards": {
|
|
76404
|
+
capName: "board-key-source",
|
|
76405
|
+
capScope: "system",
|
|
76406
|
+
addonId: null,
|
|
76407
|
+
access: "view",
|
|
76408
|
+
role: "admin"
|
|
76409
|
+
},
|
|
76410
|
+
"boardKeySource.listSources": {
|
|
76411
|
+
capName: "board-key-source",
|
|
76412
|
+
capScope: "system",
|
|
76413
|
+
addonId: null,
|
|
76414
|
+
access: "view",
|
|
76415
|
+
role: "admin"
|
|
76416
|
+
},
|
|
76056
76417
|
"brightness.getStatus": {
|
|
76057
76418
|
capName: "brightness",
|
|
76058
76419
|
capScope: "device",
|
|
@@ -78734,6 +79095,13 @@ Object.freeze({
|
|
|
78734
79095
|
access: "view",
|
|
78735
79096
|
role: "admin"
|
|
78736
79097
|
},
|
|
79098
|
+
"integrationInstances.setInstanceEnabled": {
|
|
79099
|
+
capName: "integration-instances",
|
|
79100
|
+
capScope: "system",
|
|
79101
|
+
addonId: null,
|
|
79102
|
+
access: "create",
|
|
79103
|
+
role: "admin"
|
|
79104
|
+
},
|
|
78737
79105
|
"integrationInstances.testInstance": {
|
|
78738
79106
|
capName: "integration-instances",
|
|
78739
79107
|
capScope: "system",
|
|
@@ -79532,6 +79900,20 @@ Object.freeze({
|
|
|
79532
79900
|
access: "view",
|
|
79533
79901
|
role: "admin"
|
|
79534
79902
|
},
|
|
79903
|
+
"mediaPlayer.channelDown": {
|
|
79904
|
+
capName: "media-player",
|
|
79905
|
+
capScope: "device",
|
|
79906
|
+
addonId: null,
|
|
79907
|
+
access: "create",
|
|
79908
|
+
role: "viewer"
|
|
79909
|
+
},
|
|
79910
|
+
"mediaPlayer.channelUp": {
|
|
79911
|
+
capName: "media-player",
|
|
79912
|
+
capScope: "device",
|
|
79913
|
+
addonId: null,
|
|
79914
|
+
access: "create",
|
|
79915
|
+
role: "viewer"
|
|
79916
|
+
},
|
|
79535
79917
|
"mediaPlayer.getStatus": {
|
|
79536
79918
|
capName: "media-player",
|
|
79537
79919
|
capScope: "device",
|
|
@@ -79623,6 +80005,34 @@ Object.freeze({
|
|
|
79623
80005
|
access: "create",
|
|
79624
80006
|
role: "viewer"
|
|
79625
80007
|
},
|
|
80008
|
+
"mediaPlayer.turnOff": {
|
|
80009
|
+
capName: "media-player",
|
|
80010
|
+
capScope: "device",
|
|
80011
|
+
addonId: null,
|
|
80012
|
+
access: "create",
|
|
80013
|
+
role: "viewer"
|
|
80014
|
+
},
|
|
80015
|
+
"mediaPlayer.turnOn": {
|
|
80016
|
+
capName: "media-player",
|
|
80017
|
+
capScope: "device",
|
|
80018
|
+
addonId: null,
|
|
80019
|
+
access: "create",
|
|
80020
|
+
role: "viewer"
|
|
80021
|
+
},
|
|
80022
|
+
"mediaPlayer.volumeDown": {
|
|
80023
|
+
capName: "media-player",
|
|
80024
|
+
capScope: "device",
|
|
80025
|
+
addonId: null,
|
|
80026
|
+
access: "create",
|
|
80027
|
+
role: "viewer"
|
|
80028
|
+
},
|
|
80029
|
+
"mediaPlayer.volumeUp": {
|
|
80030
|
+
capName: "media-player",
|
|
80031
|
+
capScope: "device",
|
|
80032
|
+
addonId: null,
|
|
80033
|
+
access: "create",
|
|
80034
|
+
role: "viewer"
|
|
80035
|
+
},
|
|
79626
80036
|
"meshNetwork.getStatus": {
|
|
79627
80037
|
capName: "mesh-network",
|
|
79628
80038
|
capScope: "system",
|
|
@@ -80869,27 +81279,6 @@ Object.freeze({
|
|
|
80869
81279
|
access: "create",
|
|
80870
81280
|
role: "viewer"
|
|
80871
81281
|
},
|
|
80872
|
-
"petFountain.setPumpInterval": {
|
|
80873
|
-
capName: "pet-fountain",
|
|
80874
|
-
capScope: "device",
|
|
80875
|
-
addonId: null,
|
|
80876
|
-
access: "create",
|
|
80877
|
-
role: "viewer"
|
|
80878
|
-
},
|
|
80879
|
-
"petFountain.setPumpOffMinutes": {
|
|
80880
|
-
capName: "pet-fountain",
|
|
80881
|
-
capScope: "device",
|
|
80882
|
-
addonId: null,
|
|
80883
|
-
access: "create",
|
|
80884
|
-
role: "viewer"
|
|
80885
|
-
},
|
|
80886
|
-
"petFountain.setPumpOnMinutes": {
|
|
80887
|
-
capName: "pet-fountain",
|
|
80888
|
-
capScope: "device",
|
|
80889
|
-
addonId: null,
|
|
80890
|
-
access: "create",
|
|
80891
|
-
role: "viewer"
|
|
80892
|
-
},
|
|
80893
81282
|
"pipelineAnalytics.addRetrainFrameFromRecording": {
|
|
80894
81283
|
capName: "pipeline-analytics",
|
|
80895
81284
|
capScope: "device",
|
|
@@ -82668,6 +83057,34 @@ Object.freeze({
|
|
|
82668
83057
|
access: "create",
|
|
82669
83058
|
role: "admin"
|
|
82670
83059
|
},
|
|
83060
|
+
"pumpCycle.getStatus": {
|
|
83061
|
+
capName: "pump-cycle",
|
|
83062
|
+
capScope: "device",
|
|
83063
|
+
addonId: null,
|
|
83064
|
+
access: "view",
|
|
83065
|
+
role: "viewer"
|
|
83066
|
+
},
|
|
83067
|
+
"pumpCycle.setEnabled": {
|
|
83068
|
+
capName: "pump-cycle",
|
|
83069
|
+
capScope: "device",
|
|
83070
|
+
addonId: null,
|
|
83071
|
+
access: "create",
|
|
83072
|
+
role: "viewer"
|
|
83073
|
+
},
|
|
83074
|
+
"pumpCycle.setOffMinutes": {
|
|
83075
|
+
capName: "pump-cycle",
|
|
83076
|
+
capScope: "device",
|
|
83077
|
+
addonId: null,
|
|
83078
|
+
access: "create",
|
|
83079
|
+
role: "viewer"
|
|
83080
|
+
},
|
|
83081
|
+
"pumpCycle.setOnMinutes": {
|
|
83082
|
+
capName: "pump-cycle",
|
|
83083
|
+
capScope: "device",
|
|
83084
|
+
addonId: null,
|
|
83085
|
+
access: "create",
|
|
83086
|
+
role: "viewer"
|
|
83087
|
+
},
|
|
82671
83088
|
"reboot.reboot": {
|
|
82672
83089
|
capName: "reboot",
|
|
82673
83090
|
capScope: "device",
|
|
@@ -83172,6 +83589,13 @@ Object.freeze({
|
|
|
83172
83589
|
access: "create",
|
|
83173
83590
|
role: "admin"
|
|
83174
83591
|
},
|
|
83592
|
+
"remote.getAssumedStatus": {
|
|
83593
|
+
capName: "remote",
|
|
83594
|
+
capScope: "system",
|
|
83595
|
+
addonId: null,
|
|
83596
|
+
access: "view",
|
|
83597
|
+
role: "admin"
|
|
83598
|
+
},
|
|
83175
83599
|
"remote.getConfig": {
|
|
83176
83600
|
capName: "remote",
|
|
83177
83601
|
capScope: "system",
|
|
@@ -86838,6 +87262,16 @@ Object.freeze({
|
|
|
86838
87262
|
form: "single",
|
|
86839
87263
|
optional: true
|
|
86840
87264
|
}],
|
|
87265
|
+
"mediaPlayer.channelDown": [{
|
|
87266
|
+
name: "deviceId",
|
|
87267
|
+
form: "single",
|
|
87268
|
+
optional: false
|
|
87269
|
+
}],
|
|
87270
|
+
"mediaPlayer.channelUp": [{
|
|
87271
|
+
name: "deviceId",
|
|
87272
|
+
form: "single",
|
|
87273
|
+
optional: false
|
|
87274
|
+
}],
|
|
86841
87275
|
"mediaPlayer.getStatus": [{
|
|
86842
87276
|
name: "deviceId",
|
|
86843
87277
|
form: "single",
|
|
@@ -86903,6 +87337,26 @@ Object.freeze({
|
|
|
86903
87337
|
form: "single",
|
|
86904
87338
|
optional: false
|
|
86905
87339
|
}],
|
|
87340
|
+
"mediaPlayer.turnOff": [{
|
|
87341
|
+
name: "deviceId",
|
|
87342
|
+
form: "single",
|
|
87343
|
+
optional: false
|
|
87344
|
+
}],
|
|
87345
|
+
"mediaPlayer.turnOn": [{
|
|
87346
|
+
name: "deviceId",
|
|
87347
|
+
form: "single",
|
|
87348
|
+
optional: false
|
|
87349
|
+
}],
|
|
87350
|
+
"mediaPlayer.volumeDown": [{
|
|
87351
|
+
name: "deviceId",
|
|
87352
|
+
form: "single",
|
|
87353
|
+
optional: false
|
|
87354
|
+
}],
|
|
87355
|
+
"mediaPlayer.volumeUp": [{
|
|
87356
|
+
name: "deviceId",
|
|
87357
|
+
form: "single",
|
|
87358
|
+
optional: false
|
|
87359
|
+
}],
|
|
86906
87360
|
"meter.getStatus": [{
|
|
86907
87361
|
name: "deviceId",
|
|
86908
87362
|
form: "single",
|
|
@@ -87212,21 +87666,6 @@ Object.freeze({
|
|
|
87212
87666
|
form: "single",
|
|
87213
87667
|
optional: false
|
|
87214
87668
|
}],
|
|
87215
|
-
"petFountain.setPumpInterval": [{
|
|
87216
|
-
name: "deviceId",
|
|
87217
|
-
form: "single",
|
|
87218
|
-
optional: false
|
|
87219
|
-
}],
|
|
87220
|
-
"petFountain.setPumpOffMinutes": [{
|
|
87221
|
-
name: "deviceId",
|
|
87222
|
-
form: "single",
|
|
87223
|
-
optional: false
|
|
87224
|
-
}],
|
|
87225
|
-
"petFountain.setPumpOnMinutes": [{
|
|
87226
|
-
name: "deviceId",
|
|
87227
|
-
form: "single",
|
|
87228
|
-
optional: false
|
|
87229
|
-
}],
|
|
87230
87669
|
"pipelineAnalytics.addRetrainFrameFromRecording": [{
|
|
87231
87670
|
name: "deviceId",
|
|
87232
87671
|
form: "single",
|
|
@@ -87862,6 +88301,26 @@ Object.freeze({
|
|
|
87862
88301
|
form: "single",
|
|
87863
88302
|
optional: false
|
|
87864
88303
|
}],
|
|
88304
|
+
"pumpCycle.getStatus": [{
|
|
88305
|
+
name: "deviceId",
|
|
88306
|
+
form: "single",
|
|
88307
|
+
optional: false
|
|
88308
|
+
}],
|
|
88309
|
+
"pumpCycle.setEnabled": [{
|
|
88310
|
+
name: "deviceId",
|
|
88311
|
+
form: "single",
|
|
88312
|
+
optional: false
|
|
88313
|
+
}],
|
|
88314
|
+
"pumpCycle.setOffMinutes": [{
|
|
88315
|
+
name: "deviceId",
|
|
88316
|
+
form: "single",
|
|
88317
|
+
optional: false
|
|
88318
|
+
}],
|
|
88319
|
+
"pumpCycle.setOnMinutes": [{
|
|
88320
|
+
name: "deviceId",
|
|
88321
|
+
form: "single",
|
|
88322
|
+
optional: false
|
|
88323
|
+
}],
|
|
87865
88324
|
"reboot.reboot": [{
|
|
87866
88325
|
name: "deviceId",
|
|
87867
88326
|
form: "single",
|
|
@@ -88063,6 +88522,11 @@ Object.freeze({
|
|
|
88063
88522
|
form: "single",
|
|
88064
88523
|
optional: true
|
|
88065
88524
|
}],
|
|
88525
|
+
"remote.getAssumedStatus": [{
|
|
88526
|
+
name: "deviceId",
|
|
88527
|
+
form: "single",
|
|
88528
|
+
optional: false
|
|
88529
|
+
}],
|
|
88066
88530
|
"remote.getConfig": [{
|
|
88067
88531
|
name: "deviceId",
|
|
88068
88532
|
form: "single",
|
|
@@ -89318,6 +89782,7 @@ var STATE_SUMMARY_VARS = [
|
|
|
89318
89782
|
[...RULE_TEXT],
|
|
89319
89783
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
89320
89784
|
([...RULE_TEXT], [...COMBINED_ALARM]),
|
|
89785
|
+
([...RULE_TEXT], [...COMBINED_ALARM]),
|
|
89321
89786
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
89322
89787
|
([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
|
|
89323
89788
|
([...RULE_TEXT], [...CONSUMABLE]),
|
|
@@ -90079,13 +90544,20 @@ var GoogleBrokerEntrySchema = object({
|
|
|
90079
90544
|
calendarFilter: string().default(""),
|
|
90080
90545
|
/** Default From for Gmail send — usually the connected account email. */
|
|
90081
90546
|
fromAddress: string().default(""),
|
|
90082
|
-
fromName: string().default("")
|
|
90547
|
+
fromName: string().default(""),
|
|
90548
|
+
/**
|
|
90549
|
+
* The stamped calendar ids (`<brokerId>:<googleCalendarId>`) this broker imports. `undefined`
|
|
90550
|
+
* means "not moved here yet": the reader falls back to the shared `calendarIds` setting of
|
|
90551
|
+
* the addon until the migration copied it. Empty = import none.
|
|
90552
|
+
*/
|
|
90553
|
+
calendarIds: array(string()).optional()
|
|
90083
90554
|
});
|
|
90084
90555
|
var GoogleServicesConfigSchema = object({
|
|
90085
90556
|
brokers: array(GoogleBrokerEntrySchema).default([]),
|
|
90086
90557
|
/**
|
|
90087
|
-
*
|
|
90088
|
-
*
|
|
90558
|
+
* @deprecated The shared set (`brokerId:googleCalendarId`). Each broker owns its own
|
|
90559
|
+
* `calendarIds` now; this key is read only while a broker has none, and the migration
|
|
90560
|
+
* `google-services/2026-11-03-drop-shared-calendar-ids` deletes it.
|
|
90089
90561
|
*/
|
|
90090
90562
|
calendarIds: array(string()).default([]),
|
|
90091
90563
|
/** @deprecated Pre-broker singleton — cleared after migrateLegacyToBrokers. */
|
|
@@ -90163,14 +90635,15 @@ function settingsToBrokerFields(settings) {
|
|
|
90163
90635
|
clientSecret: parsed.clientSecret,
|
|
90164
90636
|
calendarFilter: calendarIdsToFilter(parsed),
|
|
90165
90637
|
fromAddress: parsed.fromAddress ?? "",
|
|
90166
|
-
fromName: parsed.fromName ?? ""
|
|
90638
|
+
fromName: parsed.fromName ?? "",
|
|
90639
|
+
...parsed.calendarIds !== void 0 ? { calendarIds: [...parsed.calendarIds] } : {}
|
|
90167
90640
|
};
|
|
90168
90641
|
}
|
|
90169
90642
|
function brokerEntryToSettings(entry) {
|
|
90170
90643
|
return {
|
|
90171
90644
|
clientId: entry.clientId,
|
|
90172
90645
|
clientSecret: entry.clientSecret,
|
|
90173
|
-
calendarIds: [...parseCalendarFilter(entry.calendarFilter)],
|
|
90646
|
+
calendarIds: [...entry.calendarIds ?? parseCalendarFilter(entry.calendarFilter)],
|
|
90174
90647
|
fromAddress: entry.fromAddress,
|
|
90175
90648
|
fromName: entry.fromName
|
|
90176
90649
|
};
|
|
@@ -90221,6 +90694,13 @@ function migrateLegacyToBrokers(rawConfig, legacyOauth) {
|
|
|
90221
90694
|
didMigrate: true
|
|
90222
90695
|
};
|
|
90223
90696
|
}
|
|
90697
|
+
/**
|
|
90698
|
+
* The stamped calendar ids one broker imports: its own set, or — while the migration has not
|
|
90699
|
+
* reached it — the shared setting older versions wrote, so the move never changes what is imported.
|
|
90700
|
+
*/
|
|
90701
|
+
function calendarIdsOfEntry(entry, shared) {
|
|
90702
|
+
return new Set(entry?.calendarIds ?? shared);
|
|
90703
|
+
}
|
|
90224
90704
|
function parseCalendarFilter(raw) {
|
|
90225
90705
|
const trimmed = raw.trim();
|
|
90226
90706
|
if (!trimmed) return /* @__PURE__ */ new Set();
|
|
@@ -90307,6 +90787,7 @@ var GoogleBrokerRegistry = class {
|
|
|
90307
90787
|
const entry = GoogleBrokerEntrySchema.parse({
|
|
90308
90788
|
id,
|
|
90309
90789
|
name,
|
|
90790
|
+
calendarIds: [],
|
|
90310
90791
|
...fields
|
|
90311
90792
|
});
|
|
90312
90793
|
this.entries = [...this.entries, entry];
|
|
@@ -90320,6 +90801,7 @@ var GoogleBrokerRegistry = class {
|
|
|
90320
90801
|
const next = GoogleBrokerEntrySchema.parse({
|
|
90321
90802
|
id: prev.id,
|
|
90322
90803
|
name: prev.name,
|
|
90804
|
+
...prev.calendarIds !== void 0 ? { calendarIds: prev.calendarIds } : {},
|
|
90323
90805
|
...fields
|
|
90324
90806
|
});
|
|
90325
90807
|
this.entries = this.entries.map((e, i) => i === idx ? next : e);
|
|
@@ -90525,6 +91007,24 @@ function buildGoogleBrokerSettingsSchema(publicOrigin) {
|
|
|
90525
91007
|
showToggle: true
|
|
90526
91008
|
}]
|
|
90527
91009
|
},
|
|
91010
|
+
{
|
|
91011
|
+
id: "google-calendars-import",
|
|
91012
|
+
title: "Calendars to import",
|
|
91013
|
+
titleKey: "google-services:googleBrokerSettings.s_google-calendars-import.title",
|
|
91014
|
+
description: "Nothing selected = no calendars imported from this account. Options appear once the account is signed in.",
|
|
91015
|
+
descriptionKey: "google-services:googleBrokerSettings.s_google-calendars-import.description",
|
|
91016
|
+
columns: 1,
|
|
91017
|
+
fields: [{
|
|
91018
|
+
type: "multiselect",
|
|
91019
|
+
key: "calendarIds",
|
|
91020
|
+
label: "Calendars",
|
|
91021
|
+
labelKey: "google-services:googleBrokerSettings.s_google-calendars-import.calendarIds.label",
|
|
91022
|
+
options: [],
|
|
91023
|
+
optionsFrom: "_calendarCatalog",
|
|
91024
|
+
description: "The calendars of this Google account that CamStack imports.",
|
|
91025
|
+
descriptionKey: "google-services:googleBrokerSettings.s_google-calendars-import.calendarIds.description"
|
|
91026
|
+
}]
|
|
91027
|
+
},
|
|
90528
91028
|
{
|
|
90529
91029
|
id: "gmail-send",
|
|
90530
91030
|
title: "Gmail send",
|
|
@@ -90548,7 +91048,7 @@ function buildGoogleBrokerSettingsSchema(publicOrigin) {
|
|
|
90548
91048
|
}
|
|
90549
91049
|
] };
|
|
90550
91050
|
}
|
|
90551
|
-
/** Connectivity Calendars / Email pages — calendar import picker
|
|
91051
|
+
/** Connectivity Calendars / Email pages — the calendar import picker is on each broker. */
|
|
90552
91052
|
function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
|
|
90553
91053
|
const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
|
|
90554
91054
|
if (cap === "calendar-source") return { sections: [{
|
|
@@ -90564,27 +91064,10 @@ function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
|
|
|
90564
91064
|
format: "html",
|
|
90565
91065
|
content: [
|
|
90566
91066
|
"<p>Link a Google account under Connectivity → <strong>Brokers</strong> ",
|
|
90567
|
-
"(Client ID / secret + Sign in).
|
|
91067
|
+
"(Client ID / secret + Sign in). Pick the calendars to import on each broker. ",
|
|
90568
91068
|
`OAuth redirect URI: <code>${redirect}</code>.</p>`
|
|
90569
91069
|
].join("")
|
|
90570
91070
|
}]
|
|
90571
|
-
}, {
|
|
90572
|
-
id: "google-calendars-import",
|
|
90573
|
-
title: "Calendars to import",
|
|
90574
|
-
titleKey: "google-services:googleFeatureSettings.s_google-calendars-import.title",
|
|
90575
|
-
description: "Nothing selected = no calendars imported. Options appear after at least one Google broker is signed in.",
|
|
90576
|
-
descriptionKey: "google-services:googleFeatureSettings.s_google-calendars-import.description",
|
|
90577
|
-
columns: 1,
|
|
90578
|
-
fields: [{
|
|
90579
|
-
type: "multiselect",
|
|
90580
|
-
key: "calendarIds",
|
|
90581
|
-
label: "Calendars",
|
|
90582
|
-
labelKey: "google-services:googleFeatureSettings.s_google-calendars-import.calendarIds.label",
|
|
90583
|
-
options: [],
|
|
90584
|
-
optionsFrom: "_calendarCatalog",
|
|
90585
|
-
description: "Ids are stamped as brokerId:googleCalendarId across every Google broker.",
|
|
90586
|
-
descriptionKey: "google-services:googleFeatureSettings.s_google-calendars-import.calendarIds.description"
|
|
90587
|
-
}]
|
|
90588
91071
|
}] };
|
|
90589
91072
|
return { sections: [{
|
|
90590
91073
|
id: "setup-intro",
|
|
@@ -90674,11 +91157,11 @@ function createGoogleCalendarApi(getAccessToken, http = { fetch }) {
|
|
|
90674
91157
|
function createGoogleCalendarSource(deps) {
|
|
90675
91158
|
return {
|
|
90676
91159
|
listCalendars: async () => {
|
|
90677
|
-
const selected = deps.selectedCalendarIds();
|
|
90678
|
-
if (selected.size === 0) return [];
|
|
90679
91160
|
const out = [];
|
|
90680
91161
|
for (const account of deps.accounts()) {
|
|
90681
91162
|
if (!account.connected) continue;
|
|
91163
|
+
const selected = deps.selectedCalendarIds(account.brokerId);
|
|
91164
|
+
if (selected.size === 0) continue;
|
|
90682
91165
|
try {
|
|
90683
91166
|
const list = await account.api.listCalendars();
|
|
90684
91167
|
for (const c of list) {
|
|
@@ -90701,20 +91184,18 @@ function createGoogleCalendarSource(deps) {
|
|
|
90701
91184
|
return out;
|
|
90702
91185
|
},
|
|
90703
91186
|
listEvents: async (input) => {
|
|
90704
|
-
const selected = deps.selectedCalendarIds();
|
|
90705
|
-
if (selected.size === 0) return [];
|
|
90706
91187
|
const timeMin = new Date(input.startMs).toISOString();
|
|
90707
91188
|
const timeMax = new Date(input.endMs).toISOString();
|
|
90708
91189
|
const accounts = deps.accounts().filter((a) => a.connected);
|
|
90709
91190
|
const byBroker = new Map(accounts.map((a) => [a.brokerId, a]));
|
|
90710
|
-
const requestIds = input.calendarIds !== void 0 && input.calendarIds.length > 0 ? input.calendarIds : [...
|
|
91191
|
+
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)))];
|
|
90711
91192
|
/** brokerId → google calendar ids to fetch */
|
|
90712
91193
|
const wanted = /* @__PURE__ */ new Map();
|
|
90713
91194
|
for (const stamped of requestIds) {
|
|
90714
|
-
if (!selected.has(stamped)) continue;
|
|
90715
91195
|
const parsed = parseStampedCalendarId(stamped);
|
|
90716
91196
|
if (parsed === null) continue;
|
|
90717
91197
|
if (!byBroker.has(parsed.brokerId)) continue;
|
|
91198
|
+
if (!deps.selectedCalendarIds(parsed.brokerId).has(stamped)) continue;
|
|
90718
91199
|
const list = wanted.get(parsed.brokerId) ?? [];
|
|
90719
91200
|
list.push(parsed.googleCalendarId);
|
|
90720
91201
|
wanted.set(parsed.brokerId, list);
|
|
@@ -91063,11 +91544,152 @@ var googleBrokersEntryKind = defineEntryKind("brokers", {
|
|
|
91063
91544
|
clientSecret: GoogleBrokerEntrySchema.shape.clientSecret,
|
|
91064
91545
|
calendarFilter: GoogleBrokerEntrySchema.shape.calendarFilter,
|
|
91065
91546
|
fromAddress: GoogleBrokerEntrySchema.shape.fromAddress,
|
|
91066
|
-
fromName: GoogleBrokerEntrySchema.shape.fromName
|
|
91547
|
+
fromName: GoogleBrokerEntrySchema.shape.fromName,
|
|
91548
|
+
calendarIds: GoogleBrokerEntrySchema.shape.calendarIds
|
|
91067
91549
|
}),
|
|
91068
91550
|
writeTrigger: "an operator adds or edits a Google OAuth broker"
|
|
91069
91551
|
});
|
|
91070
91552
|
//#endregion
|
|
91553
|
+
//#region src/migrations/calendar-ids-to-brokers.ts
|
|
91554
|
+
/**
|
|
91555
|
+
* The calendars a Google account imports are a fact of THAT account (D985): each broker record
|
|
91556
|
+
* carries its own `calendarIds`, and the addon-level `calendarIds` setting (one shared set of
|
|
91557
|
+
* `<brokerId>:<googleCalendarId>` ids) goes away.
|
|
91558
|
+
*
|
|
91559
|
+
* Two migrations, in this order:
|
|
91560
|
+
*
|
|
91561
|
+
* 1. `calendar-ids-to-brokers` (procedure) copies the shared set to EVERY broker record that has
|
|
91562
|
+
* no `calendarIds` yet. A record that already has one is never overwritten; a broker deleted
|
|
91563
|
+
* meanwhile is skipped, never recreated; a stored value that is not a list of strings is an
|
|
91564
|
+
* ambiguity and throws (nothing is guessed).
|
|
91565
|
+
* 2. `drop-shared-calendar-ids` (plan) deletes the shared setting, but only after a read-back:
|
|
91566
|
+
* every live broker must show its own `calendarIds`. When one of them holds a set that differs
|
|
91567
|
+
* from the shared one (an operator choice made before the copy), the shared value is the
|
|
91568
|
+
* loser and stays PARKED where it is, never deleted.
|
|
91569
|
+
*
|
|
91570
|
+
* Both are idempotent: with the copy landed the first reports `already-done`, and with the row
|
|
91571
|
+
* gone the second plans nothing.
|
|
91572
|
+
*/
|
|
91573
|
+
var GOOGLE_ADDON_ID = "google-services";
|
|
91574
|
+
/** The canonical settings table: one row per key, id `<addonId>.<key>`. */
|
|
91575
|
+
var ADDON_SETTINGS_TABLE = "addon-settings";
|
|
91576
|
+
/** The legacy shared set. */
|
|
91577
|
+
var SHARED_CALENDAR_IDS_KEY = "calendarIds";
|
|
91578
|
+
async function readSharedSet(ports) {
|
|
91579
|
+
const answer = await ports.readStored(SHARED_CALENDAR_IDS_KEY);
|
|
91580
|
+
if (answer.kind === "unreadable") throw new Error(`google-services: the shared calendarIds cannot be read (${answer.reason})`);
|
|
91581
|
+
if (answer.kind === "absent") return null;
|
|
91582
|
+
const value = answer.value;
|
|
91583
|
+
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");
|
|
91584
|
+
return value;
|
|
91585
|
+
}
|
|
91586
|
+
async function copyCalendarIdsToBrokers(ports) {
|
|
91587
|
+
const shared = await readSharedSet(ports);
|
|
91588
|
+
if (shared === null) return {
|
|
91589
|
+
outcome: "already-done",
|
|
91590
|
+
summary: { reason: "no-shared-set" }
|
|
91591
|
+
};
|
|
91592
|
+
let copied = 0;
|
|
91593
|
+
let kept = 0;
|
|
91594
|
+
let gone = 0;
|
|
91595
|
+
for (const broker of await ports.listBrokers()) {
|
|
91596
|
+
if (broker.calendarIds !== void 0) {
|
|
91597
|
+
kept += 1;
|
|
91598
|
+
continue;
|
|
91599
|
+
}
|
|
91600
|
+
if (await ports.setBrokerCalendarIds(broker.id, shared)) copied += 1;
|
|
91601
|
+
else gone += 1;
|
|
91602
|
+
}
|
|
91603
|
+
return {
|
|
91604
|
+
outcome: copied === 0 ? "already-done" : "done",
|
|
91605
|
+
summary: {
|
|
91606
|
+
copied,
|
|
91607
|
+
kept,
|
|
91608
|
+
gone
|
|
91609
|
+
}
|
|
91610
|
+
};
|
|
91611
|
+
}
|
|
91612
|
+
var sameSet = (a, b) => {
|
|
91613
|
+
const left = new Set(a);
|
|
91614
|
+
const right = new Set(b);
|
|
91615
|
+
return left.size === right.size && [...left].every((id) => right.has(id));
|
|
91616
|
+
};
|
|
91617
|
+
async function planDropSharedCalendarIds(ports) {
|
|
91618
|
+
const shared = await readSharedSet(ports);
|
|
91619
|
+
if (shared === null) return { edits: [] };
|
|
91620
|
+
const brokers = await ports.listBrokers();
|
|
91621
|
+
if (brokers.length === 0) return { edits: [] };
|
|
91622
|
+
const missing = brokers.filter((broker) => broker.calendarIds === void 0);
|
|
91623
|
+
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`);
|
|
91624
|
+
if (!brokers.every((broker) => sameSet(broker.calendarIds ?? [], shared))) return { edits: [] };
|
|
91625
|
+
const rowId = `${GOOGLE_ADDON_ID}.${SHARED_CALENDAR_IDS_KEY}`;
|
|
91626
|
+
return { edits: [{
|
|
91627
|
+
op: "deleteKeys",
|
|
91628
|
+
scope: ADDON_SETTINGS_TABLE,
|
|
91629
|
+
keys: [rowId],
|
|
91630
|
+
precondition: preconditionOf({ [rowId]: {
|
|
91631
|
+
addonId: GOOGLE_ADDON_ID,
|
|
91632
|
+
key: SHARED_CALENDAR_IDS_KEY,
|
|
91633
|
+
value: shared
|
|
91634
|
+
} })
|
|
91635
|
+
}] };
|
|
91636
|
+
}
|
|
91637
|
+
//#endregion
|
|
91638
|
+
//#region src/migrations/index.ts
|
|
91639
|
+
/** The `google-services` catalogue (D987): offered to the engine when the addon initialises. */
|
|
91640
|
+
var ADDON_MIGRATIONS = [{
|
|
91641
|
+
meta: {
|
|
91642
|
+
id: "google-services/2026-11-03-calendar-ids-to-brokers",
|
|
91643
|
+
owner: "google-services",
|
|
91644
|
+
level: "addon-state",
|
|
91645
|
+
seq: 1,
|
|
91646
|
+
dependsOn: [],
|
|
91647
|
+
store: "settings",
|
|
91648
|
+
scope: "cluster",
|
|
91649
|
+
onFailure: "tolerate",
|
|
91650
|
+
bodyKind: "procedure",
|
|
91651
|
+
description: "Copy the shared calendar set to every Google broker record that has none of its own",
|
|
91652
|
+
fixtureMark: "the client secret marker of a broker record the migration does not know"
|
|
91653
|
+
},
|
|
91654
|
+
procedure: copyCalendarIdsToBrokers
|
|
91655
|
+
}, {
|
|
91656
|
+
meta: {
|
|
91657
|
+
id: "google-services/2026-11-03-drop-shared-calendar-ids",
|
|
91658
|
+
owner: "google-services",
|
|
91659
|
+
level: "addon-state",
|
|
91660
|
+
seq: 2,
|
|
91661
|
+
dependsOn: ["google-services/2026-11-03-calendar-ids-to-brokers"],
|
|
91662
|
+
store: "settings",
|
|
91663
|
+
scope: "cluster",
|
|
91664
|
+
onFailure: "tolerate",
|
|
91665
|
+
bodyKind: "plan",
|
|
91666
|
+
description: "Delete the shared calendar set once every broker shows its own",
|
|
91667
|
+
fixtureMark: "the shared set parked when a broker holds a different one"
|
|
91668
|
+
},
|
|
91669
|
+
plan: planDropSharedCalendarIds
|
|
91670
|
+
}];
|
|
91671
|
+
//#endregion
|
|
91672
|
+
//#region src/migrations/ports.ts
|
|
91673
|
+
/** The migration's ports over the `brokers` record kind and the addon's raw settings read. */
|
|
91674
|
+
function calendarIdsPortsOver(kind, readStored) {
|
|
91675
|
+
return {
|
|
91676
|
+
readStored,
|
|
91677
|
+
listBrokers: async () => {
|
|
91678
|
+
return (await kind.query({ limit: 1e3 })).records.map((record) => ({
|
|
91679
|
+
id: record.id,
|
|
91680
|
+
calendarIds: record.data.calendarIds
|
|
91681
|
+
}));
|
|
91682
|
+
},
|
|
91683
|
+
setBrokerCalendarIds: async (id, ids) => {
|
|
91684
|
+
const row = await kind.get(id);
|
|
91685
|
+
if (row === null) return false;
|
|
91686
|
+
if (row.data.calendarIds !== void 0) return true;
|
|
91687
|
+
await kind.patch(id, { calendarIds: [...ids] });
|
|
91688
|
+
return true;
|
|
91689
|
+
}
|
|
91690
|
+
};
|
|
91691
|
+
}
|
|
91692
|
+
//#endregion
|
|
91071
91693
|
//#region src/google-broker-store.ts
|
|
91072
91694
|
function rowToEntry(id, data) {
|
|
91073
91695
|
const parsed = GoogleBrokerEntrySchema.safeParse({
|
|
@@ -91105,14 +91727,15 @@ var GoogleBrokerStore = class GoogleBrokerStore {
|
|
|
91105
91727
|
const nextIds = new Set(entries.map((e) => e.id));
|
|
91106
91728
|
for (const row of page.records) if (!nextIds.has(row.id)) await this.kind.delete(row.id);
|
|
91107
91729
|
for (const entry of entries) {
|
|
91108
|
-
const { id, name, clientId, clientSecret, calendarFilter, fromAddress, fromName } = entry;
|
|
91730
|
+
const { id, name, clientId, clientSecret, calendarFilter, fromAddress, fromName, calendarIds } = entry;
|
|
91109
91731
|
const body = {
|
|
91110
91732
|
name,
|
|
91111
91733
|
clientId,
|
|
91112
91734
|
clientSecret,
|
|
91113
91735
|
calendarFilter,
|
|
91114
91736
|
fromAddress,
|
|
91115
|
-
fromName
|
|
91737
|
+
fromName,
|
|
91738
|
+
...calendarIds !== void 0 ? { calendarIds } : {}
|
|
91116
91739
|
};
|
|
91117
91740
|
const parsed = googleBrokersEntryKind.schema.safeParse(body);
|
|
91118
91741
|
if (!parsed.success) throw new Error(`invalid Google broker '${id}': ${parsed.error.message}`);
|
|
@@ -91209,6 +91832,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91209
91832
|
await this.brokerStore.replaceAll(this.config.brokers);
|
|
91210
91833
|
this.activeBrokers = await this.loadPersistedBrokers();
|
|
91211
91834
|
}
|
|
91835
|
+
await this.migrateCalendarIds(records);
|
|
91212
91836
|
this.registry.seedFrom(this.activeBrokers, oauthAccounts.accounts);
|
|
91213
91837
|
await this.refreshPublicOrigin();
|
|
91214
91838
|
const calendarProvider = createGoogleCalendarSource({
|
|
@@ -91218,7 +91842,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91218
91842
|
connected: this.registry.isConnected(entry.id),
|
|
91219
91843
|
api: createGoogleCalendarApi(() => this.ensureAccessToken(entry.id))
|
|
91220
91844
|
})),
|
|
91221
|
-
selectedCalendarIds: () =>
|
|
91845
|
+
selectedCalendarIds: (brokerId) => this.calendarIdsOf(brokerId)
|
|
91222
91846
|
});
|
|
91223
91847
|
const smtpProvider = createGmailSmtpProvider({ accounts: () => this.registry.listEntries().map((entry) => {
|
|
91224
91848
|
const oauth = this.registry.getOauth(entry.id);
|
|
@@ -91389,7 +92013,10 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91389
92013
|
getSettings: async ({ id }) => {
|
|
91390
92014
|
const entry = this.registry.getEntry(id);
|
|
91391
92015
|
if (entry === null) return null;
|
|
91392
|
-
return
|
|
92016
|
+
return {
|
|
92017
|
+
...brokerEntryToSettings(entry),
|
|
92018
|
+
_calendarCatalog: await this.loadStampedCalendarCatalog(id)
|
|
92019
|
+
};
|
|
91393
92020
|
},
|
|
91394
92021
|
setSettings: async ({ id, settings }) => {
|
|
91395
92022
|
if (!owns(id)) return;
|
|
@@ -91402,6 +92029,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91402
92029
|
const oauth = this.registry.getOauth(id);
|
|
91403
92030
|
return {
|
|
91404
92031
|
...brokerEntryToSettings(entry),
|
|
92032
|
+
_calendarCatalog: await this.loadStampedCalendarCatalog(id),
|
|
91405
92033
|
accountEmail: oauth?.accountEmail ?? "",
|
|
91406
92034
|
connected: this.registry.isConnected(id)
|
|
91407
92035
|
};
|
|
@@ -91607,6 +92235,13 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91607
92235
|
const cutoff = Date.now() - 10 * 6e4;
|
|
91608
92236
|
for (const [k, v] of this.pendingStates) if (v.issuedAt < cutoff) this.pendingStates.delete(k);
|
|
91609
92237
|
}
|
|
92238
|
+
/**
|
|
92239
|
+
* The shared calendar set becomes each broker's own (D985, D987). Brokers are reloaded once the
|
|
92240
|
+
* copy landed, so the registry is seeded with the sets the migration wrote.
|
|
92241
|
+
*/
|
|
92242
|
+
async migrateCalendarIds(records) {
|
|
92243
|
+
if ((await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, calendarIdsPortsOver(records.kind(googleBrokersEntryKind), (key) => this.readStoredGlobalKey(key))))).applied.length > 0) this.activeBrokers = await this.loadPersistedBrokers();
|
|
92244
|
+
}
|
|
91610
92245
|
async loadPersistedBrokers() {
|
|
91611
92246
|
const store = this.brokerStore;
|
|
91612
92247
|
if (store !== null) {
|
|
@@ -91630,9 +92265,10 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91630
92265
|
await this.oauthAccountsHandle.set({ accounts: [...this.registry.listOauthAccounts()] });
|
|
91631
92266
|
}
|
|
91632
92267
|
/** Stamped catalog for Connectivity → Calendars (`optionsFrom`). */
|
|
91633
|
-
async loadStampedCalendarCatalog() {
|
|
92268
|
+
async loadStampedCalendarCatalog(onlyBrokerId) {
|
|
91634
92269
|
const out = [];
|
|
91635
92270
|
for (const entry of this.registry.listEntries()) {
|
|
92271
|
+
if (onlyBrokerId !== void 0 && entry.id !== onlyBrokerId) continue;
|
|
91636
92272
|
if (!this.registry.isConnected(entry.id)) continue;
|
|
91637
92273
|
try {
|
|
91638
92274
|
const list = await createGoogleCalendarApi(() => this.ensureAccessToken(entry.id)).listCalendars();
|
|
@@ -91772,13 +92408,15 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
91772
92408
|
}
|
|
91773
92409
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
91774
92410
|
await this.refreshPublicOrigin();
|
|
91775
|
-
|
|
91776
|
-
|
|
91777
|
-
|
|
91778
|
-
|
|
91779
|
-
|
|
91780
|
-
|
|
91781
|
-
|
|
92411
|
+
return super.getGlobalSettings(overlay, cap, nodeId);
|
|
92412
|
+
}
|
|
92413
|
+
/**
|
|
92414
|
+
* The stamped calendar ids ONE broker imports. A broker the migration has not reached yet
|
|
92415
|
+
* (no `calendarIds` of its own) reads the shared setting older versions wrote, so the move
|
|
92416
|
+
* never changes what is imported mid-way.
|
|
92417
|
+
*/
|
|
92418
|
+
calendarIdsOf(brokerId) {
|
|
92419
|
+
return calendarIdsOfEntry(this.registry.getEntry(brokerId), this.config.calendarIds ?? []);
|
|
91782
92420
|
}
|
|
91783
92421
|
};
|
|
91784
92422
|
function asSettingsCap(cap) {
|