@camstack/addon-provider-homeassistant 1.2.146 → 1.2.147
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 +3 -11
- package/dist/addon.mjs +3 -11
- package/dist/{dist-BTJhdlL4.js → dist-CslhAXSs.js} +209 -56
- package/dist/{dist-D2jDnKLv.mjs → dist-DVwhiKWh.mjs} +209 -56
- package/dist/ha-export/ha-export.addon.js +1 -1
- package/dist/ha-export/ha-export.addon.mjs +1 -1
- 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-CslhAXSs.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
let node_zlib = require("node:zlib");
|
|
5
5
|
//#region src/ha-admin-link.ts
|
|
@@ -8401,17 +8401,9 @@ var HaProviderAddon = class HaProviderAddon extends require_dist.BaseDeviceProvi
|
|
|
8401
8401
|
*/
|
|
8402
8402
|
async onRestoreDevices(savedDevices) {
|
|
8403
8403
|
const { standalone, containerChildren, rest } = partitionRestoreDevices(savedDevices);
|
|
8404
|
-
|
|
8404
|
+
await this.restoreWithBoundedRetry(standalone, async (saved) => {
|
|
8405
8405
|
await this.ctx.kernel.devices.create(saved.stableId, standaloneEntityDeviceClass, {});
|
|
8406
|
-
}
|
|
8407
|
-
this.ctx.logger.warn("ha restore: standalone entity device failed", {
|
|
8408
|
-
tags: { stableId: saved.stableId },
|
|
8409
|
-
meta: {
|
|
8410
|
-
type: saved.type,
|
|
8411
|
-
error: require_dist.errMsg(err)
|
|
8412
|
-
}
|
|
8413
|
-
});
|
|
8414
|
-
}
|
|
8406
|
+
});
|
|
8415
8407
|
await super.onRestoreDevices(rest);
|
|
8416
8408
|
}
|
|
8417
8409
|
/** Re-sync every currently-connected broker's adopted devices (Containers +
|
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as parseAdminBase, A as contactCapability, At as union, B as gasCapability, C as buildAddonRouteProvider, Ct as array, D as climateControlCapability, Dt as object, E as carbonMonoxideCapability, Et as number, G as lockControlCapability, H as humiditySensorCapability, I as enumSensorCapability, J as normalizeUnit, K as mediaPlayerCapability, L as eventEmitterCapability, M as coverCapability, Mt as url, N as deviceAdminLinkCapability, Nt as EventCategory, O as colorCapability, Ot as record, P as deviceAdoptionCapability, R as fanControlCapability, S as brokerCapability, St as _enum, T as buttonCapability, Tt as literal, U as imageCapability, V as humidifierCapability, W as lawnMowerControlCapability, X as notifierCapability, Y as notificationOutputCapability, Z as numericSensorCapability, _ as automationControlCapability, _t as DeviceType, at as smokeCapability, b as binaryCapability, bt as createEvent, ct as temperatureSensorCapability, d as TargetSchema, dt as valveCapability, et as powerMeterCapability, f as accessoriesCapability, ft as vibrationCapability, g as ambientLightSensorCapability, gt as DeviceRole, h as alarmPanelCapability, ht as DeviceFeature, it as scriptRunnerCapability, j as controlCapability, jt as unknown, k as connectivityCapability, kt as string, lt as updateCapability, m as airQualitySensorCapability, mt as weatherCapability, n as BaseDevice, nt as presenceCapability, ot as switchCapability, p as addonRoutesCapability, pt as waterHeaterCapability, q as motionCapability, r as BaseDeviceProvider, rt as pressureSensorCapability, s as EnumSensorDateTimeFormatSchema, st as tamperCapability, tt as prepareNotification, ut as vacuumControlCapability, v as batteryCapability, vt as errMsg, w as buildDeviceAdminUrl, wt as discriminatedUnion, x as brightnessCapability, y as bestLocationMatch, z as floodCapability } from "./dist-
|
|
1
|
+
import { $ as parseAdminBase, A as contactCapability, At as union, B as gasCapability, C as buildAddonRouteProvider, Ct as array, D as climateControlCapability, Dt as object, E as carbonMonoxideCapability, Et as number, G as lockControlCapability, H as humiditySensorCapability, I as enumSensorCapability, J as normalizeUnit, K as mediaPlayerCapability, L as eventEmitterCapability, M as coverCapability, Mt as url, N as deviceAdminLinkCapability, Nt as EventCategory, O as colorCapability, Ot as record, P as deviceAdoptionCapability, R as fanControlCapability, S as brokerCapability, St as _enum, T as buttonCapability, Tt as literal, U as imageCapability, V as humidifierCapability, W as lawnMowerControlCapability, X as notifierCapability, Y as notificationOutputCapability, Z as numericSensorCapability, _ as automationControlCapability, _t as DeviceType, at as smokeCapability, b as binaryCapability, bt as createEvent, ct as temperatureSensorCapability, d as TargetSchema, dt as valveCapability, et as powerMeterCapability, f as accessoriesCapability, ft as vibrationCapability, g as ambientLightSensorCapability, gt as DeviceRole, h as alarmPanelCapability, ht as DeviceFeature, it as scriptRunnerCapability, j as controlCapability, jt as unknown, k as connectivityCapability, kt as string, lt as updateCapability, m as airQualitySensorCapability, mt as weatherCapability, n as BaseDevice, nt as presenceCapability, ot as switchCapability, p as addonRoutesCapability, pt as waterHeaterCapability, q as motionCapability, r as BaseDeviceProvider, rt as pressureSensorCapability, s as EnumSensorDateTimeFormatSchema, st as tamperCapability, tt as prepareNotification, ut as vacuumControlCapability, v as batteryCapability, vt as errMsg, w as buildDeviceAdminUrl, wt as discriminatedUnion, x as brightnessCapability, y as bestLocationMatch, z as floodCapability } from "./dist-DVwhiKWh.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
|
|
4
4
|
//#region src/ha-admin-link.ts
|
|
@@ -8400,17 +8400,9 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8400
8400
|
*/
|
|
8401
8401
|
async onRestoreDevices(savedDevices) {
|
|
8402
8402
|
const { standalone, containerChildren, rest } = partitionRestoreDevices(savedDevices);
|
|
8403
|
-
|
|
8403
|
+
await this.restoreWithBoundedRetry(standalone, async (saved) => {
|
|
8404
8404
|
await this.ctx.kernel.devices.create(saved.stableId, standaloneEntityDeviceClass, {});
|
|
8405
|
-
}
|
|
8406
|
-
this.ctx.logger.warn("ha restore: standalone entity device failed", {
|
|
8407
|
-
tags: { stableId: saved.stableId },
|
|
8408
|
-
meta: {
|
|
8409
|
-
type: saved.type,
|
|
8410
|
-
error: errMsg(err)
|
|
8411
|
-
}
|
|
8412
|
-
});
|
|
8413
|
-
}
|
|
8405
|
+
});
|
|
8414
8406
|
await super.onRestoreDevices(rest);
|
|
8415
8407
|
}
|
|
8416
8408
|
/** Re-sync every currently-connected broker's adopted devices (Containers +
|
|
@@ -5370,7 +5370,7 @@ var ZodIssueCode = {
|
|
|
5370
5370
|
var ZodFirstPartyTypeKind;
|
|
5371
5371
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5372
5372
|
//#endregion
|
|
5373
|
-
//#region ../types/dist/sleep-
|
|
5373
|
+
//#region ../types/dist/sleep-DlrgY4Qb.mjs
|
|
5374
5374
|
/**
|
|
5375
5375
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5376
5376
|
* window to float samples (D455).
|
|
@@ -6985,6 +6985,10 @@ function toNodeId(idOrRunnerId) {
|
|
|
6985
6985
|
const head = idOrRunnerId.split("/")[0];
|
|
6986
6986
|
return head === void 0 || head.length === 0 ? idOrRunnerId : head;
|
|
6987
6987
|
}
|
|
6988
|
+
object({
|
|
6989
|
+
field: string().min(1),
|
|
6990
|
+
on: literal("advance")
|
|
6991
|
+
});
|
|
6988
6992
|
/**
|
|
6989
6993
|
* Output schema shared by the contribution + live methods.
|
|
6990
6994
|
*
|
|
@@ -7468,7 +7472,45 @@ function errMsg(err) {
|
|
|
7468
7472
|
return String(err);
|
|
7469
7473
|
}
|
|
7470
7474
|
//#endregion
|
|
7471
|
-
//#region ../types/dist/composition-
|
|
7475
|
+
//#region ../types/dist/composition-BK-GTFYb.mjs
|
|
7476
|
+
/**
|
|
7477
|
+
* LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
|
|
7478
|
+
* valutatore puro del suo FRONTE.
|
|
7479
|
+
*
|
|
7480
|
+
* Viveva dentro il builtin virtual-doorbell
|
|
7481
|
+
* (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
|
|
7482
|
+
* predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
|
|
7483
|
+
* `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
|
|
7484
|
+
* tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
|
|
7485
|
+
* fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
|
|
7486
|
+
* SPOSTA qui e il doorbell la ri-esporta.
|
|
7487
|
+
*
|
|
7488
|
+
* ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
|
|
7489
|
+
* due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
|
|
7490
|
+
* regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
|
|
7491
|
+
* booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
|
|
7492
|
+
*/
|
|
7493
|
+
/**
|
|
7494
|
+
* Known binary / switch source caps → the boolean slice field whose
|
|
7495
|
+
* false→true rise counts as ACTIVE. Every entry is "fire on active".
|
|
7496
|
+
* Sensors whose "active" reading is not a plain boolean (presence's string
|
|
7497
|
+
* state, connectivity's connected flag) are deliberately excluded — a
|
|
7498
|
+
* reconnect is not a doorbell press, and it is not a recording either.
|
|
7499
|
+
*/
|
|
7500
|
+
var SOURCE_CAP_ACTIVE_FIELD = {
|
|
7501
|
+
contact: "entryOpen",
|
|
7502
|
+
binary: "on",
|
|
7503
|
+
switch: "on",
|
|
7504
|
+
motion: "detected",
|
|
7505
|
+
flood: "flooded",
|
|
7506
|
+
gas: "detected",
|
|
7507
|
+
smoke: "detected",
|
|
7508
|
+
"carbon-monoxide": "detected",
|
|
7509
|
+
vibration: "detected",
|
|
7510
|
+
tamper: "tampered"
|
|
7511
|
+
};
|
|
7512
|
+
/** Cap names whose presence in a device's bindings qualify it as a source. */
|
|
7513
|
+
var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
|
|
7472
7514
|
/**
|
|
7473
7515
|
* Error types for the safe expression engine. Two distinct classes so callers
|
|
7474
7516
|
* can tell a compile-time (grammar) failure from a runtime (evaluation)
|
|
@@ -7666,6 +7708,20 @@ var table = {
|
|
|
7666
7708
|
const present = args.flatMap((a, i) => a === null ? [] : [asFiniteNumber(a, "latest", i)]);
|
|
7667
7709
|
return present.length === 0 ? null : finiteResult(Math.max(...present), "latest");
|
|
7668
7710
|
}
|
|
7711
|
+
},
|
|
7712
|
+
rose: {
|
|
7713
|
+
minArgs: 2,
|
|
7714
|
+
maxArgs: 3,
|
|
7715
|
+
apply: () => {
|
|
7716
|
+
throw new ExpressionEvalError("rose: stateful evaluation context is required");
|
|
7717
|
+
}
|
|
7718
|
+
},
|
|
7719
|
+
count: {
|
|
7720
|
+
minArgs: 1,
|
|
7721
|
+
maxArgs: 1,
|
|
7722
|
+
apply: () => {
|
|
7723
|
+
throw new ExpressionEvalError("count: stateful evaluation context is required");
|
|
7724
|
+
}
|
|
7669
7725
|
}
|
|
7670
7726
|
};
|
|
7671
7727
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -8180,6 +8236,7 @@ var CompositionItemEntrySchema = object({
|
|
|
8180
8236
|
var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
8181
8237
|
kind: literal("fields"),
|
|
8182
8238
|
cap: string().min(1),
|
|
8239
|
+
enabled: boolean().optional(),
|
|
8183
8240
|
fields: record(string().min(1), CompositionFieldSourceSchema).refine((fields) => Object.keys(fields).length <= 32, { message: `at most 32 fields per capability` }),
|
|
8184
8241
|
/**
|
|
8185
8242
|
* Items of the cap's item array, keyed by item key (D663). Naming `items`
|
|
@@ -8191,6 +8248,7 @@ var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
|
8191
8248
|
}), object({
|
|
8192
8249
|
kind: literal("passthrough"),
|
|
8193
8250
|
cap: string().min(1),
|
|
8251
|
+
enabled: boolean().optional(),
|
|
8194
8252
|
source: CompositionSourceRefSchema
|
|
8195
8253
|
})]);
|
|
8196
8254
|
var CompositionSchema = object({
|
|
@@ -13138,6 +13196,15 @@ var cameraStreamsCapability = {
|
|
|
13138
13196
|
*/
|
|
13139
13197
|
durability: "session"
|
|
13140
13198
|
};
|
|
13199
|
+
EventCategory.DoorbellOnPressed;
|
|
13200
|
+
var CompositionEventPlanSchema = object({
|
|
13201
|
+
featureIndex: number().int().nonnegative(),
|
|
13202
|
+
cap: string(),
|
|
13203
|
+
eventName: string(),
|
|
13204
|
+
category: _enum(EventCategory),
|
|
13205
|
+
stateField: string(),
|
|
13206
|
+
fieldKey: string()
|
|
13207
|
+
});
|
|
13141
13208
|
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
|
|
13142
13209
|
var CompositionProblemSchema = object({
|
|
13143
13210
|
code: _enum([
|
|
@@ -13187,12 +13254,14 @@ var CompositionUnavailableModeSchema = _enum([
|
|
|
13187
13254
|
]);
|
|
13188
13255
|
/**
|
|
13189
13256
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
13190
|
-
* field of its cap changes value · `constant` reads nothing, computed once
|
|
13257
|
+
* field of its cap changes value · `constant` reads nothing, computed once ·
|
|
13258
|
+
* `accumulator` advances from the target's seed/current output.
|
|
13191
13259
|
*/
|
|
13192
13260
|
var CompositionFieldRoleSchema = _enum([
|
|
13193
13261
|
"derived",
|
|
13194
13262
|
"clock",
|
|
13195
|
-
"constant"
|
|
13263
|
+
"constant",
|
|
13264
|
+
"accumulator"
|
|
13196
13265
|
]);
|
|
13197
13266
|
/** The item a planned field belongs to; its `path` is then the dotted path INSIDE the item. */
|
|
13198
13267
|
var CompositionFieldItemSchema = object({
|
|
@@ -13228,6 +13297,7 @@ var CompositionFeatureModeSchema = _enum([
|
|
|
13228
13297
|
"replace"
|
|
13229
13298
|
]);
|
|
13230
13299
|
var CompositionFeaturePlanSchema = object({
|
|
13300
|
+
/** Index in the stored feature array; disabled features are omitted rather than re-indexed. */
|
|
13231
13301
|
featureIndex: number().int().nonnegative(),
|
|
13232
13302
|
cap: string(),
|
|
13233
13303
|
mode: CompositionFeatureModeSchema
|
|
@@ -13236,7 +13306,8 @@ var CompositionValidationSchema = object({
|
|
|
13236
13306
|
ok: boolean(),
|
|
13237
13307
|
problems: array(CompositionProblemSchema),
|
|
13238
13308
|
fields: array(CompositionFieldPlanSchema),
|
|
13239
|
-
features: array(CompositionFeaturePlanSchema)
|
|
13309
|
+
features: array(CompositionFeaturePlanSchema),
|
|
13310
|
+
events: array(CompositionEventPlanSchema)
|
|
13240
13311
|
});
|
|
13241
13312
|
/**
|
|
13242
13313
|
* Mirrors `CoreBlockStatus`, pinned by `core-blocks-source.spec.ts`. `degraded`:
|
|
@@ -15253,6 +15324,34 @@ var DeviceBindingsForDeviceSchema = object({
|
|
|
15253
15324
|
nativeAddonId: string()
|
|
15254
15325
|
}))
|
|
15255
15326
|
});
|
|
15327
|
+
/**
|
|
15328
|
+
* The STORED wrapper activation for one `(device, capability)` pair — the row
|
|
15329
|
+
* `setWrapperActive` writes, read back three-way (D675):
|
|
15330
|
+
*
|
|
15331
|
+
* - `value` — the store answered and holds an activation: the bound wrapper's
|
|
15332
|
+
* addon id, or `null`, the operator's explicit "off" marker (D62);
|
|
15333
|
+
* - `missing` — the store answered, its activation map exists, and it holds
|
|
15334
|
+
* nothing for this pair: the operator never activated or deactivated a
|
|
15335
|
+
* wrapper here;
|
|
15336
|
+
* - `failed` — the store could not be read, holds no activation map at all
|
|
15337
|
+
* (an empty or unready snapshot, D44), or holds something that is not an
|
|
15338
|
+
* activation. It carries NO information: a consumer must never read it as
|
|
15339
|
+
* `missing` (D315, D393).
|
|
15340
|
+
*
|
|
15341
|
+
* This is the persisted INPUT, not the derived `getBindings` view — no registry
|
|
15342
|
+
* resolution, no `defaultActive` auto-binding, no cluster native-cap folding.
|
|
15343
|
+
*/
|
|
15344
|
+
var WrapperActivationReadSchema = discriminatedUnion("kind", [
|
|
15345
|
+
object({
|
|
15346
|
+
kind: literal("value"),
|
|
15347
|
+
wrapperAddonId: string().nullable()
|
|
15348
|
+
}),
|
|
15349
|
+
object({ kind: literal("missing") }),
|
|
15350
|
+
object({
|
|
15351
|
+
kind: literal("failed"),
|
|
15352
|
+
error: string()
|
|
15353
|
+
})
|
|
15354
|
+
]);
|
|
15256
15355
|
var SavedDeviceRowSchema = object({
|
|
15257
15356
|
/** Numeric id reserved at allocateDeviceId time. */
|
|
15258
15357
|
id: number(),
|
|
@@ -15603,7 +15702,10 @@ method(object({
|
|
|
15603
15702
|
}), _void(), {
|
|
15604
15703
|
kind: "mutation",
|
|
15605
15704
|
auth: "admin"
|
|
15606
|
-
}), method(object({
|
|
15705
|
+
}), method(object({
|
|
15706
|
+
deviceId: number(),
|
|
15707
|
+
capName: string().min(1)
|
|
15708
|
+
}), WrapperActivationReadSchema), method(object({ capName: string() }), array(string())), method(object({ deviceType: string() }), array(object({
|
|
15607
15709
|
capName: string(),
|
|
15608
15710
|
wrappers: array(string())
|
|
15609
15711
|
}))), method(object({ deviceId: number() }), SettingsSchemaWithValuesSchema.nullable()), method(object({ deviceId: number() }), SettingsSchemaWithValuesSchema.nullable()), method(object({ deviceId: number() }), object({
|
|
@@ -27251,44 +27353,6 @@ DeviceType.Camera, method(object({
|
|
|
27251
27353
|
auth: "protected"
|
|
27252
27354
|
});
|
|
27253
27355
|
/**
|
|
27254
|
-
* LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
|
|
27255
|
-
* valutatore puro del suo FRONTE.
|
|
27256
|
-
*
|
|
27257
|
-
* Viveva dentro il builtin virtual-doorbell
|
|
27258
|
-
* (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
|
|
27259
|
-
* predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
|
|
27260
|
-
* `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
|
|
27261
|
-
* tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
|
|
27262
|
-
* fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
|
|
27263
|
-
* SPOSTA qui e il doorbell la ri-esporta.
|
|
27264
|
-
*
|
|
27265
|
-
* ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
|
|
27266
|
-
* due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
|
|
27267
|
-
* regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
|
|
27268
|
-
* booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
|
|
27269
|
-
*/
|
|
27270
|
-
/**
|
|
27271
|
-
* Known binary / switch source caps → the boolean slice field whose
|
|
27272
|
-
* false→true rise counts as ACTIVE. Every entry is "fire on active".
|
|
27273
|
-
* Sensors whose "active" reading is not a plain boolean (presence's string
|
|
27274
|
-
* state, connectivity's connected flag) are deliberately excluded — a
|
|
27275
|
-
* reconnect is not a doorbell press, and it is not a recording either.
|
|
27276
|
-
*/
|
|
27277
|
-
var SOURCE_CAP_ACTIVE_FIELD = {
|
|
27278
|
-
contact: "entryOpen",
|
|
27279
|
-
binary: "on",
|
|
27280
|
-
switch: "on",
|
|
27281
|
-
motion: "detected",
|
|
27282
|
-
flood: "flooded",
|
|
27283
|
-
gas: "detected",
|
|
27284
|
-
smoke: "detected",
|
|
27285
|
-
"carbon-monoxide": "detected",
|
|
27286
|
-
vibration: "detected",
|
|
27287
|
-
tamper: "tampered"
|
|
27288
|
-
};
|
|
27289
|
-
/** Cap names whose presence in a device's bindings qualify it as a source. */
|
|
27290
|
-
var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
|
|
27291
|
-
/**
|
|
27292
27356
|
* Doorbell button cap. Two kinds of providers coexist behind this cap
|
|
27293
27357
|
* name (same pattern as `snapshot`):
|
|
27294
27358
|
*
|
|
@@ -27361,7 +27425,13 @@ var doorbellCapability = {
|
|
|
27361
27425
|
* push (`ReolinkSimpleEvent.type === 'doorbell'`). There is no
|
|
27362
27426
|
* release/duration — it's a pulse.
|
|
27363
27427
|
*/
|
|
27364
|
-
onPressed: {
|
|
27428
|
+
onPressed: {
|
|
27429
|
+
data: DoorbellPressEventSchema,
|
|
27430
|
+
fromState: {
|
|
27431
|
+
field: "lastPressedAt",
|
|
27432
|
+
on: "advance"
|
|
27433
|
+
}
|
|
27434
|
+
} },
|
|
27365
27435
|
status: {
|
|
27366
27436
|
schema: DoorbellStatusSchema,
|
|
27367
27437
|
kind: "push"
|
|
@@ -35248,12 +35318,21 @@ var RecordingStorageUsageSchema = object({
|
|
|
35248
35318
|
*/
|
|
35249
35319
|
devicesRead: _enum(["archive", "unavailable"]).optional(),
|
|
35250
35320
|
/**
|
|
35321
|
+
* Is the `archive` answer WHOLE (D674): the hour ledger loaded without
|
|
35322
|
+
* hitting its row cap, and at least one location resolved? Only then may a
|
|
35323
|
+
* camera ABSENT from `devices[]` be read as "no footage" — and only then is a
|
|
35324
|
+
* row's `oldestMs` the archive's oldest rather than the oldest of a fraction.
|
|
35325
|
+
* OPTIONAL: a recorder predating D674 omits it, and a consumer must read the
|
|
35326
|
+
* absence as `false`. A retention floor is a delete; it never guesses.
|
|
35327
|
+
*/
|
|
35328
|
+
complete: boolean().optional(),
|
|
35329
|
+
/**
|
|
35251
35330
|
* Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
|
|
35252
|
-
* of the payload without it:
|
|
35253
|
-
*
|
|
35254
|
-
*
|
|
35255
|
-
*
|
|
35256
|
-
*
|
|
35331
|
+
* of the payload without it: a recorder that could not build it omits it. A
|
|
35332
|
+
* UI that dereferences it unconditionally crashes the whole Storage page over
|
|
35333
|
+
* a missing disk bar. (The hub's cap routers pass output through unvalidated
|
|
35334
|
+
* since 2026-07-11 — `cap-router-builder.ts` — so a newer recorder's fields
|
|
35335
|
+
* are not stripped by an older hub.)
|
|
35257
35336
|
*/
|
|
35258
35337
|
locations: array(RecordingLocationUsageSchema).optional()
|
|
35259
35338
|
});
|
|
@@ -38772,7 +38851,12 @@ Object.freeze({
|
|
|
38772
38851
|
number: {
|
|
38773
38852
|
kind: "fixed",
|
|
38774
38853
|
result: ["number", "null"]
|
|
38775
|
-
}
|
|
38854
|
+
},
|
|
38855
|
+
rose: {
|
|
38856
|
+
kind: "fixed",
|
|
38857
|
+
result: ["number", "null"]
|
|
38858
|
+
},
|
|
38859
|
+
count: NUMBER
|
|
38776
38860
|
});
|
|
38777
38861
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
38778
38862
|
/**
|
|
@@ -39813,6 +39897,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
39813
39897
|
}];
|
|
39814
39898
|
}
|
|
39815
39899
|
async onShutdown() {
|
|
39900
|
+
this._restoreShuttingDown = true;
|
|
39816
39901
|
this.cancelRestoreRetries();
|
|
39817
39902
|
const devices = await this.ctx.kernel.devices?.getAll() ?? [];
|
|
39818
39903
|
for (const device of devices) try {
|
|
@@ -39930,6 +40015,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
39930
40015
|
};
|
|
39931
40016
|
}
|
|
39932
40017
|
async restoreDevices(savedDevices) {
|
|
40018
|
+
this._restoreShuttingDown = false;
|
|
39933
40019
|
const report = await this.onRestoreDevices(savedDevices);
|
|
39934
40020
|
if (savedDevices.length === 0) return;
|
|
39935
40021
|
if (report && report.failedCount > 0) {
|
|
@@ -39944,8 +40030,13 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
39944
40030
|
/** Retry lane width. See `device-restore-retry.ts` for why retries
|
|
39945
40031
|
* never re-stampede full-width while the initial pass does (D167). */
|
|
39946
40032
|
restoreRetryConcurrency = 4;
|
|
39947
|
-
|
|
40033
|
+
/** Every bounded retry this process started — the default pass's, and each
|
|
40034
|
+
* `restoreWithBoundedRetry` of a provider's own rows. Cancelled together. */
|
|
40035
|
+
_restoreRetrySchedulers = [];
|
|
39948
40036
|
_restoreRetryCompletion = null;
|
|
40037
|
+
/** Set by `onShutdown` BEFORE the retries are cancelled: a restore pass still
|
|
40038
|
+
* in flight must not start a scheduler after the cancel ran. */
|
|
40039
|
+
_restoreShuttingDown = false;
|
|
39949
40040
|
_permanentRestoreFailures = /* @__PURE__ */ new Map();
|
|
39950
40041
|
/** Settles when the background retry rounds finish (or `null` when
|
|
39951
40042
|
* nothing failed). Exposed for tests and subclass diagnostics —
|
|
@@ -40014,8 +40105,8 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
40014
40105
|
});
|
|
40015
40106
|
}
|
|
40016
40107
|
cancelRestoreRetries() {
|
|
40017
|
-
this.
|
|
40018
|
-
this.
|
|
40108
|
+
for (const scheduler of this._restoreRetrySchedulers) scheduler.cancel();
|
|
40109
|
+
this._restoreRetrySchedulers = [];
|
|
40019
40110
|
}
|
|
40020
40111
|
recordPermanentRestoreFailure(failure) {
|
|
40021
40112
|
this._permanentRestoreFailures.set(failure.deviceId, failure);
|
|
@@ -40032,6 +40123,13 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
40032
40123
|
});
|
|
40033
40124
|
}
|
|
40034
40125
|
scheduleRestoreRetries(failures, attempt) {
|
|
40126
|
+
if (this._restoreShuttingDown) {
|
|
40127
|
+
this.ctx.logger.info("Restore retries not scheduled — the provider is shutting down", { meta: {
|
|
40128
|
+
rows: failures.length,
|
|
40129
|
+
deviceIds: failures.map((f) => f.saved.id)
|
|
40130
|
+
} });
|
|
40131
|
+
return;
|
|
40132
|
+
}
|
|
40035
40133
|
const scheduler = new DeviceRestoreRetryScheduler({
|
|
40036
40134
|
logger: this.ctx.logger,
|
|
40037
40135
|
delaysMs: this.restoreRetryDelaysMs,
|
|
@@ -40039,10 +40137,54 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
40039
40137
|
attempt,
|
|
40040
40138
|
onPermanentFailure: (failure) => this.recordPermanentRestoreFailure(failure)
|
|
40041
40139
|
});
|
|
40042
|
-
this.
|
|
40043
|
-
|
|
40140
|
+
this._restoreRetrySchedulers = [...this._restoreRetrySchedulers, scheduler];
|
|
40141
|
+
const run = scheduler.run(failures).then(() => void 0).catch((err) => {
|
|
40044
40142
|
this.ctx.logger.error("Restore retry scheduler crashed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
40045
40143
|
});
|
|
40144
|
+
const prior = this._restoreRetryCompletion;
|
|
40145
|
+
this._restoreRetryCompletion = prior === null ? run : Promise.all([prior, run]).then(() => void 0);
|
|
40146
|
+
}
|
|
40147
|
+
/**
|
|
40148
|
+
* Restore rows the default pass does not handle (a provider's own restore
|
|
40149
|
+
* loop) under the SAME bounded retry (D347): one attempt each, in order; a
|
|
40150
|
+
* row that throws is logged with `tags.deviceId` and retried in the
|
|
40151
|
+
* background rounds, then marked permanently failed like any other.
|
|
40152
|
+
*
|
|
40153
|
+
* A restore is a device build, and a build whose runtime-state load did not
|
|
40154
|
+
* answer is REFUSED rather than started on cold defaults (D677) — so a
|
|
40155
|
+
* transient hub read must cost a retry, never the device until the next
|
|
40156
|
+
* runner restart. An ANSWERED `{}` is the device's true state and never
|
|
40157
|
+
* refuses.
|
|
40158
|
+
*/
|
|
40159
|
+
async restoreWithBoundedRetry(rows, attempt) {
|
|
40160
|
+
const failures = [];
|
|
40161
|
+
for (const saved of rows) {
|
|
40162
|
+
if (this.isUnrestorableShell(saved)) {
|
|
40163
|
+
this.quarantineShell(saved);
|
|
40164
|
+
continue;
|
|
40165
|
+
}
|
|
40166
|
+
try {
|
|
40167
|
+
await attempt(saved);
|
|
40168
|
+
} catch (err) {
|
|
40169
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
40170
|
+
this.ctx.logger.warn("Failed to restore device — bounded retry scheduled", {
|
|
40171
|
+
tags: {
|
|
40172
|
+
deviceId: saved.id,
|
|
40173
|
+
stableId: saved.stableId
|
|
40174
|
+
},
|
|
40175
|
+
meta: {
|
|
40176
|
+
type: saved.type,
|
|
40177
|
+
attempt: 1,
|
|
40178
|
+
error
|
|
40179
|
+
}
|
|
40180
|
+
});
|
|
40181
|
+
failures.push({
|
|
40182
|
+
saved,
|
|
40183
|
+
error
|
|
40184
|
+
});
|
|
40185
|
+
}
|
|
40186
|
+
}
|
|
40187
|
+
if (failures.length > 0) this.scheduleRestoreRetries(failures, attempt);
|
|
40046
40188
|
}
|
|
40047
40189
|
/**
|
|
40048
40190
|
* Tear down and reconstruct ONE device from its persisted rows — the
|
|
@@ -43352,6 +43494,12 @@ Object.freeze({
|
|
|
43352
43494
|
addonId: null,
|
|
43353
43495
|
access: "view"
|
|
43354
43496
|
},
|
|
43497
|
+
"deviceManager.getWrapperActivation": {
|
|
43498
|
+
capName: "device-manager",
|
|
43499
|
+
capScope: "system",
|
|
43500
|
+
addonId: null,
|
|
43501
|
+
access: "view"
|
|
43502
|
+
},
|
|
43355
43503
|
"deviceManager.listAll": {
|
|
43356
43504
|
capName: "device-manager",
|
|
43357
43505
|
capScope: "system",
|
|
@@ -49161,6 +49309,11 @@ Object.freeze({
|
|
|
49161
49309
|
form: "single",
|
|
49162
49310
|
optional: false
|
|
49163
49311
|
}],
|
|
49312
|
+
"deviceManager.getWrapperActivation": [{
|
|
49313
|
+
name: "deviceId",
|
|
49314
|
+
form: "single",
|
|
49315
|
+
optional: false
|
|
49316
|
+
}],
|
|
49164
49317
|
"deviceManager.listAll": [{
|
|
49165
49318
|
name: "deviceIds",
|
|
49166
49319
|
form: "array",
|
|
@@ -5369,7 +5369,7 @@ var ZodIssueCode = {
|
|
|
5369
5369
|
var ZodFirstPartyTypeKind;
|
|
5370
5370
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5371
5371
|
//#endregion
|
|
5372
|
-
//#region ../types/dist/sleep-
|
|
5372
|
+
//#region ../types/dist/sleep-DlrgY4Qb.mjs
|
|
5373
5373
|
/**
|
|
5374
5374
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5375
5375
|
* window to float samples (D455).
|
|
@@ -6984,6 +6984,10 @@ function toNodeId(idOrRunnerId) {
|
|
|
6984
6984
|
const head = idOrRunnerId.split("/")[0];
|
|
6985
6985
|
return head === void 0 || head.length === 0 ? idOrRunnerId : head;
|
|
6986
6986
|
}
|
|
6987
|
+
object({
|
|
6988
|
+
field: string().min(1),
|
|
6989
|
+
on: literal("advance")
|
|
6990
|
+
});
|
|
6987
6991
|
/**
|
|
6988
6992
|
* Output schema shared by the contribution + live methods.
|
|
6989
6993
|
*
|
|
@@ -7467,7 +7471,45 @@ function errMsg(err) {
|
|
|
7467
7471
|
return String(err);
|
|
7468
7472
|
}
|
|
7469
7473
|
//#endregion
|
|
7470
|
-
//#region ../types/dist/composition-
|
|
7474
|
+
//#region ../types/dist/composition-BK-GTFYb.mjs
|
|
7475
|
+
/**
|
|
7476
|
+
* LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
|
|
7477
|
+
* valutatore puro del suo FRONTE.
|
|
7478
|
+
*
|
|
7479
|
+
* Viveva dentro il builtin virtual-doorbell
|
|
7480
|
+
* (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
|
|
7481
|
+
* predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
|
|
7482
|
+
* `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
|
|
7483
|
+
* tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
|
|
7484
|
+
* fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
|
|
7485
|
+
* SPOSTA qui e il doorbell la ri-esporta.
|
|
7486
|
+
*
|
|
7487
|
+
* ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
|
|
7488
|
+
* due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
|
|
7489
|
+
* regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
|
|
7490
|
+
* booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
|
|
7491
|
+
*/
|
|
7492
|
+
/**
|
|
7493
|
+
* Known binary / switch source caps → the boolean slice field whose
|
|
7494
|
+
* false→true rise counts as ACTIVE. Every entry is "fire on active".
|
|
7495
|
+
* Sensors whose "active" reading is not a plain boolean (presence's string
|
|
7496
|
+
* state, connectivity's connected flag) are deliberately excluded — a
|
|
7497
|
+
* reconnect is not a doorbell press, and it is not a recording either.
|
|
7498
|
+
*/
|
|
7499
|
+
var SOURCE_CAP_ACTIVE_FIELD = {
|
|
7500
|
+
contact: "entryOpen",
|
|
7501
|
+
binary: "on",
|
|
7502
|
+
switch: "on",
|
|
7503
|
+
motion: "detected",
|
|
7504
|
+
flood: "flooded",
|
|
7505
|
+
gas: "detected",
|
|
7506
|
+
smoke: "detected",
|
|
7507
|
+
"carbon-monoxide": "detected",
|
|
7508
|
+
vibration: "detected",
|
|
7509
|
+
tamper: "tampered"
|
|
7510
|
+
};
|
|
7511
|
+
/** Cap names whose presence in a device's bindings qualify it as a source. */
|
|
7512
|
+
var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
|
|
7471
7513
|
/**
|
|
7472
7514
|
* Error types for the safe expression engine. Two distinct classes so callers
|
|
7473
7515
|
* can tell a compile-time (grammar) failure from a runtime (evaluation)
|
|
@@ -7665,6 +7707,20 @@ var table = {
|
|
|
7665
7707
|
const present = args.flatMap((a, i) => a === null ? [] : [asFiniteNumber(a, "latest", i)]);
|
|
7666
7708
|
return present.length === 0 ? null : finiteResult(Math.max(...present), "latest");
|
|
7667
7709
|
}
|
|
7710
|
+
},
|
|
7711
|
+
rose: {
|
|
7712
|
+
minArgs: 2,
|
|
7713
|
+
maxArgs: 3,
|
|
7714
|
+
apply: () => {
|
|
7715
|
+
throw new ExpressionEvalError("rose: stateful evaluation context is required");
|
|
7716
|
+
}
|
|
7717
|
+
},
|
|
7718
|
+
count: {
|
|
7719
|
+
minArgs: 1,
|
|
7720
|
+
maxArgs: 1,
|
|
7721
|
+
apply: () => {
|
|
7722
|
+
throw new ExpressionEvalError("count: stateful evaluation context is required");
|
|
7723
|
+
}
|
|
7668
7724
|
}
|
|
7669
7725
|
};
|
|
7670
7726
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -8179,6 +8235,7 @@ var CompositionItemEntrySchema = object({
|
|
|
8179
8235
|
var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
8180
8236
|
kind: literal("fields"),
|
|
8181
8237
|
cap: string().min(1),
|
|
8238
|
+
enabled: boolean().optional(),
|
|
8182
8239
|
fields: record(string().min(1), CompositionFieldSourceSchema).refine((fields) => Object.keys(fields).length <= 32, { message: `at most 32 fields per capability` }),
|
|
8183
8240
|
/**
|
|
8184
8241
|
* Items of the cap's item array, keyed by item key (D663). Naming `items`
|
|
@@ -8190,6 +8247,7 @@ var CompositionFeatureSchema = discriminatedUnion("kind", [object({
|
|
|
8190
8247
|
}), object({
|
|
8191
8248
|
kind: literal("passthrough"),
|
|
8192
8249
|
cap: string().min(1),
|
|
8250
|
+
enabled: boolean().optional(),
|
|
8193
8251
|
source: CompositionSourceRefSchema
|
|
8194
8252
|
})]);
|
|
8195
8253
|
var CompositionSchema = object({
|
|
@@ -13137,6 +13195,15 @@ var cameraStreamsCapability = {
|
|
|
13137
13195
|
*/
|
|
13138
13196
|
durability: "session"
|
|
13139
13197
|
};
|
|
13198
|
+
EventCategory.DoorbellOnPressed;
|
|
13199
|
+
var CompositionEventPlanSchema = object({
|
|
13200
|
+
featureIndex: number().int().nonnegative(),
|
|
13201
|
+
cap: string(),
|
|
13202
|
+
eventName: string(),
|
|
13203
|
+
category: _enum(EventCategory),
|
|
13204
|
+
stateField: string(),
|
|
13205
|
+
fieldKey: string()
|
|
13206
|
+
});
|
|
13140
13207
|
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
|
|
13141
13208
|
var CompositionProblemSchema = object({
|
|
13142
13209
|
code: _enum([
|
|
@@ -13186,12 +13253,14 @@ var CompositionUnavailableModeSchema = _enum([
|
|
|
13186
13253
|
]);
|
|
13187
13254
|
/**
|
|
13188
13255
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
13189
|
-
* field of its cap changes value · `constant` reads nothing, computed once
|
|
13256
|
+
* field of its cap changes value · `constant` reads nothing, computed once ·
|
|
13257
|
+
* `accumulator` advances from the target's seed/current output.
|
|
13190
13258
|
*/
|
|
13191
13259
|
var CompositionFieldRoleSchema = _enum([
|
|
13192
13260
|
"derived",
|
|
13193
13261
|
"clock",
|
|
13194
|
-
"constant"
|
|
13262
|
+
"constant",
|
|
13263
|
+
"accumulator"
|
|
13195
13264
|
]);
|
|
13196
13265
|
/** The item a planned field belongs to; its `path` is then the dotted path INSIDE the item. */
|
|
13197
13266
|
var CompositionFieldItemSchema = object({
|
|
@@ -13227,6 +13296,7 @@ var CompositionFeatureModeSchema = _enum([
|
|
|
13227
13296
|
"replace"
|
|
13228
13297
|
]);
|
|
13229
13298
|
var CompositionFeaturePlanSchema = object({
|
|
13299
|
+
/** Index in the stored feature array; disabled features are omitted rather than re-indexed. */
|
|
13230
13300
|
featureIndex: number().int().nonnegative(),
|
|
13231
13301
|
cap: string(),
|
|
13232
13302
|
mode: CompositionFeatureModeSchema
|
|
@@ -13235,7 +13305,8 @@ var CompositionValidationSchema = object({
|
|
|
13235
13305
|
ok: boolean(),
|
|
13236
13306
|
problems: array(CompositionProblemSchema),
|
|
13237
13307
|
fields: array(CompositionFieldPlanSchema),
|
|
13238
|
-
features: array(CompositionFeaturePlanSchema)
|
|
13308
|
+
features: array(CompositionFeaturePlanSchema),
|
|
13309
|
+
events: array(CompositionEventPlanSchema)
|
|
13239
13310
|
});
|
|
13240
13311
|
/**
|
|
13241
13312
|
* Mirrors `CoreBlockStatus`, pinned by `core-blocks-source.spec.ts`. `degraded`:
|
|
@@ -15252,6 +15323,34 @@ var DeviceBindingsForDeviceSchema = object({
|
|
|
15252
15323
|
nativeAddonId: string()
|
|
15253
15324
|
}))
|
|
15254
15325
|
});
|
|
15326
|
+
/**
|
|
15327
|
+
* The STORED wrapper activation for one `(device, capability)` pair — the row
|
|
15328
|
+
* `setWrapperActive` writes, read back three-way (D675):
|
|
15329
|
+
*
|
|
15330
|
+
* - `value` — the store answered and holds an activation: the bound wrapper's
|
|
15331
|
+
* addon id, or `null`, the operator's explicit "off" marker (D62);
|
|
15332
|
+
* - `missing` — the store answered, its activation map exists, and it holds
|
|
15333
|
+
* nothing for this pair: the operator never activated or deactivated a
|
|
15334
|
+
* wrapper here;
|
|
15335
|
+
* - `failed` — the store could not be read, holds no activation map at all
|
|
15336
|
+
* (an empty or unready snapshot, D44), or holds something that is not an
|
|
15337
|
+
* activation. It carries NO information: a consumer must never read it as
|
|
15338
|
+
* `missing` (D315, D393).
|
|
15339
|
+
*
|
|
15340
|
+
* This is the persisted INPUT, not the derived `getBindings` view — no registry
|
|
15341
|
+
* resolution, no `defaultActive` auto-binding, no cluster native-cap folding.
|
|
15342
|
+
*/
|
|
15343
|
+
var WrapperActivationReadSchema = discriminatedUnion("kind", [
|
|
15344
|
+
object({
|
|
15345
|
+
kind: literal("value"),
|
|
15346
|
+
wrapperAddonId: string().nullable()
|
|
15347
|
+
}),
|
|
15348
|
+
object({ kind: literal("missing") }),
|
|
15349
|
+
object({
|
|
15350
|
+
kind: literal("failed"),
|
|
15351
|
+
error: string()
|
|
15352
|
+
})
|
|
15353
|
+
]);
|
|
15255
15354
|
var SavedDeviceRowSchema = object({
|
|
15256
15355
|
/** Numeric id reserved at allocateDeviceId time. */
|
|
15257
15356
|
id: number(),
|
|
@@ -15602,7 +15701,10 @@ method(object({
|
|
|
15602
15701
|
}), _void(), {
|
|
15603
15702
|
kind: "mutation",
|
|
15604
15703
|
auth: "admin"
|
|
15605
|
-
}), method(object({
|
|
15704
|
+
}), method(object({
|
|
15705
|
+
deviceId: number(),
|
|
15706
|
+
capName: string().min(1)
|
|
15707
|
+
}), WrapperActivationReadSchema), method(object({ capName: string() }), array(string())), method(object({ deviceType: string() }), array(object({
|
|
15606
15708
|
capName: string(),
|
|
15607
15709
|
wrappers: array(string())
|
|
15608
15710
|
}))), method(object({ deviceId: number() }), SettingsSchemaWithValuesSchema.nullable()), method(object({ deviceId: number() }), SettingsSchemaWithValuesSchema.nullable()), method(object({ deviceId: number() }), object({
|
|
@@ -27250,44 +27352,6 @@ DeviceType.Camera, method(object({
|
|
|
27250
27352
|
auth: "protected"
|
|
27251
27353
|
});
|
|
27252
27354
|
/**
|
|
27253
|
-
* LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
|
|
27254
|
-
* valutatore puro del suo FRONTE.
|
|
27255
|
-
*
|
|
27256
|
-
* Viveva dentro il builtin virtual-doorbell
|
|
27257
|
-
* (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
|
|
27258
|
-
* predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
|
|
27259
|
-
* `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
|
|
27260
|
-
* tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
|
|
27261
|
-
* fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
|
|
27262
|
-
* SPOSTA qui e il doorbell la ri-esporta.
|
|
27263
|
-
*
|
|
27264
|
-
* ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
|
|
27265
|
-
* due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
|
|
27266
|
-
* regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
|
|
27267
|
-
* booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
|
|
27268
|
-
*/
|
|
27269
|
-
/**
|
|
27270
|
-
* Known binary / switch source caps → the boolean slice field whose
|
|
27271
|
-
* false→true rise counts as ACTIVE. Every entry is "fire on active".
|
|
27272
|
-
* Sensors whose "active" reading is not a plain boolean (presence's string
|
|
27273
|
-
* state, connectivity's connected flag) are deliberately excluded — a
|
|
27274
|
-
* reconnect is not a doorbell press, and it is not a recording either.
|
|
27275
|
-
*/
|
|
27276
|
-
var SOURCE_CAP_ACTIVE_FIELD = {
|
|
27277
|
-
contact: "entryOpen",
|
|
27278
|
-
binary: "on",
|
|
27279
|
-
switch: "on",
|
|
27280
|
-
motion: "detected",
|
|
27281
|
-
flood: "flooded",
|
|
27282
|
-
gas: "detected",
|
|
27283
|
-
smoke: "detected",
|
|
27284
|
-
"carbon-monoxide": "detected",
|
|
27285
|
-
vibration: "detected",
|
|
27286
|
-
tamper: "tampered"
|
|
27287
|
-
};
|
|
27288
|
-
/** Cap names whose presence in a device's bindings qualify it as a source. */
|
|
27289
|
-
var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
|
|
27290
|
-
/**
|
|
27291
27355
|
* Doorbell button cap. Two kinds of providers coexist behind this cap
|
|
27292
27356
|
* name (same pattern as `snapshot`):
|
|
27293
27357
|
*
|
|
@@ -27360,7 +27424,13 @@ var doorbellCapability = {
|
|
|
27360
27424
|
* push (`ReolinkSimpleEvent.type === 'doorbell'`). There is no
|
|
27361
27425
|
* release/duration — it's a pulse.
|
|
27362
27426
|
*/
|
|
27363
|
-
onPressed: {
|
|
27427
|
+
onPressed: {
|
|
27428
|
+
data: DoorbellPressEventSchema,
|
|
27429
|
+
fromState: {
|
|
27430
|
+
field: "lastPressedAt",
|
|
27431
|
+
on: "advance"
|
|
27432
|
+
}
|
|
27433
|
+
} },
|
|
27364
27434
|
status: {
|
|
27365
27435
|
schema: DoorbellStatusSchema,
|
|
27366
27436
|
kind: "push"
|
|
@@ -35247,12 +35317,21 @@ var RecordingStorageUsageSchema = object({
|
|
|
35247
35317
|
*/
|
|
35248
35318
|
devicesRead: _enum(["archive", "unavailable"]).optional(),
|
|
35249
35319
|
/**
|
|
35320
|
+
* Is the `archive` answer WHOLE (D674): the hour ledger loaded without
|
|
35321
|
+
* hitting its row cap, and at least one location resolved? Only then may a
|
|
35322
|
+
* camera ABSENT from `devices[]` be read as "no footage" — and only then is a
|
|
35323
|
+
* row's `oldestMs` the archive's oldest rather than the oldest of a fraction.
|
|
35324
|
+
* OPTIONAL: a recorder predating D674 omits it, and a consumer must read the
|
|
35325
|
+
* absence as `false`. A retention floor is a delete; it never guesses.
|
|
35326
|
+
*/
|
|
35327
|
+
complete: boolean().optional(),
|
|
35328
|
+
/**
|
|
35250
35329
|
* Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
|
|
35251
|
-
* of the payload without it:
|
|
35252
|
-
*
|
|
35253
|
-
*
|
|
35254
|
-
*
|
|
35255
|
-
*
|
|
35330
|
+
* of the payload without it: a recorder that could not build it omits it. A
|
|
35331
|
+
* UI that dereferences it unconditionally crashes the whole Storage page over
|
|
35332
|
+
* a missing disk bar. (The hub's cap routers pass output through unvalidated
|
|
35333
|
+
* since 2026-07-11 — `cap-router-builder.ts` — so a newer recorder's fields
|
|
35334
|
+
* are not stripped by an older hub.)
|
|
35256
35335
|
*/
|
|
35257
35336
|
locations: array(RecordingLocationUsageSchema).optional()
|
|
35258
35337
|
});
|
|
@@ -38771,7 +38850,12 @@ Object.freeze({
|
|
|
38771
38850
|
number: {
|
|
38772
38851
|
kind: "fixed",
|
|
38773
38852
|
result: ["number", "null"]
|
|
38774
|
-
}
|
|
38853
|
+
},
|
|
38854
|
+
rose: {
|
|
38855
|
+
kind: "fixed",
|
|
38856
|
+
result: ["number", "null"]
|
|
38857
|
+
},
|
|
38858
|
+
count: NUMBER
|
|
38775
38859
|
});
|
|
38776
38860
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
38777
38861
|
/**
|
|
@@ -39812,6 +39896,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
39812
39896
|
}];
|
|
39813
39897
|
}
|
|
39814
39898
|
async onShutdown() {
|
|
39899
|
+
this._restoreShuttingDown = true;
|
|
39815
39900
|
this.cancelRestoreRetries();
|
|
39816
39901
|
const devices = await this.ctx.kernel.devices?.getAll() ?? [];
|
|
39817
39902
|
for (const device of devices) try {
|
|
@@ -39929,6 +40014,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
39929
40014
|
};
|
|
39930
40015
|
}
|
|
39931
40016
|
async restoreDevices(savedDevices) {
|
|
40017
|
+
this._restoreShuttingDown = false;
|
|
39932
40018
|
const report = await this.onRestoreDevices(savedDevices);
|
|
39933
40019
|
if (savedDevices.length === 0) return;
|
|
39934
40020
|
if (report && report.failedCount > 0) {
|
|
@@ -39943,8 +40029,13 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
39943
40029
|
/** Retry lane width. See `device-restore-retry.ts` for why retries
|
|
39944
40030
|
* never re-stampede full-width while the initial pass does (D167). */
|
|
39945
40031
|
restoreRetryConcurrency = 4;
|
|
39946
|
-
|
|
40032
|
+
/** Every bounded retry this process started — the default pass's, and each
|
|
40033
|
+
* `restoreWithBoundedRetry` of a provider's own rows. Cancelled together. */
|
|
40034
|
+
_restoreRetrySchedulers = [];
|
|
39947
40035
|
_restoreRetryCompletion = null;
|
|
40036
|
+
/** Set by `onShutdown` BEFORE the retries are cancelled: a restore pass still
|
|
40037
|
+
* in flight must not start a scheduler after the cancel ran. */
|
|
40038
|
+
_restoreShuttingDown = false;
|
|
39948
40039
|
_permanentRestoreFailures = /* @__PURE__ */ new Map();
|
|
39949
40040
|
/** Settles when the background retry rounds finish (or `null` when
|
|
39950
40041
|
* nothing failed). Exposed for tests and subclass diagnostics —
|
|
@@ -40013,8 +40104,8 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
40013
40104
|
});
|
|
40014
40105
|
}
|
|
40015
40106
|
cancelRestoreRetries() {
|
|
40016
|
-
this.
|
|
40017
|
-
this.
|
|
40107
|
+
for (const scheduler of this._restoreRetrySchedulers) scheduler.cancel();
|
|
40108
|
+
this._restoreRetrySchedulers = [];
|
|
40018
40109
|
}
|
|
40019
40110
|
recordPermanentRestoreFailure(failure) {
|
|
40020
40111
|
this._permanentRestoreFailures.set(failure.deviceId, failure);
|
|
@@ -40031,6 +40122,13 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
40031
40122
|
});
|
|
40032
40123
|
}
|
|
40033
40124
|
scheduleRestoreRetries(failures, attempt) {
|
|
40125
|
+
if (this._restoreShuttingDown) {
|
|
40126
|
+
this.ctx.logger.info("Restore retries not scheduled — the provider is shutting down", { meta: {
|
|
40127
|
+
rows: failures.length,
|
|
40128
|
+
deviceIds: failures.map((f) => f.saved.id)
|
|
40129
|
+
} });
|
|
40130
|
+
return;
|
|
40131
|
+
}
|
|
40034
40132
|
const scheduler = new DeviceRestoreRetryScheduler({
|
|
40035
40133
|
logger: this.ctx.logger,
|
|
40036
40134
|
delaysMs: this.restoreRetryDelaysMs,
|
|
@@ -40038,10 +40136,54 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
40038
40136
|
attempt,
|
|
40039
40137
|
onPermanentFailure: (failure) => this.recordPermanentRestoreFailure(failure)
|
|
40040
40138
|
});
|
|
40041
|
-
this.
|
|
40042
|
-
|
|
40139
|
+
this._restoreRetrySchedulers = [...this._restoreRetrySchedulers, scheduler];
|
|
40140
|
+
const run = scheduler.run(failures).then(() => void 0).catch((err) => {
|
|
40043
40141
|
this.ctx.logger.error("Restore retry scheduler crashed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
40044
40142
|
});
|
|
40143
|
+
const prior = this._restoreRetryCompletion;
|
|
40144
|
+
this._restoreRetryCompletion = prior === null ? run : Promise.all([prior, run]).then(() => void 0);
|
|
40145
|
+
}
|
|
40146
|
+
/**
|
|
40147
|
+
* Restore rows the default pass does not handle (a provider's own restore
|
|
40148
|
+
* loop) under the SAME bounded retry (D347): one attempt each, in order; a
|
|
40149
|
+
* row that throws is logged with `tags.deviceId` and retried in the
|
|
40150
|
+
* background rounds, then marked permanently failed like any other.
|
|
40151
|
+
*
|
|
40152
|
+
* A restore is a device build, and a build whose runtime-state load did not
|
|
40153
|
+
* answer is REFUSED rather than started on cold defaults (D677) — so a
|
|
40154
|
+
* transient hub read must cost a retry, never the device until the next
|
|
40155
|
+
* runner restart. An ANSWERED `{}` is the device's true state and never
|
|
40156
|
+
* refuses.
|
|
40157
|
+
*/
|
|
40158
|
+
async restoreWithBoundedRetry(rows, attempt) {
|
|
40159
|
+
const failures = [];
|
|
40160
|
+
for (const saved of rows) {
|
|
40161
|
+
if (this.isUnrestorableShell(saved)) {
|
|
40162
|
+
this.quarantineShell(saved);
|
|
40163
|
+
continue;
|
|
40164
|
+
}
|
|
40165
|
+
try {
|
|
40166
|
+
await attempt(saved);
|
|
40167
|
+
} catch (err) {
|
|
40168
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
40169
|
+
this.ctx.logger.warn("Failed to restore device — bounded retry scheduled", {
|
|
40170
|
+
tags: {
|
|
40171
|
+
deviceId: saved.id,
|
|
40172
|
+
stableId: saved.stableId
|
|
40173
|
+
},
|
|
40174
|
+
meta: {
|
|
40175
|
+
type: saved.type,
|
|
40176
|
+
attempt: 1,
|
|
40177
|
+
error
|
|
40178
|
+
}
|
|
40179
|
+
});
|
|
40180
|
+
failures.push({
|
|
40181
|
+
saved,
|
|
40182
|
+
error
|
|
40183
|
+
});
|
|
40184
|
+
}
|
|
40185
|
+
}
|
|
40186
|
+
if (failures.length > 0) this.scheduleRestoreRetries(failures, attempt);
|
|
40045
40187
|
}
|
|
40046
40188
|
/**
|
|
40047
40189
|
* Tear down and reconstruct ONE device from its persisted rows — the
|
|
@@ -43351,6 +43493,12 @@ Object.freeze({
|
|
|
43351
43493
|
addonId: null,
|
|
43352
43494
|
access: "view"
|
|
43353
43495
|
},
|
|
43496
|
+
"deviceManager.getWrapperActivation": {
|
|
43497
|
+
capName: "device-manager",
|
|
43498
|
+
capScope: "system",
|
|
43499
|
+
addonId: null,
|
|
43500
|
+
access: "view"
|
|
43501
|
+
},
|
|
43354
43502
|
"deviceManager.listAll": {
|
|
43355
43503
|
capName: "device-manager",
|
|
43356
43504
|
capScope: "system",
|
|
@@ -49160,6 +49308,11 @@ Object.freeze({
|
|
|
49160
49308
|
form: "single",
|
|
49161
49309
|
optional: false
|
|
49162
49310
|
}],
|
|
49311
|
+
"deviceManager.getWrapperActivation": [{
|
|
49312
|
+
name: "deviceId",
|
|
49313
|
+
form: "single",
|
|
49314
|
+
optional: false
|
|
49315
|
+
}],
|
|
49163
49316
|
"deviceManager.listAll": [{
|
|
49164
49317
|
name: "deviceIds",
|
|
49165
49318
|
form: "array",
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
//#endregion
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-CslhAXSs.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
//#region src/ha-export/topics.ts
|
|
9
9
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as buildAddonRouteProvider, F as deviceExportCapability, Nt as EventCategory, Q as oauthIntegrationCapability, a as COCO_TO_MACRO, c as FanDirectionSchema, i as CAMERA_SWITCH_ORDER, kt as string, l as HvacModeSchema, o as CameraSwitchIdSchema, p as addonRoutesCapability, t as AlarmArmModeSchema, u as MediaPlayerRepeatSchema, xt as nodePin, yt as BaseAddon } from "../dist-
|
|
1
|
+
import { C as buildAddonRouteProvider, F as deviceExportCapability, Nt as EventCategory, Q as oauthIntegrationCapability, a as COCO_TO_MACRO, c as FanDirectionSchema, i as CAMERA_SWITCH_ORDER, kt as string, l as HvacModeSchema, o as CameraSwitchIdSchema, p as addonRoutesCapability, t as AlarmArmModeSchema, u as MediaPlayerRepeatSchema, xt as nodePin, yt as BaseAddon } from "../dist-DVwhiKWh.mjs";
|
|
2
2
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
3
3
|
//#region src/ha-export/topics.ts
|
|
4
4
|
/**
|