@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.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import * as crypto$7 from "crypto";
|
|
|
10
10
|
import crypto$1, { createHash as createHash$1, randomUUID } from "crypto";
|
|
11
11
|
import { execFile, spawn } from "child_process";
|
|
12
12
|
import { createServer } from "node:http";
|
|
13
|
+
import "node:zlib";
|
|
13
14
|
import { mkdir, mkdtemp, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
14
15
|
import { join as join$1 } from "node:path";
|
|
15
16
|
import { PassThrough } from "stream";
|
|
@@ -27,7 +28,7 @@ import { URL as URL$1 } from "url";
|
|
|
27
28
|
import { spawn as spawn$1 } from "node:child_process";
|
|
28
29
|
import os, { tmpdir } from "node:os";
|
|
29
30
|
import { createReadStream } from "node:fs";
|
|
30
|
-
//#region ../types/dist/event-category-
|
|
31
|
+
//#region ../types/dist/event-category-C4hVnRo8.mjs
|
|
31
32
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
32
33
|
EventCategory["SystemBoot"] = "system.boot";
|
|
33
34
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -246,7 +247,17 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
246
247
|
EventCategory["RecordingStopped"] = "recording.stopped";
|
|
247
248
|
EventCategory["RecordingError"] = "recording.error";
|
|
248
249
|
EventCategory["RecordingHealthDegraded"] = "recording.health.degraded";
|
|
250
|
+
/** Recording enabled but no segment finalized for the NC stall window. */
|
|
251
|
+
EventCategory["RecordingHealthStalled"] = "recording.health.stalled";
|
|
252
|
+
/** Recovery from {@link RecordingHealthStalled}. */
|
|
253
|
+
EventCategory["RecordingHealthOk"] = "recording.health.ok";
|
|
249
254
|
EventCategory["RecordingStorageCritical"] = "recording.storage.critical";
|
|
255
|
+
/** Free space on a storage location crossed the warn bar. */
|
|
256
|
+
EventCategory["StorageLocationLow"] = "storage.location.low";
|
|
257
|
+
/** Free space at/under the critical bar, or capacity probe failed (`null`). */
|
|
258
|
+
EventCategory["StorageLocationCritical"] = "storage.location.critical";
|
|
259
|
+
/** Recovery from low/critical on a storage location. */
|
|
260
|
+
EventCategory["StorageLocationOk"] = "storage.location.ok";
|
|
250
261
|
EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
|
|
251
262
|
EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
|
|
252
263
|
EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
|
|
@@ -5405,7 +5416,7 @@ var ZodIssueCode = {
|
|
|
5405
5416
|
var ZodFirstPartyTypeKind;
|
|
5406
5417
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5407
5418
|
//#endregion
|
|
5408
|
-
//#region ../types/dist/sleep-
|
|
5419
|
+
//#region ../types/dist/sleep-vWTnHq9G.mjs
|
|
5409
5420
|
/**
|
|
5410
5421
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5411
5422
|
* window to float samples (D455).
|
|
@@ -8010,6 +8021,21 @@ var ProfileRtspEntrySchema = object({
|
|
|
8010
8021
|
codec: string().optional(),
|
|
8011
8022
|
resolution: CamStreamResolutionSchema.optional()
|
|
8012
8023
|
});
|
|
8024
|
+
/**
|
|
8025
|
+
* The model weight formats a catalog entry can ship. Schema-free on purpose:
|
|
8026
|
+
* `types/models.ts` builds Zod schemas from it, but a runner-closure consumer
|
|
8027
|
+
* (the model downloader in `@camstack/system`) needs only the list and must
|
|
8028
|
+
* reach it through `@camstack/types/addon` without building the cap schema
|
|
8029
|
+
* graph (D28).
|
|
8030
|
+
*/
|
|
8031
|
+
var MODEL_FORMATS = [
|
|
8032
|
+
"onnx",
|
|
8033
|
+
"coreml",
|
|
8034
|
+
"openvino",
|
|
8035
|
+
"tflite",
|
|
8036
|
+
"pt",
|
|
8037
|
+
"gguf"
|
|
8038
|
+
];
|
|
8013
8039
|
object({
|
|
8014
8040
|
field: string().min(1),
|
|
8015
8041
|
on: literal("advance")
|
|
@@ -8608,7 +8634,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
8608
8634
|
return DeviceRole;
|
|
8609
8635
|
}({});
|
|
8610
8636
|
//#endregion
|
|
8611
|
-
//#region ../types/dist/composition-
|
|
8637
|
+
//#region ../types/dist/composition-C-WpVYTX.mjs
|
|
8612
8638
|
/**
|
|
8613
8639
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
8614
8640
|
*
|
|
@@ -8660,7 +8686,7 @@ var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFM
|
|
|
8660
8686
|
function portable(target) {
|
|
8661
8687
|
return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
|
|
8662
8688
|
}
|
|
8663
|
-
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64")
|
|
8689
|
+
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64"), `${BASE}${FFMPEG_RELEASE}`;
|
|
8664
8690
|
/**
|
|
8665
8691
|
* THE table "which boolean of this device type counts as HIGH", and the
|
|
8666
8692
|
* pure evaluator of its EDGE.
|
|
@@ -9463,6 +9489,16 @@ var CompositionSchema = object({
|
|
|
9463
9489
|
seen.add(feature.cap);
|
|
9464
9490
|
});
|
|
9465
9491
|
});
|
|
9492
|
+
var NodeColorIdSchema = _enum([
|
|
9493
|
+
"node-a",
|
|
9494
|
+
"node-b",
|
|
9495
|
+
"node-c",
|
|
9496
|
+
"node-d",
|
|
9497
|
+
"node-e",
|
|
9498
|
+
"node-f",
|
|
9499
|
+
"node-g",
|
|
9500
|
+
"node-h"
|
|
9501
|
+
]);
|
|
9466
9502
|
new Set([
|
|
9467
9503
|
"track",
|
|
9468
9504
|
"face",
|
|
@@ -12394,14 +12430,6 @@ var MASK_SUPPLY_STEP_IDS = ["segmentation"];
|
|
|
12394
12430
|
...CROP_SUPPLY_STEP_IDS,
|
|
12395
12431
|
...MASK_SUPPLY_STEP_IDS
|
|
12396
12432
|
];
|
|
12397
|
-
var MODEL_FORMATS = [
|
|
12398
|
-
"onnx",
|
|
12399
|
-
"coreml",
|
|
12400
|
-
"openvino",
|
|
12401
|
-
"tflite",
|
|
12402
|
-
"pt",
|
|
12403
|
-
"gguf"
|
|
12404
|
-
];
|
|
12405
12433
|
/**
|
|
12406
12434
|
* Multi-file format payload.
|
|
12407
12435
|
*
|
|
@@ -14225,7 +14253,14 @@ var BrokerProviderInfoSchema = object({
|
|
|
14225
14253
|
/** Broker kinds this provider can create, with a display label. */
|
|
14226
14254
|
kinds: array(object({
|
|
14227
14255
|
kind: string(),
|
|
14228
|
-
label: string()
|
|
14256
|
+
label: string(),
|
|
14257
|
+
/**
|
|
14258
|
+
* Relative hub path to start an interactive account link (OAuth popup).
|
|
14259
|
+
* May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
|
|
14260
|
+
*/
|
|
14261
|
+
interactiveConnectPath: string().min(1).optional(),
|
|
14262
|
+
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
14263
|
+
interactiveDisconnectPath: string().min(1).optional()
|
|
14229
14264
|
}))
|
|
14230
14265
|
});
|
|
14231
14266
|
var ListInputSchema = object({
|
|
@@ -19176,14 +19211,14 @@ var LlmProfileSchema = object({
|
|
|
19176
19211
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
19177
19212
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
19178
19213
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
19179
|
-
var ConfigSchemaPassthrough$
|
|
19214
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
19180
19215
|
var LlmProfileKindDescriptorSchema = object({
|
|
19181
19216
|
kind: LlmProfileKindSchema,
|
|
19182
19217
|
label: string(),
|
|
19183
19218
|
icon: string(),
|
|
19184
19219
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
19185
19220
|
addonId: string(),
|
|
19186
|
-
configSchema: ConfigSchemaPassthrough$
|
|
19221
|
+
configSchema: ConfigSchemaPassthrough$3
|
|
19187
19222
|
});
|
|
19188
19223
|
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
19189
19224
|
var LlmDefaultSchema = object({
|
|
@@ -20325,7 +20360,7 @@ var TargetKindCapsSchema = object({
|
|
|
20325
20360
|
* the union is large and not meant for runtime validation here; the exported
|
|
20326
20361
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
20327
20362
|
*/
|
|
20328
|
-
var ConfigSchemaPassthrough = unknown();
|
|
20363
|
+
var ConfigSchemaPassthrough$2 = unknown();
|
|
20329
20364
|
var TargetKindSchema = object({
|
|
20330
20365
|
kind: string(),
|
|
20331
20366
|
label: string(),
|
|
@@ -20362,7 +20397,7 @@ var TargetKindSchema = object({
|
|
|
20362
20397
|
* its raster path, which is the safe default for an unknown image.
|
|
20363
20398
|
*/
|
|
20364
20399
|
iconMediaType: string().optional(),
|
|
20365
|
-
configSchema: ConfigSchemaPassthrough,
|
|
20400
|
+
configSchema: ConfigSchemaPassthrough$2,
|
|
20366
20401
|
/**
|
|
20367
20402
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
20368
20403
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -21319,6 +21354,12 @@ var NcSystemEventKindSchema = _enum([
|
|
|
21319
21354
|
"node-updating",
|
|
21320
21355
|
"node-inference-unavailable",
|
|
21321
21356
|
"detection-blind",
|
|
21357
|
+
"recording-stalled",
|
|
21358
|
+
"recording-healthy",
|
|
21359
|
+
"recording-degraded",
|
|
21360
|
+
"storage-location-low",
|
|
21361
|
+
"storage-location-critical",
|
|
21362
|
+
"storage-location-ok",
|
|
21322
21363
|
"addon-crash-loop",
|
|
21323
21364
|
"addon-update-available",
|
|
21324
21365
|
"server-update-available",
|
|
@@ -23699,6 +23740,61 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
23699
23740
|
});
|
|
23700
23741
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
23701
23742
|
/**
|
|
23743
|
+
* `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
|
|
23744
|
+
*
|
|
23745
|
+
* One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
|
|
23746
|
+
* ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
|
|
23747
|
+
* Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
|
|
23748
|
+
* immutable after create.
|
|
23749
|
+
*/
|
|
23750
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
23751
|
+
var OidcConnectionSchema = object({
|
|
23752
|
+
id: string().min(1),
|
|
23753
|
+
displayName: string(),
|
|
23754
|
+
icon: string(),
|
|
23755
|
+
issuerUrl: string(),
|
|
23756
|
+
clientId: string(),
|
|
23757
|
+
/** Client id + secret present — never the secret value. */
|
|
23758
|
+
configured: boolean(),
|
|
23759
|
+
/**
|
|
23760
|
+
* Values for the connection form schema. `clientSecret` is a keychain
|
|
23761
|
+
* {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
|
|
23762
|
+
*/
|
|
23763
|
+
formValues: record(string(), unknown()).optional()
|
|
23764
|
+
});
|
|
23765
|
+
/**
|
|
23766
|
+
* Patch one connection. Omitted or empty `clientSecret` keeps the stored
|
|
23767
|
+
* secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
|
|
23768
|
+
*/
|
|
23769
|
+
var OidcConnectionUpdateInputSchema = object({
|
|
23770
|
+
id: string().min(1),
|
|
23771
|
+
displayName: string().min(1),
|
|
23772
|
+
icon: string().optional(),
|
|
23773
|
+
issuerUrl: string().optional(),
|
|
23774
|
+
clientId: string().optional(),
|
|
23775
|
+
clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
23776
|
+
redirectUri: string().optional(),
|
|
23777
|
+
scopes: string().optional(),
|
|
23778
|
+
usernameClaim: _enum([
|
|
23779
|
+
"preferred_username",
|
|
23780
|
+
"email",
|
|
23781
|
+
"sub"
|
|
23782
|
+
]).optional()
|
|
23783
|
+
});
|
|
23784
|
+
method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
|
|
23785
|
+
id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
|
|
23786
|
+
displayName: string().min(1)
|
|
23787
|
+
}), object({ id: string() }), {
|
|
23788
|
+
kind: "mutation",
|
|
23789
|
+
auth: "admin"
|
|
23790
|
+
}), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
|
|
23791
|
+
kind: "mutation",
|
|
23792
|
+
auth: "admin"
|
|
23793
|
+
}), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
23794
|
+
kind: "mutation",
|
|
23795
|
+
auth: "admin"
|
|
23796
|
+
});
|
|
23797
|
+
/**
|
|
23702
23798
|
* What started a camera's onboard motion (D672). `camera` is the camera's own
|
|
23703
23799
|
* firmware; `sensor` is a device the operator ATTACHED to it. Absent on the
|
|
23704
23800
|
* wire means the camera: only the camera-sensors bridge ever says `sensor`.
|
|
@@ -25055,24 +25151,69 @@ var RetrainFrameListSchema = object({
|
|
|
25055
25151
|
/** What the page pre-selects — the native key frame when one survives. */
|
|
25056
25152
|
autoPickMediaId: number().int().optional()
|
|
25057
25153
|
});
|
|
25154
|
+
/** Exclusive partition of a corpus copy's annotation progress (design §4.2). */
|
|
25155
|
+
var RetrainFrameWorkStateSchema = _enum([
|
|
25156
|
+
"unannotated",
|
|
25157
|
+
"annotated",
|
|
25158
|
+
"complete"
|
|
25159
|
+
]);
|
|
25160
|
+
/**
|
|
25161
|
+
* Filters for paginated corpus list, KPI, and matching export (design §4.4).
|
|
25162
|
+
* Every narrowing field is ANDed; omitted fields impose no constraint.
|
|
25163
|
+
*/
|
|
25164
|
+
var RetrainCorpusFilterSchema = object({
|
|
25165
|
+
workState: RetrainFrameWorkStateSchema.optional(),
|
|
25166
|
+
deviceIds: array(number()).max(500).optional(),
|
|
25167
|
+
/** When set, only frames in this named collection. */
|
|
25168
|
+
collectionId: string().optional(),
|
|
25169
|
+
/** Frames that carry at least one saved annotation in one of these classes. */
|
|
25170
|
+
classes: array(RetrainMacroClassSchema).optional(),
|
|
25171
|
+
page: number().int().positive().default(1),
|
|
25172
|
+
pageSize: number().int().positive().max(100).default(50)
|
|
25173
|
+
});
|
|
25174
|
+
var RetrainCorpusPageSchema = object({
|
|
25175
|
+
frames: array(RetrainFrameSchema.extend({ workState: RetrainFrameWorkStateSchema })).readonly(),
|
|
25176
|
+
total: number().int().nonnegative(),
|
|
25177
|
+
page: number().int().positive(),
|
|
25178
|
+
pageSize: number().int().positive()
|
|
25179
|
+
});
|
|
25180
|
+
var RetrainCorpusKpiCameraRowSchema = object({
|
|
25181
|
+
deviceId: number(),
|
|
25182
|
+
unannotated: number().int().nonnegative(),
|
|
25183
|
+
annotated: number().int().nonnegative(),
|
|
25184
|
+
complete: number().int().nonnegative(),
|
|
25185
|
+
/** This camera's `complete` count divided by corpus `total`, in `[0,1]`.
|
|
25186
|
+
* `null` when the corpus is empty (no ratio). */
|
|
25187
|
+
pctOfCorpus: number().min(0).max(1).nullable()
|
|
25188
|
+
});
|
|
25189
|
+
var RetrainCorpusKpiSchema = object({
|
|
25190
|
+
total: number().int().nonnegative(),
|
|
25191
|
+
unannotated: number().int().nonnegative(),
|
|
25192
|
+
annotated: number().int().nonnegative(),
|
|
25193
|
+
complete: number().int().nonnegative(),
|
|
25194
|
+
cameras: array(RetrainCorpusKpiCameraRowSchema).readonly()
|
|
25195
|
+
});
|
|
25058
25196
|
/**
|
|
25059
25197
|
* Which frames an annotated export carries. Every field narrows; an empty
|
|
25060
|
-
* filter is every annotated copy.
|
|
25061
|
-
*
|
|
25062
|
-
*
|
|
25063
|
-
*
|
|
25198
|
+
* filter is every annotated copy. `frameIds` is the primitive a job or
|
|
25199
|
+
* download uses; `batchId` re-selects what an earlier export carried
|
|
25200
|
+
* (`exportedInBatch`). Frozen dataset versions were removed — live selection
|
|
25201
|
+
* resolves to `frameIds` at plan/start.
|
|
25064
25202
|
*/
|
|
25065
25203
|
var RetrainExportFilterSchema = object({
|
|
25066
25204
|
deviceIds: array(number()).optional(),
|
|
25067
25205
|
frameIds: array(string()).max(2e4).optional(),
|
|
25068
25206
|
/** Only frames in at least one of these named collections. */
|
|
25069
25207
|
collectionIds: array(string()).max(200).optional(),
|
|
25070
|
-
/**
|
|
25071
|
-
|
|
25072
|
-
datasetVersionId: string().optional(),
|
|
25208
|
+
/** Single collection (Frames filter bar). ANDed with `collectionIds` when both set. */
|
|
25209
|
+
collectionId: string().optional(),
|
|
25073
25210
|
batchId: string().optional(),
|
|
25074
25211
|
/** Only frames the operator marked complete (all subjects boxed). */
|
|
25075
|
-
completeOnly: boolean().optional()
|
|
25212
|
+
completeOnly: boolean().optional(),
|
|
25213
|
+
/** Exclusive work-state partition (`workStateOf`); same as the corpus list. */
|
|
25214
|
+
workState: RetrainFrameWorkStateSchema.optional(),
|
|
25215
|
+
/** Frames with at least one saved annotation in one of these classes. */
|
|
25216
|
+
classes: array(RetrainMacroClassSchema).optional()
|
|
25076
25217
|
});
|
|
25077
25218
|
/**
|
|
25078
25219
|
* `frames`: the full frame copies, boxes in `annotations.json`.
|
|
@@ -25272,8 +25413,7 @@ var RetrainDatasetStatsSchema = object({
|
|
|
25272
25413
|
completeFrames: number().int()
|
|
25273
25414
|
})).readonly()
|
|
25274
25415
|
});
|
|
25275
|
-
|
|
25276
|
-
var RetrainDatasetVersionSchema = object({
|
|
25416
|
+
object({
|
|
25277
25417
|
id: string(),
|
|
25278
25418
|
name: string(),
|
|
25279
25419
|
createdAt: number(),
|
|
@@ -25281,22 +25421,33 @@ var RetrainDatasetVersionSchema = object({
|
|
|
25281
25421
|
layout: RetrainExportLayoutSchema,
|
|
25282
25422
|
completeOnly: boolean(),
|
|
25283
25423
|
frameCount: number().int(),
|
|
25284
|
-
/** sha256 of what the frames taught when the version was frozen. */
|
|
25285
25424
|
annotationHash: string(),
|
|
25286
|
-
/** The export manifest schema the version was cut under. */
|
|
25287
25425
|
manifestSchema: string(),
|
|
25288
25426
|
stats: RetrainDatasetStatsSchema
|
|
25289
25427
|
});
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25428
|
+
/**
|
|
25429
|
+
* Live training corpus criteria — same shape as `TrainingCorpusSelection` on
|
|
25430
|
+
* the training cap. Resolved here so plan/start and the annotated export share
|
|
25431
|
+
* one definition of "the frames" (`buildRetrainExportPlan`).
|
|
25432
|
+
*/
|
|
25433
|
+
var RetrainTrainingCorpusSelectionSchema = object({
|
|
25434
|
+
collectionIds: array(string()).min(1),
|
|
25435
|
+
workState: _enum([
|
|
25436
|
+
"complete",
|
|
25437
|
+
"annotated_or_complete",
|
|
25438
|
+
"any_annotated"
|
|
25439
|
+
]).default("complete")
|
|
25440
|
+
});
|
|
25441
|
+
var RetrainTrainingCorpusResolveResultSchema = discriminatedUnion("kind", [object({
|
|
25442
|
+
kind: literal("resolved"),
|
|
25443
|
+
frameIds: array(string()).max(2e4).readonly(),
|
|
25444
|
+
stats: RetrainDatasetStatsSchema,
|
|
25445
|
+
annotationHash: string(),
|
|
25446
|
+
layout: RetrainExportLayoutSchema
|
|
25293
25447
|
}), object({
|
|
25294
25448
|
kind: literal("refused"),
|
|
25295
|
-
reason: _enum([
|
|
25296
|
-
|
|
25297
|
-
"duplicate-name",
|
|
25298
|
-
"no-frames"
|
|
25299
|
-
])
|
|
25449
|
+
reason: _enum(["unknown-collection", "no-frames"]),
|
|
25450
|
+
detail: string()
|
|
25300
25451
|
})]);
|
|
25301
25452
|
/** The outcome of a lifecycle move owned by the retrain page. */
|
|
25302
25453
|
var RetrainTransitionResultSchema = object({
|
|
@@ -26003,6 +26154,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
26003
26154
|
}), array(RetrainTrackSchema).readonly(), {
|
|
26004
26155
|
kind: "query",
|
|
26005
26156
|
auth: "admin"
|
|
26157
|
+
}), method(RetrainCorpusFilterSchema, RetrainCorpusPageSchema, {
|
|
26158
|
+
kind: "query",
|
|
26159
|
+
auth: "admin"
|
|
26160
|
+
}), method(object({}), RetrainCorpusKpiSchema, {
|
|
26161
|
+
kind: "query",
|
|
26162
|
+
auth: "admin"
|
|
26006
26163
|
}), method(object({ trackId: string() }), RetrainFrameListSchema, {
|
|
26007
26164
|
kind: "query",
|
|
26008
26165
|
auth: "admin"
|
|
@@ -26098,36 +26255,6 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
26098
26255
|
kind: "mutation",
|
|
26099
26256
|
auth: "admin",
|
|
26100
26257
|
timeoutMs: 12e4
|
|
26101
|
-
}), method(object({
|
|
26102
|
-
selection: RetrainDatasetSelectionSchema,
|
|
26103
|
-
completeOnly: boolean().optional()
|
|
26104
|
-
}), RetrainDatasetStatsSchema, {
|
|
26105
|
-
kind: "query",
|
|
26106
|
-
auth: "admin"
|
|
26107
|
-
}), method(object({
|
|
26108
|
-
name: string().max(120),
|
|
26109
|
-
selection: RetrainDatasetSelectionSchema,
|
|
26110
|
-
layout: RetrainExportLayoutSchema,
|
|
26111
|
-
completeOnly: boolean()
|
|
26112
|
-
}), CreateRetrainDatasetVersionResultSchema, {
|
|
26113
|
-
kind: "mutation",
|
|
26114
|
-
auth: "admin",
|
|
26115
|
-
timeoutMs: 12e4
|
|
26116
|
-
}), method(object({}), array(RetrainDatasetVersionSchema).readonly(), {
|
|
26117
|
-
kind: "query",
|
|
26118
|
-
auth: "admin"
|
|
26119
|
-
}), method(object({ id: string() }), object({ deleted: boolean() }), {
|
|
26120
|
-
kind: "mutation",
|
|
26121
|
-
auth: "admin"
|
|
26122
|
-
}), method(object({ id: string() }), object({
|
|
26123
|
-
found: boolean(),
|
|
26124
|
-
annotationHash: string(),
|
|
26125
|
-
matches: boolean(),
|
|
26126
|
-
missingFrames: number().int()
|
|
26127
|
-
}), {
|
|
26128
|
-
kind: "query",
|
|
26129
|
-
auth: "admin",
|
|
26130
|
-
timeoutMs: 12e4
|
|
26131
26258
|
}), method(object({}), array(RetrainAssistProfileSchema).readonly(), {
|
|
26132
26259
|
kind: "query",
|
|
26133
26260
|
auth: "admin"
|
|
@@ -26154,6 +26281,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
26154
26281
|
}), method(object({}), array(RetrainCollectionSchema).readonly(), {
|
|
26155
26282
|
kind: "query",
|
|
26156
26283
|
auth: "admin"
|
|
26284
|
+
}), method(RetrainTrainingCorpusSelectionSchema, RetrainTrainingCorpusResolveResultSchema, {
|
|
26285
|
+
kind: "query",
|
|
26286
|
+
auth: "admin",
|
|
26287
|
+
timeoutMs: 6e4
|
|
26157
26288
|
}), method(object({
|
|
26158
26289
|
id: string(),
|
|
26159
26290
|
name: string().max(120)
|
|
@@ -26770,11 +26901,14 @@ var HardwareClassIdSchema = _enum([
|
|
|
26770
26901
|
"intel-n-series",
|
|
26771
26902
|
"apple-m-base",
|
|
26772
26903
|
"apple-m-pro",
|
|
26904
|
+
"amd-phoenix",
|
|
26773
26905
|
"unknown"
|
|
26774
26906
|
]);
|
|
26775
26907
|
var AcceleratorKindSchema = _enum([
|
|
26776
26908
|
"intel-npu",
|
|
26777
26909
|
"intel-igpu",
|
|
26910
|
+
"amd-igpu",
|
|
26911
|
+
"amd-npu",
|
|
26778
26912
|
"x86-cpu",
|
|
26779
26913
|
"arm-cpu",
|
|
26780
26914
|
"apple-ane",
|
|
@@ -28704,6 +28838,8 @@ var AgentPipelineSettingsSchema = object({
|
|
|
28704
28838
|
* address is wrong (multi-homed host, NAT, custom interface).
|
|
28705
28839
|
*/
|
|
28706
28840
|
reachableHost: string().optional(),
|
|
28841
|
+
/** Hub-persisted UI colour (`--color-node-*` theme tokens). */
|
|
28842
|
+
color: NodeColorIdSchema.optional(),
|
|
28707
28843
|
/**
|
|
28708
28844
|
* Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
|
|
28709
28845
|
* weight, steps}. Absent / all-disabled ⇒ the node's single default
|
|
@@ -29253,8 +29389,9 @@ method(object({
|
|
|
29253
29389
|
auth: "admin"
|
|
29254
29390
|
}), method(object({
|
|
29255
29391
|
agentNodeId: string(),
|
|
29256
|
-
reachableHost: string().nullable()
|
|
29257
|
-
|
|
29392
|
+
reachableHost: string().nullable().optional(),
|
|
29393
|
+
color: NodeColorIdSchema.nullable().optional()
|
|
29394
|
+
}).refine((v) => v.reachableHost !== void 0 || v.color !== void 0, { message: "reachableHost or color required" }), object({ success: literal(true) }), {
|
|
29258
29395
|
kind: "mutation",
|
|
29259
29396
|
auth: "admin"
|
|
29260
29397
|
}), method(object({
|
|
@@ -32852,7 +32989,7 @@ var CustomActionInputSchema = object({
|
|
|
32852
32989
|
action: string().min(1),
|
|
32853
32990
|
input: unknown()
|
|
32854
32991
|
});
|
|
32855
|
-
method(
|
|
32992
|
+
method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSchema).readonly()), method(object({
|
|
32856
32993
|
addonId: string(),
|
|
32857
32994
|
limit: number().min(1).max(500).default(100),
|
|
32858
32995
|
level: LogLevelSchema$2.optional()
|
|
@@ -33322,6 +33459,17 @@ var BatteryStatusSchema = object({
|
|
|
33322
33459
|
*/
|
|
33323
33460
|
lastContactAt: number().optional(),
|
|
33324
33461
|
/**
|
|
33462
|
+
* Ms epoch of the last COMPLETE scheduled refresh — every fact the provider
|
|
33463
|
+
* holds for the device (level, charging, link, device info, state slices) and
|
|
33464
|
+
* its cached snapshot, read together. Absent = never since this slice was
|
|
33465
|
+
* born. Unlike `lastUpdated` (any write, even a push of one field) this is
|
|
33466
|
+
* the staleness an operator can trust: the provider owns the device fact and
|
|
33467
|
+
* so owns the window on its age (D224, D847). Not a clock in the
|
|
33468
|
+
* runtime-state-durability sense: it moves once per window and must survive
|
|
33469
|
+
* a restart, or every restart would owe the camera a wake.
|
|
33470
|
+
*/
|
|
33471
|
+
lastFullRefreshAt: number().optional(),
|
|
33472
|
+
/**
|
|
33325
33473
|
* True when the source is a BINARY low-battery indicator (HA
|
|
33326
33474
|
* `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
|
|
33327
33475
|
* charge level — `percentage` is then a coarse stand-in (100 = normal,
|
|
@@ -38399,6 +38547,7 @@ var OsdNamedTemplateSchema = object({
|
|
|
38399
38547
|
});
|
|
38400
38548
|
/** Where a slot's value comes from. */
|
|
38401
38549
|
var OsdSourceSchema = discriminatedUnion("kind", [
|
|
38550
|
+
object({ kind: literal("none") }),
|
|
38402
38551
|
object({
|
|
38403
38552
|
kind: literal("static"),
|
|
38404
38553
|
text: string().max(64)
|
|
@@ -38525,6 +38674,13 @@ method(object({ deviceId: number().int() }), object({
|
|
|
38525
38674
|
}), object({ success: literal(true) }), {
|
|
38526
38675
|
kind: "mutation",
|
|
38527
38676
|
auth: "admin"
|
|
38677
|
+
}), method(object({
|
|
38678
|
+
deviceId: number().int(),
|
|
38679
|
+
slotId: string().min(1),
|
|
38680
|
+
enabled: boolean()
|
|
38681
|
+
}), object({ success: literal(true) }), {
|
|
38682
|
+
kind: "mutation",
|
|
38683
|
+
auth: "admin"
|
|
38528
38684
|
}), method(object({
|
|
38529
38685
|
sourceDeviceId: number().int(),
|
|
38530
38686
|
targetDeviceId: number().int()
|
|
@@ -38956,7 +39112,15 @@ var GpuInfoSchema = object({
|
|
|
38956
39112
|
name: string(),
|
|
38957
39113
|
memoryMB: number().optional()
|
|
38958
39114
|
});
|
|
38959
|
-
var NpuInfoSchema = object({
|
|
39115
|
+
var NpuInfoSchema = object({
|
|
39116
|
+
type: _enum([
|
|
39117
|
+
"apple-ane",
|
|
39118
|
+
"intel-npu",
|
|
39119
|
+
"amd-xdna"
|
|
39120
|
+
]),
|
|
39121
|
+
name: string().optional(),
|
|
39122
|
+
generation: _enum(["xdna1", "xdna2"]).optional()
|
|
39123
|
+
});
|
|
38960
39124
|
var CoralInfoSchema = object({
|
|
38961
39125
|
type: literal("coral-edgetpu"),
|
|
38962
39126
|
bus: string().optional()
|
|
@@ -44203,9 +44367,11 @@ var zoneRulesCapability = {
|
|
|
44203
44367
|
* once is what makes a second provider (RunPod, a local NVIDIA node) a new
|
|
44204
44368
|
* manifest entry and nothing else.
|
|
44205
44369
|
*
|
|
44206
|
-
* Collection
|
|
44207
|
-
*
|
|
44208
|
-
*
|
|
44370
|
+
* Collection of provider **types** (addons). Each addon owns zero or more
|
|
44371
|
+
* **entries** (credentials + defaults). Concat-fans: `describe`,
|
|
44372
|
+
* `describeTypes`, `listEntries`, `listJobs`. Single-row work is keyed by
|
|
44373
|
+
* `entryId`; callers also pass the owning `addonId` as the collection
|
|
44374
|
+
* selector (broker posture). Every method is `admin`: these spend money.
|
|
44209
44375
|
*
|
|
44210
44376
|
* Every cloud object a provider creates is tagged with the orchestrator's
|
|
44211
44377
|
* `jobKey`, so a retried submit finds the job it already started instead of
|
|
@@ -44229,8 +44395,8 @@ var TrainingRegionOptionSchema = object({
|
|
|
44229
44395
|
priceMultiplier: number().positive()
|
|
44230
44396
|
});
|
|
44231
44397
|
/**
|
|
44232
|
-
* The provider's per-
|
|
44233
|
-
* live
|
|
44398
|
+
* The provider's per-entry settings, as the orchestrator reads them. They
|
|
44399
|
+
* live on the PROVIDER addon's entry (operator decision 2026-10-08: they
|
|
44234
44400
|
* depend on the user's account), never hard-coded in the orchestrator.
|
|
44235
44401
|
*/
|
|
44236
44402
|
var TrainingProviderDefaultsSchema = object({
|
|
@@ -44241,9 +44407,44 @@ var TrainingProviderDefaultsSchema = object({
|
|
|
44241
44407
|
/** Hours a FAILED job's cloud dataset is kept for debugging; 0 = delete at once. */
|
|
44242
44408
|
failedDatasetGraceHours: number().nonnegative()
|
|
44243
44409
|
});
|
|
44410
|
+
/**
|
|
44411
|
+
* Entry form schema for a provider type — same seam as notification
|
|
44412
|
+
* `TargetKind.configSchema` (`z.unknown()` at the wire; callers tighten to
|
|
44413
|
+
* {@link ConfigUISchema}). Drives `ConfigFormBuilder` on Training → Providers.
|
|
44414
|
+
*/
|
|
44415
|
+
var ConfigSchemaPassthrough = unknown();
|
|
44416
|
+
/** One creatable provider type, for the Add-entry dialog. */
|
|
44417
|
+
var TrainingProviderTypeSchema = object({
|
|
44418
|
+
typeId: string(),
|
|
44419
|
+
label: string(),
|
|
44420
|
+
/** Path under the addon package (e.g. `assets/training-modal/icon.svg`). */
|
|
44421
|
+
iconAsset: string(),
|
|
44422
|
+
/** Owning `training-provider` addon — collection selector for createEntry. */
|
|
44423
|
+
addonId: string(),
|
|
44424
|
+
/**
|
|
44425
|
+
* Fields for one entry of this type (credentials + defaults). Absent on a
|
|
44426
|
+
* type that only needs a name. Password fields are keychain-backed on the
|
|
44427
|
+
* provider addon (`secretStatePaths`), not D634 plaintext.
|
|
44428
|
+
*/
|
|
44429
|
+
configSchema: ConfigSchemaPassthrough.optional()
|
|
44430
|
+
});
|
|
44431
|
+
/** One configured account / workspace under a provider type. */
|
|
44432
|
+
var TrainingProviderEntrySchema = object({
|
|
44433
|
+
entryId: string(),
|
|
44434
|
+
typeId: string(),
|
|
44435
|
+
name: string(),
|
|
44436
|
+
addonId: string(),
|
|
44437
|
+
/** Credentials are present — never the values. */
|
|
44438
|
+
configured: boolean()
|
|
44439
|
+
});
|
|
44244
44440
|
var TrainingProviderDescriptorSchema = object({
|
|
44245
44441
|
addonId: string(),
|
|
44246
|
-
|
|
44442
|
+
entryId: string(),
|
|
44443
|
+
typeId: string(),
|
|
44444
|
+
/** Operator-chosen entry name. */
|
|
44445
|
+
name: string(),
|
|
44446
|
+
iconAsset: string(),
|
|
44447
|
+
/** `modal`, `runpod`, `fake`, … — same as typeId for current providers. */
|
|
44247
44448
|
kind: string(),
|
|
44248
44449
|
label: string(),
|
|
44249
44450
|
gpuTypes: array(TrainingGpuOptionSchema).readonly(),
|
|
@@ -44255,7 +44456,30 @@ var TrainingProviderDescriptorSchema = object({
|
|
|
44255
44456
|
dataResidencyNote: string(),
|
|
44256
44457
|
/** Credentials are present — never the values. */
|
|
44257
44458
|
configured: boolean(),
|
|
44258
|
-
defaults: TrainingProviderDefaultsSchema
|
|
44459
|
+
defaults: TrainingProviderDefaultsSchema,
|
|
44460
|
+
/**
|
|
44461
|
+
* Values for {@link TrainingProviderTypeSchema}'s `configSchema` — password
|
|
44462
|
+
* fields are keychain {@link SecretFieldView} or `null` / `''` (create),
|
|
44463
|
+
* never plaintext. Same hydrate contract as settings forms (D789).
|
|
44464
|
+
*/
|
|
44465
|
+
formValues: record(string(), unknown()).optional()
|
|
44466
|
+
});
|
|
44467
|
+
/**
|
|
44468
|
+
* Patch one entry's name, credentials and defaults. Pin with owning `{ addonId }`.
|
|
44469
|
+
* An omitted or empty `tokenSecret` means KEEP the stored secret (D787).
|
|
44470
|
+
* `{ $secretClear: true }` clears it.
|
|
44471
|
+
*/
|
|
44472
|
+
var TrainingProviderUpdateEntryInputSchema = object({
|
|
44473
|
+
entryId: string().min(1),
|
|
44474
|
+
name: string().min(1),
|
|
44475
|
+
tokenId: string().optional(),
|
|
44476
|
+
tokenSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
44477
|
+
environment: string().optional(),
|
|
44478
|
+
gpuType: string().optional(),
|
|
44479
|
+
region: string().optional(),
|
|
44480
|
+
costCapUsd: number().nonnegative().optional(),
|
|
44481
|
+
maxRuntimeSec: number().int().positive().optional(),
|
|
44482
|
+
failedDatasetGraceHours: number().nonnegative().optional()
|
|
44259
44483
|
});
|
|
44260
44484
|
var TrainingCredentialCheckSchema = object({
|
|
44261
44485
|
ok: boolean(),
|
|
@@ -44307,20 +44531,33 @@ var TrainerSourceRefSchema = object({
|
|
|
44307
44531
|
/** A commit sha (never a branch) — the job must be reproducible. */
|
|
44308
44532
|
revision: string().regex(/^[0-9a-f]{7,64}$/)
|
|
44309
44533
|
});
|
|
44534
|
+
/** Cloud job identity — route with the owning addon's `addonId` selector. */
|
|
44310
44535
|
var JobRef = {
|
|
44311
|
-
|
|
44536
|
+
entryId: string().min(1),
|
|
44312
44537
|
jobKey: string().uuid()
|
|
44313
44538
|
};
|
|
44314
44539
|
var TrainingLogLineSchema = object({
|
|
44315
44540
|
ts: number(),
|
|
44316
44541
|
text: string()
|
|
44317
44542
|
});
|
|
44318
|
-
method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "admin" }), method(object({
|
|
44543
|
+
method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "admin" }), method(object({}), array(TrainingProviderTypeSchema).readonly(), { auth: "admin" }), method(object({}), array(TrainingProviderEntrySchema).readonly(), { auth: "admin" }), method(object({
|
|
44544
|
+
typeId: string().min(1),
|
|
44545
|
+
name: string().min(1)
|
|
44546
|
+
}), object({ entryId: string() }), {
|
|
44547
|
+
kind: "mutation",
|
|
44548
|
+
auth: "admin"
|
|
44549
|
+
}), method(object({ entryId: string().min(1) }), object({ ok: literal(true) }), {
|
|
44550
|
+
kind: "mutation",
|
|
44551
|
+
auth: "admin"
|
|
44552
|
+
}), method(TrainingProviderUpdateEntryInputSchema, object({ ok: literal(true) }), {
|
|
44553
|
+
kind: "mutation",
|
|
44554
|
+
auth: "admin"
|
|
44555
|
+
}), method(object({ entryId: string().min(1) }), TrainingCredentialCheckSchema, {
|
|
44319
44556
|
kind: "mutation",
|
|
44320
44557
|
auth: "admin",
|
|
44321
44558
|
timeoutMs: 3e4
|
|
44322
44559
|
}), method(object({
|
|
44323
|
-
|
|
44560
|
+
entryId: string().min(1),
|
|
44324
44561
|
gpuType: string(),
|
|
44325
44562
|
region: string(),
|
|
44326
44563
|
datasetBytes: number().int().nonnegative(),
|
|
@@ -44386,7 +44623,7 @@ method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "
|
|
|
44386
44623
|
kind: "mutation",
|
|
44387
44624
|
auth: "admin",
|
|
44388
44625
|
timeoutMs: 5 * 6e4
|
|
44389
|
-
}), method(object({
|
|
44626
|
+
}), method(object({ entryId: string().min(1).optional() }), array(ProviderJobStatusSchema).readonly(), {
|
|
44390
44627
|
auth: "admin",
|
|
44391
44628
|
timeoutMs: 2 * 6e4
|
|
44392
44629
|
});
|
|
@@ -44396,20 +44633,31 @@ method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "
|
|
|
44396
44633
|
*
|
|
44397
44634
|
* It owns what every provider must agree on, written once:
|
|
44398
44635
|
*
|
|
44399
|
-
* - **Dataset versions.** Taken over from pipeline-analytics (D796): the rows
|
|
44400
|
-
* live here (D808); pipeline-analytics still CUTS a version (it owns the corpus and
|
|
44401
|
-
* the export plan) and reaches this store through the five row methods
|
|
44402
|
-
* below, so the Retrain page and the export did not change.
|
|
44403
44636
|
* - **Jobs.** A persisted state machine, a reconcile loop against the
|
|
44404
44637
|
* provider, a cost guard, a daily orphan sweep and the retention rules.
|
|
44405
44638
|
* - **The return flow.** Verified artefacts → Model Studio
|
|
44406
44639
|
* (`custom-model-registry.importModelBundle`) → canary on cameras →
|
|
44407
44640
|
* rollback. Nothing is ever registered automatically.
|
|
44408
44641
|
*
|
|
44642
|
+
* Plan/start takes a live {@link TrainingCorpusSelection} (collections +
|
|
44643
|
+
* work-state rule); frame ids are resolved via pipeline-analytics at that
|
|
44644
|
+
* moment and snapshotted on the job. No frozen dataset version.
|
|
44645
|
+
*
|
|
44409
44646
|
* Every method is `admin`.
|
|
44410
44647
|
*/
|
|
44411
|
-
/**
|
|
44412
|
-
|
|
44648
|
+
/**
|
|
44649
|
+
* Live corpus criteria for plan/start (design §5.2). Default is complete-only.
|
|
44650
|
+
* Frame ids are resolved at plan/start — never a frozen version id.
|
|
44651
|
+
*/
|
|
44652
|
+
var TrainingCorpusSelectionSchema = object({
|
|
44653
|
+
collectionIds: array(string()).min(1),
|
|
44654
|
+
/** Default complete-only for v1 */
|
|
44655
|
+
workState: _enum([
|
|
44656
|
+
"complete",
|
|
44657
|
+
"annotated_or_complete",
|
|
44658
|
+
"any_annotated"
|
|
44659
|
+
]).default("complete")
|
|
44660
|
+
});
|
|
44413
44661
|
/** A base model a detector job can fine-tune, and the baseline it is judged against. */
|
|
44414
44662
|
var TrainingBaseModelSchema = object({
|
|
44415
44663
|
modelId: string(),
|
|
@@ -44427,9 +44675,13 @@ var TrainingPresetSchema = _enum([
|
|
|
44427
44675
|
"thorough"
|
|
44428
44676
|
]);
|
|
44429
44677
|
var TrainingJobRequestSchema = object({
|
|
44430
|
-
|
|
44431
|
-
|
|
44432
|
-
|
|
44678
|
+
/** Live collection + work-state criteria; resolved at plan/start. */
|
|
44679
|
+
selection: TrainingCorpusSelectionSchema,
|
|
44680
|
+
/**
|
|
44681
|
+
* The provider **entry** that runs it (credentials + defaults).
|
|
44682
|
+
* Resolve to an owning addon via `training-provider.describe` / `listEntries`.
|
|
44683
|
+
*/
|
|
44684
|
+
entryId: string().min(1),
|
|
44433
44685
|
gpuType: string().min(1),
|
|
44434
44686
|
region: string(),
|
|
44435
44687
|
baseModelId: string().min(1),
|
|
@@ -44488,6 +44740,7 @@ var TrainingJobPlanSchema = object({
|
|
|
44488
44740
|
* result lives after the operator acted on it.
|
|
44489
44741
|
*/
|
|
44490
44742
|
var TrainingJobStateSchema = _enum([
|
|
44743
|
+
"queued",
|
|
44491
44744
|
"uploading",
|
|
44492
44745
|
"submitted",
|
|
44493
44746
|
"starting",
|
|
@@ -44541,6 +44794,11 @@ var TrainingJobSchema = object({
|
|
|
44541
44794
|
createdAt: number(),
|
|
44542
44795
|
updatedAt: number(),
|
|
44543
44796
|
state: TrainingJobStateSchema,
|
|
44797
|
+
/**
|
|
44798
|
+
* Shared by every job enqueued together via `startJobBatch`.
|
|
44799
|
+
* `null` for a lone `startJob`. The engine advances queued siblings by this id.
|
|
44800
|
+
*/
|
|
44801
|
+
batchId: string().nullable().default(null),
|
|
44544
44802
|
stage: TrainingStageSchema.nullable(),
|
|
44545
44803
|
failure: object({
|
|
44546
44804
|
reason: TrainingFailureReasonSchema,
|
|
@@ -44552,6 +44810,12 @@ var TrainingJobSchema = object({
|
|
|
44552
44810
|
maxRuntimeSec: number().int().positive(),
|
|
44553
44811
|
trainer: TrainerSourceRefSchema,
|
|
44554
44812
|
datasetLayout: RetrainExportLayoutSchema,
|
|
44813
|
+
/** Frame count snapshotted when the job started (audit; equals `frameIds.length`). */
|
|
44814
|
+
frameCount: number().int().nonnegative(),
|
|
44815
|
+
/** Resolved frame ids at start — the archive ships exactly these. */
|
|
44816
|
+
frameIds: array(string()).max(2e4).readonly(),
|
|
44817
|
+
/** Hash of what those frames taught at start (trainer `dataset.annotationHash`). */
|
|
44818
|
+
annotationHash: string(),
|
|
44555
44819
|
dataset: object({
|
|
44556
44820
|
bytes: number().int(),
|
|
44557
44821
|
sha256: string(),
|
|
@@ -44593,13 +44857,7 @@ var TrainingJobSchema = object({
|
|
|
44593
44857
|
canary: TrainingCanarySchema.nullable()
|
|
44594
44858
|
});
|
|
44595
44859
|
var IdInput = object({ id: string().min(1) });
|
|
44596
|
-
method(
|
|
44597
|
-
kind: "mutation",
|
|
44598
|
-
auth: "admin"
|
|
44599
|
-
}), 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() }), {
|
|
44600
|
-
kind: "mutation",
|
|
44601
|
-
auth: "admin"
|
|
44602
|
-
}), method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" }), method(TrainingJobRequestSchema, TrainingJobPlanSchema, {
|
|
44860
|
+
method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" }), method(TrainingJobRequestSchema, TrainingJobPlanSchema, {
|
|
44603
44861
|
auth: "admin",
|
|
44604
44862
|
timeoutMs: 6e4
|
|
44605
44863
|
}), method(TrainingJobRequestSchema.extend({
|
|
@@ -44618,6 +44876,24 @@ method(TrainingDatasetVersionRowSchema, object({ stored: boolean() }), {
|
|
|
44618
44876
|
kind: "mutation",
|
|
44619
44877
|
auth: "admin",
|
|
44620
44878
|
timeoutMs: 12e4
|
|
44879
|
+
}), method(TrainingJobRequestSchema.omit({ baseModelId: true }).extend({
|
|
44880
|
+
baseModelIds: array(string().min(1)).min(1),
|
|
44881
|
+
name: string().min(1).max(120),
|
|
44882
|
+
maxCostUsd: number().positive(),
|
|
44883
|
+
maxRuntimeSec: number().int().positive(),
|
|
44884
|
+
idempotencyKey: string().min(8).max(80)
|
|
44885
|
+
}), discriminatedUnion("kind", [object({
|
|
44886
|
+
kind: literal("started"),
|
|
44887
|
+
batchId: string(),
|
|
44888
|
+
jobs: array(TrainingJobSchema).min(1).readonly()
|
|
44889
|
+
}), object({
|
|
44890
|
+
kind: literal("refused"),
|
|
44891
|
+
reason: TrainingRefusalReasonSchema,
|
|
44892
|
+
detail: string()
|
|
44893
|
+
})]), {
|
|
44894
|
+
kind: "mutation",
|
|
44895
|
+
auth: "admin",
|
|
44896
|
+
timeoutMs: 12e4
|
|
44621
44897
|
}), method(object({}), array(TrainingJobSchema).readonly(), { auth: "admin" }), method(IdInput, TrainingJobSchema.nullable(), { auth: "admin" }), method(IdInput, TrainingJobSchema, {
|
|
44622
44898
|
kind: "mutation",
|
|
44623
44899
|
auth: "admin",
|
|
@@ -46228,17 +46504,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
46228
46504
|
return toDeviceSummary(device, this.addonId);
|
|
46229
46505
|
}
|
|
46230
46506
|
};
|
|
46231
|
-
/**
|
|
46232
|
-
* Default silence budget before a sleeping battery camera is called gone.
|
|
46233
|
-
*
|
|
46234
|
-
* Sized off the mechanism that produces contact, not off taste: a battery cam
|
|
46235
|
-
* on this deployment surfaces a firmware push (battery level, sleep/wake, or a
|
|
46236
|
-
* motion email) on the order of hours even with no visitors, and the snapshot
|
|
46237
|
-
* wrapper's own battery window is 1 hour. Six hours is therefore several
|
|
46238
|
-
* missed opportunities, not one — so a single quiet night does not raise a
|
|
46239
|
-
* fault, and a genuinely flat camera is named well inside a day.
|
|
46240
|
-
*/
|
|
46241
|
-
var BATTERY_UNREACHABLE_AFTER_MS = 360 * 6e4;
|
|
46242
46507
|
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;
|
|
46243
46508
|
new Set(Object.values(DeviceType));
|
|
46244
46509
|
Object.freeze({
|
|
@@ -50202,6 +50467,36 @@ Object.freeze({
|
|
|
50202
50467
|
addonId: null,
|
|
50203
50468
|
access: "view"
|
|
50204
50469
|
},
|
|
50470
|
+
"oidcConnections.connectionFormSchema": {
|
|
50471
|
+
capName: "oidc-connections",
|
|
50472
|
+
capScope: "system",
|
|
50473
|
+
addonId: null,
|
|
50474
|
+
access: "view"
|
|
50475
|
+
},
|
|
50476
|
+
"oidcConnections.createConnection": {
|
|
50477
|
+
capName: "oidc-connections",
|
|
50478
|
+
capScope: "system",
|
|
50479
|
+
addonId: null,
|
|
50480
|
+
access: "create"
|
|
50481
|
+
},
|
|
50482
|
+
"oidcConnections.deleteConnection": {
|
|
50483
|
+
capName: "oidc-connections",
|
|
50484
|
+
capScope: "system",
|
|
50485
|
+
addonId: null,
|
|
50486
|
+
access: "delete"
|
|
50487
|
+
},
|
|
50488
|
+
"oidcConnections.listConnections": {
|
|
50489
|
+
capName: "oidc-connections",
|
|
50490
|
+
capScope: "system",
|
|
50491
|
+
addonId: null,
|
|
50492
|
+
access: "view"
|
|
50493
|
+
},
|
|
50494
|
+
"oidcConnections.updateConnection": {
|
|
50495
|
+
capName: "oidc-connections",
|
|
50496
|
+
capScope: "system",
|
|
50497
|
+
addonId: null,
|
|
50498
|
+
access: "create"
|
|
50499
|
+
},
|
|
50205
50500
|
"osd.getStatus": {
|
|
50206
50501
|
capName: "osd",
|
|
50207
50502
|
capScope: "device",
|
|
@@ -50274,6 +50569,12 @@ Object.freeze({
|
|
|
50274
50569
|
addonId: null,
|
|
50275
50570
|
access: "create"
|
|
50276
50571
|
},
|
|
50572
|
+
"osdManager.setSlotCameraEnabled": {
|
|
50573
|
+
capName: "osd-manager",
|
|
50574
|
+
capScope: "system",
|
|
50575
|
+
addonId: null,
|
|
50576
|
+
access: "create"
|
|
50577
|
+
},
|
|
50277
50578
|
"osdManager.upsertTemplate": {
|
|
50278
50579
|
capName: "osd-manager",
|
|
50279
50580
|
capScope: "system",
|
|
@@ -50412,12 +50713,6 @@ Object.freeze({
|
|
|
50412
50713
|
addonId: null,
|
|
50413
50714
|
access: "create"
|
|
50414
50715
|
},
|
|
50415
|
-
"pipelineAnalytics.createRetrainDatasetVersion": {
|
|
50416
|
-
capName: "pipeline-analytics",
|
|
50417
|
-
capScope: "device",
|
|
50418
|
-
addonId: null,
|
|
50419
|
-
access: "create"
|
|
50420
|
-
},
|
|
50421
50716
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
50422
50717
|
capName: "pipeline-analytics",
|
|
50423
50718
|
capScope: "device",
|
|
@@ -50430,12 +50725,6 @@ Object.freeze({
|
|
|
50430
50725
|
addonId: null,
|
|
50431
50726
|
access: "delete"
|
|
50432
50727
|
},
|
|
50433
|
-
"pipelineAnalytics.deleteRetrainDatasetVersion": {
|
|
50434
|
-
capName: "pipeline-analytics",
|
|
50435
|
-
capScope: "device",
|
|
50436
|
-
addonId: null,
|
|
50437
|
-
access: "delete"
|
|
50438
|
-
},
|
|
50439
50728
|
"pipelineAnalytics.deleteTracks": {
|
|
50440
50729
|
capName: "pipeline-analytics",
|
|
50441
50730
|
capScope: "device",
|
|
@@ -50538,6 +50827,12 @@ Object.freeze({
|
|
|
50538
50827
|
addonId: null,
|
|
50539
50828
|
access: "view"
|
|
50540
50829
|
},
|
|
50830
|
+
"pipelineAnalytics.getRetrainCorpusKpi": {
|
|
50831
|
+
capName: "pipeline-analytics",
|
|
50832
|
+
capScope: "device",
|
|
50833
|
+
addonId: null,
|
|
50834
|
+
access: "view"
|
|
50835
|
+
},
|
|
50541
50836
|
"pipelineAnalytics.getRetrainExportUrl": {
|
|
50542
50837
|
capName: "pipeline-analytics",
|
|
50543
50838
|
capScope: "device",
|
|
@@ -50658,7 +50953,7 @@ Object.freeze({
|
|
|
50658
50953
|
addonId: null,
|
|
50659
50954
|
access: "view"
|
|
50660
50955
|
},
|
|
50661
|
-
"pipelineAnalytics.
|
|
50956
|
+
"pipelineAnalytics.listRetrainCorpusPage": {
|
|
50662
50957
|
capName: "pipeline-analytics",
|
|
50663
50958
|
capScope: "device",
|
|
50664
50959
|
addonId: null,
|
|
@@ -50712,12 +51007,6 @@ Object.freeze({
|
|
|
50712
51007
|
addonId: null,
|
|
50713
51008
|
access: "create"
|
|
50714
51009
|
},
|
|
50715
|
-
"pipelineAnalytics.previewRetrainDataset": {
|
|
50716
|
-
capName: "pipeline-analytics",
|
|
50717
|
-
capScope: "device",
|
|
50718
|
-
addonId: null,
|
|
50719
|
-
access: "view"
|
|
50720
|
-
},
|
|
50721
51010
|
"pipelineAnalytics.proposeRetrainAnnotations": {
|
|
50722
51011
|
capName: "pipeline-analytics",
|
|
50723
51012
|
capScope: "device",
|
|
@@ -50796,6 +51085,12 @@ Object.freeze({
|
|
|
50796
51085
|
addonId: null,
|
|
50797
51086
|
access: "create"
|
|
50798
51087
|
},
|
|
51088
|
+
"pipelineAnalytics.resolveRetrainTrainingCorpus": {
|
|
51089
|
+
capName: "pipeline-analytics",
|
|
51090
|
+
capScope: "device",
|
|
51091
|
+
addonId: null,
|
|
51092
|
+
access: "view"
|
|
51093
|
+
},
|
|
50799
51094
|
"pipelineAnalytics.restageRetrainTrack": {
|
|
50800
51095
|
capName: "pipeline-analytics",
|
|
50801
51096
|
capScope: "device",
|
|
@@ -50856,12 +51151,6 @@ Object.freeze({
|
|
|
50856
51151
|
addonId: null,
|
|
50857
51152
|
access: "create"
|
|
50858
51153
|
},
|
|
50859
|
-
"pipelineAnalytics.verifyRetrainDatasetVersion": {
|
|
50860
|
-
capName: "pipeline-analytics",
|
|
50861
|
-
capScope: "device",
|
|
50862
|
-
addonId: null,
|
|
50863
|
-
access: "view"
|
|
50864
|
-
},
|
|
50865
51154
|
"pipelineAnalytics.wipeAllAnalytics": {
|
|
50866
51155
|
capName: "pipeline-analytics",
|
|
50867
51156
|
capScope: "device",
|
|
@@ -53256,18 +53545,6 @@ Object.freeze({
|
|
|
53256
53545
|
addonId: null,
|
|
53257
53546
|
access: "create"
|
|
53258
53547
|
},
|
|
53259
|
-
"training.findDatasetVersionIdByName": {
|
|
53260
|
-
capName: "training",
|
|
53261
|
-
capScope: "system",
|
|
53262
|
-
addonId: null,
|
|
53263
|
-
access: "view"
|
|
53264
|
-
},
|
|
53265
|
-
"training.getDatasetVersion": {
|
|
53266
|
-
capName: "training",
|
|
53267
|
-
capScope: "system",
|
|
53268
|
-
addonId: null,
|
|
53269
|
-
access: "view"
|
|
53270
|
-
},
|
|
53271
53548
|
"training.getJob": {
|
|
53272
53549
|
capName: "training",
|
|
53273
53550
|
capScope: "system",
|
|
@@ -53286,12 +53563,6 @@ Object.freeze({
|
|
|
53286
53563
|
addonId: null,
|
|
53287
53564
|
access: "view"
|
|
53288
53565
|
},
|
|
53289
|
-
"training.listDatasetVersions": {
|
|
53290
|
-
capName: "training",
|
|
53291
|
-
capScope: "system",
|
|
53292
|
-
addonId: null,
|
|
53293
|
-
access: "view"
|
|
53294
|
-
},
|
|
53295
53566
|
"training.listJobs": {
|
|
53296
53567
|
capName: "training",
|
|
53297
53568
|
capScope: "system",
|
|
@@ -53304,54 +53575,66 @@ Object.freeze({
|
|
|
53304
53575
|
addonId: null,
|
|
53305
53576
|
access: "view"
|
|
53306
53577
|
},
|
|
53307
|
-
"training.
|
|
53578
|
+
"training.registerResult": {
|
|
53308
53579
|
capName: "training",
|
|
53309
53580
|
capScope: "system",
|
|
53310
53581
|
addonId: null,
|
|
53311
53582
|
access: "create"
|
|
53312
53583
|
},
|
|
53313
|
-
"training.
|
|
53584
|
+
"training.rollbackCanary": {
|
|
53314
53585
|
capName: "training",
|
|
53315
53586
|
capScope: "system",
|
|
53316
53587
|
addonId: null,
|
|
53317
53588
|
access: "create"
|
|
53318
53589
|
},
|
|
53319
|
-
"training.
|
|
53590
|
+
"training.startJob": {
|
|
53320
53591
|
capName: "training",
|
|
53321
53592
|
capScope: "system",
|
|
53322
53593
|
addonId: null,
|
|
53323
|
-
access: "
|
|
53594
|
+
access: "create"
|
|
53324
53595
|
},
|
|
53325
|
-
"training.
|
|
53596
|
+
"training.startJobBatch": {
|
|
53326
53597
|
capName: "training",
|
|
53327
53598
|
capScope: "system",
|
|
53328
53599
|
addonId: null,
|
|
53329
53600
|
access: "create"
|
|
53330
53601
|
},
|
|
53331
|
-
"
|
|
53332
|
-
capName: "training",
|
|
53602
|
+
"trainingProvider.cancelJob": {
|
|
53603
|
+
capName: "training-provider",
|
|
53333
53604
|
capScope: "system",
|
|
53334
53605
|
addonId: null,
|
|
53335
53606
|
access: "create"
|
|
53336
53607
|
},
|
|
53337
|
-
"trainingProvider.
|
|
53608
|
+
"trainingProvider.cleanupJob": {
|
|
53338
53609
|
capName: "training-provider",
|
|
53339
53610
|
capScope: "system",
|
|
53340
53611
|
addonId: null,
|
|
53341
53612
|
access: "create"
|
|
53342
53613
|
},
|
|
53343
|
-
"trainingProvider.
|
|
53614
|
+
"trainingProvider.createEntry": {
|
|
53344
53615
|
capName: "training-provider",
|
|
53345
53616
|
capScope: "system",
|
|
53346
53617
|
addonId: null,
|
|
53347
53618
|
access: "create"
|
|
53348
53619
|
},
|
|
53620
|
+
"trainingProvider.deleteEntry": {
|
|
53621
|
+
capName: "training-provider",
|
|
53622
|
+
capScope: "system",
|
|
53623
|
+
addonId: null,
|
|
53624
|
+
access: "delete"
|
|
53625
|
+
},
|
|
53349
53626
|
"trainingProvider.describe": {
|
|
53350
53627
|
capName: "training-provider",
|
|
53351
53628
|
capScope: "system",
|
|
53352
53629
|
addonId: null,
|
|
53353
53630
|
access: "view"
|
|
53354
53631
|
},
|
|
53632
|
+
"trainingProvider.describeTypes": {
|
|
53633
|
+
capName: "training-provider",
|
|
53634
|
+
capScope: "system",
|
|
53635
|
+
addonId: null,
|
|
53636
|
+
access: "view"
|
|
53637
|
+
},
|
|
53355
53638
|
"trainingProvider.estimateJob": {
|
|
53356
53639
|
capName: "training-provider",
|
|
53357
53640
|
capScope: "system",
|
|
@@ -53364,6 +53647,12 @@ Object.freeze({
|
|
|
53364
53647
|
addonId: null,
|
|
53365
53648
|
access: "view"
|
|
53366
53649
|
},
|
|
53650
|
+
"trainingProvider.listEntries": {
|
|
53651
|
+
capName: "training-provider",
|
|
53652
|
+
capScope: "system",
|
|
53653
|
+
addonId: null,
|
|
53654
|
+
access: "view"
|
|
53655
|
+
},
|
|
53367
53656
|
"trainingProvider.listJobEvents": {
|
|
53368
53657
|
capName: "training-provider",
|
|
53369
53658
|
capScope: "system",
|
|
@@ -53394,6 +53683,12 @@ Object.freeze({
|
|
|
53394
53683
|
addonId: null,
|
|
53395
53684
|
access: "create"
|
|
53396
53685
|
},
|
|
53686
|
+
"trainingProvider.updateEntry": {
|
|
53687
|
+
capName: "training-provider",
|
|
53688
|
+
capScope: "system",
|
|
53689
|
+
addonId: null,
|
|
53690
|
+
access: "create"
|
|
53691
|
+
},
|
|
53397
53692
|
"trainingProvider.uploadDataset": {
|
|
53398
53693
|
capName: "training-provider",
|
|
53399
53694
|
capScope: "system",
|
|
@@ -56752,6 +57047,11 @@ Object.freeze({
|
|
|
56752
57047
|
form: "single",
|
|
56753
57048
|
optional: false
|
|
56754
57049
|
}],
|
|
57050
|
+
"osdManager.setSlotCameraEnabled": [{
|
|
57051
|
+
name: "deviceId",
|
|
57052
|
+
form: "single",
|
|
57053
|
+
optional: false
|
|
57054
|
+
}],
|
|
56755
57055
|
"petFeeder.callPet": [{
|
|
56756
57056
|
name: "deviceId",
|
|
56757
57057
|
form: "single",
|
|
@@ -56972,6 +57272,11 @@ Object.freeze({
|
|
|
56972
57272
|
form: "array",
|
|
56973
57273
|
optional: false
|
|
56974
57274
|
}],
|
|
57275
|
+
"pipelineAnalytics.listRetrainCorpusPage": [{
|
|
57276
|
+
name: "deviceIds",
|
|
57277
|
+
form: "array",
|
|
57278
|
+
optional: true
|
|
57279
|
+
}],
|
|
56975
57280
|
"pipelineAnalytics.listRetrainStaging": [{
|
|
56976
57281
|
name: "deviceIds",
|
|
56977
57282
|
form: "array",
|
|
@@ -58081,6 +58386,7 @@ Object.freeze({
|
|
|
58081
58386
|
});
|
|
58082
58387
|
Object.freeze({
|
|
58083
58388
|
"broker": "broker",
|
|
58389
|
+
"calendar-source": "calendar",
|
|
58084
58390
|
"device-export": "device-export",
|
|
58085
58391
|
"llm": "ai",
|
|
58086
58392
|
"mesh-network": "mesh",
|
|
@@ -58205,6 +58511,9 @@ new Map(Object.entries({
|
|
|
58205
58511
|
"stream-online": "device-events",
|
|
58206
58512
|
"stream-offline": "device-events",
|
|
58207
58513
|
"detection-blind": "device-events",
|
|
58514
|
+
"recording-stalled": "device-events",
|
|
58515
|
+
"recording-healthy": "device-events",
|
|
58516
|
+
"recording-degraded": "device-events",
|
|
58208
58517
|
"node-online": "system",
|
|
58209
58518
|
"node-offline": "system",
|
|
58210
58519
|
"node-restarting": "system",
|
|
@@ -58218,6 +58527,9 @@ new Map(Object.entries({
|
|
|
58218
58527
|
"wrapper-update-available": "system",
|
|
58219
58528
|
"backup-completed": "system",
|
|
58220
58529
|
"export-completed": "system",
|
|
58530
|
+
"storage-location-low": "system",
|
|
58531
|
+
"storage-location-critical": "system",
|
|
58532
|
+
"storage-location-ok": "system",
|
|
58221
58533
|
"alarm-triggered": "alarm",
|
|
58222
58534
|
"alarm-armed": "alarm",
|
|
58223
58535
|
"alarm-disarmed": "alarm",
|
|
@@ -58279,6 +58591,16 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
58279
58591
|
label: "Detecting nothing",
|
|
58280
58592
|
kinds: ["detection-blind"]
|
|
58281
58593
|
},
|
|
58594
|
+
{
|
|
58595
|
+
id: "recording",
|
|
58596
|
+
family: "device-events",
|
|
58597
|
+
label: "Recording",
|
|
58598
|
+
kinds: [
|
|
58599
|
+
"recording-stalled",
|
|
58600
|
+
"recording-healthy",
|
|
58601
|
+
"recording-degraded"
|
|
58602
|
+
]
|
|
58603
|
+
},
|
|
58282
58604
|
{
|
|
58283
58605
|
id: "nodes",
|
|
58284
58606
|
family: "system",
|
|
@@ -58319,6 +58641,16 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
58319
58641
|
label: "Backups & exports",
|
|
58320
58642
|
kinds: ["backup-completed", "export-completed"]
|
|
58321
58643
|
},
|
|
58644
|
+
{
|
|
58645
|
+
id: "storage",
|
|
58646
|
+
family: "system",
|
|
58647
|
+
label: "Storage locations",
|
|
58648
|
+
kinds: [
|
|
58649
|
+
"storage-location-low",
|
|
58650
|
+
"storage-location-critical",
|
|
58651
|
+
"storage-location-ok"
|
|
58652
|
+
]
|
|
58653
|
+
},
|
|
58322
58654
|
{
|
|
58323
58655
|
id: "alarm",
|
|
58324
58656
|
family: "alarm",
|
|
@@ -58401,6 +58733,9 @@ var DEVICE_EVENTS = [
|
|
|
58401
58733
|
"stream-online",
|
|
58402
58734
|
"stream-offline",
|
|
58403
58735
|
"detection-blind",
|
|
58736
|
+
"recording-stalled",
|
|
58737
|
+
"recording-healthy",
|
|
58738
|
+
"recording-degraded",
|
|
58404
58739
|
...ALARM
|
|
58405
58740
|
];
|
|
58406
58741
|
/** Every kind a summary may speak about — the two summarisable families. */
|
|
@@ -58568,6 +58903,11 @@ var STATE_SUMMARY_VARS = [
|
|
|
58568
58903
|
[...RULE_TEXT],
|
|
58569
58904
|
[...RULE_TEXT],
|
|
58570
58905
|
[...RULE_TEXT],
|
|
58906
|
+
[...RULE_TEXT],
|
|
58907
|
+
[...RULE_TEXT],
|
|
58908
|
+
[...RULE_TEXT],
|
|
58909
|
+
[...RULE_TEXT],
|
|
58910
|
+
[...RULE_TEXT],
|
|
58571
58911
|
([...RULE_TEXT], [...UPDATES]),
|
|
58572
58912
|
([...RULE_TEXT], [...PACKAGE_UPDATES]),
|
|
58573
58913
|
([...RULE_TEXT], [...PACKAGE_UPDATES]),
|
|
@@ -71239,7 +71579,7 @@ var require_snapshot_utils = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
71239
71579
|
//#region ../../node_modules/undici/lib/mock/snapshot-recorder.js
|
|
71240
71580
|
var require_snapshot_recorder = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
71241
71581
|
var { writeFile: writeFile$1, readFile: readFile$1, mkdir: mkdir$2 } = __require("node:fs/promises");
|
|
71242
|
-
var { dirname: dirname$
|
|
71582
|
+
var { dirname: dirname$2, resolve: resolve$1 } = __require("node:path");
|
|
71243
71583
|
var { setTimeout: setTimeout$1, clearTimeout: clearTimeout$1 } = __require("node:timers");
|
|
71244
71584
|
var { InvalidArgumentError, UndiciError } = require_errors$1();
|
|
71245
71585
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -71504,7 +71844,7 @@ var require_snapshot_recorder = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
71504
71844
|
const path = filePath || this.#snapshotPath;
|
|
71505
71845
|
if (!path) throw new InvalidArgumentError("Snapshot path is required");
|
|
71506
71846
|
try {
|
|
71507
|
-
const data = await readFile$1(resolve(path), "utf8");
|
|
71847
|
+
const data = await readFile$1(resolve$1(path), "utf8");
|
|
71508
71848
|
const parsed = JSON.parse(data);
|
|
71509
71849
|
if (Array.isArray(parsed)) {
|
|
71510
71850
|
this.#snapshots.clear();
|
|
@@ -71524,8 +71864,8 @@ var require_snapshot_recorder = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
71524
71864
|
async saveSnapshots(filePath) {
|
|
71525
71865
|
const path = filePath || this.#snapshotPath;
|
|
71526
71866
|
if (!path) throw new InvalidArgumentError("Snapshot path is required");
|
|
71527
|
-
const resolvedPath = resolve(path);
|
|
71528
|
-
await mkdir$2(dirname$
|
|
71867
|
+
const resolvedPath = resolve$1(path);
|
|
71868
|
+
await mkdir$2(dirname$2(resolvedPath), { recursive: true });
|
|
71529
71869
|
const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
|
|
71530
71870
|
hash,
|
|
71531
71871
|
snapshot
|
|
@@ -160162,6 +160502,7 @@ function buildInitialStatus2(config, enabled) {
|
|
|
160162
160502
|
lastErrorMessage: void 0
|
|
160163
160503
|
};
|
|
160164
160504
|
}
|
|
160505
|
+
process.platform, process.env["SystemRoot"];
|
|
160165
160506
|
/**
|
|
160166
160507
|
* The spawn a one-shot ffmpeg site uses: the spec's seam when one is injected,
|
|
160167
160508
|
* else the node's PINNED binary (D540), else `null` — and the site refuses by
|
|
@@ -160734,6 +161075,13 @@ var reolinkCameraSchema = object({
|
|
|
160734
161075
|
*/
|
|
160735
161076
|
clipCatalogForcedWake: boolean().optional(),
|
|
160736
161077
|
/**
|
|
161078
|
+
* Battery cameras only: the window, in minutes, in which the camera refreshes
|
|
161079
|
+
* ALL its metadata and its cached snapshot exactly once (D847). Absent means
|
|
161080
|
+
* the default (60); `0` turns the scheduled refresh off for this camera; a
|
|
161081
|
+
* value is clamped to 15..240 (below the 6 h presence budget).
|
|
161082
|
+
*/
|
|
161083
|
+
batteryRefreshMinutes: number().int().min(0).max(240).optional(),
|
|
161084
|
+
/**
|
|
160737
161085
|
* Per-AI-class sensitivity overrides — pushed via
|
|
160738
161086
|
* `setAiDetection(aiType, sensitivity)` (cmdId=343). Reolink
|
|
160739
161087
|
* range 0..100 — same scale the reolink_aio CGI exposes
|
|
@@ -166991,14 +167339,266 @@ function deriveChargingSource(adapterStatus, chargeStatus) {
|
|
|
166991
167339
|
function reportsPowerSource(info) {
|
|
166992
167340
|
return info.adapterStatus !== void 0 || info.chargeStatus !== void 0;
|
|
166993
167341
|
}
|
|
167342
|
+
/** The settings key, per device. */
|
|
167343
|
+
var BATTERY_REFRESH_MINUTES_KEY = "batteryRefreshMinutes";
|
|
167344
|
+
/** How often the in-memory check runs. Costs nothing: no I/O unless it fires. */
|
|
167345
|
+
var BATTERY_REFRESH_TICK_MS = 6e4;
|
|
167346
|
+
/** The least time between two attempts in one window. */
|
|
167347
|
+
var RETRY_BACKOFF_MS = 10 * 6e4;
|
|
167348
|
+
var MAX_WAKE_LEAD_MS = 5 * 6e4;
|
|
167349
|
+
var STAGGER_SLOTS = 60;
|
|
167350
|
+
/** The operator's window in ms, or `0` for off. Out-of-range values clamp. */
|
|
167351
|
+
function resolveBatteryRefreshIntervalMs(configured) {
|
|
167352
|
+
if (typeof configured !== "number" || !Number.isFinite(configured)) return 60 * 6e4;
|
|
167353
|
+
if (configured <= 0) return 0;
|
|
167354
|
+
return Math.min(240, Math.max(15, Math.round(configured))) * 6e4;
|
|
167355
|
+
}
|
|
167356
|
+
/** Deterministic per-device offset so a fleet's windows do not align. */
|
|
167357
|
+
function staggerOffsetMs(deviceId, intervalMs) {
|
|
167358
|
+
const slot = Math.abs(Math.trunc(deviceId)) % STAGGER_SLOTS;
|
|
167359
|
+
return Math.floor(intervalMs / STAGGER_SLOTS * slot);
|
|
167360
|
+
}
|
|
167361
|
+
/** Which window a wall-clock instant falls in. */
|
|
167362
|
+
function windowIndex(at, intervalMs, offsetMs) {
|
|
167363
|
+
return Math.floor((at - offsetMs) / intervalMs);
|
|
167364
|
+
}
|
|
167365
|
+
/** How long before a window's end the wake is allowed to fire. */
|
|
167366
|
+
function wakeLeadMs(intervalMs) {
|
|
167367
|
+
return Math.min(MAX_WAKE_LEAD_MS, Math.floor(intervalMs / 10));
|
|
167368
|
+
}
|
|
167369
|
+
function decideBatteryRefresh(input) {
|
|
167370
|
+
if (input.intervalMs <= 0) return {
|
|
167371
|
+
kind: "skip",
|
|
167372
|
+
reason: "off"
|
|
167373
|
+
};
|
|
167374
|
+
const current = windowIndex(input.nowMs, input.intervalMs, input.offsetMs);
|
|
167375
|
+
if ((typeof input.lastFullRefreshAt === "number" && input.lastFullRefreshAt > 0 ? windowIndex(input.lastFullRefreshAt, input.intervalMs, input.offsetMs) : Number.NEGATIVE_INFINITY) >= current) return {
|
|
167376
|
+
kind: "skip",
|
|
167377
|
+
reason: "done"
|
|
167378
|
+
};
|
|
167379
|
+
if (input.attemptsInWindow >= 2) return {
|
|
167380
|
+
kind: "skip",
|
|
167381
|
+
reason: "attempts-exhausted"
|
|
167382
|
+
};
|
|
167383
|
+
if (input.lastAttemptAt !== void 0 && input.attemptsInWindow > 0 && input.nowMs - input.lastAttemptAt < 6e5) return {
|
|
167384
|
+
kind: "skip",
|
|
167385
|
+
reason: "backoff"
|
|
167386
|
+
};
|
|
167387
|
+
if (input.awake) return { kind: "refresh-awake" };
|
|
167388
|
+
const windowEnd = input.offsetMs + (current + 1) * input.intervalMs;
|
|
167389
|
+
if (input.nowMs < windowEnd - wakeLeadMs(input.intervalMs)) return {
|
|
167390
|
+
kind: "skip",
|
|
167391
|
+
reason: "asleep-before-deadline"
|
|
167392
|
+
};
|
|
167393
|
+
return { kind: "wake-then-refresh" };
|
|
167394
|
+
}
|
|
167395
|
+
var BatteryHourlyRefresh = class {
|
|
167396
|
+
deps;
|
|
167397
|
+
inFlight = null;
|
|
167398
|
+
attemptWindow = Number.NEGATIVE_INFINITY;
|
|
167399
|
+
attempts = 0;
|
|
167400
|
+
lastAttemptAt;
|
|
167401
|
+
constructor(deps) {
|
|
167402
|
+
this.deps = deps;
|
|
167403
|
+
}
|
|
167404
|
+
/**
|
|
167405
|
+
* The periodic check. Resolves `true` when a refresh landed in this call.
|
|
167406
|
+
* Never throws.
|
|
167407
|
+
*/
|
|
167408
|
+
async tick() {
|
|
167409
|
+
return this.consider("tick");
|
|
167410
|
+
}
|
|
167411
|
+
/**
|
|
167412
|
+
* The camera was just observed coming up (event, sleep poll, hub summary, a
|
|
167413
|
+
* confirmed wake). Resolves `true` when this call refreshed everything, so
|
|
167414
|
+
* the caller can skip the parts of its own fan-out the refresh already did.
|
|
167415
|
+
*/
|
|
167416
|
+
async noteAwake() {
|
|
167417
|
+
return this.consider("wake-transition");
|
|
167418
|
+
}
|
|
167419
|
+
async consider(trigger) {
|
|
167420
|
+
if (this.inFlight !== null) return this.inFlight;
|
|
167421
|
+
const nowMs = this.deps.now();
|
|
167422
|
+
const intervalMs = this.deps.intervalMs();
|
|
167423
|
+
const offsetMs = intervalMs > 0 ? staggerOffsetMs(this.deps.deviceId, intervalMs) : 0;
|
|
167424
|
+
const window = intervalMs > 0 ? windowIndex(nowMs, intervalMs, offsetMs) : 0;
|
|
167425
|
+
if (window !== this.attemptWindow) {
|
|
167426
|
+
this.attemptWindow = window;
|
|
167427
|
+
this.attempts = 0;
|
|
167428
|
+
this.lastAttemptAt = void 0;
|
|
167429
|
+
}
|
|
167430
|
+
const decision = decideBatteryRefresh({
|
|
167431
|
+
nowMs,
|
|
167432
|
+
intervalMs,
|
|
167433
|
+
offsetMs,
|
|
167434
|
+
lastFullRefreshAt: this.deps.lastFullRefreshAt(),
|
|
167435
|
+
awake: trigger === "wake-transition" || this.deps.isAwake(),
|
|
167436
|
+
attemptsInWindow: this.attempts,
|
|
167437
|
+
lastAttemptAt: this.lastAttemptAt
|
|
167438
|
+
});
|
|
167439
|
+
if (decision.kind === "skip") {
|
|
167440
|
+
this.logSkip(decision.reason, trigger);
|
|
167441
|
+
return false;
|
|
167442
|
+
}
|
|
167443
|
+
this.attempts += 1;
|
|
167444
|
+
this.lastAttemptAt = nowMs;
|
|
167445
|
+
const run = this.run(decision.kind, trigger).finally(() => {
|
|
167446
|
+
this.inFlight = null;
|
|
167447
|
+
});
|
|
167448
|
+
this.inFlight = run;
|
|
167449
|
+
return run;
|
|
167450
|
+
}
|
|
167451
|
+
async run(kind, trigger) {
|
|
167452
|
+
const { logger, deviceId } = this.deps;
|
|
167453
|
+
try {
|
|
167454
|
+
if (kind === "wake-then-refresh") {
|
|
167455
|
+
logger.info("battery hourly refresh: not awake in the window — waking once", {
|
|
167456
|
+
tags: { deviceId },
|
|
167457
|
+
meta: { attempt: this.attempts }
|
|
167458
|
+
});
|
|
167459
|
+
if (!await this.deps.wake()) {
|
|
167460
|
+
logger.warn("battery hourly refresh: the wake did not bring the camera up", {
|
|
167461
|
+
tags: { deviceId },
|
|
167462
|
+
meta: {
|
|
167463
|
+
attempt: this.attempts,
|
|
167464
|
+
retryAfterMs: RETRY_BACKOFF_MS
|
|
167465
|
+
}
|
|
167466
|
+
});
|
|
167467
|
+
return false;
|
|
167468
|
+
}
|
|
167469
|
+
if (this.windowSatisfied()) return true;
|
|
167470
|
+
} else logger.info("battery hourly refresh: camera is awake — refreshing now, no wake", {
|
|
167471
|
+
tags: { deviceId },
|
|
167472
|
+
meta: {
|
|
167473
|
+
trigger,
|
|
167474
|
+
attempt: this.attempts
|
|
167475
|
+
}
|
|
167476
|
+
});
|
|
167477
|
+
const outcome = await this.deps.refreshAll();
|
|
167478
|
+
if (outcome.failed.length > 0) {
|
|
167479
|
+
logger.warn("battery hourly refresh incomplete — not stamped, retried at the next chance", {
|
|
167480
|
+
tags: { deviceId },
|
|
167481
|
+
meta: {
|
|
167482
|
+
failed: outcome.failed,
|
|
167483
|
+
attempt: this.attempts,
|
|
167484
|
+
retryAfterMs: RETRY_BACKOFF_MS
|
|
167485
|
+
}
|
|
167486
|
+
});
|
|
167487
|
+
return false;
|
|
167488
|
+
}
|
|
167489
|
+
const at = this.deps.now();
|
|
167490
|
+
this.deps.stamp(at);
|
|
167491
|
+
logger.info("battery hourly refresh complete", {
|
|
167492
|
+
tags: { deviceId },
|
|
167493
|
+
meta: {
|
|
167494
|
+
kind,
|
|
167495
|
+
trigger,
|
|
167496
|
+
at
|
|
167497
|
+
}
|
|
167498
|
+
});
|
|
167499
|
+
return true;
|
|
167500
|
+
} catch (err) {
|
|
167501
|
+
logger.warn("battery hourly refresh failed — retried at the next chance", {
|
|
167502
|
+
tags: { deviceId },
|
|
167503
|
+
meta: {
|
|
167504
|
+
error: err instanceof Error ? err.message : String(err),
|
|
167505
|
+
attempt: this.attempts,
|
|
167506
|
+
retryAfterMs: RETRY_BACKOFF_MS
|
|
167507
|
+
}
|
|
167508
|
+
});
|
|
167509
|
+
return false;
|
|
167510
|
+
}
|
|
167511
|
+
}
|
|
167512
|
+
windowSatisfied() {
|
|
167513
|
+
const intervalMs = this.deps.intervalMs();
|
|
167514
|
+
if (intervalMs <= 0) return false;
|
|
167515
|
+
const offsetMs = staggerOffsetMs(this.deps.deviceId, intervalMs);
|
|
167516
|
+
const last = this.deps.lastFullRefreshAt();
|
|
167517
|
+
if (typeof last !== "number" || last <= 0) return false;
|
|
167518
|
+
return windowIndex(last, intervalMs, offsetMs) >= windowIndex(this.deps.now(), intervalMs, offsetMs);
|
|
167519
|
+
}
|
|
167520
|
+
logSkip(reason, trigger) {
|
|
167521
|
+
if (reason === "backoff" || reason === "attempts-exhausted") {
|
|
167522
|
+
this.deps.logger.info("battery hourly refresh skipped", {
|
|
167523
|
+
tags: { deviceId: this.deps.deviceId },
|
|
167524
|
+
meta: {
|
|
167525
|
+
reason,
|
|
167526
|
+
trigger,
|
|
167527
|
+
attempts: this.attempts
|
|
167528
|
+
}
|
|
167529
|
+
});
|
|
167530
|
+
return;
|
|
167531
|
+
}
|
|
167532
|
+
this.deps.logger.debug("battery hourly refresh not due", {
|
|
167533
|
+
tags: { deviceId: this.deps.deviceId },
|
|
167534
|
+
meta: {
|
|
167535
|
+
reason,
|
|
167536
|
+
trigger
|
|
167537
|
+
}
|
|
167538
|
+
});
|
|
167539
|
+
}
|
|
167540
|
+
};
|
|
167541
|
+
//#endregion
|
|
167542
|
+
//#region src/battery-refresh-settings.ts
|
|
167543
|
+
var BATTERY_REFRESH_SECTION_ID = "battery-refresh";
|
|
167544
|
+
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).`;
|
|
167545
|
+
/** Battery cameras only: a mains camera keeps its facts fresh on its own socket. */
|
|
167546
|
+
function buildBatteryRefreshDeviceSection(input) {
|
|
167547
|
+
if (!input.isBattery) return [];
|
|
167548
|
+
return [{
|
|
167549
|
+
id: BATTERY_REFRESH_SECTION_ID,
|
|
167550
|
+
tab: "general",
|
|
167551
|
+
title: "Scheduled refresh",
|
|
167552
|
+
description: "How often this battery camera is guaranteed to have been read, so a battery level never goes stale unseen.",
|
|
167553
|
+
columns: 1,
|
|
167554
|
+
fields: [{
|
|
167555
|
+
type: "number",
|
|
167556
|
+
key: BATTERY_REFRESH_MINUTES_KEY,
|
|
167557
|
+
label: "Refresh window (min)",
|
|
167558
|
+
description: BATTERY_REFRESH_MINUTES_DESCRIPTION,
|
|
167559
|
+
default: 60,
|
|
167560
|
+
min: 0,
|
|
167561
|
+
max: 240,
|
|
167562
|
+
step: 5
|
|
167563
|
+
}]
|
|
167564
|
+
}];
|
|
167565
|
+
}
|
|
166994
167566
|
//#endregion
|
|
166995
|
-
//#region src/battery-
|
|
166996
|
-
|
|
166997
|
-
|
|
166998
|
-
|
|
166999
|
-
const
|
|
167000
|
-
|
|
167001
|
-
|
|
167567
|
+
//#region src/battery-snapshot-refresh.ts
|
|
167568
|
+
/** The wrapper may run on another node; a stale frame is hours old, skew is seconds. */
|
|
167569
|
+
var CLOCK_SKEW_TOLERANCE_MS = 6e4;
|
|
167570
|
+
async function refreshSnapshotCacheOnce(input) {
|
|
167571
|
+
const { deviceId, api, logger } = input;
|
|
167572
|
+
const startedAt = input.now();
|
|
167573
|
+
if (await api.getSnapshot({
|
|
167574
|
+
deviceId,
|
|
167575
|
+
force: true
|
|
167576
|
+
}) === null) {
|
|
167577
|
+
logger.warn("battery hourly refresh: the snapshot came back empty", { tags: { deviceId } });
|
|
167578
|
+
return false;
|
|
167579
|
+
}
|
|
167580
|
+
let rows;
|
|
167581
|
+
try {
|
|
167582
|
+
rows = await api.getSnapshotOverview({ deviceIds: [deviceId] });
|
|
167583
|
+
} catch (err) {
|
|
167584
|
+
logger.debug("battery hourly refresh: snapshot overview unreadable — counting the image", {
|
|
167585
|
+
tags: { deviceId },
|
|
167586
|
+
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
167587
|
+
});
|
|
167588
|
+
return true;
|
|
167589
|
+
}
|
|
167590
|
+
const capturedAt = rows.find((row) => row.deviceId === deviceId)?.lastCapturedAt ?? null;
|
|
167591
|
+
if (capturedAt === null || capturedAt < startedAt - CLOCK_SKEW_TOLERANCE_MS) {
|
|
167592
|
+
logger.warn("battery hourly refresh: the snapshot cache did not move (a stale frame was served)", {
|
|
167593
|
+
tags: { deviceId },
|
|
167594
|
+
meta: {
|
|
167595
|
+
capturedAt,
|
|
167596
|
+
startedAt
|
|
167597
|
+
}
|
|
167598
|
+
});
|
|
167599
|
+
return false;
|
|
167600
|
+
}
|
|
167601
|
+
return true;
|
|
167002
167602
|
}
|
|
167003
167603
|
//#endregion
|
|
167004
167604
|
//#region src/log-channels.ts
|
|
@@ -171001,7 +171601,6 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
171001
171601
|
* passively checks sleep state, and short-circuits if sleeping. */
|
|
171002
171602
|
batteryUpdateTimer = null;
|
|
171003
171603
|
/** Single-flight gate so two interval fires can't overlap. */
|
|
171004
|
-
batteryUpdateInFlight = null;
|
|
171005
171604
|
/** Aux accessory references registered by spawned children
|
|
171006
171605
|
* (siren / floodlight / pir). The parent's centralised align
|
|
171007
171606
|
* method iterates these to refresh their slices on a single
|
|
@@ -171339,7 +171938,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
171339
171938
|
* snapshot also drives the dispatch's known-good baseline.
|
|
171340
171939
|
*/
|
|
171341
171940
|
async refreshParentSettingsSnapshot(slices, reason = "background") {
|
|
171342
|
-
if (this.isBattery && this.sleeping && reason !== "post-write") {
|
|
171941
|
+
if (this.isBattery && this.sleeping && reason !== "post-write" && reason !== "scheduled") {
|
|
171343
171942
|
this.ctx.logger.info("refreshParentSettingsSnapshot skipped — battery cam is sleeping", { tags: {
|
|
171344
171943
|
deviceId: this.id,
|
|
171345
171944
|
reason
|
|
@@ -172285,7 +172884,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
172285
172884
|
tags: { deviceId: this.id },
|
|
172286
172885
|
meta: { reason }
|
|
172287
172886
|
});
|
|
172288
|
-
return;
|
|
172887
|
+
return false;
|
|
172289
172888
|
}
|
|
172290
172889
|
let api;
|
|
172291
172890
|
try {
|
|
@@ -172295,17 +172894,19 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
172295
172894
|
tags: { deviceId: this.id },
|
|
172296
172895
|
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
172297
172896
|
});
|
|
172298
|
-
return;
|
|
172897
|
+
return false;
|
|
172299
172898
|
}
|
|
172300
172899
|
try {
|
|
172301
172900
|
const info = await api.getBatteryInfo(this.getChannel());
|
|
172302
172901
|
this.updateBatteryCache(info);
|
|
172303
172902
|
await this.refreshNetworkLinkFromApi(api, "battery");
|
|
172903
|
+
return true;
|
|
172304
172904
|
} catch (err) {
|
|
172305
172905
|
this.ctx.logger.debug("battery refresh failed", {
|
|
172306
172906
|
tags: { deviceId: this.id },
|
|
172307
172907
|
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
172308
172908
|
});
|
|
172909
|
+
return false;
|
|
172309
172910
|
}
|
|
172310
172911
|
}
|
|
172311
172912
|
/**
|
|
@@ -173551,6 +174152,18 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
173551
174152
|
* proactive gate lets it through because `sleeping` is false here.
|
|
173552
174153
|
* Mirrors the reference's `updateBatteryAndSnapshot`.
|
|
173553
174154
|
*/
|
|
174155
|
+
/** The scheduled refresh's snapshot step: true only when the cache really moved. */
|
|
174156
|
+
refreshSnapshotCache() {
|
|
174157
|
+
return refreshSnapshotCacheOnce({
|
|
174158
|
+
deviceId: this.id,
|
|
174159
|
+
logger: this.ctx.logger,
|
|
174160
|
+
now: () => Date.now(),
|
|
174161
|
+
api: {
|
|
174162
|
+
getSnapshot: (input) => this.ctx.api.snapshot.getSnapshot.query(input),
|
|
174163
|
+
getSnapshotOverview: (input) => this.ctx.api.snapshot.getSnapshotOverview.query({ deviceIds: [...input.deviceIds] })
|
|
174164
|
+
}
|
|
174165
|
+
});
|
|
174166
|
+
}
|
|
173554
174167
|
async warmSnapshotOnWake() {
|
|
173555
174168
|
try {
|
|
173556
174169
|
await this.ctx.api.snapshot.getSnapshot.query({
|
|
@@ -175986,119 +176599,84 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
175986
176599
|
this.alignAuxTimer = null;
|
|
175987
176600
|
}
|
|
175988
176601
|
}
|
|
175989
|
-
|
|
175990
|
-
|
|
175991
|
-
|
|
175992
|
-
|
|
175993
|
-
|
|
176602
|
+
batteryRefresh = null;
|
|
176603
|
+
getBatteryRefresh() {
|
|
176604
|
+
if (this.batteryRefresh === null) this.batteryRefresh = new BatteryHourlyRefresh({
|
|
176605
|
+
deviceId: this.id,
|
|
176606
|
+
logger: this.ctx.logger,
|
|
176607
|
+
now: () => Date.now(),
|
|
176608
|
+
isAwake: () => !this.sleeping,
|
|
176609
|
+
intervalMs: () => resolveBatteryRefreshIntervalMs(this.config.get("batteryRefreshMinutes")),
|
|
176610
|
+
lastFullRefreshAt: () => this.state.battery.lastFullRefreshAt,
|
|
176611
|
+
wake: async () => {
|
|
176612
|
+
if (!this.canProactivelyWake("scheduled-refresh")) return false;
|
|
176613
|
+
this.markWakeIssued();
|
|
176614
|
+
return this.wakeIfSleeping();
|
|
176615
|
+
},
|
|
176616
|
+
refreshAll: () => this.refreshEverythingNow(),
|
|
176617
|
+
stamp: (at) => {
|
|
176618
|
+
this.state.battery.lastFullRefreshAt = at;
|
|
176619
|
+
}
|
|
176620
|
+
});
|
|
176621
|
+
return this.batteryRefresh;
|
|
176622
|
+
}
|
|
176623
|
+
/**
|
|
176624
|
+
* Every fact and the snapshot, read while the camera is up. SERIALIZED (one
|
|
176625
|
+
* Baichuan round-trip at a time on the single BCUDP stream), and each step
|
|
176626
|
+
* names itself in `failed` instead of throwing, so one dead read neither
|
|
176627
|
+
* hides the others nor stamps the window as done.
|
|
176628
|
+
*/
|
|
176629
|
+
async refreshEverythingNow() {
|
|
176630
|
+
const failed = [];
|
|
176631
|
+
const step = async (name, run) => {
|
|
175994
176632
|
try {
|
|
175995
|
-
if (
|
|
175996
|
-
|
|
175997
|
-
|
|
175998
|
-
|
|
175999
|
-
sleeping: true,
|
|
176000
|
-
hubChild: this.isHubChild(),
|
|
176001
|
-
unreachableAfterMs: 216e5,
|
|
176002
|
-
tickMs: ReolinkCamera.BATTERY_UPDATE_INTERVAL_MS
|
|
176003
|
-
})) {
|
|
176004
|
-
await this.wakeForContactRefresh();
|
|
176005
|
-
return;
|
|
176006
|
-
}
|
|
176007
|
-
this.ctx.logger.debug("battery-update: cam sleeping — skipping cycle without connecting", { tags: { deviceId: this.id } });
|
|
176008
|
-
return;
|
|
176009
|
-
}
|
|
176010
|
-
let api;
|
|
176011
|
-
try {
|
|
176012
|
-
api = await this.ensureApi();
|
|
176013
|
-
} catch (err) {
|
|
176014
|
-
this.ctx.logger.debug("battery-update: ensureApi failed — skipping cycle", {
|
|
176015
|
-
tags: { deviceId: this.id },
|
|
176016
|
-
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
176017
|
-
});
|
|
176018
|
-
return;
|
|
176019
|
-
}
|
|
176020
|
-
let sleeping = false;
|
|
176021
|
-
try {
|
|
176022
|
-
sleeping = api.getSleepStatus({ channel: this.getChannel() }).state === "sleeping";
|
|
176023
|
-
} catch (err) {
|
|
176024
|
-
this.ctx.logger.debug("battery-update: getSleepStatus threw — skipping cycle", {
|
|
176025
|
-
tags: { deviceId: this.id },
|
|
176026
|
-
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
176027
|
-
});
|
|
176028
|
-
return;
|
|
176029
|
-
}
|
|
176030
|
-
if (sleeping) {
|
|
176031
|
-
this.ctx.logger.debug("battery-update: cam sleeping — skipping periodic refresh", { tags: { deviceId: this.id } });
|
|
176032
|
-
return;
|
|
176033
|
-
}
|
|
176034
|
-
this.ctx.logger.info("battery-update: cam awake — running periodic refresh", {
|
|
176633
|
+
if (await run() === false) failed.push(name);
|
|
176634
|
+
} catch (err) {
|
|
176635
|
+
failed.push(name);
|
|
176636
|
+
this.ctx.logger.debug("battery hourly refresh: step threw", {
|
|
176035
176637
|
tags: { deviceId: this.id },
|
|
176036
176638
|
meta: {
|
|
176037
|
-
|
|
176038
|
-
|
|
176639
|
+
step: name,
|
|
176640
|
+
error: err instanceof Error ? err.message : String(err)
|
|
176039
176641
|
}
|
|
176040
176642
|
});
|
|
176041
|
-
await this.refreshBatteryFromApi("periodic").catch(() => {});
|
|
176042
|
-
await this.alignAuxDevicesState("periodic").catch(() => {});
|
|
176043
|
-
await this.refreshParentSettingsSnapshot().catch(() => {});
|
|
176044
|
-
} finally {
|
|
176045
|
-
this.batteryUpdateInFlight = null;
|
|
176046
176643
|
}
|
|
176047
|
-
}
|
|
176048
|
-
this.
|
|
176049
|
-
|
|
176644
|
+
};
|
|
176645
|
+
await step("battery", () => this.refreshBatteryFromApi("demand"));
|
|
176646
|
+
await step("device-info", async () => {
|
|
176647
|
+
await this.populateMetadataFromFirmware(await this.ensureApi());
|
|
176648
|
+
});
|
|
176649
|
+
await step("cap-slices", async () => {
|
|
176650
|
+
await this.warmCapSlicesOnWake({ force: true });
|
|
176651
|
+
});
|
|
176652
|
+
await step("aux-accessories", () => this.alignAuxDevicesState("wake"));
|
|
176653
|
+
await step("settings-snapshot", () => this.refreshParentSettingsSnapshot(void 0, "scheduled"));
|
|
176654
|
+
await step("snapshot", () => this.refreshSnapshotCache());
|
|
176655
|
+
return { failed };
|
|
176050
176656
|
}
|
|
176051
176657
|
/**
|
|
176052
|
-
*
|
|
176053
|
-
*
|
|
176054
|
-
* Bounded by `canProactivelyWake` (the shared cooldown) and by the fact
|
|
176055
|
-
* that a wake that answers resets the silence. A wake that fails is left
|
|
176056
|
-
* alone: the silence then reaches the budget and `deriveBatteryPresence`
|
|
176057
|
-
* says `unreachable` — for the first time on evidence.
|
|
176658
|
+
* Run one scheduled-refresh check. Never throws; costs nothing unless a
|
|
176659
|
+
* refresh is due (see {@link decideBatteryRefresh}).
|
|
176058
176660
|
*/
|
|
176059
|
-
async
|
|
176060
|
-
if (!this.
|
|
176061
|
-
this.
|
|
176062
|
-
const lastContactAt = this.state.battery.lastContactAt ?? 0;
|
|
176063
|
-
const silenceMs = Date.now() - lastContactAt;
|
|
176064
|
-
this.ctx.logger.info("battery contact expiring — waking once to refresh every slice (D355)", {
|
|
176065
|
-
tags: { deviceId: this.id },
|
|
176066
|
-
meta: {
|
|
176067
|
-
silenceMs,
|
|
176068
|
-
budgetMs: BATTERY_UNREACHABLE_AFTER_MS
|
|
176069
|
-
}
|
|
176070
|
-
});
|
|
176071
|
-
try {
|
|
176072
|
-
await this.ensureApi();
|
|
176073
|
-
} catch (err) {
|
|
176074
|
-
this.ctx.logger.warn("battery contact wake FAILED — silence continues and the camera will read unreachable at the budget", {
|
|
176075
|
-
tags: { deviceId: this.id },
|
|
176076
|
-
meta: {
|
|
176077
|
-
silenceMs,
|
|
176078
|
-
error: err instanceof Error ? err.message : String(err)
|
|
176079
|
-
}
|
|
176080
|
-
});
|
|
176081
|
-
return;
|
|
176082
|
-
}
|
|
176083
|
-
await this.refreshBatteryFromApi("demand").catch(() => {});
|
|
176084
|
-
await this.warmCapSlicesOnWake().catch(() => {});
|
|
176085
|
-
await this.alignAuxDevicesState("wake").catch(() => {});
|
|
176086
|
-
await this.warmSnapshotOnWake().catch(() => {});
|
|
176661
|
+
async runBatteryUpdate() {
|
|
176662
|
+
if (!this.isBattery) return;
|
|
176663
|
+
await this.getBatteryRefresh().tick();
|
|
176087
176664
|
}
|
|
176088
|
-
/** Arm the
|
|
176089
|
-
*
|
|
176090
|
-
* events). Idempotent. */
|
|
176665
|
+
/** Arm the scheduled-refresh check. No-op for wired cams (their state is
|
|
176666
|
+
* kept fresh by the 10 s aux-align poll and push events). Idempotent. */
|
|
176091
176667
|
startBatteryUpdatePolling() {
|
|
176092
176668
|
if (this.batteryUpdateTimer) return;
|
|
176093
176669
|
if (!this.isBattery) return;
|
|
176094
176670
|
this.batteryUpdateTimer = setInterval(() => {
|
|
176095
176671
|
this.runBatteryUpdate().catch(() => {});
|
|
176096
|
-
},
|
|
176672
|
+
}, BATTERY_REFRESH_TICK_MS);
|
|
176097
176673
|
if (typeof this.batteryUpdateTimer.unref === "function") this.batteryUpdateTimer.unref();
|
|
176098
|
-
this.ctx.logger.info("battery-update:
|
|
176674
|
+
this.ctx.logger.info("battery-update: scheduled full refresh armed", {
|
|
176099
176675
|
tags: { deviceId: this.id },
|
|
176100
176676
|
meta: {
|
|
176101
|
-
|
|
176677
|
+
tickMs: BATTERY_REFRESH_TICK_MS,
|
|
176678
|
+
intervalMs: resolveBatteryRefreshIntervalMs(this.config.get("batteryRefreshMinutes")),
|
|
176679
|
+
lastFullRefreshAt: this.state.battery.lastFullRefreshAt ?? null,
|
|
176102
176680
|
isHubChild: this.isHubChild()
|
|
176103
176681
|
}
|
|
176104
176682
|
});
|
|
@@ -176194,22 +176772,18 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
176194
176772
|
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
176195
176773
|
});
|
|
176196
176774
|
}
|
|
176197
|
-
await this.
|
|
176198
|
-
|
|
176199
|
-
|
|
176200
|
-
|
|
176775
|
+
if (!(this.isBattery ? await this.getBatteryRefresh().noteAwake().catch(() => false) : false)) {
|
|
176776
|
+
await this.warmCapSlicesOnWake();
|
|
176777
|
+
await this.warmSnapshotOnWake();
|
|
176778
|
+
await this.alignAuxDevicesState("wake");
|
|
176779
|
+
await this.refreshParentSettingsSnapshot();
|
|
176780
|
+
}
|
|
176201
176781
|
await this.refreshClipCatalogOnWake();
|
|
176202
176782
|
}
|
|
176203
176783
|
static PING_INTERVAL_MS = 3e4;
|
|
176204
176784
|
static PING_MAX_FAILURES = 3;
|
|
176205
176785
|
static EVENT_CHECK_INTERVAL_MS = 6e4;
|
|
176206
176786
|
static EVENT_STALE_THRESHOLD_MS = 10 * 6e4;
|
|
176207
|
-
/** Battery-cam periodic refresh cadence. The timer fires on this
|
|
176208
|
-
* interval, passively checks sleep state, and only runs the
|
|
176209
|
-
* refresh when the cam is already awake — so it never forces a
|
|
176210
|
-
* wake. Mirrors the reference's `batteryUpdateIntervalMinutes`
|
|
176211
|
-
* default (60 minutes). */
|
|
176212
|
-
static BATTERY_UPDATE_INTERVAL_MS = 60 * 6e4;
|
|
176213
176787
|
/**
|
|
176214
176788
|
* Arm the connection-liveness ping watchdog and the event-watchdog.
|
|
176215
176789
|
* Battery cams skip the ping watchdog — they keep `setIdleDisconnect=true`
|
|
@@ -177393,7 +177967,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
177393
177967
|
...buildClipRefreshDeviceSection({
|
|
177394
177968
|
isHubChild: this.isHubChild(),
|
|
177395
177969
|
isBattery: this.isBattery
|
|
177396
|
-
})
|
|
177970
|
+
}),
|
|
177971
|
+
...buildBatteryRefreshDeviceSection({ isBattery: this.isBattery })
|
|
177397
177972
|
]
|
|
177398
177973
|
};
|
|
177399
177974
|
const emailSnap = cache?.emailConfigSnapshot;
|
|
@@ -177414,6 +177989,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
177414
177989
|
emailScheduleEnabled: emailSnap?.taskEnabled ?? false,
|
|
177415
177990
|
clipCatalogRefreshMinutes: this.config.get("clipCatalogRefreshMinutes"),
|
|
177416
177991
|
clipCatalogForcedWake: this.config.get("clipCatalogForcedWake") ?? false,
|
|
177992
|
+
batteryRefreshMinutes: this.config.get("batteryRefreshMinutes"),
|
|
177417
177993
|
debugGeneral: this.config.get("debugGeneral") ?? false,
|
|
177418
177994
|
debugSocketLogs: this.config.get("debugSocketLogs") ?? [],
|
|
177419
177995
|
motionEnabled: this.config.get("motionEnabled") ?? motionSnap.enabled ?? true,
|