@camstack/addon-provider-reolink 1.2.182 → 1.2.184
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 +859 -283
- package/dist/addon.mjs +858 -282
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -11,6 +11,7 @@ let crypto$1 = require("crypto");
|
|
|
11
11
|
crypto$1 = require_chunk.__toESM(crypto$1, 1);
|
|
12
12
|
let child_process = require("child_process");
|
|
13
13
|
let node_http = require("node:http");
|
|
14
|
+
require("node:zlib");
|
|
14
15
|
let node_fs_promises = require("node:fs/promises");
|
|
15
16
|
let node_path = require("node:path");
|
|
16
17
|
node_path = require_chunk.__toESM(node_path, 1);
|
|
@@ -32,10 +33,10 @@ http = require_chunk.__toESM(http, 1);
|
|
|
32
33
|
let url = require("url");
|
|
33
34
|
let node_child_process = require("node:child_process");
|
|
34
35
|
let node_os = require("node:os");
|
|
35
|
-
node_os = require_chunk.__toESM(node_os);
|
|
36
|
+
node_os = require_chunk.__toESM(node_os, 1);
|
|
36
37
|
let node_fs = require("node:fs");
|
|
37
38
|
node_fs = require_chunk.__toESM(node_fs, 1);
|
|
38
|
-
//#region ../types/dist/event-category-
|
|
39
|
+
//#region ../types/dist/event-category-C4hVnRo8.mjs
|
|
39
40
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
40
41
|
EventCategory["SystemBoot"] = "system.boot";
|
|
41
42
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -254,7 +255,17 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
254
255
|
EventCategory["RecordingStopped"] = "recording.stopped";
|
|
255
256
|
EventCategory["RecordingError"] = "recording.error";
|
|
256
257
|
EventCategory["RecordingHealthDegraded"] = "recording.health.degraded";
|
|
258
|
+
/** Recording enabled but no segment finalized for the NC stall window. */
|
|
259
|
+
EventCategory["RecordingHealthStalled"] = "recording.health.stalled";
|
|
260
|
+
/** Recovery from {@link RecordingHealthStalled}. */
|
|
261
|
+
EventCategory["RecordingHealthOk"] = "recording.health.ok";
|
|
257
262
|
EventCategory["RecordingStorageCritical"] = "recording.storage.critical";
|
|
263
|
+
/** Free space on a storage location crossed the warn bar. */
|
|
264
|
+
EventCategory["StorageLocationLow"] = "storage.location.low";
|
|
265
|
+
/** Free space at/under the critical bar, or capacity probe failed (`null`). */
|
|
266
|
+
EventCategory["StorageLocationCritical"] = "storage.location.critical";
|
|
267
|
+
/** Recovery from low/critical on a storage location. */
|
|
268
|
+
EventCategory["StorageLocationOk"] = "storage.location.ok";
|
|
258
269
|
EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
|
|
259
270
|
EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
|
|
260
271
|
EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
|
|
@@ -5413,7 +5424,7 @@ var ZodIssueCode = {
|
|
|
5413
5424
|
var ZodFirstPartyTypeKind;
|
|
5414
5425
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5415
5426
|
//#endregion
|
|
5416
|
-
//#region ../types/dist/sleep-
|
|
5427
|
+
//#region ../types/dist/sleep-vWTnHq9G.mjs
|
|
5417
5428
|
/**
|
|
5418
5429
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5419
5430
|
* window to float samples (D455).
|
|
@@ -8018,6 +8029,21 @@ var ProfileRtspEntrySchema = object({
|
|
|
8018
8029
|
codec: string().optional(),
|
|
8019
8030
|
resolution: CamStreamResolutionSchema.optional()
|
|
8020
8031
|
});
|
|
8032
|
+
/**
|
|
8033
|
+
* The model weight formats a catalog entry can ship. Schema-free on purpose:
|
|
8034
|
+
* `types/models.ts` builds Zod schemas from it, but a runner-closure consumer
|
|
8035
|
+
* (the model downloader in `@camstack/system`) needs only the list and must
|
|
8036
|
+
* reach it through `@camstack/types/addon` without building the cap schema
|
|
8037
|
+
* graph (D28).
|
|
8038
|
+
*/
|
|
8039
|
+
var MODEL_FORMATS = [
|
|
8040
|
+
"onnx",
|
|
8041
|
+
"coreml",
|
|
8042
|
+
"openvino",
|
|
8043
|
+
"tflite",
|
|
8044
|
+
"pt",
|
|
8045
|
+
"gguf"
|
|
8046
|
+
];
|
|
8021
8047
|
object({
|
|
8022
8048
|
field: string().min(1),
|
|
8023
8049
|
on: literal("advance")
|
|
@@ -8616,7 +8642,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
8616
8642
|
return DeviceRole;
|
|
8617
8643
|
}({});
|
|
8618
8644
|
//#endregion
|
|
8619
|
-
//#region ../types/dist/composition-
|
|
8645
|
+
//#region ../types/dist/composition-C-WpVYTX.mjs
|
|
8620
8646
|
/**
|
|
8621
8647
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
8622
8648
|
*
|
|
@@ -8668,7 +8694,7 @@ var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFM
|
|
|
8668
8694
|
function portable(target) {
|
|
8669
8695
|
return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
|
|
8670
8696
|
}
|
|
8671
|
-
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64")
|
|
8697
|
+
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64"), `${BASE}${FFMPEG_RELEASE}`;
|
|
8672
8698
|
/**
|
|
8673
8699
|
* THE table "which boolean of this device type counts as HIGH", and the
|
|
8674
8700
|
* pure evaluator of its EDGE.
|
|
@@ -9471,6 +9497,16 @@ var CompositionSchema = object({
|
|
|
9471
9497
|
seen.add(feature.cap);
|
|
9472
9498
|
});
|
|
9473
9499
|
});
|
|
9500
|
+
var NodeColorIdSchema = _enum([
|
|
9501
|
+
"node-a",
|
|
9502
|
+
"node-b",
|
|
9503
|
+
"node-c",
|
|
9504
|
+
"node-d",
|
|
9505
|
+
"node-e",
|
|
9506
|
+
"node-f",
|
|
9507
|
+
"node-g",
|
|
9508
|
+
"node-h"
|
|
9509
|
+
]);
|
|
9474
9510
|
new Set([
|
|
9475
9511
|
"track",
|
|
9476
9512
|
"face",
|
|
@@ -12402,14 +12438,6 @@ var MASK_SUPPLY_STEP_IDS = ["segmentation"];
|
|
|
12402
12438
|
...CROP_SUPPLY_STEP_IDS,
|
|
12403
12439
|
...MASK_SUPPLY_STEP_IDS
|
|
12404
12440
|
];
|
|
12405
|
-
var MODEL_FORMATS = [
|
|
12406
|
-
"onnx",
|
|
12407
|
-
"coreml",
|
|
12408
|
-
"openvino",
|
|
12409
|
-
"tflite",
|
|
12410
|
-
"pt",
|
|
12411
|
-
"gguf"
|
|
12412
|
-
];
|
|
12413
12441
|
/**
|
|
12414
12442
|
* Multi-file format payload.
|
|
12415
12443
|
*
|
|
@@ -14233,7 +14261,14 @@ var BrokerProviderInfoSchema = object({
|
|
|
14233
14261
|
/** Broker kinds this provider can create, with a display label. */
|
|
14234
14262
|
kinds: array(object({
|
|
14235
14263
|
kind: string(),
|
|
14236
|
-
label: string()
|
|
14264
|
+
label: string(),
|
|
14265
|
+
/**
|
|
14266
|
+
* Relative hub path to start an interactive account link (OAuth popup).
|
|
14267
|
+
* May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
|
|
14268
|
+
*/
|
|
14269
|
+
interactiveConnectPath: string().min(1).optional(),
|
|
14270
|
+
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
14271
|
+
interactiveDisconnectPath: string().min(1).optional()
|
|
14237
14272
|
}))
|
|
14238
14273
|
});
|
|
14239
14274
|
var ListInputSchema = object({
|
|
@@ -19184,14 +19219,14 @@ var LlmProfileSchema = object({
|
|
|
19184
19219
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
19185
19220
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
19186
19221
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
19187
|
-
var ConfigSchemaPassthrough$
|
|
19222
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
19188
19223
|
var LlmProfileKindDescriptorSchema = object({
|
|
19189
19224
|
kind: LlmProfileKindSchema,
|
|
19190
19225
|
label: string(),
|
|
19191
19226
|
icon: string(),
|
|
19192
19227
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
19193
19228
|
addonId: string(),
|
|
19194
|
-
configSchema: ConfigSchemaPassthrough$
|
|
19229
|
+
configSchema: ConfigSchemaPassthrough$3
|
|
19195
19230
|
});
|
|
19196
19231
|
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
19197
19232
|
var LlmDefaultSchema = object({
|
|
@@ -20333,7 +20368,7 @@ var TargetKindCapsSchema = object({
|
|
|
20333
20368
|
* the union is large and not meant for runtime validation here; the exported
|
|
20334
20369
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
20335
20370
|
*/
|
|
20336
|
-
var ConfigSchemaPassthrough = unknown();
|
|
20371
|
+
var ConfigSchemaPassthrough$2 = unknown();
|
|
20337
20372
|
var TargetKindSchema = object({
|
|
20338
20373
|
kind: string(),
|
|
20339
20374
|
label: string(),
|
|
@@ -20370,7 +20405,7 @@ var TargetKindSchema = object({
|
|
|
20370
20405
|
* its raster path, which is the safe default for an unknown image.
|
|
20371
20406
|
*/
|
|
20372
20407
|
iconMediaType: string().optional(),
|
|
20373
|
-
configSchema: ConfigSchemaPassthrough,
|
|
20408
|
+
configSchema: ConfigSchemaPassthrough$2,
|
|
20374
20409
|
/**
|
|
20375
20410
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
20376
20411
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -21327,6 +21362,12 @@ var NcSystemEventKindSchema = _enum([
|
|
|
21327
21362
|
"node-updating",
|
|
21328
21363
|
"node-inference-unavailable",
|
|
21329
21364
|
"detection-blind",
|
|
21365
|
+
"recording-stalled",
|
|
21366
|
+
"recording-healthy",
|
|
21367
|
+
"recording-degraded",
|
|
21368
|
+
"storage-location-low",
|
|
21369
|
+
"storage-location-critical",
|
|
21370
|
+
"storage-location-ok",
|
|
21330
21371
|
"addon-crash-loop",
|
|
21331
21372
|
"addon-update-available",
|
|
21332
21373
|
"server-update-available",
|
|
@@ -23707,6 +23748,61 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
23707
23748
|
});
|
|
23708
23749
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
23709
23750
|
/**
|
|
23751
|
+
* `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
|
|
23752
|
+
*
|
|
23753
|
+
* One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
|
|
23754
|
+
* ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
|
|
23755
|
+
* Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
|
|
23756
|
+
* immutable after create.
|
|
23757
|
+
*/
|
|
23758
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
23759
|
+
var OidcConnectionSchema = object({
|
|
23760
|
+
id: string().min(1),
|
|
23761
|
+
displayName: string(),
|
|
23762
|
+
icon: string(),
|
|
23763
|
+
issuerUrl: string(),
|
|
23764
|
+
clientId: string(),
|
|
23765
|
+
/** Client id + secret present — never the secret value. */
|
|
23766
|
+
configured: boolean(),
|
|
23767
|
+
/**
|
|
23768
|
+
* Values for the connection form schema. `clientSecret` is a keychain
|
|
23769
|
+
* {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
|
|
23770
|
+
*/
|
|
23771
|
+
formValues: record(string(), unknown()).optional()
|
|
23772
|
+
});
|
|
23773
|
+
/**
|
|
23774
|
+
* Patch one connection. Omitted or empty `clientSecret` keeps the stored
|
|
23775
|
+
* secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
|
|
23776
|
+
*/
|
|
23777
|
+
var OidcConnectionUpdateInputSchema = object({
|
|
23778
|
+
id: string().min(1),
|
|
23779
|
+
displayName: string().min(1),
|
|
23780
|
+
icon: string().optional(),
|
|
23781
|
+
issuerUrl: string().optional(),
|
|
23782
|
+
clientId: string().optional(),
|
|
23783
|
+
clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
23784
|
+
redirectUri: string().optional(),
|
|
23785
|
+
scopes: string().optional(),
|
|
23786
|
+
usernameClaim: _enum([
|
|
23787
|
+
"preferred_username",
|
|
23788
|
+
"email",
|
|
23789
|
+
"sub"
|
|
23790
|
+
]).optional()
|
|
23791
|
+
});
|
|
23792
|
+
method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
|
|
23793
|
+
id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
|
|
23794
|
+
displayName: string().min(1)
|
|
23795
|
+
}), object({ id: string() }), {
|
|
23796
|
+
kind: "mutation",
|
|
23797
|
+
auth: "admin"
|
|
23798
|
+
}), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
|
|
23799
|
+
kind: "mutation",
|
|
23800
|
+
auth: "admin"
|
|
23801
|
+
}), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
23802
|
+
kind: "mutation",
|
|
23803
|
+
auth: "admin"
|
|
23804
|
+
});
|
|
23805
|
+
/**
|
|
23710
23806
|
* What started a camera's onboard motion (D672). `camera` is the camera's own
|
|
23711
23807
|
* firmware; `sensor` is a device the operator ATTACHED to it. Absent on the
|
|
23712
23808
|
* wire means the camera: only the camera-sensors bridge ever says `sensor`.
|
|
@@ -25063,24 +25159,69 @@ var RetrainFrameListSchema = object({
|
|
|
25063
25159
|
/** What the page pre-selects — the native key frame when one survives. */
|
|
25064
25160
|
autoPickMediaId: number().int().optional()
|
|
25065
25161
|
});
|
|
25162
|
+
/** Exclusive partition of a corpus copy's annotation progress (design §4.2). */
|
|
25163
|
+
var RetrainFrameWorkStateSchema = _enum([
|
|
25164
|
+
"unannotated",
|
|
25165
|
+
"annotated",
|
|
25166
|
+
"complete"
|
|
25167
|
+
]);
|
|
25168
|
+
/**
|
|
25169
|
+
* Filters for paginated corpus list, KPI, and matching export (design §4.4).
|
|
25170
|
+
* Every narrowing field is ANDed; omitted fields impose no constraint.
|
|
25171
|
+
*/
|
|
25172
|
+
var RetrainCorpusFilterSchema = object({
|
|
25173
|
+
workState: RetrainFrameWorkStateSchema.optional(),
|
|
25174
|
+
deviceIds: array(number()).max(500).optional(),
|
|
25175
|
+
/** When set, only frames in this named collection. */
|
|
25176
|
+
collectionId: string().optional(),
|
|
25177
|
+
/** Frames that carry at least one saved annotation in one of these classes. */
|
|
25178
|
+
classes: array(RetrainMacroClassSchema).optional(),
|
|
25179
|
+
page: number().int().positive().default(1),
|
|
25180
|
+
pageSize: number().int().positive().max(100).default(50)
|
|
25181
|
+
});
|
|
25182
|
+
var RetrainCorpusPageSchema = object({
|
|
25183
|
+
frames: array(RetrainFrameSchema.extend({ workState: RetrainFrameWorkStateSchema })).readonly(),
|
|
25184
|
+
total: number().int().nonnegative(),
|
|
25185
|
+
page: number().int().positive(),
|
|
25186
|
+
pageSize: number().int().positive()
|
|
25187
|
+
});
|
|
25188
|
+
var RetrainCorpusKpiCameraRowSchema = object({
|
|
25189
|
+
deviceId: number(),
|
|
25190
|
+
unannotated: number().int().nonnegative(),
|
|
25191
|
+
annotated: number().int().nonnegative(),
|
|
25192
|
+
complete: number().int().nonnegative(),
|
|
25193
|
+
/** This camera's `complete` count divided by corpus `total`, in `[0,1]`.
|
|
25194
|
+
* `null` when the corpus is empty (no ratio). */
|
|
25195
|
+
pctOfCorpus: number().min(0).max(1).nullable()
|
|
25196
|
+
});
|
|
25197
|
+
var RetrainCorpusKpiSchema = object({
|
|
25198
|
+
total: number().int().nonnegative(),
|
|
25199
|
+
unannotated: number().int().nonnegative(),
|
|
25200
|
+
annotated: number().int().nonnegative(),
|
|
25201
|
+
complete: number().int().nonnegative(),
|
|
25202
|
+
cameras: array(RetrainCorpusKpiCameraRowSchema).readonly()
|
|
25203
|
+
});
|
|
25066
25204
|
/**
|
|
25067
25205
|
* Which frames an annotated export carries. Every field narrows; an empty
|
|
25068
|
-
* filter is every annotated copy.
|
|
25069
|
-
*
|
|
25070
|
-
*
|
|
25071
|
-
*
|
|
25206
|
+
* filter is every annotated copy. `frameIds` is the primitive a job or
|
|
25207
|
+
* download uses; `batchId` re-selects what an earlier export carried
|
|
25208
|
+
* (`exportedInBatch`). Frozen dataset versions were removed — live selection
|
|
25209
|
+
* resolves to `frameIds` at plan/start.
|
|
25072
25210
|
*/
|
|
25073
25211
|
var RetrainExportFilterSchema = object({
|
|
25074
25212
|
deviceIds: array(number()).optional(),
|
|
25075
25213
|
frameIds: array(string()).max(2e4).optional(),
|
|
25076
25214
|
/** Only frames in at least one of these named collections. */
|
|
25077
25215
|
collectionIds: array(string()).max(200).optional(),
|
|
25078
|
-
/**
|
|
25079
|
-
|
|
25080
|
-
datasetVersionId: string().optional(),
|
|
25216
|
+
/** Single collection (Frames filter bar). ANDed with `collectionIds` when both set. */
|
|
25217
|
+
collectionId: string().optional(),
|
|
25081
25218
|
batchId: string().optional(),
|
|
25082
25219
|
/** Only frames the operator marked complete (all subjects boxed). */
|
|
25083
|
-
completeOnly: boolean().optional()
|
|
25220
|
+
completeOnly: boolean().optional(),
|
|
25221
|
+
/** Exclusive work-state partition (`workStateOf`); same as the corpus list. */
|
|
25222
|
+
workState: RetrainFrameWorkStateSchema.optional(),
|
|
25223
|
+
/** Frames with at least one saved annotation in one of these classes. */
|
|
25224
|
+
classes: array(RetrainMacroClassSchema).optional()
|
|
25084
25225
|
});
|
|
25085
25226
|
/**
|
|
25086
25227
|
* `frames`: the full frame copies, boxes in `annotations.json`.
|
|
@@ -25280,8 +25421,7 @@ var RetrainDatasetStatsSchema = object({
|
|
|
25280
25421
|
completeFrames: number().int()
|
|
25281
25422
|
})).readonly()
|
|
25282
25423
|
});
|
|
25283
|
-
|
|
25284
|
-
var RetrainDatasetVersionSchema = object({
|
|
25424
|
+
object({
|
|
25285
25425
|
id: string(),
|
|
25286
25426
|
name: string(),
|
|
25287
25427
|
createdAt: number(),
|
|
@@ -25289,22 +25429,33 @@ var RetrainDatasetVersionSchema = object({
|
|
|
25289
25429
|
layout: RetrainExportLayoutSchema,
|
|
25290
25430
|
completeOnly: boolean(),
|
|
25291
25431
|
frameCount: number().int(),
|
|
25292
|
-
/** sha256 of what the frames taught when the version was frozen. */
|
|
25293
25432
|
annotationHash: string(),
|
|
25294
|
-
/** The export manifest schema the version was cut under. */
|
|
25295
25433
|
manifestSchema: string(),
|
|
25296
25434
|
stats: RetrainDatasetStatsSchema
|
|
25297
25435
|
});
|
|
25298
|
-
|
|
25299
|
-
|
|
25300
|
-
|
|
25436
|
+
/**
|
|
25437
|
+
* Live training corpus criteria — same shape as `TrainingCorpusSelection` on
|
|
25438
|
+
* the training cap. Resolved here so plan/start and the annotated export share
|
|
25439
|
+
* one definition of "the frames" (`buildRetrainExportPlan`).
|
|
25440
|
+
*/
|
|
25441
|
+
var RetrainTrainingCorpusSelectionSchema = object({
|
|
25442
|
+
collectionIds: array(string()).min(1),
|
|
25443
|
+
workState: _enum([
|
|
25444
|
+
"complete",
|
|
25445
|
+
"annotated_or_complete",
|
|
25446
|
+
"any_annotated"
|
|
25447
|
+
]).default("complete")
|
|
25448
|
+
});
|
|
25449
|
+
var RetrainTrainingCorpusResolveResultSchema = discriminatedUnion("kind", [object({
|
|
25450
|
+
kind: literal("resolved"),
|
|
25451
|
+
frameIds: array(string()).max(2e4).readonly(),
|
|
25452
|
+
stats: RetrainDatasetStatsSchema,
|
|
25453
|
+
annotationHash: string(),
|
|
25454
|
+
layout: RetrainExportLayoutSchema
|
|
25301
25455
|
}), object({
|
|
25302
25456
|
kind: literal("refused"),
|
|
25303
|
-
reason: _enum([
|
|
25304
|
-
|
|
25305
|
-
"duplicate-name",
|
|
25306
|
-
"no-frames"
|
|
25307
|
-
])
|
|
25457
|
+
reason: _enum(["unknown-collection", "no-frames"]),
|
|
25458
|
+
detail: string()
|
|
25308
25459
|
})]);
|
|
25309
25460
|
/** The outcome of a lifecycle move owned by the retrain page. */
|
|
25310
25461
|
var RetrainTransitionResultSchema = object({
|
|
@@ -26011,6 +26162,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
26011
26162
|
}), array(RetrainTrackSchema).readonly(), {
|
|
26012
26163
|
kind: "query",
|
|
26013
26164
|
auth: "admin"
|
|
26165
|
+
}), method(RetrainCorpusFilterSchema, RetrainCorpusPageSchema, {
|
|
26166
|
+
kind: "query",
|
|
26167
|
+
auth: "admin"
|
|
26168
|
+
}), method(object({}), RetrainCorpusKpiSchema, {
|
|
26169
|
+
kind: "query",
|
|
26170
|
+
auth: "admin"
|
|
26014
26171
|
}), method(object({ trackId: string() }), RetrainFrameListSchema, {
|
|
26015
26172
|
kind: "query",
|
|
26016
26173
|
auth: "admin"
|
|
@@ -26106,36 +26263,6 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
26106
26263
|
kind: "mutation",
|
|
26107
26264
|
auth: "admin",
|
|
26108
26265
|
timeoutMs: 12e4
|
|
26109
|
-
}), method(object({
|
|
26110
|
-
selection: RetrainDatasetSelectionSchema,
|
|
26111
|
-
completeOnly: boolean().optional()
|
|
26112
|
-
}), RetrainDatasetStatsSchema, {
|
|
26113
|
-
kind: "query",
|
|
26114
|
-
auth: "admin"
|
|
26115
|
-
}), method(object({
|
|
26116
|
-
name: string().max(120),
|
|
26117
|
-
selection: RetrainDatasetSelectionSchema,
|
|
26118
|
-
layout: RetrainExportLayoutSchema,
|
|
26119
|
-
completeOnly: boolean()
|
|
26120
|
-
}), CreateRetrainDatasetVersionResultSchema, {
|
|
26121
|
-
kind: "mutation",
|
|
26122
|
-
auth: "admin",
|
|
26123
|
-
timeoutMs: 12e4
|
|
26124
|
-
}), method(object({}), array(RetrainDatasetVersionSchema).readonly(), {
|
|
26125
|
-
kind: "query",
|
|
26126
|
-
auth: "admin"
|
|
26127
|
-
}), method(object({ id: string() }), object({ deleted: boolean() }), {
|
|
26128
|
-
kind: "mutation",
|
|
26129
|
-
auth: "admin"
|
|
26130
|
-
}), method(object({ id: string() }), object({
|
|
26131
|
-
found: boolean(),
|
|
26132
|
-
annotationHash: string(),
|
|
26133
|
-
matches: boolean(),
|
|
26134
|
-
missingFrames: number().int()
|
|
26135
|
-
}), {
|
|
26136
|
-
kind: "query",
|
|
26137
|
-
auth: "admin",
|
|
26138
|
-
timeoutMs: 12e4
|
|
26139
26266
|
}), method(object({}), array(RetrainAssistProfileSchema).readonly(), {
|
|
26140
26267
|
kind: "query",
|
|
26141
26268
|
auth: "admin"
|
|
@@ -26162,6 +26289,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
26162
26289
|
}), method(object({}), array(RetrainCollectionSchema).readonly(), {
|
|
26163
26290
|
kind: "query",
|
|
26164
26291
|
auth: "admin"
|
|
26292
|
+
}), method(RetrainTrainingCorpusSelectionSchema, RetrainTrainingCorpusResolveResultSchema, {
|
|
26293
|
+
kind: "query",
|
|
26294
|
+
auth: "admin",
|
|
26295
|
+
timeoutMs: 6e4
|
|
26165
26296
|
}), method(object({
|
|
26166
26297
|
id: string(),
|
|
26167
26298
|
name: string().max(120)
|
|
@@ -26778,11 +26909,14 @@ var HardwareClassIdSchema = _enum([
|
|
|
26778
26909
|
"intel-n-series",
|
|
26779
26910
|
"apple-m-base",
|
|
26780
26911
|
"apple-m-pro",
|
|
26912
|
+
"amd-phoenix",
|
|
26781
26913
|
"unknown"
|
|
26782
26914
|
]);
|
|
26783
26915
|
var AcceleratorKindSchema = _enum([
|
|
26784
26916
|
"intel-npu",
|
|
26785
26917
|
"intel-igpu",
|
|
26918
|
+
"amd-igpu",
|
|
26919
|
+
"amd-npu",
|
|
26786
26920
|
"x86-cpu",
|
|
26787
26921
|
"arm-cpu",
|
|
26788
26922
|
"apple-ane",
|
|
@@ -28712,6 +28846,8 @@ var AgentPipelineSettingsSchema = object({
|
|
|
28712
28846
|
* address is wrong (multi-homed host, NAT, custom interface).
|
|
28713
28847
|
*/
|
|
28714
28848
|
reachableHost: string().optional(),
|
|
28849
|
+
/** Hub-persisted UI colour (`--color-node-*` theme tokens). */
|
|
28850
|
+
color: NodeColorIdSchema.optional(),
|
|
28715
28851
|
/**
|
|
28716
28852
|
* Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
|
|
28717
28853
|
* weight, steps}. Absent / all-disabled ⇒ the node's single default
|
|
@@ -29261,8 +29397,9 @@ method(object({
|
|
|
29261
29397
|
auth: "admin"
|
|
29262
29398
|
}), method(object({
|
|
29263
29399
|
agentNodeId: string(),
|
|
29264
|
-
reachableHost: string().nullable()
|
|
29265
|
-
|
|
29400
|
+
reachableHost: string().nullable().optional(),
|
|
29401
|
+
color: NodeColorIdSchema.nullable().optional()
|
|
29402
|
+
}).refine((v) => v.reachableHost !== void 0 || v.color !== void 0, { message: "reachableHost or color required" }), object({ success: literal(true) }), {
|
|
29266
29403
|
kind: "mutation",
|
|
29267
29404
|
auth: "admin"
|
|
29268
29405
|
}), method(object({
|
|
@@ -32860,7 +32997,7 @@ var CustomActionInputSchema = object({
|
|
|
32860
32997
|
action: string().min(1),
|
|
32861
32998
|
input: unknown()
|
|
32862
32999
|
});
|
|
32863
|
-
method(
|
|
33000
|
+
method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSchema).readonly()), method(object({
|
|
32864
33001
|
addonId: string(),
|
|
32865
33002
|
limit: number().min(1).max(500).default(100),
|
|
32866
33003
|
level: LogLevelSchema$2.optional()
|
|
@@ -33330,6 +33467,17 @@ var BatteryStatusSchema = object({
|
|
|
33330
33467
|
*/
|
|
33331
33468
|
lastContactAt: number().optional(),
|
|
33332
33469
|
/**
|
|
33470
|
+
* Ms epoch of the last COMPLETE scheduled refresh — every fact the provider
|
|
33471
|
+
* holds for the device (level, charging, link, device info, state slices) and
|
|
33472
|
+
* its cached snapshot, read together. Absent = never since this slice was
|
|
33473
|
+
* born. Unlike `lastUpdated` (any write, even a push of one field) this is
|
|
33474
|
+
* the staleness an operator can trust: the provider owns the device fact and
|
|
33475
|
+
* so owns the window on its age (D224, D847). Not a clock in the
|
|
33476
|
+
* runtime-state-durability sense: it moves once per window and must survive
|
|
33477
|
+
* a restart, or every restart would owe the camera a wake.
|
|
33478
|
+
*/
|
|
33479
|
+
lastFullRefreshAt: number().optional(),
|
|
33480
|
+
/**
|
|
33333
33481
|
* True when the source is a BINARY low-battery indicator (HA
|
|
33334
33482
|
* `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
|
|
33335
33483
|
* charge level — `percentage` is then a coarse stand-in (100 = normal,
|
|
@@ -38407,6 +38555,7 @@ var OsdNamedTemplateSchema = object({
|
|
|
38407
38555
|
});
|
|
38408
38556
|
/** Where a slot's value comes from. */
|
|
38409
38557
|
var OsdSourceSchema = discriminatedUnion("kind", [
|
|
38558
|
+
object({ kind: literal("none") }),
|
|
38410
38559
|
object({
|
|
38411
38560
|
kind: literal("static"),
|
|
38412
38561
|
text: string().max(64)
|
|
@@ -38533,6 +38682,13 @@ method(object({ deviceId: number().int() }), object({
|
|
|
38533
38682
|
}), object({ success: literal(true) }), {
|
|
38534
38683
|
kind: "mutation",
|
|
38535
38684
|
auth: "admin"
|
|
38685
|
+
}), method(object({
|
|
38686
|
+
deviceId: number().int(),
|
|
38687
|
+
slotId: string().min(1),
|
|
38688
|
+
enabled: boolean()
|
|
38689
|
+
}), object({ success: literal(true) }), {
|
|
38690
|
+
kind: "mutation",
|
|
38691
|
+
auth: "admin"
|
|
38536
38692
|
}), method(object({
|
|
38537
38693
|
sourceDeviceId: number().int(),
|
|
38538
38694
|
targetDeviceId: number().int()
|
|
@@ -38964,7 +39120,15 @@ var GpuInfoSchema = object({
|
|
|
38964
39120
|
name: string(),
|
|
38965
39121
|
memoryMB: number().optional()
|
|
38966
39122
|
});
|
|
38967
|
-
var NpuInfoSchema = object({
|
|
39123
|
+
var NpuInfoSchema = object({
|
|
39124
|
+
type: _enum([
|
|
39125
|
+
"apple-ane",
|
|
39126
|
+
"intel-npu",
|
|
39127
|
+
"amd-xdna"
|
|
39128
|
+
]),
|
|
39129
|
+
name: string().optional(),
|
|
39130
|
+
generation: _enum(["xdna1", "xdna2"]).optional()
|
|
39131
|
+
});
|
|
38968
39132
|
var CoralInfoSchema = object({
|
|
38969
39133
|
type: literal("coral-edgetpu"),
|
|
38970
39134
|
bus: string().optional()
|
|
@@ -44211,9 +44375,11 @@ var zoneRulesCapability = {
|
|
|
44211
44375
|
* once is what makes a second provider (RunPod, a local NVIDIA node) a new
|
|
44212
44376
|
* manifest entry and nothing else.
|
|
44213
44377
|
*
|
|
44214
|
-
* Collection
|
|
44215
|
-
*
|
|
44216
|
-
*
|
|
44378
|
+
* Collection of provider **types** (addons). Each addon owns zero or more
|
|
44379
|
+
* **entries** (credentials + defaults). Concat-fans: `describe`,
|
|
44380
|
+
* `describeTypes`, `listEntries`, `listJobs`. Single-row work is keyed by
|
|
44381
|
+
* `entryId`; callers also pass the owning `addonId` as the collection
|
|
44382
|
+
* selector (broker posture). Every method is `admin`: these spend money.
|
|
44217
44383
|
*
|
|
44218
44384
|
* Every cloud object a provider creates is tagged with the orchestrator's
|
|
44219
44385
|
* `jobKey`, so a retried submit finds the job it already started instead of
|
|
@@ -44237,8 +44403,8 @@ var TrainingRegionOptionSchema = object({
|
|
|
44237
44403
|
priceMultiplier: number().positive()
|
|
44238
44404
|
});
|
|
44239
44405
|
/**
|
|
44240
|
-
* The provider's per-
|
|
44241
|
-
* live
|
|
44406
|
+
* The provider's per-entry settings, as the orchestrator reads them. They
|
|
44407
|
+
* live on the PROVIDER addon's entry (operator decision 2026-10-08: they
|
|
44242
44408
|
* depend on the user's account), never hard-coded in the orchestrator.
|
|
44243
44409
|
*/
|
|
44244
44410
|
var TrainingProviderDefaultsSchema = object({
|
|
@@ -44249,9 +44415,44 @@ var TrainingProviderDefaultsSchema = object({
|
|
|
44249
44415
|
/** Hours a FAILED job's cloud dataset is kept for debugging; 0 = delete at once. */
|
|
44250
44416
|
failedDatasetGraceHours: number().nonnegative()
|
|
44251
44417
|
});
|
|
44418
|
+
/**
|
|
44419
|
+
* Entry form schema for a provider type — same seam as notification
|
|
44420
|
+
* `TargetKind.configSchema` (`z.unknown()` at the wire; callers tighten to
|
|
44421
|
+
* {@link ConfigUISchema}). Drives `ConfigFormBuilder` on Training → Providers.
|
|
44422
|
+
*/
|
|
44423
|
+
var ConfigSchemaPassthrough = unknown();
|
|
44424
|
+
/** One creatable provider type, for the Add-entry dialog. */
|
|
44425
|
+
var TrainingProviderTypeSchema = object({
|
|
44426
|
+
typeId: string(),
|
|
44427
|
+
label: string(),
|
|
44428
|
+
/** Path under the addon package (e.g. `assets/training-modal/icon.svg`). */
|
|
44429
|
+
iconAsset: string(),
|
|
44430
|
+
/** Owning `training-provider` addon — collection selector for createEntry. */
|
|
44431
|
+
addonId: string(),
|
|
44432
|
+
/**
|
|
44433
|
+
* Fields for one entry of this type (credentials + defaults). Absent on a
|
|
44434
|
+
* type that only needs a name. Password fields are keychain-backed on the
|
|
44435
|
+
* provider addon (`secretStatePaths`), not D634 plaintext.
|
|
44436
|
+
*/
|
|
44437
|
+
configSchema: ConfigSchemaPassthrough.optional()
|
|
44438
|
+
});
|
|
44439
|
+
/** One configured account / workspace under a provider type. */
|
|
44440
|
+
var TrainingProviderEntrySchema = object({
|
|
44441
|
+
entryId: string(),
|
|
44442
|
+
typeId: string(),
|
|
44443
|
+
name: string(),
|
|
44444
|
+
addonId: string(),
|
|
44445
|
+
/** Credentials are present — never the values. */
|
|
44446
|
+
configured: boolean()
|
|
44447
|
+
});
|
|
44252
44448
|
var TrainingProviderDescriptorSchema = object({
|
|
44253
44449
|
addonId: string(),
|
|
44254
|
-
|
|
44450
|
+
entryId: string(),
|
|
44451
|
+
typeId: string(),
|
|
44452
|
+
/** Operator-chosen entry name. */
|
|
44453
|
+
name: string(),
|
|
44454
|
+
iconAsset: string(),
|
|
44455
|
+
/** `modal`, `runpod`, `fake`, … — same as typeId for current providers. */
|
|
44255
44456
|
kind: string(),
|
|
44256
44457
|
label: string(),
|
|
44257
44458
|
gpuTypes: array(TrainingGpuOptionSchema).readonly(),
|
|
@@ -44263,7 +44464,30 @@ var TrainingProviderDescriptorSchema = object({
|
|
|
44263
44464
|
dataResidencyNote: string(),
|
|
44264
44465
|
/** Credentials are present — never the values. */
|
|
44265
44466
|
configured: boolean(),
|
|
44266
|
-
defaults: TrainingProviderDefaultsSchema
|
|
44467
|
+
defaults: TrainingProviderDefaultsSchema,
|
|
44468
|
+
/**
|
|
44469
|
+
* Values for {@link TrainingProviderTypeSchema}'s `configSchema` — password
|
|
44470
|
+
* fields are keychain {@link SecretFieldView} or `null` / `''` (create),
|
|
44471
|
+
* never plaintext. Same hydrate contract as settings forms (D789).
|
|
44472
|
+
*/
|
|
44473
|
+
formValues: record(string(), unknown()).optional()
|
|
44474
|
+
});
|
|
44475
|
+
/**
|
|
44476
|
+
* Patch one entry's name, credentials and defaults. Pin with owning `{ addonId }`.
|
|
44477
|
+
* An omitted or empty `tokenSecret` means KEEP the stored secret (D787).
|
|
44478
|
+
* `{ $secretClear: true }` clears it.
|
|
44479
|
+
*/
|
|
44480
|
+
var TrainingProviderUpdateEntryInputSchema = object({
|
|
44481
|
+
entryId: string().min(1),
|
|
44482
|
+
name: string().min(1),
|
|
44483
|
+
tokenId: string().optional(),
|
|
44484
|
+
tokenSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
44485
|
+
environment: string().optional(),
|
|
44486
|
+
gpuType: string().optional(),
|
|
44487
|
+
region: string().optional(),
|
|
44488
|
+
costCapUsd: number().nonnegative().optional(),
|
|
44489
|
+
maxRuntimeSec: number().int().positive().optional(),
|
|
44490
|
+
failedDatasetGraceHours: number().nonnegative().optional()
|
|
44267
44491
|
});
|
|
44268
44492
|
var TrainingCredentialCheckSchema = object({
|
|
44269
44493
|
ok: boolean(),
|
|
@@ -44315,20 +44539,33 @@ var TrainerSourceRefSchema = object({
|
|
|
44315
44539
|
/** A commit sha (never a branch) — the job must be reproducible. */
|
|
44316
44540
|
revision: string().regex(/^[0-9a-f]{7,64}$/)
|
|
44317
44541
|
});
|
|
44542
|
+
/** Cloud job identity — route with the owning addon's `addonId` selector. */
|
|
44318
44543
|
var JobRef = {
|
|
44319
|
-
|
|
44544
|
+
entryId: string().min(1),
|
|
44320
44545
|
jobKey: string().uuid()
|
|
44321
44546
|
};
|
|
44322
44547
|
var TrainingLogLineSchema = object({
|
|
44323
44548
|
ts: number(),
|
|
44324
44549
|
text: string()
|
|
44325
44550
|
});
|
|
44326
|
-
method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "admin" }), method(object({
|
|
44551
|
+
method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "admin" }), method(object({}), array(TrainingProviderTypeSchema).readonly(), { auth: "admin" }), method(object({}), array(TrainingProviderEntrySchema).readonly(), { auth: "admin" }), method(object({
|
|
44552
|
+
typeId: string().min(1),
|
|
44553
|
+
name: string().min(1)
|
|
44554
|
+
}), object({ entryId: string() }), {
|
|
44555
|
+
kind: "mutation",
|
|
44556
|
+
auth: "admin"
|
|
44557
|
+
}), method(object({ entryId: string().min(1) }), object({ ok: literal(true) }), {
|
|
44558
|
+
kind: "mutation",
|
|
44559
|
+
auth: "admin"
|
|
44560
|
+
}), method(TrainingProviderUpdateEntryInputSchema, object({ ok: literal(true) }), {
|
|
44561
|
+
kind: "mutation",
|
|
44562
|
+
auth: "admin"
|
|
44563
|
+
}), method(object({ entryId: string().min(1) }), TrainingCredentialCheckSchema, {
|
|
44327
44564
|
kind: "mutation",
|
|
44328
44565
|
auth: "admin",
|
|
44329
44566
|
timeoutMs: 3e4
|
|
44330
44567
|
}), method(object({
|
|
44331
|
-
|
|
44568
|
+
entryId: string().min(1),
|
|
44332
44569
|
gpuType: string(),
|
|
44333
44570
|
region: string(),
|
|
44334
44571
|
datasetBytes: number().int().nonnegative(),
|
|
@@ -44394,7 +44631,7 @@ method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "
|
|
|
44394
44631
|
kind: "mutation",
|
|
44395
44632
|
auth: "admin",
|
|
44396
44633
|
timeoutMs: 5 * 6e4
|
|
44397
|
-
}), method(object({
|
|
44634
|
+
}), method(object({ entryId: string().min(1).optional() }), array(ProviderJobStatusSchema).readonly(), {
|
|
44398
44635
|
auth: "admin",
|
|
44399
44636
|
timeoutMs: 2 * 6e4
|
|
44400
44637
|
});
|
|
@@ -44404,20 +44641,31 @@ method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "
|
|
|
44404
44641
|
*
|
|
44405
44642
|
* It owns what every provider must agree on, written once:
|
|
44406
44643
|
*
|
|
44407
|
-
* - **Dataset versions.** Taken over from pipeline-analytics (D796): the rows
|
|
44408
|
-
* live here (D808); pipeline-analytics still CUTS a version (it owns the corpus and
|
|
44409
|
-
* the export plan) and reaches this store through the five row methods
|
|
44410
|
-
* below, so the Retrain page and the export did not change.
|
|
44411
44644
|
* - **Jobs.** A persisted state machine, a reconcile loop against the
|
|
44412
44645
|
* provider, a cost guard, a daily orphan sweep and the retention rules.
|
|
44413
44646
|
* - **The return flow.** Verified artefacts → Model Studio
|
|
44414
44647
|
* (`custom-model-registry.importModelBundle`) → canary on cameras →
|
|
44415
44648
|
* rollback. Nothing is ever registered automatically.
|
|
44416
44649
|
*
|
|
44650
|
+
* Plan/start takes a live {@link TrainingCorpusSelection} (collections +
|
|
44651
|
+
* work-state rule); frame ids are resolved via pipeline-analytics at that
|
|
44652
|
+
* moment and snapshotted on the job. No frozen dataset version.
|
|
44653
|
+
*
|
|
44417
44654
|
* Every method is `admin`.
|
|
44418
44655
|
*/
|
|
44419
|
-
/**
|
|
44420
|
-
|
|
44656
|
+
/**
|
|
44657
|
+
* Live corpus criteria for plan/start (design §5.2). Default is complete-only.
|
|
44658
|
+
* Frame ids are resolved at plan/start — never a frozen version id.
|
|
44659
|
+
*/
|
|
44660
|
+
var TrainingCorpusSelectionSchema = object({
|
|
44661
|
+
collectionIds: array(string()).min(1),
|
|
44662
|
+
/** Default complete-only for v1 */
|
|
44663
|
+
workState: _enum([
|
|
44664
|
+
"complete",
|
|
44665
|
+
"annotated_or_complete",
|
|
44666
|
+
"any_annotated"
|
|
44667
|
+
]).default("complete")
|
|
44668
|
+
});
|
|
44421
44669
|
/** A base model a detector job can fine-tune, and the baseline it is judged against. */
|
|
44422
44670
|
var TrainingBaseModelSchema = object({
|
|
44423
44671
|
modelId: string(),
|
|
@@ -44435,9 +44683,13 @@ var TrainingPresetSchema = _enum([
|
|
|
44435
44683
|
"thorough"
|
|
44436
44684
|
]);
|
|
44437
44685
|
var TrainingJobRequestSchema = object({
|
|
44438
|
-
|
|
44439
|
-
|
|
44440
|
-
|
|
44686
|
+
/** Live collection + work-state criteria; resolved at plan/start. */
|
|
44687
|
+
selection: TrainingCorpusSelectionSchema,
|
|
44688
|
+
/**
|
|
44689
|
+
* The provider **entry** that runs it (credentials + defaults).
|
|
44690
|
+
* Resolve to an owning addon via `training-provider.describe` / `listEntries`.
|
|
44691
|
+
*/
|
|
44692
|
+
entryId: string().min(1),
|
|
44441
44693
|
gpuType: string().min(1),
|
|
44442
44694
|
region: string(),
|
|
44443
44695
|
baseModelId: string().min(1),
|
|
@@ -44496,6 +44748,7 @@ var TrainingJobPlanSchema = object({
|
|
|
44496
44748
|
* result lives after the operator acted on it.
|
|
44497
44749
|
*/
|
|
44498
44750
|
var TrainingJobStateSchema = _enum([
|
|
44751
|
+
"queued",
|
|
44499
44752
|
"uploading",
|
|
44500
44753
|
"submitted",
|
|
44501
44754
|
"starting",
|
|
@@ -44549,6 +44802,11 @@ var TrainingJobSchema = object({
|
|
|
44549
44802
|
createdAt: number(),
|
|
44550
44803
|
updatedAt: number(),
|
|
44551
44804
|
state: TrainingJobStateSchema,
|
|
44805
|
+
/**
|
|
44806
|
+
* Shared by every job enqueued together via `startJobBatch`.
|
|
44807
|
+
* `null` for a lone `startJob`. The engine advances queued siblings by this id.
|
|
44808
|
+
*/
|
|
44809
|
+
batchId: string().nullable().default(null),
|
|
44552
44810
|
stage: TrainingStageSchema.nullable(),
|
|
44553
44811
|
failure: object({
|
|
44554
44812
|
reason: TrainingFailureReasonSchema,
|
|
@@ -44560,6 +44818,12 @@ var TrainingJobSchema = object({
|
|
|
44560
44818
|
maxRuntimeSec: number().int().positive(),
|
|
44561
44819
|
trainer: TrainerSourceRefSchema,
|
|
44562
44820
|
datasetLayout: RetrainExportLayoutSchema,
|
|
44821
|
+
/** Frame count snapshotted when the job started (audit; equals `frameIds.length`). */
|
|
44822
|
+
frameCount: number().int().nonnegative(),
|
|
44823
|
+
/** Resolved frame ids at start — the archive ships exactly these. */
|
|
44824
|
+
frameIds: array(string()).max(2e4).readonly(),
|
|
44825
|
+
/** Hash of what those frames taught at start (trainer `dataset.annotationHash`). */
|
|
44826
|
+
annotationHash: string(),
|
|
44563
44827
|
dataset: object({
|
|
44564
44828
|
bytes: number().int(),
|
|
44565
44829
|
sha256: string(),
|
|
@@ -44601,13 +44865,7 @@ var TrainingJobSchema = object({
|
|
|
44601
44865
|
canary: TrainingCanarySchema.nullable()
|
|
44602
44866
|
});
|
|
44603
44867
|
var IdInput = object({ id: string().min(1) });
|
|
44604
|
-
method(
|
|
44605
|
-
kind: "mutation",
|
|
44606
|
-
auth: "admin"
|
|
44607
|
-
}), method(IdInput, TrainingDatasetVersionRowSchema.nullable(), { auth: "admin" }), method(object({}), array(RetrainDatasetVersionSchema).readonly(), { auth: "admin" }), method(object({ name: string() }), object({ id: string().nullable() }), { auth: "admin" }), method(IdInput, object({ removed: boolean() }), {
|
|
44608
|
-
kind: "mutation",
|
|
44609
|
-
auth: "admin"
|
|
44610
|
-
}), method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" }), method(TrainingJobRequestSchema, TrainingJobPlanSchema, {
|
|
44868
|
+
method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" }), method(TrainingJobRequestSchema, TrainingJobPlanSchema, {
|
|
44611
44869
|
auth: "admin",
|
|
44612
44870
|
timeoutMs: 6e4
|
|
44613
44871
|
}), method(TrainingJobRequestSchema.extend({
|
|
@@ -44626,6 +44884,24 @@ method(TrainingDatasetVersionRowSchema, object({ stored: boolean() }), {
|
|
|
44626
44884
|
kind: "mutation",
|
|
44627
44885
|
auth: "admin",
|
|
44628
44886
|
timeoutMs: 12e4
|
|
44887
|
+
}), method(TrainingJobRequestSchema.omit({ baseModelId: true }).extend({
|
|
44888
|
+
baseModelIds: array(string().min(1)).min(1),
|
|
44889
|
+
name: string().min(1).max(120),
|
|
44890
|
+
maxCostUsd: number().positive(),
|
|
44891
|
+
maxRuntimeSec: number().int().positive(),
|
|
44892
|
+
idempotencyKey: string().min(8).max(80)
|
|
44893
|
+
}), discriminatedUnion("kind", [object({
|
|
44894
|
+
kind: literal("started"),
|
|
44895
|
+
batchId: string(),
|
|
44896
|
+
jobs: array(TrainingJobSchema).min(1).readonly()
|
|
44897
|
+
}), object({
|
|
44898
|
+
kind: literal("refused"),
|
|
44899
|
+
reason: TrainingRefusalReasonSchema,
|
|
44900
|
+
detail: string()
|
|
44901
|
+
})]), {
|
|
44902
|
+
kind: "mutation",
|
|
44903
|
+
auth: "admin",
|
|
44904
|
+
timeoutMs: 12e4
|
|
44629
44905
|
}), method(object({}), array(TrainingJobSchema).readonly(), { auth: "admin" }), method(IdInput, TrainingJobSchema.nullable(), { auth: "admin" }), method(IdInput, TrainingJobSchema, {
|
|
44630
44906
|
kind: "mutation",
|
|
44631
44907
|
auth: "admin",
|
|
@@ -46236,17 +46512,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
46236
46512
|
return toDeviceSummary(device, this.addonId);
|
|
46237
46513
|
}
|
|
46238
46514
|
};
|
|
46239
|
-
/**
|
|
46240
|
-
* Default silence budget before a sleeping battery camera is called gone.
|
|
46241
|
-
*
|
|
46242
|
-
* Sized off the mechanism that produces contact, not off taste: a battery cam
|
|
46243
|
-
* on this deployment surfaces a firmware push (battery level, sleep/wake, or a
|
|
46244
|
-
* motion email) on the order of hours even with no visitors, and the snapshot
|
|
46245
|
-
* wrapper's own battery window is 1 hour. Six hours is therefore several
|
|
46246
|
-
* missed opportunities, not one — so a single quiet night does not raise a
|
|
46247
|
-
* fault, and a genuinely flat camera is named well inside a day.
|
|
46248
|
-
*/
|
|
46249
|
-
var BATTERY_UNREACHABLE_AFTER_MS = 360 * 6e4;
|
|
46250
46515
|
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder;
|
|
46251
46516
|
new Set(Object.values(DeviceType));
|
|
46252
46517
|
Object.freeze({
|
|
@@ -50210,6 +50475,36 @@ Object.freeze({
|
|
|
50210
50475
|
addonId: null,
|
|
50211
50476
|
access: "view"
|
|
50212
50477
|
},
|
|
50478
|
+
"oidcConnections.connectionFormSchema": {
|
|
50479
|
+
capName: "oidc-connections",
|
|
50480
|
+
capScope: "system",
|
|
50481
|
+
addonId: null,
|
|
50482
|
+
access: "view"
|
|
50483
|
+
},
|
|
50484
|
+
"oidcConnections.createConnection": {
|
|
50485
|
+
capName: "oidc-connections",
|
|
50486
|
+
capScope: "system",
|
|
50487
|
+
addonId: null,
|
|
50488
|
+
access: "create"
|
|
50489
|
+
},
|
|
50490
|
+
"oidcConnections.deleteConnection": {
|
|
50491
|
+
capName: "oidc-connections",
|
|
50492
|
+
capScope: "system",
|
|
50493
|
+
addonId: null,
|
|
50494
|
+
access: "delete"
|
|
50495
|
+
},
|
|
50496
|
+
"oidcConnections.listConnections": {
|
|
50497
|
+
capName: "oidc-connections",
|
|
50498
|
+
capScope: "system",
|
|
50499
|
+
addonId: null,
|
|
50500
|
+
access: "view"
|
|
50501
|
+
},
|
|
50502
|
+
"oidcConnections.updateConnection": {
|
|
50503
|
+
capName: "oidc-connections",
|
|
50504
|
+
capScope: "system",
|
|
50505
|
+
addonId: null,
|
|
50506
|
+
access: "create"
|
|
50507
|
+
},
|
|
50213
50508
|
"osd.getStatus": {
|
|
50214
50509
|
capName: "osd",
|
|
50215
50510
|
capScope: "device",
|
|
@@ -50282,6 +50577,12 @@ Object.freeze({
|
|
|
50282
50577
|
addonId: null,
|
|
50283
50578
|
access: "create"
|
|
50284
50579
|
},
|
|
50580
|
+
"osdManager.setSlotCameraEnabled": {
|
|
50581
|
+
capName: "osd-manager",
|
|
50582
|
+
capScope: "system",
|
|
50583
|
+
addonId: null,
|
|
50584
|
+
access: "create"
|
|
50585
|
+
},
|
|
50285
50586
|
"osdManager.upsertTemplate": {
|
|
50286
50587
|
capName: "osd-manager",
|
|
50287
50588
|
capScope: "system",
|
|
@@ -50420,12 +50721,6 @@ Object.freeze({
|
|
|
50420
50721
|
addonId: null,
|
|
50421
50722
|
access: "create"
|
|
50422
50723
|
},
|
|
50423
|
-
"pipelineAnalytics.createRetrainDatasetVersion": {
|
|
50424
|
-
capName: "pipeline-analytics",
|
|
50425
|
-
capScope: "device",
|
|
50426
|
-
addonId: null,
|
|
50427
|
-
access: "create"
|
|
50428
|
-
},
|
|
50429
50724
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
50430
50725
|
capName: "pipeline-analytics",
|
|
50431
50726
|
capScope: "device",
|
|
@@ -50438,12 +50733,6 @@ Object.freeze({
|
|
|
50438
50733
|
addonId: null,
|
|
50439
50734
|
access: "delete"
|
|
50440
50735
|
},
|
|
50441
|
-
"pipelineAnalytics.deleteRetrainDatasetVersion": {
|
|
50442
|
-
capName: "pipeline-analytics",
|
|
50443
|
-
capScope: "device",
|
|
50444
|
-
addonId: null,
|
|
50445
|
-
access: "delete"
|
|
50446
|
-
},
|
|
50447
50736
|
"pipelineAnalytics.deleteTracks": {
|
|
50448
50737
|
capName: "pipeline-analytics",
|
|
50449
50738
|
capScope: "device",
|
|
@@ -50546,6 +50835,12 @@ Object.freeze({
|
|
|
50546
50835
|
addonId: null,
|
|
50547
50836
|
access: "view"
|
|
50548
50837
|
},
|
|
50838
|
+
"pipelineAnalytics.getRetrainCorpusKpi": {
|
|
50839
|
+
capName: "pipeline-analytics",
|
|
50840
|
+
capScope: "device",
|
|
50841
|
+
addonId: null,
|
|
50842
|
+
access: "view"
|
|
50843
|
+
},
|
|
50549
50844
|
"pipelineAnalytics.getRetrainExportUrl": {
|
|
50550
50845
|
capName: "pipeline-analytics",
|
|
50551
50846
|
capScope: "device",
|
|
@@ -50666,7 +50961,7 @@ Object.freeze({
|
|
|
50666
50961
|
addonId: null,
|
|
50667
50962
|
access: "view"
|
|
50668
50963
|
},
|
|
50669
|
-
"pipelineAnalytics.
|
|
50964
|
+
"pipelineAnalytics.listRetrainCorpusPage": {
|
|
50670
50965
|
capName: "pipeline-analytics",
|
|
50671
50966
|
capScope: "device",
|
|
50672
50967
|
addonId: null,
|
|
@@ -50720,12 +51015,6 @@ Object.freeze({
|
|
|
50720
51015
|
addonId: null,
|
|
50721
51016
|
access: "create"
|
|
50722
51017
|
},
|
|
50723
|
-
"pipelineAnalytics.previewRetrainDataset": {
|
|
50724
|
-
capName: "pipeline-analytics",
|
|
50725
|
-
capScope: "device",
|
|
50726
|
-
addonId: null,
|
|
50727
|
-
access: "view"
|
|
50728
|
-
},
|
|
50729
51018
|
"pipelineAnalytics.proposeRetrainAnnotations": {
|
|
50730
51019
|
capName: "pipeline-analytics",
|
|
50731
51020
|
capScope: "device",
|
|
@@ -50804,6 +51093,12 @@ Object.freeze({
|
|
|
50804
51093
|
addonId: null,
|
|
50805
51094
|
access: "create"
|
|
50806
51095
|
},
|
|
51096
|
+
"pipelineAnalytics.resolveRetrainTrainingCorpus": {
|
|
51097
|
+
capName: "pipeline-analytics",
|
|
51098
|
+
capScope: "device",
|
|
51099
|
+
addonId: null,
|
|
51100
|
+
access: "view"
|
|
51101
|
+
},
|
|
50807
51102
|
"pipelineAnalytics.restageRetrainTrack": {
|
|
50808
51103
|
capName: "pipeline-analytics",
|
|
50809
51104
|
capScope: "device",
|
|
@@ -50864,12 +51159,6 @@ Object.freeze({
|
|
|
50864
51159
|
addonId: null,
|
|
50865
51160
|
access: "create"
|
|
50866
51161
|
},
|
|
50867
|
-
"pipelineAnalytics.verifyRetrainDatasetVersion": {
|
|
50868
|
-
capName: "pipeline-analytics",
|
|
50869
|
-
capScope: "device",
|
|
50870
|
-
addonId: null,
|
|
50871
|
-
access: "view"
|
|
50872
|
-
},
|
|
50873
51162
|
"pipelineAnalytics.wipeAllAnalytics": {
|
|
50874
51163
|
capName: "pipeline-analytics",
|
|
50875
51164
|
capScope: "device",
|
|
@@ -53264,18 +53553,6 @@ Object.freeze({
|
|
|
53264
53553
|
addonId: null,
|
|
53265
53554
|
access: "create"
|
|
53266
53555
|
},
|
|
53267
|
-
"training.findDatasetVersionIdByName": {
|
|
53268
|
-
capName: "training",
|
|
53269
|
-
capScope: "system",
|
|
53270
|
-
addonId: null,
|
|
53271
|
-
access: "view"
|
|
53272
|
-
},
|
|
53273
|
-
"training.getDatasetVersion": {
|
|
53274
|
-
capName: "training",
|
|
53275
|
-
capScope: "system",
|
|
53276
|
-
addonId: null,
|
|
53277
|
-
access: "view"
|
|
53278
|
-
},
|
|
53279
53556
|
"training.getJob": {
|
|
53280
53557
|
capName: "training",
|
|
53281
53558
|
capScope: "system",
|
|
@@ -53294,12 +53571,6 @@ Object.freeze({
|
|
|
53294
53571
|
addonId: null,
|
|
53295
53572
|
access: "view"
|
|
53296
53573
|
},
|
|
53297
|
-
"training.listDatasetVersions": {
|
|
53298
|
-
capName: "training",
|
|
53299
|
-
capScope: "system",
|
|
53300
|
-
addonId: null,
|
|
53301
|
-
access: "view"
|
|
53302
|
-
},
|
|
53303
53574
|
"training.listJobs": {
|
|
53304
53575
|
capName: "training",
|
|
53305
53576
|
capScope: "system",
|
|
@@ -53312,54 +53583,66 @@ Object.freeze({
|
|
|
53312
53583
|
addonId: null,
|
|
53313
53584
|
access: "view"
|
|
53314
53585
|
},
|
|
53315
|
-
"training.
|
|
53586
|
+
"training.registerResult": {
|
|
53316
53587
|
capName: "training",
|
|
53317
53588
|
capScope: "system",
|
|
53318
53589
|
addonId: null,
|
|
53319
53590
|
access: "create"
|
|
53320
53591
|
},
|
|
53321
|
-
"training.
|
|
53592
|
+
"training.rollbackCanary": {
|
|
53322
53593
|
capName: "training",
|
|
53323
53594
|
capScope: "system",
|
|
53324
53595
|
addonId: null,
|
|
53325
53596
|
access: "create"
|
|
53326
53597
|
},
|
|
53327
|
-
"training.
|
|
53598
|
+
"training.startJob": {
|
|
53328
53599
|
capName: "training",
|
|
53329
53600
|
capScope: "system",
|
|
53330
53601
|
addonId: null,
|
|
53331
|
-
access: "
|
|
53602
|
+
access: "create"
|
|
53332
53603
|
},
|
|
53333
|
-
"training.
|
|
53604
|
+
"training.startJobBatch": {
|
|
53334
53605
|
capName: "training",
|
|
53335
53606
|
capScope: "system",
|
|
53336
53607
|
addonId: null,
|
|
53337
53608
|
access: "create"
|
|
53338
53609
|
},
|
|
53339
|
-
"
|
|
53340
|
-
capName: "training",
|
|
53610
|
+
"trainingProvider.cancelJob": {
|
|
53611
|
+
capName: "training-provider",
|
|
53341
53612
|
capScope: "system",
|
|
53342
53613
|
addonId: null,
|
|
53343
53614
|
access: "create"
|
|
53344
53615
|
},
|
|
53345
|
-
"trainingProvider.
|
|
53616
|
+
"trainingProvider.cleanupJob": {
|
|
53346
53617
|
capName: "training-provider",
|
|
53347
53618
|
capScope: "system",
|
|
53348
53619
|
addonId: null,
|
|
53349
53620
|
access: "create"
|
|
53350
53621
|
},
|
|
53351
|
-
"trainingProvider.
|
|
53622
|
+
"trainingProvider.createEntry": {
|
|
53352
53623
|
capName: "training-provider",
|
|
53353
53624
|
capScope: "system",
|
|
53354
53625
|
addonId: null,
|
|
53355
53626
|
access: "create"
|
|
53356
53627
|
},
|
|
53628
|
+
"trainingProvider.deleteEntry": {
|
|
53629
|
+
capName: "training-provider",
|
|
53630
|
+
capScope: "system",
|
|
53631
|
+
addonId: null,
|
|
53632
|
+
access: "delete"
|
|
53633
|
+
},
|
|
53357
53634
|
"trainingProvider.describe": {
|
|
53358
53635
|
capName: "training-provider",
|
|
53359
53636
|
capScope: "system",
|
|
53360
53637
|
addonId: null,
|
|
53361
53638
|
access: "view"
|
|
53362
53639
|
},
|
|
53640
|
+
"trainingProvider.describeTypes": {
|
|
53641
|
+
capName: "training-provider",
|
|
53642
|
+
capScope: "system",
|
|
53643
|
+
addonId: null,
|
|
53644
|
+
access: "view"
|
|
53645
|
+
},
|
|
53363
53646
|
"trainingProvider.estimateJob": {
|
|
53364
53647
|
capName: "training-provider",
|
|
53365
53648
|
capScope: "system",
|
|
@@ -53372,6 +53655,12 @@ Object.freeze({
|
|
|
53372
53655
|
addonId: null,
|
|
53373
53656
|
access: "view"
|
|
53374
53657
|
},
|
|
53658
|
+
"trainingProvider.listEntries": {
|
|
53659
|
+
capName: "training-provider",
|
|
53660
|
+
capScope: "system",
|
|
53661
|
+
addonId: null,
|
|
53662
|
+
access: "view"
|
|
53663
|
+
},
|
|
53375
53664
|
"trainingProvider.listJobEvents": {
|
|
53376
53665
|
capName: "training-provider",
|
|
53377
53666
|
capScope: "system",
|
|
@@ -53402,6 +53691,12 @@ Object.freeze({
|
|
|
53402
53691
|
addonId: null,
|
|
53403
53692
|
access: "create"
|
|
53404
53693
|
},
|
|
53694
|
+
"trainingProvider.updateEntry": {
|
|
53695
|
+
capName: "training-provider",
|
|
53696
|
+
capScope: "system",
|
|
53697
|
+
addonId: null,
|
|
53698
|
+
access: "create"
|
|
53699
|
+
},
|
|
53405
53700
|
"trainingProvider.uploadDataset": {
|
|
53406
53701
|
capName: "training-provider",
|
|
53407
53702
|
capScope: "system",
|
|
@@ -56760,6 +57055,11 @@ Object.freeze({
|
|
|
56760
57055
|
form: "single",
|
|
56761
57056
|
optional: false
|
|
56762
57057
|
}],
|
|
57058
|
+
"osdManager.setSlotCameraEnabled": [{
|
|
57059
|
+
name: "deviceId",
|
|
57060
|
+
form: "single",
|
|
57061
|
+
optional: false
|
|
57062
|
+
}],
|
|
56763
57063
|
"petFeeder.callPet": [{
|
|
56764
57064
|
name: "deviceId",
|
|
56765
57065
|
form: "single",
|
|
@@ -56980,6 +57280,11 @@ Object.freeze({
|
|
|
56980
57280
|
form: "array",
|
|
56981
57281
|
optional: false
|
|
56982
57282
|
}],
|
|
57283
|
+
"pipelineAnalytics.listRetrainCorpusPage": [{
|
|
57284
|
+
name: "deviceIds",
|
|
57285
|
+
form: "array",
|
|
57286
|
+
optional: true
|
|
57287
|
+
}],
|
|
56983
57288
|
"pipelineAnalytics.listRetrainStaging": [{
|
|
56984
57289
|
name: "deviceIds",
|
|
56985
57290
|
form: "array",
|
|
@@ -58089,6 +58394,7 @@ Object.freeze({
|
|
|
58089
58394
|
});
|
|
58090
58395
|
Object.freeze({
|
|
58091
58396
|
"broker": "broker",
|
|
58397
|
+
"calendar-source": "calendar",
|
|
58092
58398
|
"device-export": "device-export",
|
|
58093
58399
|
"llm": "ai",
|
|
58094
58400
|
"mesh-network": "mesh",
|
|
@@ -58213,6 +58519,9 @@ new Map(Object.entries({
|
|
|
58213
58519
|
"stream-online": "device-events",
|
|
58214
58520
|
"stream-offline": "device-events",
|
|
58215
58521
|
"detection-blind": "device-events",
|
|
58522
|
+
"recording-stalled": "device-events",
|
|
58523
|
+
"recording-healthy": "device-events",
|
|
58524
|
+
"recording-degraded": "device-events",
|
|
58216
58525
|
"node-online": "system",
|
|
58217
58526
|
"node-offline": "system",
|
|
58218
58527
|
"node-restarting": "system",
|
|
@@ -58226,6 +58535,9 @@ new Map(Object.entries({
|
|
|
58226
58535
|
"wrapper-update-available": "system",
|
|
58227
58536
|
"backup-completed": "system",
|
|
58228
58537
|
"export-completed": "system",
|
|
58538
|
+
"storage-location-low": "system",
|
|
58539
|
+
"storage-location-critical": "system",
|
|
58540
|
+
"storage-location-ok": "system",
|
|
58229
58541
|
"alarm-triggered": "alarm",
|
|
58230
58542
|
"alarm-armed": "alarm",
|
|
58231
58543
|
"alarm-disarmed": "alarm",
|
|
@@ -58287,6 +58599,16 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
58287
58599
|
label: "Detecting nothing",
|
|
58288
58600
|
kinds: ["detection-blind"]
|
|
58289
58601
|
},
|
|
58602
|
+
{
|
|
58603
|
+
id: "recording",
|
|
58604
|
+
family: "device-events",
|
|
58605
|
+
label: "Recording",
|
|
58606
|
+
kinds: [
|
|
58607
|
+
"recording-stalled",
|
|
58608
|
+
"recording-healthy",
|
|
58609
|
+
"recording-degraded"
|
|
58610
|
+
]
|
|
58611
|
+
},
|
|
58290
58612
|
{
|
|
58291
58613
|
id: "nodes",
|
|
58292
58614
|
family: "system",
|
|
@@ -58327,6 +58649,16 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
58327
58649
|
label: "Backups & exports",
|
|
58328
58650
|
kinds: ["backup-completed", "export-completed"]
|
|
58329
58651
|
},
|
|
58652
|
+
{
|
|
58653
|
+
id: "storage",
|
|
58654
|
+
family: "system",
|
|
58655
|
+
label: "Storage locations",
|
|
58656
|
+
kinds: [
|
|
58657
|
+
"storage-location-low",
|
|
58658
|
+
"storage-location-critical",
|
|
58659
|
+
"storage-location-ok"
|
|
58660
|
+
]
|
|
58661
|
+
},
|
|
58330
58662
|
{
|
|
58331
58663
|
id: "alarm",
|
|
58332
58664
|
family: "alarm",
|
|
@@ -58409,6 +58741,9 @@ var DEVICE_EVENTS = [
|
|
|
58409
58741
|
"stream-online",
|
|
58410
58742
|
"stream-offline",
|
|
58411
58743
|
"detection-blind",
|
|
58744
|
+
"recording-stalled",
|
|
58745
|
+
"recording-healthy",
|
|
58746
|
+
"recording-degraded",
|
|
58412
58747
|
...ALARM
|
|
58413
58748
|
];
|
|
58414
58749
|
/** Every kind a summary may speak about — the two summarisable families. */
|
|
@@ -58576,6 +58911,11 @@ var STATE_SUMMARY_VARS = [
|
|
|
58576
58911
|
[...RULE_TEXT],
|
|
58577
58912
|
[...RULE_TEXT],
|
|
58578
58913
|
[...RULE_TEXT],
|
|
58914
|
+
[...RULE_TEXT],
|
|
58915
|
+
[...RULE_TEXT],
|
|
58916
|
+
[...RULE_TEXT],
|
|
58917
|
+
[...RULE_TEXT],
|
|
58918
|
+
[...RULE_TEXT],
|
|
58579
58919
|
([...RULE_TEXT], [...UPDATES]),
|
|
58580
58920
|
([...RULE_TEXT], [...PACKAGE_UPDATES]),
|
|
58581
58921
|
([...RULE_TEXT], [...PACKAGE_UPDATES]),
|
|
@@ -71247,7 +71587,7 @@ var require_snapshot_utils = /* @__PURE__ */ require_chunk.__commonJSMin(((expor
|
|
|
71247
71587
|
//#region ../../node_modules/undici/lib/mock/snapshot-recorder.js
|
|
71248
71588
|
var require_snapshot_recorder = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module) => {
|
|
71249
71589
|
var { writeFile: writeFile$2, readFile: readFile$3, mkdir: mkdir$2 } = require("node:fs/promises");
|
|
71250
|
-
var { dirname: dirname$
|
|
71590
|
+
var { dirname: dirname$2, resolve: resolve$1 } = require("node:path");
|
|
71251
71591
|
var { setTimeout: setTimeout$1, clearTimeout: clearTimeout$1 } = require("node:timers");
|
|
71252
71592
|
var { InvalidArgumentError, UndiciError } = require_errors$1();
|
|
71253
71593
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -71512,7 +71852,7 @@ var require_snapshot_recorder = /* @__PURE__ */ require_chunk.__commonJSMin(((ex
|
|
|
71512
71852
|
const path = filePath || this.#snapshotPath;
|
|
71513
71853
|
if (!path) throw new InvalidArgumentError("Snapshot path is required");
|
|
71514
71854
|
try {
|
|
71515
|
-
const data = await readFile$3(resolve(path), "utf8");
|
|
71855
|
+
const data = await readFile$3(resolve$1(path), "utf8");
|
|
71516
71856
|
const parsed = JSON.parse(data);
|
|
71517
71857
|
if (Array.isArray(parsed)) {
|
|
71518
71858
|
this.#snapshots.clear();
|
|
@@ -71532,8 +71872,8 @@ var require_snapshot_recorder = /* @__PURE__ */ require_chunk.__commonJSMin(((ex
|
|
|
71532
71872
|
async saveSnapshots(filePath) {
|
|
71533
71873
|
const path = filePath || this.#snapshotPath;
|
|
71534
71874
|
if (!path) throw new InvalidArgumentError("Snapshot path is required");
|
|
71535
|
-
const resolvedPath = resolve(path);
|
|
71536
|
-
await mkdir$2(dirname$
|
|
71875
|
+
const resolvedPath = resolve$1(path);
|
|
71876
|
+
await mkdir$2(dirname$2(resolvedPath), { recursive: true });
|
|
71537
71877
|
const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
|
|
71538
71878
|
hash,
|
|
71539
71879
|
snapshot
|
|
@@ -160170,6 +160510,7 @@ function buildInitialStatus2(config, enabled) {
|
|
|
160170
160510
|
lastErrorMessage: void 0
|
|
160171
160511
|
};
|
|
160172
160512
|
}
|
|
160513
|
+
process.platform, process.env["SystemRoot"];
|
|
160173
160514
|
/**
|
|
160174
160515
|
* The spawn a one-shot ffmpeg site uses: the spec's seam when one is injected,
|
|
160175
160516
|
* else the node's PINNED binary (D540), else `null` — and the site refuses by
|
|
@@ -160742,6 +161083,13 @@ var reolinkCameraSchema = object({
|
|
|
160742
161083
|
*/
|
|
160743
161084
|
clipCatalogForcedWake: boolean().optional(),
|
|
160744
161085
|
/**
|
|
161086
|
+
* Battery cameras only: the window, in minutes, in which the camera refreshes
|
|
161087
|
+
* ALL its metadata and its cached snapshot exactly once (D847). Absent means
|
|
161088
|
+
* the default (60); `0` turns the scheduled refresh off for this camera; a
|
|
161089
|
+
* value is clamped to 15..240 (below the 6 h presence budget).
|
|
161090
|
+
*/
|
|
161091
|
+
batteryRefreshMinutes: number().int().min(0).max(240).optional(),
|
|
161092
|
+
/**
|
|
160745
161093
|
* Per-AI-class sensitivity overrides — pushed via
|
|
160746
161094
|
* `setAiDetection(aiType, sensitivity)` (cmdId=343). Reolink
|
|
160747
161095
|
* range 0..100 — same scale the reolink_aio CGI exposes
|
|
@@ -166999,14 +167347,266 @@ function deriveChargingSource(adapterStatus, chargeStatus) {
|
|
|
166999
167347
|
function reportsPowerSource(info) {
|
|
167000
167348
|
return info.adapterStatus !== void 0 || info.chargeStatus !== void 0;
|
|
167001
167349
|
}
|
|
167350
|
+
/** The settings key, per device. */
|
|
167351
|
+
var BATTERY_REFRESH_MINUTES_KEY = "batteryRefreshMinutes";
|
|
167352
|
+
/** How often the in-memory check runs. Costs nothing: no I/O unless it fires. */
|
|
167353
|
+
var BATTERY_REFRESH_TICK_MS = 6e4;
|
|
167354
|
+
/** The least time between two attempts in one window. */
|
|
167355
|
+
var RETRY_BACKOFF_MS = 10 * 6e4;
|
|
167356
|
+
var MAX_WAKE_LEAD_MS = 5 * 6e4;
|
|
167357
|
+
var STAGGER_SLOTS = 60;
|
|
167358
|
+
/** The operator's window in ms, or `0` for off. Out-of-range values clamp. */
|
|
167359
|
+
function resolveBatteryRefreshIntervalMs(configured) {
|
|
167360
|
+
if (typeof configured !== "number" || !Number.isFinite(configured)) return 60 * 6e4;
|
|
167361
|
+
if (configured <= 0) return 0;
|
|
167362
|
+
return Math.min(240, Math.max(15, Math.round(configured))) * 6e4;
|
|
167363
|
+
}
|
|
167364
|
+
/** Deterministic per-device offset so a fleet's windows do not align. */
|
|
167365
|
+
function staggerOffsetMs(deviceId, intervalMs) {
|
|
167366
|
+
const slot = Math.abs(Math.trunc(deviceId)) % STAGGER_SLOTS;
|
|
167367
|
+
return Math.floor(intervalMs / STAGGER_SLOTS * slot);
|
|
167368
|
+
}
|
|
167369
|
+
/** Which window a wall-clock instant falls in. */
|
|
167370
|
+
function windowIndex(at, intervalMs, offsetMs) {
|
|
167371
|
+
return Math.floor((at - offsetMs) / intervalMs);
|
|
167372
|
+
}
|
|
167373
|
+
/** How long before a window's end the wake is allowed to fire. */
|
|
167374
|
+
function wakeLeadMs(intervalMs) {
|
|
167375
|
+
return Math.min(MAX_WAKE_LEAD_MS, Math.floor(intervalMs / 10));
|
|
167376
|
+
}
|
|
167377
|
+
function decideBatteryRefresh(input) {
|
|
167378
|
+
if (input.intervalMs <= 0) return {
|
|
167379
|
+
kind: "skip",
|
|
167380
|
+
reason: "off"
|
|
167381
|
+
};
|
|
167382
|
+
const current = windowIndex(input.nowMs, input.intervalMs, input.offsetMs);
|
|
167383
|
+
if ((typeof input.lastFullRefreshAt === "number" && input.lastFullRefreshAt > 0 ? windowIndex(input.lastFullRefreshAt, input.intervalMs, input.offsetMs) : Number.NEGATIVE_INFINITY) >= current) return {
|
|
167384
|
+
kind: "skip",
|
|
167385
|
+
reason: "done"
|
|
167386
|
+
};
|
|
167387
|
+
if (input.attemptsInWindow >= 2) return {
|
|
167388
|
+
kind: "skip",
|
|
167389
|
+
reason: "attempts-exhausted"
|
|
167390
|
+
};
|
|
167391
|
+
if (input.lastAttemptAt !== void 0 && input.attemptsInWindow > 0 && input.nowMs - input.lastAttemptAt < 6e5) return {
|
|
167392
|
+
kind: "skip",
|
|
167393
|
+
reason: "backoff"
|
|
167394
|
+
};
|
|
167395
|
+
if (input.awake) return { kind: "refresh-awake" };
|
|
167396
|
+
const windowEnd = input.offsetMs + (current + 1) * input.intervalMs;
|
|
167397
|
+
if (input.nowMs < windowEnd - wakeLeadMs(input.intervalMs)) return {
|
|
167398
|
+
kind: "skip",
|
|
167399
|
+
reason: "asleep-before-deadline"
|
|
167400
|
+
};
|
|
167401
|
+
return { kind: "wake-then-refresh" };
|
|
167402
|
+
}
|
|
167403
|
+
var BatteryHourlyRefresh = class {
|
|
167404
|
+
deps;
|
|
167405
|
+
inFlight = null;
|
|
167406
|
+
attemptWindow = Number.NEGATIVE_INFINITY;
|
|
167407
|
+
attempts = 0;
|
|
167408
|
+
lastAttemptAt;
|
|
167409
|
+
constructor(deps) {
|
|
167410
|
+
this.deps = deps;
|
|
167411
|
+
}
|
|
167412
|
+
/**
|
|
167413
|
+
* The periodic check. Resolves `true` when a refresh landed in this call.
|
|
167414
|
+
* Never throws.
|
|
167415
|
+
*/
|
|
167416
|
+
async tick() {
|
|
167417
|
+
return this.consider("tick");
|
|
167418
|
+
}
|
|
167419
|
+
/**
|
|
167420
|
+
* The camera was just observed coming up (event, sleep poll, hub summary, a
|
|
167421
|
+
* confirmed wake). Resolves `true` when this call refreshed everything, so
|
|
167422
|
+
* the caller can skip the parts of its own fan-out the refresh already did.
|
|
167423
|
+
*/
|
|
167424
|
+
async noteAwake() {
|
|
167425
|
+
return this.consider("wake-transition");
|
|
167426
|
+
}
|
|
167427
|
+
async consider(trigger) {
|
|
167428
|
+
if (this.inFlight !== null) return this.inFlight;
|
|
167429
|
+
const nowMs = this.deps.now();
|
|
167430
|
+
const intervalMs = this.deps.intervalMs();
|
|
167431
|
+
const offsetMs = intervalMs > 0 ? staggerOffsetMs(this.deps.deviceId, intervalMs) : 0;
|
|
167432
|
+
const window = intervalMs > 0 ? windowIndex(nowMs, intervalMs, offsetMs) : 0;
|
|
167433
|
+
if (window !== this.attemptWindow) {
|
|
167434
|
+
this.attemptWindow = window;
|
|
167435
|
+
this.attempts = 0;
|
|
167436
|
+
this.lastAttemptAt = void 0;
|
|
167437
|
+
}
|
|
167438
|
+
const decision = decideBatteryRefresh({
|
|
167439
|
+
nowMs,
|
|
167440
|
+
intervalMs,
|
|
167441
|
+
offsetMs,
|
|
167442
|
+
lastFullRefreshAt: this.deps.lastFullRefreshAt(),
|
|
167443
|
+
awake: trigger === "wake-transition" || this.deps.isAwake(),
|
|
167444
|
+
attemptsInWindow: this.attempts,
|
|
167445
|
+
lastAttemptAt: this.lastAttemptAt
|
|
167446
|
+
});
|
|
167447
|
+
if (decision.kind === "skip") {
|
|
167448
|
+
this.logSkip(decision.reason, trigger);
|
|
167449
|
+
return false;
|
|
167450
|
+
}
|
|
167451
|
+
this.attempts += 1;
|
|
167452
|
+
this.lastAttemptAt = nowMs;
|
|
167453
|
+
const run = this.run(decision.kind, trigger).finally(() => {
|
|
167454
|
+
this.inFlight = null;
|
|
167455
|
+
});
|
|
167456
|
+
this.inFlight = run;
|
|
167457
|
+
return run;
|
|
167458
|
+
}
|
|
167459
|
+
async run(kind, trigger) {
|
|
167460
|
+
const { logger, deviceId } = this.deps;
|
|
167461
|
+
try {
|
|
167462
|
+
if (kind === "wake-then-refresh") {
|
|
167463
|
+
logger.info("battery hourly refresh: not awake in the window — waking once", {
|
|
167464
|
+
tags: { deviceId },
|
|
167465
|
+
meta: { attempt: this.attempts }
|
|
167466
|
+
});
|
|
167467
|
+
if (!await this.deps.wake()) {
|
|
167468
|
+
logger.warn("battery hourly refresh: the wake did not bring the camera up", {
|
|
167469
|
+
tags: { deviceId },
|
|
167470
|
+
meta: {
|
|
167471
|
+
attempt: this.attempts,
|
|
167472
|
+
retryAfterMs: RETRY_BACKOFF_MS
|
|
167473
|
+
}
|
|
167474
|
+
});
|
|
167475
|
+
return false;
|
|
167476
|
+
}
|
|
167477
|
+
if (this.windowSatisfied()) return true;
|
|
167478
|
+
} else logger.info("battery hourly refresh: camera is awake — refreshing now, no wake", {
|
|
167479
|
+
tags: { deviceId },
|
|
167480
|
+
meta: {
|
|
167481
|
+
trigger,
|
|
167482
|
+
attempt: this.attempts
|
|
167483
|
+
}
|
|
167484
|
+
});
|
|
167485
|
+
const outcome = await this.deps.refreshAll();
|
|
167486
|
+
if (outcome.failed.length > 0) {
|
|
167487
|
+
logger.warn("battery hourly refresh incomplete — not stamped, retried at the next chance", {
|
|
167488
|
+
tags: { deviceId },
|
|
167489
|
+
meta: {
|
|
167490
|
+
failed: outcome.failed,
|
|
167491
|
+
attempt: this.attempts,
|
|
167492
|
+
retryAfterMs: RETRY_BACKOFF_MS
|
|
167493
|
+
}
|
|
167494
|
+
});
|
|
167495
|
+
return false;
|
|
167496
|
+
}
|
|
167497
|
+
const at = this.deps.now();
|
|
167498
|
+
this.deps.stamp(at);
|
|
167499
|
+
logger.info("battery hourly refresh complete", {
|
|
167500
|
+
tags: { deviceId },
|
|
167501
|
+
meta: {
|
|
167502
|
+
kind,
|
|
167503
|
+
trigger,
|
|
167504
|
+
at
|
|
167505
|
+
}
|
|
167506
|
+
});
|
|
167507
|
+
return true;
|
|
167508
|
+
} catch (err) {
|
|
167509
|
+
logger.warn("battery hourly refresh failed — retried at the next chance", {
|
|
167510
|
+
tags: { deviceId },
|
|
167511
|
+
meta: {
|
|
167512
|
+
error: err instanceof Error ? err.message : String(err),
|
|
167513
|
+
attempt: this.attempts,
|
|
167514
|
+
retryAfterMs: RETRY_BACKOFF_MS
|
|
167515
|
+
}
|
|
167516
|
+
});
|
|
167517
|
+
return false;
|
|
167518
|
+
}
|
|
167519
|
+
}
|
|
167520
|
+
windowSatisfied() {
|
|
167521
|
+
const intervalMs = this.deps.intervalMs();
|
|
167522
|
+
if (intervalMs <= 0) return false;
|
|
167523
|
+
const offsetMs = staggerOffsetMs(this.deps.deviceId, intervalMs);
|
|
167524
|
+
const last = this.deps.lastFullRefreshAt();
|
|
167525
|
+
if (typeof last !== "number" || last <= 0) return false;
|
|
167526
|
+
return windowIndex(last, intervalMs, offsetMs) >= windowIndex(this.deps.now(), intervalMs, offsetMs);
|
|
167527
|
+
}
|
|
167528
|
+
logSkip(reason, trigger) {
|
|
167529
|
+
if (reason === "backoff" || reason === "attempts-exhausted") {
|
|
167530
|
+
this.deps.logger.info("battery hourly refresh skipped", {
|
|
167531
|
+
tags: { deviceId: this.deps.deviceId },
|
|
167532
|
+
meta: {
|
|
167533
|
+
reason,
|
|
167534
|
+
trigger,
|
|
167535
|
+
attempts: this.attempts
|
|
167536
|
+
}
|
|
167537
|
+
});
|
|
167538
|
+
return;
|
|
167539
|
+
}
|
|
167540
|
+
this.deps.logger.debug("battery hourly refresh not due", {
|
|
167541
|
+
tags: { deviceId: this.deps.deviceId },
|
|
167542
|
+
meta: {
|
|
167543
|
+
reason,
|
|
167544
|
+
trigger
|
|
167545
|
+
}
|
|
167546
|
+
});
|
|
167547
|
+
}
|
|
167548
|
+
};
|
|
167549
|
+
//#endregion
|
|
167550
|
+
//#region src/battery-refresh-settings.ts
|
|
167551
|
+
var BATTERY_REFRESH_SECTION_ID = "battery-refresh";
|
|
167552
|
+
var BATTERY_REFRESH_MINUTES_DESCRIPTION = `Once per window this camera refreshes everything CamStack knows about it — battery level and charging, signal, device info, every state panel and the cached snapshot. If the camera is awake at some point in the window (a motion event, a live view, anything that woke it) it is refreshed then and not woken again; if the whole window passes with the camera asleep, it is woken ONCE near the end of the window, refreshed, and left to go back to sleep. A camera that does not answer is retried at most twice per window. 15–240 minutes, default 60; 0 turns the scheduled refresh off (the battery level then ages until the camera next wakes by itself).`;
|
|
167553
|
+
/** Battery cameras only: a mains camera keeps its facts fresh on its own socket. */
|
|
167554
|
+
function buildBatteryRefreshDeviceSection(input) {
|
|
167555
|
+
if (!input.isBattery) return [];
|
|
167556
|
+
return [{
|
|
167557
|
+
id: BATTERY_REFRESH_SECTION_ID,
|
|
167558
|
+
tab: "general",
|
|
167559
|
+
title: "Scheduled refresh",
|
|
167560
|
+
description: "How often this battery camera is guaranteed to have been read, so a battery level never goes stale unseen.",
|
|
167561
|
+
columns: 1,
|
|
167562
|
+
fields: [{
|
|
167563
|
+
type: "number",
|
|
167564
|
+
key: BATTERY_REFRESH_MINUTES_KEY,
|
|
167565
|
+
label: "Refresh window (min)",
|
|
167566
|
+
description: BATTERY_REFRESH_MINUTES_DESCRIPTION,
|
|
167567
|
+
default: 60,
|
|
167568
|
+
min: 0,
|
|
167569
|
+
max: 240,
|
|
167570
|
+
step: 5
|
|
167571
|
+
}]
|
|
167572
|
+
}];
|
|
167573
|
+
}
|
|
167002
167574
|
//#endregion
|
|
167003
|
-
//#region src/battery-
|
|
167004
|
-
|
|
167005
|
-
|
|
167006
|
-
|
|
167007
|
-
const
|
|
167008
|
-
|
|
167009
|
-
|
|
167575
|
+
//#region src/battery-snapshot-refresh.ts
|
|
167576
|
+
/** The wrapper may run on another node; a stale frame is hours old, skew is seconds. */
|
|
167577
|
+
var CLOCK_SKEW_TOLERANCE_MS = 6e4;
|
|
167578
|
+
async function refreshSnapshotCacheOnce(input) {
|
|
167579
|
+
const { deviceId, api, logger } = input;
|
|
167580
|
+
const startedAt = input.now();
|
|
167581
|
+
if (await api.getSnapshot({
|
|
167582
|
+
deviceId,
|
|
167583
|
+
force: true
|
|
167584
|
+
}) === null) {
|
|
167585
|
+
logger.warn("battery hourly refresh: the snapshot came back empty", { tags: { deviceId } });
|
|
167586
|
+
return false;
|
|
167587
|
+
}
|
|
167588
|
+
let rows;
|
|
167589
|
+
try {
|
|
167590
|
+
rows = await api.getSnapshotOverview({ deviceIds: [deviceId] });
|
|
167591
|
+
} catch (err) {
|
|
167592
|
+
logger.debug("battery hourly refresh: snapshot overview unreadable — counting the image", {
|
|
167593
|
+
tags: { deviceId },
|
|
167594
|
+
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
167595
|
+
});
|
|
167596
|
+
return true;
|
|
167597
|
+
}
|
|
167598
|
+
const capturedAt = rows.find((row) => row.deviceId === deviceId)?.lastCapturedAt ?? null;
|
|
167599
|
+
if (capturedAt === null || capturedAt < startedAt - CLOCK_SKEW_TOLERANCE_MS) {
|
|
167600
|
+
logger.warn("battery hourly refresh: the snapshot cache did not move (a stale frame was served)", {
|
|
167601
|
+
tags: { deviceId },
|
|
167602
|
+
meta: {
|
|
167603
|
+
capturedAt,
|
|
167604
|
+
startedAt
|
|
167605
|
+
}
|
|
167606
|
+
});
|
|
167607
|
+
return false;
|
|
167608
|
+
}
|
|
167609
|
+
return true;
|
|
167010
167610
|
}
|
|
167011
167611
|
//#endregion
|
|
167012
167612
|
//#region src/log-channels.ts
|
|
@@ -171009,7 +171609,6 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
171009
171609
|
* passively checks sleep state, and short-circuits if sleeping. */
|
|
171010
171610
|
batteryUpdateTimer = null;
|
|
171011
171611
|
/** Single-flight gate so two interval fires can't overlap. */
|
|
171012
|
-
batteryUpdateInFlight = null;
|
|
171013
171612
|
/** Aux accessory references registered by spawned children
|
|
171014
171613
|
* (siren / floodlight / pir). The parent's centralised align
|
|
171015
171614
|
* method iterates these to refresh their slices on a single
|
|
@@ -171347,7 +171946,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
171347
171946
|
* snapshot also drives the dispatch's known-good baseline.
|
|
171348
171947
|
*/
|
|
171349
171948
|
async refreshParentSettingsSnapshot(slices, reason = "background") {
|
|
171350
|
-
if (this.isBattery && this.sleeping && reason !== "post-write") {
|
|
171949
|
+
if (this.isBattery && this.sleeping && reason !== "post-write" && reason !== "scheduled") {
|
|
171351
171950
|
this.ctx.logger.info("refreshParentSettingsSnapshot skipped — battery cam is sleeping", { tags: {
|
|
171352
171951
|
deviceId: this.id,
|
|
171353
171952
|
reason
|
|
@@ -172293,7 +172892,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
172293
172892
|
tags: { deviceId: this.id },
|
|
172294
172893
|
meta: { reason }
|
|
172295
172894
|
});
|
|
172296
|
-
return;
|
|
172895
|
+
return false;
|
|
172297
172896
|
}
|
|
172298
172897
|
let api;
|
|
172299
172898
|
try {
|
|
@@ -172303,17 +172902,19 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
172303
172902
|
tags: { deviceId: this.id },
|
|
172304
172903
|
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
172305
172904
|
});
|
|
172306
|
-
return;
|
|
172905
|
+
return false;
|
|
172307
172906
|
}
|
|
172308
172907
|
try {
|
|
172309
172908
|
const info = await api.getBatteryInfo(this.getChannel());
|
|
172310
172909
|
this.updateBatteryCache(info);
|
|
172311
172910
|
await this.refreshNetworkLinkFromApi(api, "battery");
|
|
172911
|
+
return true;
|
|
172312
172912
|
} catch (err) {
|
|
172313
172913
|
this.ctx.logger.debug("battery refresh failed", {
|
|
172314
172914
|
tags: { deviceId: this.id },
|
|
172315
172915
|
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
172316
172916
|
});
|
|
172917
|
+
return false;
|
|
172317
172918
|
}
|
|
172318
172919
|
}
|
|
172319
172920
|
/**
|
|
@@ -173559,6 +174160,18 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173559
174160
|
* proactive gate lets it through because `sleeping` is false here.
|
|
173560
174161
|
* Mirrors the reference's `updateBatteryAndSnapshot`.
|
|
173561
174162
|
*/
|
|
174163
|
+
/** The scheduled refresh's snapshot step: true only when the cache really moved. */
|
|
174164
|
+
refreshSnapshotCache() {
|
|
174165
|
+
return refreshSnapshotCacheOnce({
|
|
174166
|
+
deviceId: this.id,
|
|
174167
|
+
logger: this.ctx.logger,
|
|
174168
|
+
now: () => Date.now(),
|
|
174169
|
+
api: {
|
|
174170
|
+
getSnapshot: (input) => this.ctx.api.snapshot.getSnapshot.query(input),
|
|
174171
|
+
getSnapshotOverview: (input) => this.ctx.api.snapshot.getSnapshotOverview.query({ deviceIds: [...input.deviceIds] })
|
|
174172
|
+
}
|
|
174173
|
+
});
|
|
174174
|
+
}
|
|
173562
174175
|
async warmSnapshotOnWake() {
|
|
173563
174176
|
try {
|
|
173564
174177
|
await this.ctx.api.snapshot.getSnapshot.query({
|
|
@@ -175994,119 +176607,84 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
175994
176607
|
this.alignAuxTimer = null;
|
|
175995
176608
|
}
|
|
175996
176609
|
}
|
|
175997
|
-
|
|
175998
|
-
|
|
175999
|
-
|
|
176000
|
-
|
|
176001
|
-
|
|
176610
|
+
batteryRefresh = null;
|
|
176611
|
+
getBatteryRefresh() {
|
|
176612
|
+
if (this.batteryRefresh === null) this.batteryRefresh = new BatteryHourlyRefresh({
|
|
176613
|
+
deviceId: this.id,
|
|
176614
|
+
logger: this.ctx.logger,
|
|
176615
|
+
now: () => Date.now(),
|
|
176616
|
+
isAwake: () => !this.sleeping,
|
|
176617
|
+
intervalMs: () => resolveBatteryRefreshIntervalMs(this.config.get("batteryRefreshMinutes")),
|
|
176618
|
+
lastFullRefreshAt: () => this.state.battery.lastFullRefreshAt,
|
|
176619
|
+
wake: async () => {
|
|
176620
|
+
if (!this.canProactivelyWake("scheduled-refresh")) return false;
|
|
176621
|
+
this.markWakeIssued();
|
|
176622
|
+
return this.wakeIfSleeping();
|
|
176623
|
+
},
|
|
176624
|
+
refreshAll: () => this.refreshEverythingNow(),
|
|
176625
|
+
stamp: (at) => {
|
|
176626
|
+
this.state.battery.lastFullRefreshAt = at;
|
|
176627
|
+
}
|
|
176628
|
+
});
|
|
176629
|
+
return this.batteryRefresh;
|
|
176630
|
+
}
|
|
176631
|
+
/**
|
|
176632
|
+
* Every fact and the snapshot, read while the camera is up. SERIALIZED (one
|
|
176633
|
+
* Baichuan round-trip at a time on the single BCUDP stream), and each step
|
|
176634
|
+
* names itself in `failed` instead of throwing, so one dead read neither
|
|
176635
|
+
* hides the others nor stamps the window as done.
|
|
176636
|
+
*/
|
|
176637
|
+
async refreshEverythingNow() {
|
|
176638
|
+
const failed = [];
|
|
176639
|
+
const step = async (name, run) => {
|
|
176002
176640
|
try {
|
|
176003
|
-
if (
|
|
176004
|
-
|
|
176005
|
-
|
|
176006
|
-
|
|
176007
|
-
sleeping: true,
|
|
176008
|
-
hubChild: this.isHubChild(),
|
|
176009
|
-
unreachableAfterMs: 216e5,
|
|
176010
|
-
tickMs: ReolinkCamera.BATTERY_UPDATE_INTERVAL_MS
|
|
176011
|
-
})) {
|
|
176012
|
-
await this.wakeForContactRefresh();
|
|
176013
|
-
return;
|
|
176014
|
-
}
|
|
176015
|
-
this.ctx.logger.debug("battery-update: cam sleeping — skipping cycle without connecting", { tags: { deviceId: this.id } });
|
|
176016
|
-
return;
|
|
176017
|
-
}
|
|
176018
|
-
let api;
|
|
176019
|
-
try {
|
|
176020
|
-
api = await this.ensureApi();
|
|
176021
|
-
} catch (err) {
|
|
176022
|
-
this.ctx.logger.debug("battery-update: ensureApi failed — skipping cycle", {
|
|
176023
|
-
tags: { deviceId: this.id },
|
|
176024
|
-
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
176025
|
-
});
|
|
176026
|
-
return;
|
|
176027
|
-
}
|
|
176028
|
-
let sleeping = false;
|
|
176029
|
-
try {
|
|
176030
|
-
sleeping = api.getSleepStatus({ channel: this.getChannel() }).state === "sleeping";
|
|
176031
|
-
} catch (err) {
|
|
176032
|
-
this.ctx.logger.debug("battery-update: getSleepStatus threw — skipping cycle", {
|
|
176033
|
-
tags: { deviceId: this.id },
|
|
176034
|
-
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
176035
|
-
});
|
|
176036
|
-
return;
|
|
176037
|
-
}
|
|
176038
|
-
if (sleeping) {
|
|
176039
|
-
this.ctx.logger.debug("battery-update: cam sleeping — skipping periodic refresh", { tags: { deviceId: this.id } });
|
|
176040
|
-
return;
|
|
176041
|
-
}
|
|
176042
|
-
this.ctx.logger.info("battery-update: cam awake — running periodic refresh", {
|
|
176641
|
+
if (await run() === false) failed.push(name);
|
|
176642
|
+
} catch (err) {
|
|
176643
|
+
failed.push(name);
|
|
176644
|
+
this.ctx.logger.debug("battery hourly refresh: step threw", {
|
|
176043
176645
|
tags: { deviceId: this.id },
|
|
176044
176646
|
meta: {
|
|
176045
|
-
|
|
176046
|
-
|
|
176647
|
+
step: name,
|
|
176648
|
+
error: err instanceof Error ? err.message : String(err)
|
|
176047
176649
|
}
|
|
176048
176650
|
});
|
|
176049
|
-
await this.refreshBatteryFromApi("periodic").catch(() => {});
|
|
176050
|
-
await this.alignAuxDevicesState("periodic").catch(() => {});
|
|
176051
|
-
await this.refreshParentSettingsSnapshot().catch(() => {});
|
|
176052
|
-
} finally {
|
|
176053
|
-
this.batteryUpdateInFlight = null;
|
|
176054
176651
|
}
|
|
176055
|
-
}
|
|
176056
|
-
this.
|
|
176057
|
-
|
|
176652
|
+
};
|
|
176653
|
+
await step("battery", () => this.refreshBatteryFromApi("demand"));
|
|
176654
|
+
await step("device-info", async () => {
|
|
176655
|
+
await this.populateMetadataFromFirmware(await this.ensureApi());
|
|
176656
|
+
});
|
|
176657
|
+
await step("cap-slices", async () => {
|
|
176658
|
+
await this.warmCapSlicesOnWake({ force: true });
|
|
176659
|
+
});
|
|
176660
|
+
await step("aux-accessories", () => this.alignAuxDevicesState("wake"));
|
|
176661
|
+
await step("settings-snapshot", () => this.refreshParentSettingsSnapshot(void 0, "scheduled"));
|
|
176662
|
+
await step("snapshot", () => this.refreshSnapshotCache());
|
|
176663
|
+
return { failed };
|
|
176058
176664
|
}
|
|
176059
176665
|
/**
|
|
176060
|
-
*
|
|
176061
|
-
*
|
|
176062
|
-
* Bounded by `canProactivelyWake` (the shared cooldown) and by the fact
|
|
176063
|
-
* that a wake that answers resets the silence. A wake that fails is left
|
|
176064
|
-
* alone: the silence then reaches the budget and `deriveBatteryPresence`
|
|
176065
|
-
* says `unreachable` — for the first time on evidence.
|
|
176666
|
+
* Run one scheduled-refresh check. Never throws; costs nothing unless a
|
|
176667
|
+
* refresh is due (see {@link decideBatteryRefresh}).
|
|
176066
176668
|
*/
|
|
176067
|
-
async
|
|
176068
|
-
if (!this.
|
|
176069
|
-
this.
|
|
176070
|
-
const lastContactAt = this.state.battery.lastContactAt ?? 0;
|
|
176071
|
-
const silenceMs = Date.now() - lastContactAt;
|
|
176072
|
-
this.ctx.logger.info("battery contact expiring — waking once to refresh every slice (D355)", {
|
|
176073
|
-
tags: { deviceId: this.id },
|
|
176074
|
-
meta: {
|
|
176075
|
-
silenceMs,
|
|
176076
|
-
budgetMs: BATTERY_UNREACHABLE_AFTER_MS
|
|
176077
|
-
}
|
|
176078
|
-
});
|
|
176079
|
-
try {
|
|
176080
|
-
await this.ensureApi();
|
|
176081
|
-
} catch (err) {
|
|
176082
|
-
this.ctx.logger.warn("battery contact wake FAILED — silence continues and the camera will read unreachable at the budget", {
|
|
176083
|
-
tags: { deviceId: this.id },
|
|
176084
|
-
meta: {
|
|
176085
|
-
silenceMs,
|
|
176086
|
-
error: err instanceof Error ? err.message : String(err)
|
|
176087
|
-
}
|
|
176088
|
-
});
|
|
176089
|
-
return;
|
|
176090
|
-
}
|
|
176091
|
-
await this.refreshBatteryFromApi("demand").catch(() => {});
|
|
176092
|
-
await this.warmCapSlicesOnWake().catch(() => {});
|
|
176093
|
-
await this.alignAuxDevicesState("wake").catch(() => {});
|
|
176094
|
-
await this.warmSnapshotOnWake().catch(() => {});
|
|
176669
|
+
async runBatteryUpdate() {
|
|
176670
|
+
if (!this.isBattery) return;
|
|
176671
|
+
await this.getBatteryRefresh().tick();
|
|
176095
176672
|
}
|
|
176096
|
-
/** Arm the
|
|
176097
|
-
*
|
|
176098
|
-
* events). Idempotent. */
|
|
176673
|
+
/** Arm the scheduled-refresh check. No-op for wired cams (their state is
|
|
176674
|
+
* kept fresh by the 10 s aux-align poll and push events). Idempotent. */
|
|
176099
176675
|
startBatteryUpdatePolling() {
|
|
176100
176676
|
if (this.batteryUpdateTimer) return;
|
|
176101
176677
|
if (!this.isBattery) return;
|
|
176102
176678
|
this.batteryUpdateTimer = setInterval(() => {
|
|
176103
176679
|
this.runBatteryUpdate().catch(() => {});
|
|
176104
|
-
},
|
|
176680
|
+
}, BATTERY_REFRESH_TICK_MS);
|
|
176105
176681
|
if (typeof this.batteryUpdateTimer.unref === "function") this.batteryUpdateTimer.unref();
|
|
176106
|
-
this.ctx.logger.info("battery-update:
|
|
176682
|
+
this.ctx.logger.info("battery-update: scheduled full refresh armed", {
|
|
176107
176683
|
tags: { deviceId: this.id },
|
|
176108
176684
|
meta: {
|
|
176109
|
-
|
|
176685
|
+
tickMs: BATTERY_REFRESH_TICK_MS,
|
|
176686
|
+
intervalMs: resolveBatteryRefreshIntervalMs(this.config.get("batteryRefreshMinutes")),
|
|
176687
|
+
lastFullRefreshAt: this.state.battery.lastFullRefreshAt ?? null,
|
|
176110
176688
|
isHubChild: this.isHubChild()
|
|
176111
176689
|
}
|
|
176112
176690
|
});
|
|
@@ -176202,22 +176780,18 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
176202
176780
|
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
176203
176781
|
});
|
|
176204
176782
|
}
|
|
176205
|
-
await this.
|
|
176206
|
-
|
|
176207
|
-
|
|
176208
|
-
|
|
176783
|
+
if (!(this.isBattery ? await this.getBatteryRefresh().noteAwake().catch(() => false) : false)) {
|
|
176784
|
+
await this.warmCapSlicesOnWake();
|
|
176785
|
+
await this.warmSnapshotOnWake();
|
|
176786
|
+
await this.alignAuxDevicesState("wake");
|
|
176787
|
+
await this.refreshParentSettingsSnapshot();
|
|
176788
|
+
}
|
|
176209
176789
|
await this.refreshClipCatalogOnWake();
|
|
176210
176790
|
}
|
|
176211
176791
|
static PING_INTERVAL_MS = 3e4;
|
|
176212
176792
|
static PING_MAX_FAILURES = 3;
|
|
176213
176793
|
static EVENT_CHECK_INTERVAL_MS = 6e4;
|
|
176214
176794
|
static EVENT_STALE_THRESHOLD_MS = 10 * 6e4;
|
|
176215
|
-
/** Battery-cam periodic refresh cadence. The timer fires on this
|
|
176216
|
-
* interval, passively checks sleep state, and only runs the
|
|
176217
|
-
* refresh when the cam is already awake — so it never forces a
|
|
176218
|
-
* wake. Mirrors the reference's `batteryUpdateIntervalMinutes`
|
|
176219
|
-
* default (60 minutes). */
|
|
176220
|
-
static BATTERY_UPDATE_INTERVAL_MS = 60 * 6e4;
|
|
176221
176795
|
/**
|
|
176222
176796
|
* Arm the connection-liveness ping watchdog and the event-watchdog.
|
|
176223
176797
|
* Battery cams skip the ping watchdog — they keep `setIdleDisconnect=true`
|
|
@@ -177401,7 +177975,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
177401
177975
|
...buildClipRefreshDeviceSection({
|
|
177402
177976
|
isHubChild: this.isHubChild(),
|
|
177403
177977
|
isBattery: this.isBattery
|
|
177404
|
-
})
|
|
177978
|
+
}),
|
|
177979
|
+
...buildBatteryRefreshDeviceSection({ isBattery: this.isBattery })
|
|
177405
177980
|
]
|
|
177406
177981
|
};
|
|
177407
177982
|
const emailSnap = cache?.emailConfigSnapshot;
|
|
@@ -177422,6 +177997,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
177422
177997
|
emailScheduleEnabled: emailSnap?.taskEnabled ?? false,
|
|
177423
177998
|
clipCatalogRefreshMinutes: this.config.get("clipCatalogRefreshMinutes"),
|
|
177424
177999
|
clipCatalogForcedWake: this.config.get("clipCatalogForcedWake") ?? false,
|
|
178000
|
+
batteryRefreshMinutes: this.config.get("batteryRefreshMinutes"),
|
|
177425
178001
|
debugGeneral: this.config.get("debugGeneral") ?? false,
|
|
177426
178002
|
debugSocketLogs: this.config.get("debugSocketLogs") ?? [],
|
|
177427
178003
|
motionEnabled: this.config.get("motionEnabled") ?? motionSnap.enabled ?? true,
|