@camstack/addon-provider-dreame 0.2.74 → 0.2.76
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 +124 -34
- package/dist/addon.mjs +124 -34
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -60522,17 +60522,29 @@ var HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
|
60522
60522
|
/**
|
|
60523
60523
|
* DERIVED per-camera storage summary — the single field cheap consumers read
|
|
60524
60524
|
* (the viewer's status dot, the camera list) instead of walking `bands`:
|
|
60525
|
-
* - `off`
|
|
60526
|
-
* - `events`
|
|
60527
|
-
* - `continuous`
|
|
60528
|
-
*
|
|
60529
|
-
*
|
|
60530
|
-
*
|
|
60525
|
+
* - `off` — no band covers the camera (or it is disabled).
|
|
60526
|
+
* - `events` — every band records around triggers only.
|
|
60527
|
+
* - `continuous` — at least one band records continuously.
|
|
60528
|
+
* - `on-device-decision`— the DEVICE decides: recording runs for as long as the
|
|
60529
|
+
* camera raises its own `recording-signal` level (a robot that cleans). There
|
|
60530
|
+
* is no schedule to author, because there is no hour to program — see
|
|
60531
|
+
* {@link RecordingConfigSchema}`.deviceDecision`.
|
|
60532
|
+
*
|
|
60533
|
+
* NEVER authored: the recorder stamps it from the authoritative intent
|
|
60534
|
+
* (`bands` + `deviceDecision`) on every save (`activeModeForConfig`). Writing it
|
|
60535
|
+
* has no effect.
|
|
60536
|
+
*
|
|
60537
|
+
* `on-device-decision` is named for WHO decides, not for how the recording is
|
|
60538
|
+
* requested. `on-demand` was rejected: in this repo's vocabulary a "demand" is
|
|
60539
|
+
* something the operator makes (the live gate is the recorder's own "demand
|
|
60540
|
+
* window"), and a knob whose name suggests the operator starts it while the
|
|
60541
|
+
* device actually does is the D62 shape — a control nobody can predict.
|
|
60531
60542
|
*/
|
|
60532
60543
|
var RecordingStorageModeSchema = _enum([
|
|
60533
60544
|
"off",
|
|
60534
60545
|
"events",
|
|
60535
|
-
"continuous"
|
|
60546
|
+
"continuous",
|
|
60547
|
+
"on-device-decision"
|
|
60536
60548
|
]);
|
|
60537
60549
|
/**
|
|
60538
60550
|
* Le macro classi che possono aprire una finestra di registrazione — le stesse
|
|
@@ -60582,22 +60594,7 @@ var RecordingTriggersSchema = object({
|
|
|
60582
60594
|
* il livello: un contatto trovato già aperto al riavvio del runner non fa
|
|
60583
60595
|
* registrare.
|
|
60584
60596
|
*/
|
|
60585
|
-
sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
|
|
60586
|
-
/**
|
|
60587
|
-
* La camera registra per tutta la durata del SEGNALE che lei stessa
|
|
60588
|
-
* pubblica — la cap `recording-signal` (`active: true` mentre il device è in
|
|
60589
|
-
* funzione: un robot che pulisce). È un LIVELLO, non un fronte: la finestra
|
|
60590
|
-
* resta aperta finché il segnale è alto e si chiude `postBufferSec` dopo la
|
|
60591
|
-
* caduta, così una pulizia di quaranta minuti è UNA clip.
|
|
60592
|
-
*
|
|
60593
|
-
* Non nomina un device: il segnale è della camera stessa, tenuto fresco dal
|
|
60594
|
-
* suo provider (D224); il recorder lo ascolta su `DeviceStateChanged` e lo
|
|
60595
|
-
* riconcilia leggendo la slice via RPC, con un tetto oltre il quale un
|
|
60596
|
-
* segnale mai abbassato (un evento perso) non può tenere aperta una
|
|
60597
|
-
* registrazione (D11). Vedi `recorder/addon/band-decision.ts`
|
|
60598
|
-
* (`holdTrigger` / `releaseTrigger`).
|
|
60599
|
-
*/
|
|
60600
|
-
deviceSignal: boolean().optional()
|
|
60597
|
+
sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
|
|
60601
60598
|
});
|
|
60602
60599
|
/**
|
|
60603
60600
|
* Mode of a single recording band — the recorder per-band vocabulary.
|
|
@@ -60681,8 +60678,29 @@ var RecordingConfigSchema = object({
|
|
|
60681
60678
|
* "off" is the absence of a covering band, never a band value.
|
|
60682
60679
|
*/
|
|
60683
60680
|
bands: array(RecordingBandSchema).default([]),
|
|
60681
|
+
/**
|
|
60682
|
+
* THE device-decided intent: record for as long as the camera itself raises
|
|
60683
|
+
* `recording-signal` (`active: true` while the device is in function — a
|
|
60684
|
+
* robot that cleans). AUTHORED, unlike `mode`, and the only authored
|
|
60685
|
+
* recording intent that is not a band.
|
|
60686
|
+
*
|
|
60687
|
+
* It carries NO schedule on purpose. A band is an HOUR, and this feature has
|
|
60688
|
+
* no hour to program: the device decides. What it costs the operator is one
|
|
60689
|
+
* flag — "this camera can record on its own" — and what it buys is the same
|
|
60690
|
+
* hold/release the recorder already implements (D371, `holdTrigger` /
|
|
60691
|
+
* `releaseTrigger`): the window opens on the rise, stays open for the whole
|
|
60692
|
+
* job however long it is, and closes one pad after the fall.
|
|
60693
|
+
*
|
|
60694
|
+
* EXCLUSIVE with `bands` (below): two authorities deciding when the same
|
|
60695
|
+
* camera records is the D62 failure. `off` remains `enabled: false`, so an
|
|
60696
|
+
* operator switching this camera off is REPORTED off, never as broken.
|
|
60697
|
+
*/
|
|
60698
|
+
deviceDecision: boolean().optional(),
|
|
60684
60699
|
retention: RecordingRetentionSchema.optional()
|
|
60685
|
-
}).strict()
|
|
60700
|
+
}).strict().refine((config) => config.deviceDecision !== true || config.bands.length === 0, {
|
|
60701
|
+
message: "deviceDecision is a whole-config mode and carries no schedule: it cannot be combined with bands",
|
|
60702
|
+
path: ["bands"]
|
|
60703
|
+
});
|
|
60686
60704
|
/**
|
|
60687
60705
|
* Entity-relocation job state (storage entity-routing spec, Phase 4).
|
|
60688
60706
|
*
|
|
@@ -73715,11 +73733,7 @@ var CameraAudioStatusSchema = object({
|
|
|
73715
73733
|
});
|
|
73716
73734
|
/** Recording block — null when no recording cap is active for this device. */
|
|
73717
73735
|
var CameraRecordingStatusSchema = object({
|
|
73718
|
-
mode:
|
|
73719
|
-
"off",
|
|
73720
|
-
"continuous",
|
|
73721
|
-
"events"
|
|
73722
|
-
]),
|
|
73736
|
+
mode: RecordingStorageModeSchema,
|
|
73723
73737
|
active: boolean(),
|
|
73724
73738
|
storageBytes: number()
|
|
73725
73739
|
});
|
|
@@ -82146,11 +82160,10 @@ DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceI
|
|
|
82146
82160
|
var RecordingStatusSchema = object({
|
|
82147
82161
|
deviceId: number(),
|
|
82148
82162
|
enabled: boolean(),
|
|
82149
|
-
|
|
82150
|
-
|
|
82151
|
-
|
|
82152
|
-
|
|
82153
|
-
]),
|
|
82163
|
+
/** THE derived storage mode, from the one definition
|
|
82164
|
+
* (`deriveRecordingMode`) — never a second enum. A duplicated list is how
|
|
82165
|
+
* `on-device-decision` could have reached the recorder and not the status. */
|
|
82166
|
+
activeMode: RecordingStorageModeSchema,
|
|
82154
82167
|
nodeId: string(),
|
|
82155
82168
|
storageBytes: number()
|
|
82156
82169
|
});
|
|
@@ -96799,6 +96812,16 @@ var dreameChildSchema = object({
|
|
|
96799
96812
|
navigationDisabledEntryIds: array(string()).optional(),
|
|
96800
96813
|
/** Privacy PIN set at pairing — required to wake a coded camera (camera child only). */
|
|
96801
96814
|
navigationAccessCode: string().optional(),
|
|
96815
|
+
/**
|
|
96816
|
+
* ms-epoch of the ONE-SHOT recording seed (camera child only, D380).
|
|
96817
|
+
*
|
|
96818
|
+
* A MARK, never the value: it says this camera was seeded, which stays true
|
|
96819
|
+
* after the operator switches recording off — and an operator's `off` is
|
|
96820
|
+
* byte-identical to a camera that was never seeded (`enabled: false,
|
|
96821
|
+
* bands: []`), so nothing but this field can tell the two apart. The
|
|
96822
|
+
* authority for what is recorded remains `RecordingConfig` (D62).
|
|
96823
|
+
*/
|
|
96824
|
+
recordingSeededAt: number().optional(),
|
|
96802
96825
|
mapShowNoGo: boolean().optional(),
|
|
96803
96826
|
mapShowVirtualWalls: boolean().optional(),
|
|
96804
96827
|
mapShowObstacles: boolean().optional(),
|
|
@@ -97849,6 +97872,51 @@ var DreameNetworkLinkMonitor = class {
|
|
|
97849
97872
|
}
|
|
97850
97873
|
};
|
|
97851
97874
|
//#endregion
|
|
97875
|
+
//#region src/devices/dreame-recording-seed.ts
|
|
97876
|
+
/**
|
|
97877
|
+
* THE config the provider seeds: recording ON, decided by the device.
|
|
97878
|
+
*
|
|
97879
|
+
* No bands (the mode carries none, D380) and no profiles/retention: those are
|
|
97880
|
+
* cluster-wide defaults the recorder already resolves, and pinning them here
|
|
97881
|
+
* would be the provider deciding storage policy for a camera it does not own.
|
|
97882
|
+
*/
|
|
97883
|
+
var DEVICE_DECISION_SEED_CONFIG = Object.freeze({
|
|
97884
|
+
enabled: true,
|
|
97885
|
+
deviceDecision: true,
|
|
97886
|
+
bands: []
|
|
97887
|
+
});
|
|
97888
|
+
/**
|
|
97889
|
+
* Seed `on-device-decision` recording for this camera, once, ever.
|
|
97890
|
+
*
|
|
97891
|
+
* Never throws: it runs on the activation path and a camera that fails to be
|
|
97892
|
+
* seeded must still come up. Every branch that does not write says why — a
|
|
97893
|
+
* silent skip here is indistinguishable from the feature not existing.
|
|
97894
|
+
*/
|
|
97895
|
+
async function seedDeviceDecisionRecording(deps) {
|
|
97896
|
+
const { deviceId, logger } = deps;
|
|
97897
|
+
if (deps.alreadySeeded) return "already-seeded";
|
|
97898
|
+
try {
|
|
97899
|
+
await deps.writeConfig(DEVICE_DECISION_SEED_CONFIG);
|
|
97900
|
+
} catch (err) {
|
|
97901
|
+
logger.warn("robot camera: could not seed on-device-decision recording — retried at the next start", {
|
|
97902
|
+
tags: { deviceId },
|
|
97903
|
+
meta: { error: errMsg(err) }
|
|
97904
|
+
});
|
|
97905
|
+
return "failed";
|
|
97906
|
+
}
|
|
97907
|
+
try {
|
|
97908
|
+
await deps.markSeeded();
|
|
97909
|
+
} catch (err) {
|
|
97910
|
+
logger.warn("robot camera: recording seeded but the one-shot mark could not be persisted — a restart may seed it again and override an operator who switched it off", {
|
|
97911
|
+
tags: { deviceId },
|
|
97912
|
+
meta: { error: errMsg(err) }
|
|
97913
|
+
});
|
|
97914
|
+
return "seeded";
|
|
97915
|
+
}
|
|
97916
|
+
logger.info("robot camera: seeded on-device-decision recording (once, at first start)", { tags: { deviceId } });
|
|
97917
|
+
return "seeded";
|
|
97918
|
+
}
|
|
97919
|
+
//#endregion
|
|
97852
97920
|
//#region src/devices/dreame-recording-signal.ts
|
|
97853
97921
|
/** Vacuum lifecycle states during which the camera asks to be recorded. */
|
|
97854
97922
|
var ACTIVE_STATES = new Set(["cleaning", "returning"]);
|
|
@@ -143563,6 +143631,28 @@ var DreameCameraDevice = class extends DreameChildDevice {
|
|
|
143563
143631
|
this.networkLink.start();
|
|
143564
143632
|
this.registerStreamCatalogCap();
|
|
143565
143633
|
this.registerRecordingSignalCap();
|
|
143634
|
+
this.seedRecordingConfig();
|
|
143635
|
+
}
|
|
143636
|
+
/**
|
|
143637
|
+
* Seed `on-device-decision` recording the FIRST time this camera comes up.
|
|
143638
|
+
*
|
|
143639
|
+
* Gated on the durable mark and NOT on the current config: an operator who
|
|
143640
|
+
* switched recording off leaves a config that is byte-identical to one that
|
|
143641
|
+
* was never seeded, so reading the value would re-enable it at every restart.
|
|
143642
|
+
* The value stays the recorder's (D62) — this writes a default through the
|
|
143643
|
+
* same cap method an operator uses and keeps no copy of it.
|
|
143644
|
+
*/
|
|
143645
|
+
async seedRecordingConfig() {
|
|
143646
|
+
await seedDeviceDecisionRecording({
|
|
143647
|
+
deviceId: this.id,
|
|
143648
|
+
alreadySeeded: this.config.get("recordingSeededAt") !== void 0,
|
|
143649
|
+
writeConfig: (config) => this.ctx.api.recording.setDeviceConfig.mutate({
|
|
143650
|
+
deviceId: this.id,
|
|
143651
|
+
config
|
|
143652
|
+
}),
|
|
143653
|
+
markSeeded: () => this.config.set("recordingSeededAt", Date.now()),
|
|
143654
|
+
logger: this.ctx.logger
|
|
143655
|
+
});
|
|
143566
143656
|
}
|
|
143567
143657
|
/**
|
|
143568
143658
|
* The camera's own "record me" LEVEL, derived from the vacuum lifecycle
|
package/dist/addon.mjs
CHANGED
|
@@ -60519,17 +60519,29 @@ var HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
|
60519
60519
|
/**
|
|
60520
60520
|
* DERIVED per-camera storage summary — the single field cheap consumers read
|
|
60521
60521
|
* (the viewer's status dot, the camera list) instead of walking `bands`:
|
|
60522
|
-
* - `off`
|
|
60523
|
-
* - `events`
|
|
60524
|
-
* - `continuous`
|
|
60525
|
-
*
|
|
60526
|
-
*
|
|
60527
|
-
*
|
|
60522
|
+
* - `off` — no band covers the camera (or it is disabled).
|
|
60523
|
+
* - `events` — every band records around triggers only.
|
|
60524
|
+
* - `continuous` — at least one band records continuously.
|
|
60525
|
+
* - `on-device-decision`— the DEVICE decides: recording runs for as long as the
|
|
60526
|
+
* camera raises its own `recording-signal` level (a robot that cleans). There
|
|
60527
|
+
* is no schedule to author, because there is no hour to program — see
|
|
60528
|
+
* {@link RecordingConfigSchema}`.deviceDecision`.
|
|
60529
|
+
*
|
|
60530
|
+
* NEVER authored: the recorder stamps it from the authoritative intent
|
|
60531
|
+
* (`bands` + `deviceDecision`) on every save (`activeModeForConfig`). Writing it
|
|
60532
|
+
* has no effect.
|
|
60533
|
+
*
|
|
60534
|
+
* `on-device-decision` is named for WHO decides, not for how the recording is
|
|
60535
|
+
* requested. `on-demand` was rejected: in this repo's vocabulary a "demand" is
|
|
60536
|
+
* something the operator makes (the live gate is the recorder's own "demand
|
|
60537
|
+
* window"), and a knob whose name suggests the operator starts it while the
|
|
60538
|
+
* device actually does is the D62 shape — a control nobody can predict.
|
|
60528
60539
|
*/
|
|
60529
60540
|
var RecordingStorageModeSchema = _enum([
|
|
60530
60541
|
"off",
|
|
60531
60542
|
"events",
|
|
60532
|
-
"continuous"
|
|
60543
|
+
"continuous",
|
|
60544
|
+
"on-device-decision"
|
|
60533
60545
|
]);
|
|
60534
60546
|
/**
|
|
60535
60547
|
* Le macro classi che possono aprire una finestra di registrazione — le stesse
|
|
@@ -60579,22 +60591,7 @@ var RecordingTriggersSchema = object({
|
|
|
60579
60591
|
* il livello: un contatto trovato già aperto al riavvio del runner non fa
|
|
60580
60592
|
* registrare.
|
|
60581
60593
|
*/
|
|
60582
|
-
sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
|
|
60583
|
-
/**
|
|
60584
|
-
* La camera registra per tutta la durata del SEGNALE che lei stessa
|
|
60585
|
-
* pubblica — la cap `recording-signal` (`active: true` mentre il device è in
|
|
60586
|
-
* funzione: un robot che pulisce). È un LIVELLO, non un fronte: la finestra
|
|
60587
|
-
* resta aperta finché il segnale è alto e si chiude `postBufferSec` dopo la
|
|
60588
|
-
* caduta, così una pulizia di quaranta minuti è UNA clip.
|
|
60589
|
-
*
|
|
60590
|
-
* Non nomina un device: il segnale è della camera stessa, tenuto fresco dal
|
|
60591
|
-
* suo provider (D224); il recorder lo ascolta su `DeviceStateChanged` e lo
|
|
60592
|
-
* riconcilia leggendo la slice via RPC, con un tetto oltre il quale un
|
|
60593
|
-
* segnale mai abbassato (un evento perso) non può tenere aperta una
|
|
60594
|
-
* registrazione (D11). Vedi `recorder/addon/band-decision.ts`
|
|
60595
|
-
* (`holdTrigger` / `releaseTrigger`).
|
|
60596
|
-
*/
|
|
60597
|
-
deviceSignal: boolean().optional()
|
|
60594
|
+
sensorDeviceIds: array(number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
|
|
60598
60595
|
});
|
|
60599
60596
|
/**
|
|
60600
60597
|
* Mode of a single recording band — the recorder per-band vocabulary.
|
|
@@ -60678,8 +60675,29 @@ var RecordingConfigSchema = object({
|
|
|
60678
60675
|
* "off" is the absence of a covering band, never a band value.
|
|
60679
60676
|
*/
|
|
60680
60677
|
bands: array(RecordingBandSchema).default([]),
|
|
60678
|
+
/**
|
|
60679
|
+
* THE device-decided intent: record for as long as the camera itself raises
|
|
60680
|
+
* `recording-signal` (`active: true` while the device is in function — a
|
|
60681
|
+
* robot that cleans). AUTHORED, unlike `mode`, and the only authored
|
|
60682
|
+
* recording intent that is not a band.
|
|
60683
|
+
*
|
|
60684
|
+
* It carries NO schedule on purpose. A band is an HOUR, and this feature has
|
|
60685
|
+
* no hour to program: the device decides. What it costs the operator is one
|
|
60686
|
+
* flag — "this camera can record on its own" — and what it buys is the same
|
|
60687
|
+
* hold/release the recorder already implements (D371, `holdTrigger` /
|
|
60688
|
+
* `releaseTrigger`): the window opens on the rise, stays open for the whole
|
|
60689
|
+
* job however long it is, and closes one pad after the fall.
|
|
60690
|
+
*
|
|
60691
|
+
* EXCLUSIVE with `bands` (below): two authorities deciding when the same
|
|
60692
|
+
* camera records is the D62 failure. `off` remains `enabled: false`, so an
|
|
60693
|
+
* operator switching this camera off is REPORTED off, never as broken.
|
|
60694
|
+
*/
|
|
60695
|
+
deviceDecision: boolean().optional(),
|
|
60681
60696
|
retention: RecordingRetentionSchema.optional()
|
|
60682
|
-
}).strict()
|
|
60697
|
+
}).strict().refine((config) => config.deviceDecision !== true || config.bands.length === 0, {
|
|
60698
|
+
message: "deviceDecision is a whole-config mode and carries no schedule: it cannot be combined with bands",
|
|
60699
|
+
path: ["bands"]
|
|
60700
|
+
});
|
|
60683
60701
|
/**
|
|
60684
60702
|
* Entity-relocation job state (storage entity-routing spec, Phase 4).
|
|
60685
60703
|
*
|
|
@@ -73712,11 +73730,7 @@ var CameraAudioStatusSchema = object({
|
|
|
73712
73730
|
});
|
|
73713
73731
|
/** Recording block — null when no recording cap is active for this device. */
|
|
73714
73732
|
var CameraRecordingStatusSchema = object({
|
|
73715
|
-
mode:
|
|
73716
|
-
"off",
|
|
73717
|
-
"continuous",
|
|
73718
|
-
"events"
|
|
73719
|
-
]),
|
|
73733
|
+
mode: RecordingStorageModeSchema,
|
|
73720
73734
|
active: boolean(),
|
|
73721
73735
|
storageBytes: number()
|
|
73722
73736
|
});
|
|
@@ -82143,11 +82157,10 @@ DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceI
|
|
|
82143
82157
|
var RecordingStatusSchema = object({
|
|
82144
82158
|
deviceId: number(),
|
|
82145
82159
|
enabled: boolean(),
|
|
82146
|
-
|
|
82147
|
-
|
|
82148
|
-
|
|
82149
|
-
|
|
82150
|
-
]),
|
|
82160
|
+
/** THE derived storage mode, from the one definition
|
|
82161
|
+
* (`deriveRecordingMode`) — never a second enum. A duplicated list is how
|
|
82162
|
+
* `on-device-decision` could have reached the recorder and not the status. */
|
|
82163
|
+
activeMode: RecordingStorageModeSchema,
|
|
82151
82164
|
nodeId: string(),
|
|
82152
82165
|
storageBytes: number()
|
|
82153
82166
|
});
|
|
@@ -96796,6 +96809,16 @@ var dreameChildSchema = object({
|
|
|
96796
96809
|
navigationDisabledEntryIds: array(string()).optional(),
|
|
96797
96810
|
/** Privacy PIN set at pairing — required to wake a coded camera (camera child only). */
|
|
96798
96811
|
navigationAccessCode: string().optional(),
|
|
96812
|
+
/**
|
|
96813
|
+
* ms-epoch of the ONE-SHOT recording seed (camera child only, D380).
|
|
96814
|
+
*
|
|
96815
|
+
* A MARK, never the value: it says this camera was seeded, which stays true
|
|
96816
|
+
* after the operator switches recording off — and an operator's `off` is
|
|
96817
|
+
* byte-identical to a camera that was never seeded (`enabled: false,
|
|
96818
|
+
* bands: []`), so nothing but this field can tell the two apart. The
|
|
96819
|
+
* authority for what is recorded remains `RecordingConfig` (D62).
|
|
96820
|
+
*/
|
|
96821
|
+
recordingSeededAt: number().optional(),
|
|
96799
96822
|
mapShowNoGo: boolean().optional(),
|
|
96800
96823
|
mapShowVirtualWalls: boolean().optional(),
|
|
96801
96824
|
mapShowObstacles: boolean().optional(),
|
|
@@ -97846,6 +97869,51 @@ var DreameNetworkLinkMonitor = class {
|
|
|
97846
97869
|
}
|
|
97847
97870
|
};
|
|
97848
97871
|
//#endregion
|
|
97872
|
+
//#region src/devices/dreame-recording-seed.ts
|
|
97873
|
+
/**
|
|
97874
|
+
* THE config the provider seeds: recording ON, decided by the device.
|
|
97875
|
+
*
|
|
97876
|
+
* No bands (the mode carries none, D380) and no profiles/retention: those are
|
|
97877
|
+
* cluster-wide defaults the recorder already resolves, and pinning them here
|
|
97878
|
+
* would be the provider deciding storage policy for a camera it does not own.
|
|
97879
|
+
*/
|
|
97880
|
+
var DEVICE_DECISION_SEED_CONFIG = Object.freeze({
|
|
97881
|
+
enabled: true,
|
|
97882
|
+
deviceDecision: true,
|
|
97883
|
+
bands: []
|
|
97884
|
+
});
|
|
97885
|
+
/**
|
|
97886
|
+
* Seed `on-device-decision` recording for this camera, once, ever.
|
|
97887
|
+
*
|
|
97888
|
+
* Never throws: it runs on the activation path and a camera that fails to be
|
|
97889
|
+
* seeded must still come up. Every branch that does not write says why — a
|
|
97890
|
+
* silent skip here is indistinguishable from the feature not existing.
|
|
97891
|
+
*/
|
|
97892
|
+
async function seedDeviceDecisionRecording(deps) {
|
|
97893
|
+
const { deviceId, logger } = deps;
|
|
97894
|
+
if (deps.alreadySeeded) return "already-seeded";
|
|
97895
|
+
try {
|
|
97896
|
+
await deps.writeConfig(DEVICE_DECISION_SEED_CONFIG);
|
|
97897
|
+
} catch (err) {
|
|
97898
|
+
logger.warn("robot camera: could not seed on-device-decision recording — retried at the next start", {
|
|
97899
|
+
tags: { deviceId },
|
|
97900
|
+
meta: { error: errMsg(err) }
|
|
97901
|
+
});
|
|
97902
|
+
return "failed";
|
|
97903
|
+
}
|
|
97904
|
+
try {
|
|
97905
|
+
await deps.markSeeded();
|
|
97906
|
+
} catch (err) {
|
|
97907
|
+
logger.warn("robot camera: recording seeded but the one-shot mark could not be persisted — a restart may seed it again and override an operator who switched it off", {
|
|
97908
|
+
tags: { deviceId },
|
|
97909
|
+
meta: { error: errMsg(err) }
|
|
97910
|
+
});
|
|
97911
|
+
return "seeded";
|
|
97912
|
+
}
|
|
97913
|
+
logger.info("robot camera: seeded on-device-decision recording (once, at first start)", { tags: { deviceId } });
|
|
97914
|
+
return "seeded";
|
|
97915
|
+
}
|
|
97916
|
+
//#endregion
|
|
97849
97917
|
//#region src/devices/dreame-recording-signal.ts
|
|
97850
97918
|
/** Vacuum lifecycle states during which the camera asks to be recorded. */
|
|
97851
97919
|
var ACTIVE_STATES = new Set(["cleaning", "returning"]);
|
|
@@ -143560,6 +143628,28 @@ var DreameCameraDevice = class extends DreameChildDevice {
|
|
|
143560
143628
|
this.networkLink.start();
|
|
143561
143629
|
this.registerStreamCatalogCap();
|
|
143562
143630
|
this.registerRecordingSignalCap();
|
|
143631
|
+
this.seedRecordingConfig();
|
|
143632
|
+
}
|
|
143633
|
+
/**
|
|
143634
|
+
* Seed `on-device-decision` recording the FIRST time this camera comes up.
|
|
143635
|
+
*
|
|
143636
|
+
* Gated on the durable mark and NOT on the current config: an operator who
|
|
143637
|
+
* switched recording off leaves a config that is byte-identical to one that
|
|
143638
|
+
* was never seeded, so reading the value would re-enable it at every restart.
|
|
143639
|
+
* The value stays the recorder's (D62) — this writes a default through the
|
|
143640
|
+
* same cap method an operator uses and keeps no copy of it.
|
|
143641
|
+
*/
|
|
143642
|
+
async seedRecordingConfig() {
|
|
143643
|
+
await seedDeviceDecisionRecording({
|
|
143644
|
+
deviceId: this.id,
|
|
143645
|
+
alreadySeeded: this.config.get("recordingSeededAt") !== void 0,
|
|
143646
|
+
writeConfig: (config) => this.ctx.api.recording.setDeviceConfig.mutate({
|
|
143647
|
+
deviceId: this.id,
|
|
143648
|
+
config
|
|
143649
|
+
}),
|
|
143650
|
+
markSeeded: () => this.config.set("recordingSeededAt", Date.now()),
|
|
143651
|
+
logger: this.ctx.logger
|
|
143652
|
+
});
|
|
143563
143653
|
}
|
|
143564
143654
|
/**
|
|
143565
143655
|
* The camera's own "record me" LEVEL, derived from the vacuum lifecycle
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-dreame",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.76",
|
|
4
4
|
"description": "Dreame robot-vacuum / lawn-mower device-provider addon for CamStack — wraps the @apocaliss92/nodedreame Dreamehome cloud client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|