@camstack/addon-pipeline-orchestrator 1.2.27 → 1.2.28
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/_stub.js +2 -2
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-DZurdE2f.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-Myx4bNgp.mjs} +2 -2
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-LWwgi5Gh.mjs +26 -0
- package/dist/{_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CkysRyul.mjs → _virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DuE5H5lK.mjs} +1 -1
- package/dist/{hostInit-Bz4OrWey.mjs → hostInit-BVCY0SPK.mjs} +2 -2
- package/dist/index.js +423 -17
- package/dist/index.mjs +423 -17
- package/dist/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CA_bQsJB.mjs +0 -26
package/dist/index.mjs
CHANGED
|
@@ -6490,7 +6490,20 @@ var BrokerStatsSchema = object({
|
|
|
6490
6490
|
sampleRate: number(),
|
|
6491
6491
|
channels: number(),
|
|
6492
6492
|
supported: boolean()
|
|
6493
|
-
}).nullable().optional()
|
|
6493
|
+
}).nullable().optional(),
|
|
6494
|
+
/**
|
|
6495
|
+
* BROKER-SIDE AUDIO MUTE (D83). `true` = this broker is deliberately
|
|
6496
|
+
* distributing none of the device's audio, on live or recording.
|
|
6497
|
+
*
|
|
6498
|
+
* Present so a silent camera can be told apart from a broken one on the
|
|
6499
|
+
* stream panel itself, without cross-referencing the switch group: a
|
|
6500
|
+
* broker holding an `audio` track descriptor while `audioMuted` is true is
|
|
6501
|
+
* working exactly as asked. `audioMutedDropped` counts the audio units
|
|
6502
|
+
* thrown away since the current dial — it is how you confirm from stats
|
|
6503
|
+
* alone that the mute is on the packet path and not merely persisted.
|
|
6504
|
+
*/
|
|
6505
|
+
audioMuted: boolean().optional(),
|
|
6506
|
+
audioMutedDropped: number().optional()
|
|
6494
6507
|
});
|
|
6495
6508
|
/**
|
|
6496
6509
|
* Exporter-facing "profile restream" entry. Returned by
|
|
@@ -7657,6 +7670,19 @@ object({
|
|
|
7657
7670
|
* | `notifications` | `notificationRules.setDeviceMuted` | `NotificationCenter.evaluateAndEnqueue` returns before any rule is evaluated |
|
|
7658
7671
|
* | `privacy-mask` | `privacyMask.setMask({ enabled })` → the CAMERA | the camera blanks the masked regions itself; every stream and recording carries the black boxes |
|
|
7659
7672
|
* | `device-audio` | `privacyMask.setAudioEnabled` → the CAMERA | the camera stops encoding an audio track at all; every consumer sees silent video |
|
|
7673
|
+
* | `broker-audio` | `streamBroker.setDeviceAudioMute` → `DeviceOverride.audioMuted` | `StreamBroker.setAudioMuted` drops the audio plane at the source: no `type:'audio'` packet leaves `fanOutEncoded`, no RTP reaches the restreamer, and the restreamer serves the video-only SDP |
|
|
7674
|
+
*
|
|
7675
|
+
* ## `device-audio` and `broker-audio` are two functions, not two knobs
|
|
7676
|
+
*
|
|
7677
|
+
* They look adjacent and they are not the same control ([D83](../../../../docs/decisions/adr-0083.md)):
|
|
7678
|
+
* `device-audio` writes the CAMERA, so it is hardware privacy — the microphone
|
|
7679
|
+
* genuinely stops, it survives CamStack entirely, and it costs a multi-second
|
|
7680
|
+
* encoder restart on every flip. `broker-audio` writes THIS server, so it is
|
|
7681
|
+
* instant, vendor-independent and reversible without touching the camera, and
|
|
7682
|
+
* a camera that ignores or lacks the ISAPI/Reolink control is still silenced.
|
|
7683
|
+
* D62 forbids a second switch that *disagrees* with the first; these two
|
|
7684
|
+
* cannot disagree, because neither reads the other's store — the camera holds
|
|
7685
|
+
* one, the broker holds the other, and each reports its own fact.
|
|
7660
7686
|
*
|
|
7661
7687
|
* ## The two switches whose authority is not on this server
|
|
7662
7688
|
*
|
|
@@ -7718,6 +7744,7 @@ var CameraSwitchIdSchema = _enum([
|
|
|
7718
7744
|
"object-detection",
|
|
7719
7745
|
"privacy-mask",
|
|
7720
7746
|
"device-audio",
|
|
7747
|
+
"broker-audio",
|
|
7721
7748
|
"audio-analysis",
|
|
7722
7749
|
"recording",
|
|
7723
7750
|
"notifications"
|
|
@@ -7733,12 +7760,17 @@ var CameraSwitchIdSchema = _enum([
|
|
|
7733
7760
|
* `object-detection` despite feeding it: a mask blanks REGIONS, so its blast
|
|
7734
7761
|
* radius is partial, and the "broadest first" rule does not rank a partial
|
|
7735
7762
|
* control above a whole-function one.
|
|
7763
|
+
*
|
|
7764
|
+
* `broker-audio` sits directly BELOW `device-audio` by the same source-first
|
|
7765
|
+
* rule: the camera's microphone feeds the broker, so silencing the camera
|
|
7766
|
+
* leaves the broker's mute with nothing to suppress; the reverse is not true.
|
|
7736
7767
|
*/
|
|
7737
7768
|
var CAMERA_SWITCH_ORDER = [
|
|
7738
7769
|
"stream-broker",
|
|
7739
7770
|
"object-detection",
|
|
7740
7771
|
"privacy-mask",
|
|
7741
7772
|
"device-audio",
|
|
7773
|
+
"broker-audio",
|
|
7742
7774
|
"audio-analysis",
|
|
7743
7775
|
"recording",
|
|
7744
7776
|
"notifications"
|
|
@@ -7764,7 +7796,8 @@ var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
|
|
|
7764
7796
|
object({
|
|
7765
7797
|
kind: literal("camera-mask"),
|
|
7766
7798
|
capName: string()
|
|
7767
|
-
})
|
|
7799
|
+
}),
|
|
7800
|
+
object({ kind: literal("broker-audio-mute") })
|
|
7768
7801
|
]);
|
|
7769
7802
|
/**
|
|
7770
7803
|
* Why a switch is not offered for this camera. Rendered instead of the
|
|
@@ -7863,6 +7896,13 @@ var CAMERA_SWITCH_CATALOG = {
|
|
|
7863
7896
|
},
|
|
7864
7897
|
countsAsSwitchedOff: true
|
|
7865
7898
|
},
|
|
7899
|
+
"broker-audio": {
|
|
7900
|
+
id: "broker-audio",
|
|
7901
|
+
label: "Audio distribution",
|
|
7902
|
+
costWhenOff: "Off: this server distributes no sound for this camera — live view is silent and everything recorded while it is off is silent FOREVER, even after you turn it back on. Audio detection and classification also have nothing to analyse. The camera keeps capturing sound, so nothing about the camera changes and turning this back on is instant, with no interruption to the picture.",
|
|
7903
|
+
authority: { kind: "broker-audio-mute" },
|
|
7904
|
+
countsAsSwitchedOff: true
|
|
7905
|
+
},
|
|
7866
7906
|
"audio-analysis": {
|
|
7867
7907
|
id: "audio-analysis",
|
|
7868
7908
|
label: "Audio detection & classification",
|
|
@@ -7981,6 +8021,18 @@ function resolveState(descriptor, input) {
|
|
|
7981
8021
|
enabled: mask.enabled
|
|
7982
8022
|
};
|
|
7983
8023
|
}
|
|
8024
|
+
case "broker-audio-mute": {
|
|
8025
|
+
const broker = input.brokerAudio;
|
|
8026
|
+
if (broker === null) return {
|
|
8027
|
+
available: false,
|
|
8028
|
+
enabled: true,
|
|
8029
|
+
unavailableReason: "source-unreachable"
|
|
8030
|
+
};
|
|
8031
|
+
return {
|
|
8032
|
+
available: true,
|
|
8033
|
+
enabled: !broker.muted
|
|
8034
|
+
};
|
|
8035
|
+
}
|
|
7984
8036
|
}
|
|
7985
8037
|
}
|
|
7986
8038
|
/**
|
|
@@ -10223,7 +10275,25 @@ method(object({
|
|
|
10223
10275
|
}), _void(), {
|
|
10224
10276
|
kind: "mutation",
|
|
10225
10277
|
auth: "admin"
|
|
10226
|
-
}), method(object({ brokerId: string() }), boolean()), object({
|
|
10278
|
+
}), method(object({ brokerId: string() }), boolean()), method(object({ deviceId: number().int() }), object({
|
|
10279
|
+
muted: boolean(),
|
|
10280
|
+
/**
|
|
10281
|
+
* How many live non-derived brokers currently hold the mute. Purely
|
|
10282
|
+
* diagnostic: `muted` is the policy and is authoritative on its own
|
|
10283
|
+
* (it applies to brokers that do not exist yet), while this says
|
|
10284
|
+
* whether anything is presently being silenced.
|
|
10285
|
+
*/
|
|
10286
|
+
appliedBrokers: number().int().nonnegative()
|
|
10287
|
+
})), method(object({
|
|
10288
|
+
deviceId: number().int(),
|
|
10289
|
+
muted: boolean()
|
|
10290
|
+
}), object({
|
|
10291
|
+
muted: boolean(),
|
|
10292
|
+
appliedBrokers: number().int().nonnegative()
|
|
10293
|
+
}), {
|
|
10294
|
+
kind: "mutation",
|
|
10295
|
+
auth: "admin"
|
|
10296
|
+
}), object({
|
|
10227
10297
|
deviceId: number().int().nonnegative(),
|
|
10228
10298
|
camStreamId: string(),
|
|
10229
10299
|
profile: CamProfileSchema
|
|
@@ -15487,6 +15557,30 @@ var TrackSourceSchema = _enum([
|
|
|
15487
15557
|
"audio"
|
|
15488
15558
|
]);
|
|
15489
15559
|
/**
|
|
15560
|
+
* Where a track sits in the RETRAIN lifecycle (D81).
|
|
15561
|
+
*
|
|
15562
|
+
* - `none` — never marked, or un-marked. Evictable.
|
|
15563
|
+
* - `staging` — the operator wants this track as training material and has not
|
|
15564
|
+
* finished with it. **This is the only state retention holds**: the track and
|
|
15565
|
+
* everything it owns (object events, crops, keyframes, CLIP vector) survive
|
|
15566
|
+
* the device's age window.
|
|
15567
|
+
* - `trained` — the retrain page has taken what it needed. The frames it chose
|
|
15568
|
+
* were COPIED into the retrain dataset at selection time, so the dataset no
|
|
15569
|
+
* longer depends on the track's media and the track becomes EVICTABLE again.
|
|
15570
|
+
* Terminal for the plain `markForTrain` toggle: returning it to `staging` is
|
|
15571
|
+
* a deliberate action of the retrain page, not a side effect of a checkbox.
|
|
15572
|
+
*
|
|
15573
|
+
* There is no `null`. The state is stored `TEXT NOT NULL DEFAULT 'none'` because
|
|
15574
|
+
* the store's filter language has only positive equality and `whereIn` — no
|
|
15575
|
+
* negation, no IS NULL — so a NULL would be unselectable by ANY predicate and
|
|
15576
|
+
* would make the entire pre-column history immortal in one deploy.
|
|
15577
|
+
*/
|
|
15578
|
+
var RetrainStatusSchema = _enum([
|
|
15579
|
+
"none",
|
|
15580
|
+
"staging",
|
|
15581
|
+
"trained"
|
|
15582
|
+
]);
|
|
15583
|
+
/**
|
|
15490
15584
|
* Per-track OPERATOR flags — set by hand from the admin UI or the viewer, never
|
|
15491
15585
|
* by the pipeline. Spread into `TrackSchema` and `KeyEventSchema` from one place
|
|
15492
15586
|
* so the two surfaces cannot drift.
|
|
@@ -15496,18 +15590,31 @@ var TrackSourceSchema = _enum([
|
|
|
15496
15590
|
* columns existed read as absent, and a consumer that needs a boolean should say
|
|
15497
15591
|
* `flag === true`, not `flag !== false`.
|
|
15498
15592
|
*
|
|
15499
|
-
*
|
|
15500
|
-
*
|
|
15501
|
-
*
|
|
15502
|
-
* `
|
|
15593
|
+
* `markForTrain` is the WIRE FACE of {@link RetrainStatusSchema}, not a column:
|
|
15594
|
+
* it is exactly `retrainStatus === 'staging'`, in both directions. Writing
|
|
15595
|
+
* `true` moves `none → staging`, writing `false` moves `staging → none`, and a
|
|
15596
|
+
* `trained` track reports `false` while refusing both writes. The boolean is
|
|
15597
|
+
* kept because three surfaces drive a toggle off it; anything that needs to tell
|
|
15598
|
+
* "never marked" from "already trained" must read `retrainStatus`.
|
|
15599
|
+
*
|
|
15600
|
+
* `debug` does NOT pin; it is attention, not durability.
|
|
15503
15601
|
*/
|
|
15504
15602
|
var TrackFlagFields = {
|
|
15505
|
-
/** Operator marked this track as training material.
|
|
15603
|
+
/** Operator marked this track as training material — i.e. `retrainStatus` is
|
|
15604
|
+
* `'staging'`. */
|
|
15506
15605
|
markForTrain: boolean().optional(),
|
|
15507
15606
|
/** Operator marked this track for diagnostic attention. */
|
|
15508
15607
|
debug: boolean().optional()
|
|
15509
15608
|
};
|
|
15510
15609
|
/**
|
|
15610
|
+
* The lifecycle field itself, on the READ surfaces only (`Track`, `KeyEvent`).
|
|
15611
|
+
* Deliberately NOT part of {@link TrackFlagFields}: that group also builds the
|
|
15612
|
+
* write patch, and the status is not something the toggle sets — it is what the
|
|
15613
|
+
* toggle's boolean is derived from. Absent on an in-RAM track never touched;
|
|
15614
|
+
* always present on a persisted row (the column default materialises `'none'`).
|
|
15615
|
+
*/
|
|
15616
|
+
var TrackRetrainFields = { retrainStatus: RetrainStatusSchema.optional() };
|
|
15617
|
+
/**
|
|
15511
15618
|
* The write half: a PARTIAL patch. An omitted key is left untouched, so setting
|
|
15512
15619
|
* one flag can never clear the other — the toggles are independent and are
|
|
15513
15620
|
* driven from three surfaces that do not know about each other.
|
|
@@ -15521,7 +15628,32 @@ var TrackFlagsPatchSchema = object(TrackFlagFields);
|
|
|
15521
15628
|
var TrackFlagsSchema = object({
|
|
15522
15629
|
trackId: string(),
|
|
15523
15630
|
markForTrain: boolean(),
|
|
15524
|
-
debug: boolean()
|
|
15631
|
+
debug: boolean(),
|
|
15632
|
+
/** The lifecycle state the boolean was derived from. Required here (unlike on
|
|
15633
|
+
* a track row) because this shape is only ever produced by the write body,
|
|
15634
|
+
* which always knows it — and a surface that has just written needs to render
|
|
15635
|
+
* `trained` without a re-fetch. */
|
|
15636
|
+
retrainStatus: RetrainStatusSchema
|
|
15637
|
+
});
|
|
15638
|
+
/** Per-camera slice of a training-export estimate. */
|
|
15639
|
+
var TrainingExportDeviceTotalsSchema = object({
|
|
15640
|
+
deviceId: number(),
|
|
15641
|
+
tracks: number().int(),
|
|
15642
|
+
files: number().int(),
|
|
15643
|
+
bytes: number().int()
|
|
15644
|
+
});
|
|
15645
|
+
/**
|
|
15646
|
+
* What a training export WOULD contain. Computed from media index rows only —
|
|
15647
|
+
* no blob is read to produce this.
|
|
15648
|
+
*/
|
|
15649
|
+
var TrainingExportSummarySchema = object({
|
|
15650
|
+
generatedAt: number(),
|
|
15651
|
+
trackCount: number().int(),
|
|
15652
|
+
fileCount: number().int(),
|
|
15653
|
+
byteCount: number().int(),
|
|
15654
|
+
/** More marked tracks exist than a single pass carries. */
|
|
15655
|
+
truncated: boolean(),
|
|
15656
|
+
devices: array(TrainingExportDeviceTotalsSchema).readonly()
|
|
15525
15657
|
});
|
|
15526
15658
|
var TrackSchema = object({
|
|
15527
15659
|
trackId: string(),
|
|
@@ -15566,7 +15698,8 @@ var TrackSchema = object({
|
|
|
15566
15698
|
* Populated from the persisted envelope columns on historical reads;
|
|
15567
15699
|
* absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
|
|
15568
15700
|
envelope: TrackEnvelopeSchema.optional(),
|
|
15569
|
-
...TrackFlagFields
|
|
15701
|
+
...TrackFlagFields,
|
|
15702
|
+
...TrackRetrainFields
|
|
15570
15703
|
});
|
|
15571
15704
|
var BaseEventFields = {
|
|
15572
15705
|
id: string(),
|
|
@@ -15780,7 +15913,8 @@ var KeyEventSchema = object({
|
|
|
15780
15913
|
bestEventId: string(),
|
|
15781
15914
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
15782
15915
|
windowMs: number().optional(),
|
|
15783
|
-
...TrackFlagFields
|
|
15916
|
+
...TrackFlagFields,
|
|
15917
|
+
...TrackRetrainFields
|
|
15784
15918
|
});
|
|
15785
15919
|
object({
|
|
15786
15920
|
trackId: string(),
|
|
@@ -16041,6 +16175,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
16041
16175
|
}), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
|
|
16042
16176
|
kind: "query",
|
|
16043
16177
|
auth: "admin"
|
|
16178
|
+
}), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
|
|
16179
|
+
kind: "query",
|
|
16180
|
+
auth: "admin"
|
|
16181
|
+
}), method(object({ deviceIds: array(number()).optional() }), object({ url: string() }), {
|
|
16182
|
+
kind: "query",
|
|
16183
|
+
auth: "admin"
|
|
16044
16184
|
}), method(object({
|
|
16045
16185
|
eventId: string(),
|
|
16046
16186
|
kind: MediaFileKindEnum.optional()
|
|
@@ -22317,6 +22457,173 @@ DeviceType.Camera, method(object({
|
|
|
22317
22457
|
status: OsdStatusSchema
|
|
22318
22458
|
});
|
|
22319
22459
|
/**
|
|
22460
|
+
* `osd-manager` — the ORCHESTRATOR over the device-scope `osd` cap.
|
|
22461
|
+
*
|
|
22462
|
+
* The `osd` cap is the firmware contract: it probes a camera's overlay
|
|
22463
|
+
* SLOTS and writes literal text into one. It has no idea WHERE that text
|
|
22464
|
+
* comes from, and it must not — a driver that grew a "show the temperature
|
|
22465
|
+
* here" feature would grow it once per vendor.
|
|
22466
|
+
*
|
|
22467
|
+
* This cap owns the other half: a per-(camera, slot) BINDING that says
|
|
22468
|
+
* which value feeds the slot, how it is formatted, and under which
|
|
22469
|
+
* conditions it is shown at all. One addon renders every binding on every
|
|
22470
|
+
* camera, so a new source costs zero driver code.
|
|
22471
|
+
*
|
|
22472
|
+
* Three deliberate choices, each with a rejected alternative:
|
|
22473
|
+
*
|
|
22474
|
+
* 1. A source is `(capName, valuePath)` over the kernel's device
|
|
22475
|
+
* runtime-state mirror — NOT a closed enum of source kinds. Every
|
|
22476
|
+
* cap-keyed slice a device publishes is bindable the day the cap
|
|
22477
|
+
* ships. The rejected alternative (one enum member per source, with
|
|
22478
|
+
* a resolver branch each) is what makes "add the humidity too" a
|
|
22479
|
+
* code change.
|
|
22480
|
+
* 2. The display gate reuses `NcConditionsSchema` verbatim — the
|
|
22481
|
+
* notification centre's condition vocabulary — rather than a parallel
|
|
22482
|
+
* model. An operator who has learned one condition editor has learned
|
|
22483
|
+
* both.
|
|
22484
|
+
* 3. Because the renderer's facts are device STATE and not a detection
|
|
22485
|
+
* record, only a SUBSET of that vocabulary can be answered here.
|
|
22486
|
+
* `setSlotBinding` REJECTS the rest at write time (see
|
|
22487
|
+
* `getConditionSupport`). It does not accept-then-fail-closed: a
|
|
22488
|
+
* condition that can never be true renders a permanently blank
|
|
22489
|
+
* overlay, and a blank overlay looks exactly like a broken camera.
|
|
22490
|
+
*/
|
|
22491
|
+
/** Where a slot's value comes from. */
|
|
22492
|
+
var OsdSourceSchema = discriminatedUnion("kind", [
|
|
22493
|
+
object({
|
|
22494
|
+
kind: literal("static"),
|
|
22495
|
+
text: string().max(64)
|
|
22496
|
+
}),
|
|
22497
|
+
object({
|
|
22498
|
+
kind: literal("clock"),
|
|
22499
|
+
/** Token pattern: `YYYY MM DD HH mm ss`. Everything else is literal. */
|
|
22500
|
+
pattern: string().min(1).max(32).default("HH:mm"),
|
|
22501
|
+
/** IANA zone. Omitted = the server's zone. */
|
|
22502
|
+
timezone: string().min(1).max(64).optional()
|
|
22503
|
+
}),
|
|
22504
|
+
object({
|
|
22505
|
+
kind: literal("device-state"),
|
|
22506
|
+
deviceId: number().int().optional(),
|
|
22507
|
+
capName: string().min(1).max(64),
|
|
22508
|
+
/** Dot path inside the slice, e.g. `detected`, `value`, `mode`. */
|
|
22509
|
+
valuePath: string().min(1).max(64)
|
|
22510
|
+
})
|
|
22511
|
+
]);
|
|
22512
|
+
var OsdSlotBindingSchema = object({
|
|
22513
|
+
/** Off = the manager stops driving this slot. It does NOT clear it. */
|
|
22514
|
+
enabled: boolean().default(true),
|
|
22515
|
+
source: OsdSourceSchema,
|
|
22516
|
+
/** `${value}` and `${unit}` are substituted; every occurrence. */
|
|
22517
|
+
template: string().max(96).default("${value}"),
|
|
22518
|
+
/** Truncate with an ellipsis past this length. Absent = no limit. */
|
|
22519
|
+
maxCharacters: number().int().min(4).max(64).optional(),
|
|
22520
|
+
/**
|
|
22521
|
+
* Decimal places for a numeric value. `0` yields an integer — the
|
|
22522
|
+
* documented workaround for firmwares that reject `.` in overlay text.
|
|
22523
|
+
*/
|
|
22524
|
+
maxDecimals: number().int().min(0).max(4).default(1),
|
|
22525
|
+
/** Appended via `${unit}`. The state mirror does not carry units. */
|
|
22526
|
+
unitLabel: string().max(8).optional(),
|
|
22527
|
+
/** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
|
|
22528
|
+
valueMap: record(string(), string()).optional(),
|
|
22529
|
+
/** Time windows in which the slot is shown. Absent = always. */
|
|
22530
|
+
schedule: NcScheduleSchema.optional(),
|
|
22531
|
+
/**
|
|
22532
|
+
* Display gate, in the notification centre's condition vocabulary.
|
|
22533
|
+
* Only the keys reported by `getConditionSupport` are accepted.
|
|
22534
|
+
*/
|
|
22535
|
+
conditions: NcConditionsSchema.optional(),
|
|
22536
|
+
/** Rendered when the gate is closed or the value unreadable. Empty = hide. */
|
|
22537
|
+
fallbackText: string().max(64).default("")
|
|
22538
|
+
});
|
|
22539
|
+
/** One camera slot, as the operator sees it: firmware truth + our binding. */
|
|
22540
|
+
var OsdSlotViewSchema = object({
|
|
22541
|
+
slotId: string(),
|
|
22542
|
+
kind: OsdOverlayKindEnum,
|
|
22543
|
+
/** Firmware refuses text edits (a timestamp, the channel name). */
|
|
22544
|
+
readOnly: boolean(),
|
|
22545
|
+
cameraEnabled: boolean(),
|
|
22546
|
+
cameraText: string().optional(),
|
|
22547
|
+
binding: OsdSlotBindingSchema.nullable()
|
|
22548
|
+
});
|
|
22549
|
+
/**
|
|
22550
|
+
* What happened to one slot on one render pass. `unchanged` exists so the
|
|
22551
|
+
* operator can tell "we are driving this and the value is steady" from
|
|
22552
|
+
* "we never got there" — and so the loop can prove it is not rewriting
|
|
22553
|
+
* identical text to the camera every tick.
|
|
22554
|
+
*/
|
|
22555
|
+
var OsdRenderOutcomeEnum = _enum([
|
|
22556
|
+
"written",
|
|
22557
|
+
"unchanged",
|
|
22558
|
+
"gated",
|
|
22559
|
+
"unreadable",
|
|
22560
|
+
"disabled",
|
|
22561
|
+
"unbound",
|
|
22562
|
+
"failed"
|
|
22563
|
+
]);
|
|
22564
|
+
var OsdRenderResultSchema = object({
|
|
22565
|
+
slotId: string(),
|
|
22566
|
+
outcome: OsdRenderOutcomeEnum,
|
|
22567
|
+
/** The text the slot should carry. Empty = the slot is switched off. */
|
|
22568
|
+
text: string(),
|
|
22569
|
+
/** Why, whenever the outcome is not a plain write. Never silent. */
|
|
22570
|
+
reason: string().optional()
|
|
22571
|
+
});
|
|
22572
|
+
var OsdSourceValueTypeEnum = _enum([
|
|
22573
|
+
"number",
|
|
22574
|
+
"boolean",
|
|
22575
|
+
"string",
|
|
22576
|
+
"enum"
|
|
22577
|
+
]);
|
|
22578
|
+
/**
|
|
22579
|
+
* One bindable value, derived from a cap's `runtimeState` schema — never
|
|
22580
|
+
* hand-listed. The editor renders from this, so a cap that ships a new
|
|
22581
|
+
* state field becomes bindable with no UI change.
|
|
22582
|
+
*/
|
|
22583
|
+
var OsdSourceOptionSchema = object({
|
|
22584
|
+
deviceId: number().int(),
|
|
22585
|
+
deviceName: string(),
|
|
22586
|
+
capName: string(),
|
|
22587
|
+
valuePath: string(),
|
|
22588
|
+
label: string(),
|
|
22589
|
+
valueType: OsdSourceValueTypeEnum,
|
|
22590
|
+
/** Present for `enum`; the editor offers these as `valueMap` keys. */
|
|
22591
|
+
enumValues: array(string()).readonly().optional()
|
|
22592
|
+
});
|
|
22593
|
+
method(object({ deviceId: number().int() }), object({
|
|
22594
|
+
supported: boolean(),
|
|
22595
|
+
slots: array(OsdSlotViewSchema)
|
|
22596
|
+
}), { auth: "admin" }), method(object({ deviceId: number().int() }), object({ sources: array(OsdSourceOptionSchema) }), { auth: "admin" }), method(object({}), object({
|
|
22597
|
+
supported: array(string()),
|
|
22598
|
+
catalog: array(NcConditionDescriptorSchema)
|
|
22599
|
+
}), { auth: "admin" }), method(object({
|
|
22600
|
+
deviceId: number().int(),
|
|
22601
|
+
slotId: string().min(1),
|
|
22602
|
+
binding: OsdSlotBindingSchema
|
|
22603
|
+
}), object({
|
|
22604
|
+
slot: OsdSlotViewSchema,
|
|
22605
|
+
render: OsdRenderResultSchema
|
|
22606
|
+
}), {
|
|
22607
|
+
kind: "mutation",
|
|
22608
|
+
auth: "admin"
|
|
22609
|
+
}), method(object({
|
|
22610
|
+
deviceId: number().int(),
|
|
22611
|
+
slotId: string().min(1)
|
|
22612
|
+
}), object({ success: literal(true) }), {
|
|
22613
|
+
kind: "mutation",
|
|
22614
|
+
auth: "admin"
|
|
22615
|
+
}), method(object({
|
|
22616
|
+
deviceId: number().int(),
|
|
22617
|
+
slotId: string().min(1),
|
|
22618
|
+
binding: OsdSlotBindingSchema.optional()
|
|
22619
|
+
}), OsdRenderResultSchema, {
|
|
22620
|
+
kind: "mutation",
|
|
22621
|
+
auth: "admin"
|
|
22622
|
+
}), method(object({ deviceId: number().int() }), object({ results: array(OsdRenderResultSchema) }), {
|
|
22623
|
+
kind: "mutation",
|
|
22624
|
+
auth: "admin"
|
|
22625
|
+
});
|
|
22626
|
+
/**
|
|
22320
22627
|
* Feeder connectivity / power status — mirrors the HA petkit device-status
|
|
22321
22628
|
* enum: `normal` (online, mains), `offline` (not reaching PetKit cloud),
|
|
22322
22629
|
* `on_batteries` (running on battery backup). `null` until first reported.
|
|
@@ -27349,6 +27656,48 @@ Object.freeze({
|
|
|
27349
27656
|
addonId: null,
|
|
27350
27657
|
access: "create"
|
|
27351
27658
|
},
|
|
27659
|
+
"osdManager.clearSlotBinding": {
|
|
27660
|
+
capName: "osd-manager",
|
|
27661
|
+
capScope: "system",
|
|
27662
|
+
addonId: null,
|
|
27663
|
+
access: "delete"
|
|
27664
|
+
},
|
|
27665
|
+
"osdManager.getConditionSupport": {
|
|
27666
|
+
capName: "osd-manager",
|
|
27667
|
+
capScope: "system",
|
|
27668
|
+
addonId: null,
|
|
27669
|
+
access: "view"
|
|
27670
|
+
},
|
|
27671
|
+
"osdManager.getDeviceOsd": {
|
|
27672
|
+
capName: "osd-manager",
|
|
27673
|
+
capScope: "system",
|
|
27674
|
+
addonId: null,
|
|
27675
|
+
access: "view"
|
|
27676
|
+
},
|
|
27677
|
+
"osdManager.getSourceCatalog": {
|
|
27678
|
+
capName: "osd-manager",
|
|
27679
|
+
capScope: "system",
|
|
27680
|
+
addonId: null,
|
|
27681
|
+
access: "view"
|
|
27682
|
+
},
|
|
27683
|
+
"osdManager.previewSlot": {
|
|
27684
|
+
capName: "osd-manager",
|
|
27685
|
+
capScope: "system",
|
|
27686
|
+
addonId: null,
|
|
27687
|
+
access: "create"
|
|
27688
|
+
},
|
|
27689
|
+
"osdManager.renderDevice": {
|
|
27690
|
+
capName: "osd-manager",
|
|
27691
|
+
capScope: "system",
|
|
27692
|
+
addonId: null,
|
|
27693
|
+
access: "create"
|
|
27694
|
+
},
|
|
27695
|
+
"osdManager.setSlotBinding": {
|
|
27696
|
+
capName: "osd-manager",
|
|
27697
|
+
capScope: "system",
|
|
27698
|
+
addonId: null,
|
|
27699
|
+
access: "create"
|
|
27700
|
+
},
|
|
27352
27701
|
"petFeeder.callPet": {
|
|
27353
27702
|
capName: "pet-feeder",
|
|
27354
27703
|
capScope: "device",
|
|
@@ -27511,6 +27860,18 @@ Object.freeze({
|
|
|
27511
27860
|
addonId: null,
|
|
27512
27861
|
access: "view"
|
|
27513
27862
|
},
|
|
27863
|
+
"pipelineAnalytics.getTrainingExportSummary": {
|
|
27864
|
+
capName: "pipeline-analytics",
|
|
27865
|
+
capScope: "device",
|
|
27866
|
+
addonId: null,
|
|
27867
|
+
access: "view"
|
|
27868
|
+
},
|
|
27869
|
+
"pipelineAnalytics.getTrainingExportUrl": {
|
|
27870
|
+
capName: "pipeline-analytics",
|
|
27871
|
+
capScope: "device",
|
|
27872
|
+
addonId: null,
|
|
27873
|
+
access: "view"
|
|
27874
|
+
},
|
|
27514
27875
|
"pipelineAnalytics.listEventKinds": {
|
|
27515
27876
|
capName: "pipeline-analytics",
|
|
27516
27877
|
capScope: "device",
|
|
@@ -28987,6 +29348,12 @@ Object.freeze({
|
|
|
28987
29348
|
addonId: null,
|
|
28988
29349
|
access: "view"
|
|
28989
29350
|
},
|
|
29351
|
+
"streamBroker.getDeviceAudioMute": {
|
|
29352
|
+
capName: "stream-broker",
|
|
29353
|
+
capScope: "system",
|
|
29354
|
+
addonId: null,
|
|
29355
|
+
access: "view"
|
|
29356
|
+
},
|
|
28990
29357
|
"streamBroker.getPreBufferInfo": {
|
|
28991
29358
|
capName: "stream-broker",
|
|
28992
29359
|
capScope: "system",
|
|
@@ -29107,6 +29474,12 @@ Object.freeze({
|
|
|
29107
29474
|
addonId: null,
|
|
29108
29475
|
access: "create"
|
|
29109
29476
|
},
|
|
29477
|
+
"streamBroker.setDeviceAudioMute": {
|
|
29478
|
+
capName: "stream-broker",
|
|
29479
|
+
capScope: "system",
|
|
29480
|
+
addonId: null,
|
|
29481
|
+
access: "create"
|
|
29482
|
+
},
|
|
29110
29483
|
"streamBroker.setPreBufferDuration": {
|
|
29111
29484
|
capName: "stream-broker",
|
|
29112
29485
|
capScope: "system",
|
|
@@ -32079,6 +32452,18 @@ function parseDeviceKeyEngine(deviceKey) {
|
|
|
32079
32452
|
}
|
|
32080
32453
|
/** Per-stage timeout applied by `boundedStage` to every remote-fetch stage. */
|
|
32081
32454
|
var STAGE_TIMEOUT_MS = 3e3;
|
|
32455
|
+
/**
|
|
32456
|
+
* Every switch whose OFF position leaves audio analysis with nothing to
|
|
32457
|
+
* analyse — the camera not capturing, this server not distributing, or the
|
|
32458
|
+
* analyzer not running. Named once because `CameraStatus.audio.enabled` must
|
|
32459
|
+
* agree with all three, and a list rebuilt at the call site is a list that
|
|
32460
|
+
* will miss the next one.
|
|
32461
|
+
*/
|
|
32462
|
+
var AUDIO_SILENCING_SWITCH_IDS = [
|
|
32463
|
+
"device-audio",
|
|
32464
|
+
"broker-audio",
|
|
32465
|
+
"audio-analysis"
|
|
32466
|
+
];
|
|
32082
32467
|
var CameraStatusService = class {
|
|
32083
32468
|
deps;
|
|
32084
32469
|
constructor(deps) {
|
|
@@ -32273,12 +32658,19 @@ var CameraStatusService = class {
|
|
|
32273
32658
|
* is precisely the failure the switch group exists to remove, and leaving
|
|
32274
32659
|
* the lie in place would have made the group's own status block disagree
|
|
32275
32660
|
* with it.
|
|
32661
|
+
*
|
|
32662
|
+
* THREE switches can silence this stage, not one (D83). `audio-analysis`
|
|
32663
|
+
* stops the analyzer; `device-audio` stops the camera capturing; and
|
|
32664
|
+
* `broker-audio` stops this server distributing. Any of them leaves the
|
|
32665
|
+
* analyzer with nothing, and reporting `enabled: true` for the other two
|
|
32666
|
+
* would reintroduce the exact hardcoded lie the previous paragraph is about
|
|
32667
|
+
* — one row further down the group.
|
|
32276
32668
|
*/
|
|
32277
32669
|
buildAudioStage(audioNodeId, switchedOff) {
|
|
32278
32670
|
if (audioNodeId === null) return null;
|
|
32279
32671
|
return {
|
|
32280
32672
|
nodeId: audioNodeId,
|
|
32281
|
-
enabled: !switchedOff.includes(
|
|
32673
|
+
enabled: !AUDIO_SILENCING_SWITCH_IDS.some((id) => switchedOff.includes(id))
|
|
32282
32674
|
};
|
|
32283
32675
|
}
|
|
32284
32676
|
/**
|
|
@@ -32525,6 +32917,12 @@ var CameraSwitchService = class {
|
|
|
32525
32917
|
patch: { enabled }
|
|
32526
32918
|
});
|
|
32527
32919
|
return;
|
|
32920
|
+
case "broker-audio-mute":
|
|
32921
|
+
await api.streamBroker.setDeviceAudioMute.mutate({
|
|
32922
|
+
deviceId,
|
|
32923
|
+
muted: !enabled
|
|
32924
|
+
}, nodePin(this.deps.assignSource(deviceId)));
|
|
32925
|
+
return;
|
|
32528
32926
|
}
|
|
32529
32927
|
}
|
|
32530
32928
|
/**
|
|
@@ -32543,7 +32941,8 @@ var CameraSwitchService = class {
|
|
|
32543
32941
|
recordingEnabled: null,
|
|
32544
32942
|
notificationsMuted: null,
|
|
32545
32943
|
deviceAudio: null,
|
|
32546
|
-
privacyMask: null
|
|
32944
|
+
privacyMask: null,
|
|
32945
|
+
brokerAudio: null
|
|
32547
32946
|
},
|
|
32548
32947
|
wrapperAddonIdByCap: /* @__PURE__ */ new Map(),
|
|
32549
32948
|
recordingConfig: null
|
|
@@ -32583,11 +32982,16 @@ var CameraSwitchService = class {
|
|
|
32583
32982
|
this.warn(deviceId, "notificationRules.listDeviceMutes", err);
|
|
32584
32983
|
return null;
|
|
32585
32984
|
});
|
|
32586
|
-
const
|
|
32985
|
+
const brokerAudioPromise = api.streamBroker.getDeviceAudioMute.query({ deviceId }, nodePin(this.deps.assignSource(deviceId))).then((r) => ({ muted: r.muted })).catch((err) => {
|
|
32986
|
+
this.warn(deviceId, "streamBroker.getDeviceAudioMute", err);
|
|
32987
|
+
return null;
|
|
32988
|
+
});
|
|
32989
|
+
const [device, bindings, recordingConfig, mutedDeviceIds, brokerAudio] = await Promise.all([
|
|
32587
32990
|
devicePromise,
|
|
32588
32991
|
bindingsPromise,
|
|
32589
32992
|
recordingPromise,
|
|
32590
|
-
mutesPromise
|
|
32993
|
+
mutesPromise,
|
|
32994
|
+
brokerAudioPromise
|
|
32591
32995
|
]);
|
|
32592
32996
|
const [bindable, privacy] = await Promise.all([device === null ? Promise.resolve(null) : api.deviceManager.listBindableCapsForDeviceType.query({ deviceType: device.type }).catch((err) => {
|
|
32593
32997
|
this.warn(deviceId, "listBindableCapsForDeviceType", err);
|
|
@@ -32608,7 +33012,8 @@ var CameraSwitchService = class {
|
|
|
32608
33012
|
recordingEnabled: recordingConfig === null ? null : recordingConfig.enabled,
|
|
32609
33013
|
notificationsMuted: mutedDeviceIds === null ? null : mutedDeviceIds.includes(deviceId),
|
|
32610
33014
|
deviceAudio: privacy.deviceAudio,
|
|
32611
|
-
privacyMask: privacy.privacyMask
|
|
33015
|
+
privacyMask: privacy.privacyMask,
|
|
33016
|
+
brokerAudio
|
|
32612
33017
|
},
|
|
32613
33018
|
wrapperAddonIdByCap,
|
|
32614
33019
|
recordingConfig
|
|
@@ -38388,7 +38793,8 @@ async function buildOrchestratorControllers(deps) {
|
|
|
38388
38793
|
});
|
|
38389
38794
|
const cameraSwitchService = new CameraSwitchService({
|
|
38390
38795
|
api: () => deps.ctx().api ?? null,
|
|
38391
|
-
logger: deps.ctx().logger
|
|
38796
|
+
logger: deps.ctx().logger,
|
|
38797
|
+
assignSource: (deviceId) => topology.assignSource(deviceId)
|
|
38392
38798
|
});
|
|
38393
38799
|
const cameraStatusService = new CameraStatusService({
|
|
38394
38800
|
api: () => deps.ctx().api,
|
package/dist/remoteEntry.js
CHANGED
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-Myx4bNgp.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline-orchestrator",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.28",
|
|
4
4
|
"description": "Hub-side camera-to-agent load balancer — tracks runner capacity and dispatches attachCamera calls to the optimal pipeline-runner instance",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|