@camstack/addon-provider-homeassistant 1.2.15 → 1.2.17
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/dist/addon.js +52 -1
- package/dist/addon.mjs +52 -1
- package/dist/{dist-DJqSvADu.js → dist-DIa87XAf.js} +248 -1
- package/dist/{dist-DdyXnBOa.mjs → dist-reK3lvnr.mjs} +248 -1
- package/dist/ha-export/ha-export.addon.js +276 -57
- package/dist/ha-export/ha-export.addon.mjs +275 -57
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-DIa87XAf.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
let node_zlib = require("node:zlib");
|
|
5
5
|
//#region src/ha-device-source.ts
|
|
@@ -7824,11 +7824,39 @@ var HaProviderAddon = class HaProviderAddon extends require_dist.BaseDeviceProvi
|
|
|
7824
7824
|
const haDevices = await this.fetchHaDiscoveredDevices(brokerId);
|
|
7825
7825
|
const haDeviceMap = new Map(haDevices.map((d) => [d.haDeviceId, d]));
|
|
7826
7826
|
let knownLocations = importLocations === true ? await this.ctx.api.deviceManager.listLocations.query() : [];
|
|
7827
|
+
const existingByStableId = /* @__PURE__ */ new Map();
|
|
7828
|
+
for (const dev of await this.ctx.kernel.devices.getAll()) {
|
|
7829
|
+
if (dev.parentDeviceId !== null) continue;
|
|
7830
|
+
existingByStableId.set(dev.stableId, dev);
|
|
7831
|
+
}
|
|
7827
7832
|
const adopted = [];
|
|
7828
7833
|
let failures = 0;
|
|
7834
|
+
let reused = 0;
|
|
7829
7835
|
for (const haDeviceId of childNativeIds) try {
|
|
7830
7836
|
const haDevice = haDeviceMap.get(haDeviceId);
|
|
7831
7837
|
const deviceName = (perCandidate?.[haDeviceId])?.name ?? haDevice?.name ?? haDeviceId;
|
|
7838
|
+
const existing = existingByStableId.get(`ha:${brokerId}:dev:${haDeviceId}`);
|
|
7839
|
+
if (existing) {
|
|
7840
|
+
const kids = await this.ctx.kernel.devices.getChildren(existing.id);
|
|
7841
|
+
adopted.push({
|
|
7842
|
+
childNativeId: haDeviceId,
|
|
7843
|
+
parentDeviceId: existing.id,
|
|
7844
|
+
accessoryDeviceIds: kids.map((c) => c.id)
|
|
7845
|
+
});
|
|
7846
|
+
reused += 1;
|
|
7847
|
+
this.ctx.logger.info("ha-adoption: candidate already adopted — returning existing", {
|
|
7848
|
+
tags: {
|
|
7849
|
+
deviceId: existing.id,
|
|
7850
|
+
integrationId
|
|
7851
|
+
},
|
|
7852
|
+
meta: {
|
|
7853
|
+
brokerId,
|
|
7854
|
+
haDeviceId,
|
|
7855
|
+
accessories: kids.length
|
|
7856
|
+
}
|
|
7857
|
+
});
|
|
7858
|
+
continue;
|
|
7859
|
+
}
|
|
7832
7860
|
const containerConfig = {
|
|
7833
7861
|
system: "homeassistant",
|
|
7834
7862
|
brokerId,
|
|
@@ -7848,6 +7876,19 @@ var HaProviderAddon = class HaProviderAddon extends require_dist.BaseDeviceProvi
|
|
|
7848
7876
|
parentDeviceId: summary.id,
|
|
7849
7877
|
accessoryDeviceIds
|
|
7850
7878
|
});
|
|
7879
|
+
this.ctx.logger.info("ha-adoption: candidate adopted", {
|
|
7880
|
+
tags: {
|
|
7881
|
+
deviceId: summary.id,
|
|
7882
|
+
integrationId
|
|
7883
|
+
},
|
|
7884
|
+
meta: {
|
|
7885
|
+
brokerId,
|
|
7886
|
+
haDeviceId,
|
|
7887
|
+
accessories: accessoryDeviceIds.length,
|
|
7888
|
+
index: adopted.length,
|
|
7889
|
+
of: childNativeIds.length
|
|
7890
|
+
}
|
|
7891
|
+
});
|
|
7851
7892
|
} catch (err) {
|
|
7852
7893
|
failures += 1;
|
|
7853
7894
|
this.ctx.logger.warn("ha-adoption: adopt failed for candidate (skipped)", {
|
|
@@ -7859,6 +7900,16 @@ var HaProviderAddon = class HaProviderAddon extends require_dist.BaseDeviceProvi
|
|
|
7859
7900
|
}
|
|
7860
7901
|
});
|
|
7861
7902
|
}
|
|
7903
|
+
this.ctx.logger.info("ha-adoption: adopt batch finished", {
|
|
7904
|
+
tags: { integrationId },
|
|
7905
|
+
meta: {
|
|
7906
|
+
brokerId,
|
|
7907
|
+
requested: childNativeIds.length,
|
|
7908
|
+
created: adopted.length - reused,
|
|
7909
|
+
alreadyAdopted: reused,
|
|
7910
|
+
failed: failures
|
|
7911
|
+
}
|
|
7912
|
+
});
|
|
7862
7913
|
if (failures > 0 && adopted.length === 0) throw new Error(`ha-adoption: all ${failures} adopt(s) failed for integration '${integrationId}'`);
|
|
7863
7914
|
return { adopted };
|
|
7864
7915
|
},
|
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as temperatureSensorCapability, A as eventEmitterCapability, B as motionCapability, C as connectivityCapability, Ct as EventCategory, D as deviceAdoptionCapability, E as coverCapability, F as humiditySensorCapability, G as powerMeterCapability, H as notificationOutputCapability, I as imageCapability, J as pressureSensorCapability, K as prepareNotification, L as lawnMowerControlCapability, M as floodCapability, N as gasCapability, P as humidifierCapability, Q as tamperCapability, R as lockControlCapability, S as colorCapability, St as url, T as controlCapability, U as notifierCapability, V as normalizeUnit, W as numericSensorCapability, X as smokeCapability, Y as scriptRunnerCapability, Z as switchCapability, _ as brokerCapability, _t as object, a as EnumSensorDateTimeFormatSchema, at as weatherCapability, b as carbonMonoxideCapability, bt as union, c as addonRoutesCapability, ct as DeviceFeature, d as ambientLightSensorCapability, dt as createEvent, et as updateCapability, f as automationControlCapability, ft as _enum, g as brightnessCapability, gt as number, h as binaryCapability, ht as literal, it as waterHeaterCapability, j as fanControlCapability, k as enumSensorCapability, l as airQualitySensorCapability, lt as DeviceRole, m as bestLocationMatch, mt as discriminatedUnion, n as BaseDeviceProvider, nt as valveCapability, o as TargetSchema, ot as errMsg, p as batteryCapability, pt as array, q as presenceCapability, rt as vibrationCapability, s as accessoriesCapability, t as BaseDevice, tt as vacuumControlCapability, u as alarmPanelCapability, ut as DeviceType, v as buildAddonRouteProvider, vt as record, w as contactCapability, x as climateControlCapability, xt as unknown, y as buttonCapability, yt as string, z as mediaPlayerCapability } from "./dist-
|
|
1
|
+
import { $ as temperatureSensorCapability, A as eventEmitterCapability, B as motionCapability, C as connectivityCapability, Ct as EventCategory, D as deviceAdoptionCapability, E as coverCapability, F as humiditySensorCapability, G as powerMeterCapability, H as notificationOutputCapability, I as imageCapability, J as pressureSensorCapability, K as prepareNotification, L as lawnMowerControlCapability, M as floodCapability, N as gasCapability, P as humidifierCapability, Q as tamperCapability, R as lockControlCapability, S as colorCapability, St as url, T as controlCapability, U as notifierCapability, V as normalizeUnit, W as numericSensorCapability, X as smokeCapability, Y as scriptRunnerCapability, Z as switchCapability, _ as brokerCapability, _t as object, a as EnumSensorDateTimeFormatSchema, at as weatherCapability, b as carbonMonoxideCapability, bt as union, c as addonRoutesCapability, ct as DeviceFeature, d as ambientLightSensorCapability, dt as createEvent, et as updateCapability, f as automationControlCapability, ft as _enum, g as brightnessCapability, gt as number, h as binaryCapability, ht as literal, it as waterHeaterCapability, j as fanControlCapability, k as enumSensorCapability, l as airQualitySensorCapability, lt as DeviceRole, m as bestLocationMatch, mt as discriminatedUnion, n as BaseDeviceProvider, nt as valveCapability, o as TargetSchema, ot as errMsg, p as batteryCapability, pt as array, q as presenceCapability, rt as vibrationCapability, s as accessoriesCapability, t as BaseDevice, tt as vacuumControlCapability, u as alarmPanelCapability, ut as DeviceType, v as buildAddonRouteProvider, vt as record, w as contactCapability, x as climateControlCapability, xt as unknown, y as buttonCapability, yt as string, z as mediaPlayerCapability } from "./dist-reK3lvnr.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
|
|
4
4
|
//#region src/ha-device-source.ts
|
|
@@ -7823,11 +7823,39 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
7823
7823
|
const haDevices = await this.fetchHaDiscoveredDevices(brokerId);
|
|
7824
7824
|
const haDeviceMap = new Map(haDevices.map((d) => [d.haDeviceId, d]));
|
|
7825
7825
|
let knownLocations = importLocations === true ? await this.ctx.api.deviceManager.listLocations.query() : [];
|
|
7826
|
+
const existingByStableId = /* @__PURE__ */ new Map();
|
|
7827
|
+
for (const dev of await this.ctx.kernel.devices.getAll()) {
|
|
7828
|
+
if (dev.parentDeviceId !== null) continue;
|
|
7829
|
+
existingByStableId.set(dev.stableId, dev);
|
|
7830
|
+
}
|
|
7826
7831
|
const adopted = [];
|
|
7827
7832
|
let failures = 0;
|
|
7833
|
+
let reused = 0;
|
|
7828
7834
|
for (const haDeviceId of childNativeIds) try {
|
|
7829
7835
|
const haDevice = haDeviceMap.get(haDeviceId);
|
|
7830
7836
|
const deviceName = (perCandidate?.[haDeviceId])?.name ?? haDevice?.name ?? haDeviceId;
|
|
7837
|
+
const existing = existingByStableId.get(`ha:${brokerId}:dev:${haDeviceId}`);
|
|
7838
|
+
if (existing) {
|
|
7839
|
+
const kids = await this.ctx.kernel.devices.getChildren(existing.id);
|
|
7840
|
+
adopted.push({
|
|
7841
|
+
childNativeId: haDeviceId,
|
|
7842
|
+
parentDeviceId: existing.id,
|
|
7843
|
+
accessoryDeviceIds: kids.map((c) => c.id)
|
|
7844
|
+
});
|
|
7845
|
+
reused += 1;
|
|
7846
|
+
this.ctx.logger.info("ha-adoption: candidate already adopted — returning existing", {
|
|
7847
|
+
tags: {
|
|
7848
|
+
deviceId: existing.id,
|
|
7849
|
+
integrationId
|
|
7850
|
+
},
|
|
7851
|
+
meta: {
|
|
7852
|
+
brokerId,
|
|
7853
|
+
haDeviceId,
|
|
7854
|
+
accessories: kids.length
|
|
7855
|
+
}
|
|
7856
|
+
});
|
|
7857
|
+
continue;
|
|
7858
|
+
}
|
|
7831
7859
|
const containerConfig = {
|
|
7832
7860
|
system: "homeassistant",
|
|
7833
7861
|
brokerId,
|
|
@@ -7847,6 +7875,19 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
7847
7875
|
parentDeviceId: summary.id,
|
|
7848
7876
|
accessoryDeviceIds
|
|
7849
7877
|
});
|
|
7878
|
+
this.ctx.logger.info("ha-adoption: candidate adopted", {
|
|
7879
|
+
tags: {
|
|
7880
|
+
deviceId: summary.id,
|
|
7881
|
+
integrationId
|
|
7882
|
+
},
|
|
7883
|
+
meta: {
|
|
7884
|
+
brokerId,
|
|
7885
|
+
haDeviceId,
|
|
7886
|
+
accessories: accessoryDeviceIds.length,
|
|
7887
|
+
index: adopted.length,
|
|
7888
|
+
of: childNativeIds.length
|
|
7889
|
+
}
|
|
7890
|
+
});
|
|
7850
7891
|
} catch (err) {
|
|
7851
7892
|
failures += 1;
|
|
7852
7893
|
this.ctx.logger.warn("ha-adoption: adopt failed for candidate (skipped)", {
|
|
@@ -7858,6 +7899,16 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
7858
7899
|
}
|
|
7859
7900
|
});
|
|
7860
7901
|
}
|
|
7902
|
+
this.ctx.logger.info("ha-adoption: adopt batch finished", {
|
|
7903
|
+
tags: { integrationId },
|
|
7904
|
+
meta: {
|
|
7905
|
+
brokerId,
|
|
7906
|
+
requested: childNativeIds.length,
|
|
7907
|
+
created: adopted.length - reused,
|
|
7908
|
+
alreadyAdopted: reused,
|
|
7909
|
+
failed: failures
|
|
7910
|
+
}
|
|
7911
|
+
});
|
|
7861
7912
|
if (failures > 0 && adopted.length === 0) throw new Error(`ha-adoption: all ${failures} adopt(s) failed for integration '${integrationId}'`);
|
|
7862
7913
|
return { adopted };
|
|
7863
7914
|
},
|
|
@@ -7218,6 +7218,104 @@ object({
|
|
|
7218
7218
|
})
|
|
7219
7219
|
});
|
|
7220
7220
|
/**
|
|
7221
|
+
* Adoption job — the background form of `device-adoption.adopt`.
|
|
7222
|
+
*
|
|
7223
|
+
* ## Why this exists
|
|
7224
|
+
*
|
|
7225
|
+
* `adopt({childNativeIds: [...]})` materialises one CamStack device per
|
|
7226
|
+
* candidate PLUS every accessory child, and the whole array shares ONE UDS
|
|
7227
|
+
* request deadline (60s). Measured on the live hub against Home Assistant:
|
|
7228
|
+
* each device the kernel creates costs ~450 ms — `devices.create` pre-seeds
|
|
7229
|
+
* meta with up to eleven SEQUENTIAL round trips (`setName`, `setType`,
|
|
7230
|
+
* `setRole`, … `persistConfig`) before the class is constructed — and an
|
|
7231
|
+
* accessory child costs the same as its parent. So the real unit of work is
|
|
7232
|
+
* the CHILD, not the candidate:
|
|
7233
|
+
*
|
|
7234
|
+
* - 25 candidates averaging 6 children → ~150 devices → **>60s, times out**
|
|
7235
|
+
* - ONE candidate with 217 children → ~217 devices → **>60s, times out**
|
|
7236
|
+
*
|
|
7237
|
+
* That second line is why this is a job and not a smaller batch. No chunking,
|
|
7238
|
+
* no bounded concurrency over candidates and no per-call tuning can fix a
|
|
7239
|
+
* shape where **N=1 already exceeds the deadline** — the count that blows the
|
|
7240
|
+
* budget is the source system's accessory fan-out, which the operator does not
|
|
7241
|
+
* choose and cannot see. A design that only works below some N is the same bug
|
|
7242
|
+
* deferred.
|
|
7243
|
+
*
|
|
7244
|
+
* ## What the timeout did NOT do
|
|
7245
|
+
*
|
|
7246
|
+
* It did not stop the work. The UDS deadline ends the CALLER's wait; the
|
|
7247
|
+
* provider's loop runs to completion. Measured: a 25-candidate adopt that
|
|
7248
|
+
* "failed" at 60s had adopted 17 by 87s and all 25 by ~130s. The operator saw
|
|
7249
|
+
* an error and had no way to learn that. Every field below exists so that
|
|
7250
|
+
* question has an answer.
|
|
7251
|
+
*
|
|
7252
|
+
* ## Idempotency
|
|
7253
|
+
*
|
|
7254
|
+
* Jobs are in-RAM; a restart forgets them. That is safe here because adoption
|
|
7255
|
+
* is keyed by a stable id (`ha:<broker>:dev:<nativeId>` and equivalents), so
|
|
7256
|
+
* re-running a job re-adopts nothing: an already-adopted candidate is SKIPPED
|
|
7257
|
+
* by the engine before any provider call and lands in `alreadyAdopted`. It is
|
|
7258
|
+
* never a duplicate device, and never an error the operator has to interpret.
|
|
7259
|
+
*/
|
|
7260
|
+
var AdoptionJobStateSchema = _enum([
|
|
7261
|
+
"running",
|
|
7262
|
+
"done",
|
|
7263
|
+
"failed",
|
|
7264
|
+
"cancelled"
|
|
7265
|
+
]);
|
|
7266
|
+
/**
|
|
7267
|
+
* Per-candidate result. Every candidate the job was asked to adopt ends in
|
|
7268
|
+
* exactly one of these buckets — there is no silent drop, and the operator can
|
|
7269
|
+
* always answer "which of my 25 landed?".
|
|
7270
|
+
*
|
|
7271
|
+
* - `adopted` — created now by this job.
|
|
7272
|
+
* - `already-adopted` — a device for this candidate existed before the job
|
|
7273
|
+
* reached it (a re-run, or a retry after a timeout). Not an error.
|
|
7274
|
+
* - `failed` — the provider threw; `error` carries the message.
|
|
7275
|
+
* - `cancelled` — the operator cancelled before this candidate was reached.
|
|
7276
|
+
*/
|
|
7277
|
+
var AdoptionOutcomeSchema = _enum([
|
|
7278
|
+
"adopted",
|
|
7279
|
+
"already-adopted",
|
|
7280
|
+
"failed",
|
|
7281
|
+
"cancelled"
|
|
7282
|
+
]);
|
|
7283
|
+
var AdoptionCandidateResultSchema = object({
|
|
7284
|
+
childNativeId: string(),
|
|
7285
|
+
outcome: AdoptionOutcomeSchema,
|
|
7286
|
+
/** The materialised parent device id — null for `failed` / `cancelled`. */
|
|
7287
|
+
parentDeviceId: number().int().nonnegative().nullable(),
|
|
7288
|
+
/** Accessory children created for this candidate. */
|
|
7289
|
+
accessoryCount: number().int().nonnegative(),
|
|
7290
|
+
/** Failure message; null unless `outcome === 'failed'`. */
|
|
7291
|
+
error: string().nullable()
|
|
7292
|
+
});
|
|
7293
|
+
var AdoptionJobSchema = object({
|
|
7294
|
+
jobId: string(),
|
|
7295
|
+
/** The integration provider this job adopts through (the `addonId` pin). */
|
|
7296
|
+
addonId: string(),
|
|
7297
|
+
integrationId: string(),
|
|
7298
|
+
state: AdoptionJobStateSchema,
|
|
7299
|
+
/** Candidates the job was asked to adopt. Known up front, so never null. */
|
|
7300
|
+
total: number().int().nonnegative(),
|
|
7301
|
+
/** Candidates that have reached a terminal bucket. */
|
|
7302
|
+
processed: number().int().nonnegative(),
|
|
7303
|
+
adopted: number().int().nonnegative(),
|
|
7304
|
+
alreadyAdopted: number().int().nonnegative(),
|
|
7305
|
+
failed: number().int().nonnegative(),
|
|
7306
|
+
/** Accessory child devices created across every candidate — the real unit
|
|
7307
|
+
* of work, surfaced so a slow job is legible rather than mysterious. */
|
|
7308
|
+
accessoriesCreated: number().int().nonnegative(),
|
|
7309
|
+
/** The candidate currently being adopted; null when idle or finished. */
|
|
7310
|
+
currentChildNativeId: string().nullable(),
|
|
7311
|
+
/** One entry per candidate, in the order they were processed. */
|
|
7312
|
+
results: array(AdoptionCandidateResultSchema).readonly(),
|
|
7313
|
+
startedAt: number(),
|
|
7314
|
+
finishedAt: number().nullable(),
|
|
7315
|
+
/** Set only when the job itself broke (not a per-candidate failure). */
|
|
7316
|
+
error: string().nullable()
|
|
7317
|
+
});
|
|
7318
|
+
/**
|
|
7221
7319
|
* Per-camera FUNCTION SWITCHES — the one coherent on/off surface over the
|
|
7222
7320
|
* pipeline functions an operator thinks in terms of.
|
|
7223
7321
|
*
|
|
@@ -12289,6 +12387,15 @@ method(object({
|
|
|
12289
12387
|
}), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
|
|
12290
12388
|
kind: "mutation",
|
|
12291
12389
|
auth: "admin"
|
|
12390
|
+
}), method(AdoptInputSchema.extend({ addonId: string() }), object({ jobId: string() }), {
|
|
12391
|
+
kind: "mutation",
|
|
12392
|
+
auth: "admin"
|
|
12393
|
+
}), method(object({
|
|
12394
|
+
addonId: string(),
|
|
12395
|
+
integrationId: string().optional()
|
|
12396
|
+
}), array(AdoptionJobSchema).readonly(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
12397
|
+
kind: "mutation",
|
|
12398
|
+
auth: "admin"
|
|
12292
12399
|
}), method(ResyncInputSchema, ResyncResultSchema, {
|
|
12293
12400
|
kind: "mutation",
|
|
12294
12401
|
auth: "admin"
|
|
@@ -18301,6 +18408,37 @@ DeviceType.Camera, method(object({
|
|
|
18301
18408
|
lastCapturedAt: number().nullable(),
|
|
18302
18409
|
cacheAgeMs: number().nullable(),
|
|
18303
18410
|
etag: string().nullable()
|
|
18411
|
+
}))), systemMethod(object({
|
|
18412
|
+
/** The tiles a surface is actually rendering. One entry per (device,
|
|
18413
|
+
* width) the caller will paint — the width is snapped to the server's
|
|
18414
|
+
* ladder and becomes part of the link's SIGNED identity. */
|
|
18415
|
+
targets: array(object({
|
|
18416
|
+
deviceId: number(),
|
|
18417
|
+
/** Target width in px. Omit for the frame as captured — correct
|
|
18418
|
+
* for a full-bleed surface, wrong (and expensive) for a grid. */
|
|
18419
|
+
width: number().int().positive().optional()
|
|
18420
|
+
})).min(1).max(200) }), array(object({
|
|
18421
|
+
deviceId: number(),
|
|
18422
|
+
/** Root-relative signed path, or null when the link plane is not
|
|
18423
|
+
* served (no data-plane facility). Present even for a device that has
|
|
18424
|
+
* never captured — the request is what triggers the first one (D94). */
|
|
18425
|
+
url: string().nullable(),
|
|
18426
|
+
/** Epoch ms of the frame this link serves. Null = never captured.
|
|
18427
|
+
* THE honest age: the tRPC path carried none before this. */
|
|
18428
|
+
capturedAt: number().nullable(),
|
|
18429
|
+
/** Age of that frame at the moment the answer was built. */
|
|
18430
|
+
ageMs: number().nullable(),
|
|
18431
|
+
/** Epoch ms after which `url` stops verifying. */
|
|
18432
|
+
expiresAt: number().nullable(),
|
|
18433
|
+
/** Ladder rung the bytes are at; null = the frame as captured. */
|
|
18434
|
+
width: number().nullable(),
|
|
18435
|
+
/** The device has never produced a frame. An empty state, not a
|
|
18436
|
+
* failure — and never a reason to withhold the link (D94). */
|
|
18437
|
+
neverCaptured: boolean(),
|
|
18438
|
+
/** A sleeping battery camera: the frame is deliberately stale and will
|
|
18439
|
+
* NOT refresh in the background. A surface should say so rather than
|
|
18440
|
+
* present it as current. */
|
|
18441
|
+
sleeping: boolean()
|
|
18304
18442
|
})));
|
|
18305
18443
|
/**
|
|
18306
18444
|
* `sso-bridge` — internal hub-only cap that lets SSO-style auth
|
|
@@ -20420,6 +20558,37 @@ onColorChanged: { data: object({
|
|
|
20420
20558
|
*/
|
|
20421
20559
|
runtimeState: ColorStatusSchema
|
|
20422
20560
|
};
|
|
20561
|
+
var ConnectionTestOutcomeSchema = discriminatedUnion("outcome", [
|
|
20562
|
+
object({
|
|
20563
|
+
outcome: literal("validated"),
|
|
20564
|
+
/** Round-trip of the sign-in, when the provider measured it. */
|
|
20565
|
+
latencyMs: number().nonnegative().optional(),
|
|
20566
|
+
/** Optional human detail worth showing next to the tick
|
|
20567
|
+
* ("3 devices visible on this account"). */
|
|
20568
|
+
detail: string().optional()
|
|
20569
|
+
}).strict(),
|
|
20570
|
+
object({
|
|
20571
|
+
outcome: literal("rejected"),
|
|
20572
|
+
error: string()
|
|
20573
|
+
}).strict(),
|
|
20574
|
+
object({
|
|
20575
|
+
outcome: literal("inconclusive"),
|
|
20576
|
+
error: string()
|
|
20577
|
+
}).strict()
|
|
20578
|
+
]);
|
|
20579
|
+
var ConnectionTestInputSchema = object({
|
|
20580
|
+
/** Candidate integration settings, exactly as the create form collected them. */
|
|
20581
|
+
settings: record(string(), unknown()) });
|
|
20582
|
+
/**
|
|
20583
|
+
* What the provider's test actually DOES, so the UI can say it in words before
|
|
20584
|
+
* the operator presses the button ("Signs in to the Dreo cloud"). Purely
|
|
20585
|
+
* descriptive — it never changes routing.
|
|
20586
|
+
*/
|
|
20587
|
+
var ConnectionTestDescriptorSchema = object({ label: string() });
|
|
20588
|
+
method(ConnectionTestInputSchema, ConnectionTestOutcomeSchema, {
|
|
20589
|
+
kind: "mutation",
|
|
20590
|
+
auth: "admin"
|
|
20591
|
+
}), method(_void(), ConnectionTestDescriptorSchema, { auth: "admin" });
|
|
20423
20592
|
/**
|
|
20424
20593
|
* Upstream-system connectivity sensor — distinct from `device-status`,
|
|
20425
20594
|
* which is the kernel-managed online/offline flag for the device's
|
|
@@ -21781,15 +21950,57 @@ var AvailableIntegrationTypeSchema = object({
|
|
|
21781
21950
|
* flow can import (e.g. HA areas). Drives the adopt modal's "import
|
|
21782
21951
|
* locations" checkbox. Provider-declared in the addon manifest. */
|
|
21783
21952
|
supportsLocationImport: boolean(),
|
|
21953
|
+
/**
|
|
21954
|
+
* True when this integration DECLARES a pre-creation test (the
|
|
21955
|
+
* `connection-test` cap, or a broker whose settings it stores). Drives the
|
|
21956
|
+
* Test button: an integration that cannot be tested must say so up front
|
|
21957
|
+
* rather than offering a button that always answers the same nonsense.
|
|
21958
|
+
*/
|
|
21959
|
+
canTest: boolean(),
|
|
21784
21960
|
existingInstances: array(object({
|
|
21785
21961
|
id: string(),
|
|
21786
21962
|
name: string()
|
|
21787
21963
|
})),
|
|
21788
21964
|
canAdd: boolean()
|
|
21789
21965
|
});
|
|
21966
|
+
/**
|
|
21967
|
+
* Why a test could not be answered as a plain boolean.
|
|
21968
|
+
*
|
|
21969
|
+
* `success` alone collapsed four different situations into one red box, and the
|
|
21970
|
+
* one that mattered most — "nobody ever asked the remote anything" — looked
|
|
21971
|
+
* exactly like "the remote said no". The status is the discriminator:
|
|
21972
|
+
*
|
|
21973
|
+
* - `validated` — a provider-declared test ran and the remote ACCEPTED.
|
|
21974
|
+
* - `rejected` — a provider-declared test ran and the remote REFUSED.
|
|
21975
|
+
* The only status that blocks `integrations.create`.
|
|
21976
|
+
* - `inconclusive` — a test IS declared but could not complete (timeout,
|
|
21977
|
+
* DNS, 5xx). Nothing was observed; not a failure.
|
|
21978
|
+
* - `unsupported` — this integration declares NO test. Nothing was
|
|
21979
|
+
* observed either; not a failure, and not a pass.
|
|
21980
|
+
*
|
|
21981
|
+
* `unsupported` and `inconclusive` both carry `success: false` so an older
|
|
21982
|
+
* client can never read them as a green tick, and both carry an `error` string
|
|
21983
|
+
* that SAYS the test did not run rather than inventing a failure.
|
|
21984
|
+
*/
|
|
21985
|
+
var TestConnectionStatusEnum = _enum([
|
|
21986
|
+
"validated",
|
|
21987
|
+
"rejected",
|
|
21988
|
+
"inconclusive",
|
|
21989
|
+
"unsupported"
|
|
21990
|
+
]);
|
|
21790
21991
|
var TestConnectionResultSchema$1 = object({
|
|
21992
|
+
/** True ONLY for `validated`. Never true for a test that did not run. */
|
|
21791
21993
|
success: boolean(),
|
|
21792
|
-
error: string().optional()
|
|
21994
|
+
error: string().optional(),
|
|
21995
|
+
/** Optional for wire back-compat with clients built before the tri-state;
|
|
21996
|
+
* the server always sets it. */
|
|
21997
|
+
status: TestConnectionStatusEnum.optional(),
|
|
21998
|
+
/** Addon id whose declared test answered — `null` when none did. Lets the UI
|
|
21999
|
+
* attribute a result instead of blaming "the integration". */
|
|
22000
|
+
testedBy: string().nullable().optional(),
|
|
22001
|
+
latencyMs: number().nonnegative().optional(),
|
|
22002
|
+
/** Human detail from a `validated` result ("3 devices on this account"). */
|
|
22003
|
+
detail: string().optional()
|
|
21793
22004
|
});
|
|
21794
22005
|
var CreateIntegrationInputSchema = object({
|
|
21795
22006
|
addonId: string(),
|
|
@@ -28012,6 +28223,18 @@ Object.freeze({
|
|
|
28012
28223
|
addonId: null,
|
|
28013
28224
|
access: "create"
|
|
28014
28225
|
},
|
|
28226
|
+
"connectionTest.describeTest": {
|
|
28227
|
+
capName: "connection-test",
|
|
28228
|
+
capScope: "system",
|
|
28229
|
+
addonId: null,
|
|
28230
|
+
access: "view"
|
|
28231
|
+
},
|
|
28232
|
+
"connectionTest.testSettings": {
|
|
28233
|
+
capName: "connection-test",
|
|
28234
|
+
capScope: "system",
|
|
28235
|
+
addonId: null,
|
|
28236
|
+
access: "create"
|
|
28237
|
+
},
|
|
28015
28238
|
"consumables.reset": {
|
|
28016
28239
|
capName: "consumables",
|
|
28017
28240
|
capScope: "device",
|
|
@@ -28402,6 +28625,12 @@ Object.freeze({
|
|
|
28402
28625
|
addonId: null,
|
|
28403
28626
|
access: "create"
|
|
28404
28627
|
},
|
|
28628
|
+
"deviceManager.adoptionCancelJob": {
|
|
28629
|
+
capName: "device-manager",
|
|
28630
|
+
capScope: "system",
|
|
28631
|
+
addonId: null,
|
|
28632
|
+
access: "create"
|
|
28633
|
+
},
|
|
28405
28634
|
"deviceManager.adoptionListCandidateFilters": {
|
|
28406
28635
|
capName: "device-manager",
|
|
28407
28636
|
capScope: "system",
|
|
@@ -28414,6 +28643,12 @@ Object.freeze({
|
|
|
28414
28643
|
addonId: null,
|
|
28415
28644
|
access: "view"
|
|
28416
28645
|
},
|
|
28646
|
+
"deviceManager.adoptionListJobs": {
|
|
28647
|
+
capName: "device-manager",
|
|
28648
|
+
capScope: "system",
|
|
28649
|
+
addonId: null,
|
|
28650
|
+
access: "view"
|
|
28651
|
+
},
|
|
28417
28652
|
"deviceManager.adoptionRefresh": {
|
|
28418
28653
|
capName: "device-manager",
|
|
28419
28654
|
capScope: "system",
|
|
@@ -28432,6 +28667,12 @@ Object.freeze({
|
|
|
28432
28667
|
addonId: null,
|
|
28433
28668
|
access: "create"
|
|
28434
28669
|
},
|
|
28670
|
+
"deviceManager.adoptionStartJob": {
|
|
28671
|
+
capName: "device-manager",
|
|
28672
|
+
capScope: "system",
|
|
28673
|
+
addonId: null,
|
|
28674
|
+
access: "create"
|
|
28675
|
+
},
|
|
28435
28676
|
"deviceManager.allocateDeviceId": {
|
|
28436
28677
|
capName: "device-manager",
|
|
28437
28678
|
capScope: "system",
|
|
@@ -31570,6 +31811,12 @@ Object.freeze({
|
|
|
31570
31811
|
addonId: null,
|
|
31571
31812
|
access: "view"
|
|
31572
31813
|
},
|
|
31814
|
+
"snapshot.getSnapshotLinks": {
|
|
31815
|
+
capName: "snapshot",
|
|
31816
|
+
capScope: "device",
|
|
31817
|
+
addonId: null,
|
|
31818
|
+
access: "view"
|
|
31819
|
+
},
|
|
31573
31820
|
"snapshot.getSnapshotOverview": {
|
|
31574
31821
|
capName: "snapshot",
|
|
31575
31822
|
capScope: "device",
|