@camstack/addon-provider-homeassistant 1.2.138 → 1.2.140
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 +1 -1
- package/dist/addon.mjs +1 -1
- package/dist/{dist-CwIqCT2h.js → dist-D1vVHAPR.js} +190 -8
- package/dist/{dist-mrfDukqc.mjs → dist-DEPiNusb.mjs} +190 -8
- package/dist/ha-export/ha-export.addon.js +73 -3
- package/dist/ha-export/ha-export.addon.mjs +73 -3
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-D1vVHAPR.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
let node_zlib = require("node:zlib");
|
|
5
5
|
//#region src/ha-device-source.ts
|
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as presenceCapability, A as controlCapability, At as EventCategory, B as humiditySensorCapability, C as buildAddonRouteProvider, Ct as number, D as colorCapability, Dt as union, E as climateControlCapability, Et as string, F as eventEmitterCapability, G as motionCapability, H as lawnMowerControlCapability, I as fanControlCapability, J as notifierCapability, K as normalizeUnit, L as floodCapability, M as deviceAdoptionCapability, O as connectivityCapability, Ot as unknown, P as enumSensorCapability, Q as prepareNotification, R as gasCapability, S as brokerCapability, St as literal, T as carbonMonoxideCapability, Tt as record, U as lockControlCapability, V as imageCapability, W as mediaPlayerCapability, Y as numericSensorCapability, Z as powerMeterCapability, _ as automationControlCapability, _t as createEvent, at as temperatureSensorCapability, b as binaryCapability, bt as array, ct as valveCapability, d as TargetSchema, dt as weatherCapability, et as pressureSensorCapability, f as accessoriesCapability, ft as errMsg, g as ambientLightSensorCapability, gt as DeviceType, h as alarmPanelCapability, ht as DeviceRole, it as tamperCapability, j as coverCapability, k as contactCapability, kt as url, lt as vibrationCapability, m as airQualitySensorCapability, mt as DeviceFeature, n as BaseDevice, nt as smokeCapability, ot as updateCapability, p as addonRoutesCapability, q as notificationOutputCapability, r as BaseDeviceProvider, rt as switchCapability, s as EnumSensorDateTimeFormatSchema, st as vacuumControlCapability, tt as scriptRunnerCapability, ut as waterHeaterCapability, v as batteryCapability, w as buttonCapability, wt as object, x as brightnessCapability, xt as discriminatedUnion, y as bestLocationMatch, yt as _enum, z as humidifierCapability } from "./dist-
|
|
1
|
+
import { $ as presenceCapability, A as controlCapability, At as EventCategory, B as humiditySensorCapability, C as buildAddonRouteProvider, Ct as number, D as colorCapability, Dt as union, E as climateControlCapability, Et as string, F as eventEmitterCapability, G as motionCapability, H as lawnMowerControlCapability, I as fanControlCapability, J as notifierCapability, K as normalizeUnit, L as floodCapability, M as deviceAdoptionCapability, O as connectivityCapability, Ot as unknown, P as enumSensorCapability, Q as prepareNotification, R as gasCapability, S as brokerCapability, St as literal, T as carbonMonoxideCapability, Tt as record, U as lockControlCapability, V as imageCapability, W as mediaPlayerCapability, Y as numericSensorCapability, Z as powerMeterCapability, _ as automationControlCapability, _t as createEvent, at as temperatureSensorCapability, b as binaryCapability, bt as array, ct as valveCapability, d as TargetSchema, dt as weatherCapability, et as pressureSensorCapability, f as accessoriesCapability, ft as errMsg, g as ambientLightSensorCapability, gt as DeviceType, h as alarmPanelCapability, ht as DeviceRole, it as tamperCapability, j as coverCapability, k as contactCapability, kt as url, lt as vibrationCapability, m as airQualitySensorCapability, mt as DeviceFeature, n as BaseDevice, nt as smokeCapability, ot as updateCapability, p as addonRoutesCapability, q as notificationOutputCapability, r as BaseDeviceProvider, rt as switchCapability, s as EnumSensorDateTimeFormatSchema, st as vacuumControlCapability, tt as scriptRunnerCapability, ut as waterHeaterCapability, v as batteryCapability, w as buttonCapability, wt as object, x as brightnessCapability, xt as discriminatedUnion, y as bestLocationMatch, yt as _enum, z as humidifierCapability } from "./dist-DEPiNusb.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
|
|
4
4
|
//#region src/ha-device-source.ts
|
|
@@ -5359,7 +5359,7 @@ var ZodIssueCode = {
|
|
|
5359
5359
|
var ZodFirstPartyTypeKind;
|
|
5360
5360
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5361
5361
|
//#endregion
|
|
5362
|
-
//#region ../types/dist/sleep-
|
|
5362
|
+
//#region ../types/dist/sleep-CiR4z7r9.mjs
|
|
5363
5363
|
/**
|
|
5364
5364
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5365
5365
|
* window to float samples (D455).
|
|
@@ -6501,6 +6501,17 @@ var CamProfileSchema = _enum([
|
|
|
6501
6501
|
"mid",
|
|
6502
6502
|
"low"
|
|
6503
6503
|
]);
|
|
6504
|
+
/**
|
|
6505
|
+
* Which rung a per-camera consumer listens on: an explicit profile, or `auto`.
|
|
6506
|
+
*
|
|
6507
|
+
* `auto` is a REAL value, not an absent one — it means "resolve it by the rule
|
|
6508
|
+
* that consumer owns". It lives beside `CamProfile` because a rung is the only
|
|
6509
|
+
* operator-facing way to name a stream: a cam-stream id is broker-internal and
|
|
6510
|
+
* moves when a slot is reassigned, so a stored id silently stops meaning what
|
|
6511
|
+
* the operator picked. Today's one user is the audio analyser, whose rule is
|
|
6512
|
+
* "the lowest AVAILABLE native rung" (D537).
|
|
6513
|
+
*/
|
|
6514
|
+
var AudioStreamPickSchema = union([CamProfileSchema, literal("auto")]);
|
|
6504
6515
|
var CamStreamKindSchema = _enum([
|
|
6505
6516
|
"pull-rtsp",
|
|
6506
6517
|
"pull-rtmp",
|
|
@@ -7897,6 +7908,7 @@ var CameraSwitchIdSchema = _enum([
|
|
|
7897
7908
|
"device-audio",
|
|
7898
7909
|
"broker-audio",
|
|
7899
7910
|
"audio-analysis",
|
|
7911
|
+
"event-saving",
|
|
7900
7912
|
"recording",
|
|
7901
7913
|
"notifications"
|
|
7902
7914
|
]);
|
|
@@ -7923,6 +7935,7 @@ var CAMERA_SWITCH_ORDER = [
|
|
|
7923
7935
|
"device-audio",
|
|
7924
7936
|
"broker-audio",
|
|
7925
7937
|
"audio-analysis",
|
|
7938
|
+
"event-saving",
|
|
7926
7939
|
"recording",
|
|
7927
7940
|
"notifications"
|
|
7928
7941
|
];
|
|
@@ -20636,6 +20649,7 @@ var MediaFileKindEnum = _enum([
|
|
|
20636
20649
|
"keyFrameSmall",
|
|
20637
20650
|
"thumbnailSmall",
|
|
20638
20651
|
"confirmationCrop",
|
|
20652
|
+
"debugCrop",
|
|
20639
20653
|
"sceneFrame"
|
|
20640
20654
|
]);
|
|
20641
20655
|
/**
|
|
@@ -22300,6 +22314,18 @@ var RunnerCameraConfigSchema = object({
|
|
|
22300
22314
|
motionStreamId: string(),
|
|
22301
22315
|
detectionStreamId: string(),
|
|
22302
22316
|
/**
|
|
22317
|
+
* Which rung this camera's audio analysis listens on — `auto` (the lowest
|
|
22318
|
+
* AVAILABLE native rung) or an explicit one.
|
|
22319
|
+
*
|
|
22320
|
+
* Not consumed by the runner: carried here so it travels with the camera's
|
|
22321
|
+
* config the way `audioMotionWindowMs` does, and so the orchestrator's
|
|
22322
|
+
* watchdog resolves the same stream the audio subscription did. OPTIONAL
|
|
22323
|
+
* with no default — absent means `auto`, which is what every camera written
|
|
22324
|
+
* before this field had, and an absent optional adds nothing to the
|
|
22325
|
+
* generated cap-input defaults (D537).
|
|
22326
|
+
*/
|
|
22327
|
+
audioStreamPick: AudioStreamPickSchema.optional(),
|
|
22328
|
+
/**
|
|
22303
22329
|
* Motion sources that can activate the pipeline. At least one must
|
|
22304
22330
|
* be present. Multiple sources OR-ed: any one firing arms the
|
|
22305
22331
|
* cooldown timer and transitions watching → active. The same
|
|
@@ -24791,6 +24817,104 @@ var WebrtcStreamChoiceSchema = object({
|
|
|
24791
24817
|
/** Bitrate in kbps (null for adaptive). */
|
|
24792
24818
|
bitrateKbps: number().nullable()
|
|
24793
24819
|
});
|
|
24820
|
+
/**
|
|
24821
|
+
* The last move the adaptive ladder made on this session, and the numbers it
|
|
24822
|
+
* rested on. Mirrors `LadderDecision` in the stream-broker's
|
|
24823
|
+
* `adaptive-controller.ts` — the log line's evidence, made readable.
|
|
24824
|
+
*
|
|
24825
|
+
* Nullability is load-bearing: `lossRatio: null` means the loss tracker had
|
|
24826
|
+
* no window to report, which is NOT a link with zero loss.
|
|
24827
|
+
*/
|
|
24828
|
+
var WebrtcLadderDecisionSchema = object({
|
|
24829
|
+
/** `loss` — RTCP / client-reported packet loss; `stall` — the picture
|
|
24830
|
+
* stopped moving; `healthy` — a promotion. */
|
|
24831
|
+
cause: _enum([
|
|
24832
|
+
"loss",
|
|
24833
|
+
"stall",
|
|
24834
|
+
"healthy"
|
|
24835
|
+
]),
|
|
24836
|
+
/** The rung the ladder moved TO. Null for a move that changed no rung
|
|
24837
|
+
* (an MTU reduction, a forced transcode-down at the bottom). */
|
|
24838
|
+
tier: CamProfileSchema.nullable(),
|
|
24839
|
+
/** Whether that move forced the egress transcode-down rung. */
|
|
24840
|
+
forcedTranscodeDown: boolean(),
|
|
24841
|
+
/** Loss ratio over the tracker's window at the moment of the decision. */
|
|
24842
|
+
lossRatio: number().nullable(),
|
|
24843
|
+
/** The client's reported downlink at that moment, in kbps. */
|
|
24844
|
+
clientKbps: number().nullable(),
|
|
24845
|
+
/** How long the stall detector had been seeing nothing arrive. */
|
|
24846
|
+
stalledForMs: number().nullable(),
|
|
24847
|
+
/** Whether the broker WAS sending while that was measured — the half that
|
|
24848
|
+
* separates "the camera stopped" from "the link stopped". */
|
|
24849
|
+
serverSending: boolean().nullable(),
|
|
24850
|
+
/** Epoch ms. The overlay prints its age; a decision from 40 minutes ago
|
|
24851
|
+
* must not read like one from this second. */
|
|
24852
|
+
at: number()
|
|
24853
|
+
});
|
|
24854
|
+
/** Per-tier count of MEASURED downgrades this viewer session has suffered. */
|
|
24855
|
+
var WebrtcTierFailureSchema = object({
|
|
24856
|
+
tier: CamProfileSchema,
|
|
24857
|
+
failures: number().int().nonnegative()
|
|
24858
|
+
});
|
|
24859
|
+
/**
|
|
24860
|
+
* What the SERVER decided for one WebRTC session — the half the client cannot
|
|
24861
|
+
* measure. See `getSessionDebug`.
|
|
24862
|
+
*/
|
|
24863
|
+
var WebrtcSessionDebugSchema = object({
|
|
24864
|
+
sessionId: string(),
|
|
24865
|
+
/** Whether the session still exists here. Everything below is null when
|
|
24866
|
+
* it does not — a dead session has no measurements, not zero ones. */
|
|
24867
|
+
alive: boolean(),
|
|
24868
|
+
/** Epoch ms this snapshot was taken, so the overlay can show its age. */
|
|
24869
|
+
observedAt: number(),
|
|
24870
|
+
/** The broker actually feeding this session (`<deviceId>/<camStreamId>`). */
|
|
24871
|
+
brokerId: string().nullable(),
|
|
24872
|
+
camStreamId: string().nullable(),
|
|
24873
|
+
/** `derived` = this hub BUILT the rung being served (a transcode/scale of
|
|
24874
|
+
* another stream); `native` = the camera's own encode. */
|
|
24875
|
+
sourceKind: _enum([
|
|
24876
|
+
"native",
|
|
24877
|
+
"derived",
|
|
24878
|
+
"unknown"
|
|
24879
|
+
]),
|
|
24880
|
+
/** Whether the ladder is running at all (an `adaptive` target). */
|
|
24881
|
+
adaptive: boolean(),
|
|
24882
|
+
/** The rung `auto` ACTUALLY resolved to right now. Null when the broker
|
|
24883
|
+
* is bound to no profile slot (a manually-activated stream). */
|
|
24884
|
+
servedTier: CamProfileSchema.nullable(),
|
|
24885
|
+
/** Which node ingests the source. Null when unresolved. */
|
|
24886
|
+
ingestNodeId: string().nullable(),
|
|
24887
|
+
/** Whether that node is a remote one (the media crosses a node boundary
|
|
24888
|
+
* before it reaches this session). Null when unresolved. */
|
|
24889
|
+
ingestIsRemote: boolean().nullable(),
|
|
24890
|
+
/** Codec off the source, and the one negotiated with this client. */
|
|
24891
|
+
sourceCodec: string().nullable(),
|
|
24892
|
+
negotiatedCodec: string().nullable(),
|
|
24893
|
+
/** Whether the EGRESS leg re-encodes for this client or forwards the
|
|
24894
|
+
* source bytes untouched. */
|
|
24895
|
+
egressLeg: _enum([
|
|
24896
|
+
"copy",
|
|
24897
|
+
"transcode",
|
|
24898
|
+
"unknown"
|
|
24899
|
+
]),
|
|
24900
|
+
/** What the broker itself reports about the rung it is serving. */
|
|
24901
|
+
brokerStatus: string().nullable(),
|
|
24902
|
+
brokerCodec: string().nullable(),
|
|
24903
|
+
brokerBitrateKbps: number().nullable(),
|
|
24904
|
+
brokerInputFps: number().nullable(),
|
|
24905
|
+
brokerUptimeMs: number().nullable(),
|
|
24906
|
+
/** RTP packets this session has sent — the server's own "am I sending?". */
|
|
24907
|
+
rtpPacketsSent: number().nullable(),
|
|
24908
|
+
/** D42: the track may be carrying the recorded feeder, not live. The ladder
|
|
24909
|
+
* is suspended there, so a frozen picture in `recorded` mode means
|
|
24910
|
+
* something entirely different. */
|
|
24911
|
+
trackMode: _enum(["live", "recorded"]).nullable(),
|
|
24912
|
+
lastDecision: WebrtcLadderDecisionSchema.nullable(),
|
|
24913
|
+
tierFailures: array(WebrtcTierFailureSchema),
|
|
24914
|
+
/** Successful promotions in this viewer session (drives the progressive
|
|
24915
|
+
* healthy timer). Null when no ladder is running. */
|
|
24916
|
+
upgradeCount: number().nullable()
|
|
24917
|
+
});
|
|
24794
24918
|
DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(WebrtcStreamChoiceSchema)), method(object({
|
|
24795
24919
|
deviceId: number().int().nonnegative(),
|
|
24796
24920
|
target: WebrtcStreamTargetSchema,
|
|
@@ -24955,7 +25079,10 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
|
|
|
24955
25079
|
* the same answer as "it ended": either way nothing is holding it up.
|
|
24956
25080
|
*/
|
|
24957
25081
|
alive: boolean()
|
|
24958
|
-
}))
|
|
25082
|
+
})), method(object({
|
|
25083
|
+
deviceId: number().int().nonnegative(),
|
|
25084
|
+
sessionId: string()
|
|
25085
|
+
}), WebrtcSessionDebugSchema, { kind: "query" });
|
|
24959
25086
|
/**
|
|
24960
25087
|
* List of accessory children linked to a parent device. The cap surfaces
|
|
24961
25088
|
* the grouping and the per-child UI-visibility flag — the single source
|
|
@@ -29443,16 +29570,37 @@ var GridProfileOfferSchema = object({
|
|
|
29443
29570
|
]),
|
|
29444
29571
|
offered: boolean(),
|
|
29445
29572
|
/** Sources that cannot serve it. Empty when it is offered, or when there are no cells. */
|
|
29446
|
-
missingSources: array(number().int().positive())
|
|
29573
|
+
missingSources: array(number().int().positive()),
|
|
29574
|
+
/**
|
|
29575
|
+
* The canvas this profile composes onto, `WxH`, or empty when it is not
|
|
29576
|
+
* offered. DERIVED from the cells and the sources' own size at this profile —
|
|
29577
|
+
* it is reported because nothing else in the system would ever say what the
|
|
29578
|
+
* grid came out as, and because it is the number an operator would otherwise
|
|
29579
|
+
* expect to type.
|
|
29580
|
+
*/
|
|
29581
|
+
canvas: string(),
|
|
29582
|
+
/**
|
|
29583
|
+
* Whether this profile is PUBLISHED, of the ones the grid could serve.
|
|
29584
|
+
*
|
|
29585
|
+
* A grid's `high` is composed of its sources' `high`, so on a 4K fleet it is
|
|
29586
|
+
* a 4K canvas built from 4K decodes — something to opt into, not something a
|
|
29587
|
+
* viewer's adaptive should be handed by climbing to the top rung it can see.
|
|
29588
|
+
* Default is `mid` + `low`.
|
|
29589
|
+
*/
|
|
29590
|
+
published: boolean()
|
|
29447
29591
|
});
|
|
29448
29592
|
var GridLayoutViewSchema = object({
|
|
29449
29593
|
/** The persisted grid row this camera was declared from. */
|
|
29450
29594
|
instanceId: string(),
|
|
29451
29595
|
deviceId: number().int().nonnegative(),
|
|
29452
29596
|
name: string(),
|
|
29453
|
-
/**
|
|
29454
|
-
|
|
29455
|
-
|
|
29597
|
+
/**
|
|
29598
|
+
* NO canvas size. A grid's resolution is not authored: each profile derives
|
|
29599
|
+
* its own from the cells and its sources' dimensions. The two numbers that
|
|
29600
|
+
* used to be here were a text field that silently decided both how much the
|
|
29601
|
+
* composite cost and how sharp it was — see `profiles[].canvas` for what it
|
|
29602
|
+
* came out as.
|
|
29603
|
+
*/
|
|
29456
29604
|
fps: number().int(),
|
|
29457
29605
|
cells: array(GridLayoutCellSchema),
|
|
29458
29606
|
/** What the catalog will publish, and what it refuses to. Read-only. */
|
|
@@ -29461,9 +29609,13 @@ var GridLayoutViewSchema = object({
|
|
|
29461
29609
|
var GridLayoutPatchSchema = object({
|
|
29462
29610
|
deviceId: number().int().nonnegative(),
|
|
29463
29611
|
name: string().min(1).max(160).optional(),
|
|
29464
|
-
width: number().int().min(160).max(7680).optional(),
|
|
29465
|
-
height: number().int().min(120).max(4320).optional(),
|
|
29466
29612
|
fps: number().int().min(1).max(60).optional(),
|
|
29613
|
+
/** Which profiles to publish. See `GridProfileOffer.published`. */
|
|
29614
|
+
publishedProfiles: array(_enum([
|
|
29615
|
+
"high",
|
|
29616
|
+
"mid",
|
|
29617
|
+
"low"
|
|
29618
|
+
])).max(3).optional(),
|
|
29467
29619
|
/**
|
|
29468
29620
|
* The whole cell list at once. A per-cell patch would need an ordering the
|
|
29469
29621
|
* editor does not have, and a half-applied layout is a picture nobody asked
|
|
@@ -31746,6 +31898,25 @@ var ptzAutotrackCapability = {
|
|
|
31746
31898
|
scope: "device",
|
|
31747
31899
|
deviceNative: true,
|
|
31748
31900
|
mode: "singleton",
|
|
31901
|
+
/**
|
|
31902
|
+
* Two kinds of provider behind one name, the same shape `snapshot` and
|
|
31903
|
+
* `doorbell` use:
|
|
31904
|
+
*
|
|
31905
|
+
* - **Native**: the camera's own firmware autotrack, registered per device by
|
|
31906
|
+
* the driver when the probe says it exists (Reolink: yes; Hikvision:
|
|
31907
|
+
* model-dependent).
|
|
31908
|
+
* - **Wrapper**: the emulated implementation (`@camstack/system`'s `autotrack` builtin),
|
|
31909
|
+
* which follows a subject with `ptz.move` pulses driven by the detection
|
|
31910
|
+
* pipeline. `defaultActive: false` — the operator binds it per camera in
|
|
31911
|
+
* the bindings UI, exactly as `virtual-doorbell` is bound.
|
|
31912
|
+
*
|
|
31913
|
+
* The binding chooses WHICH implementation answers; `setEnabled` stays the
|
|
31914
|
+
* one switch for the function either way. A camera never has both driving its
|
|
31915
|
+
* head, which is the whole reason this is not a second cap with a second
|
|
31916
|
+
* toggle (D62, D520).
|
|
31917
|
+
*/
|
|
31918
|
+
kind: "wrapper",
|
|
31919
|
+
defaultActive: false,
|
|
31749
31920
|
deviceTypes: [DeviceType.Camera],
|
|
31750
31921
|
deviceConfig: { ui: {
|
|
31751
31922
|
kind: "widget",
|
|
@@ -42716,6 +42887,12 @@ Object.freeze({
|
|
|
42716
42887
|
addonId: null,
|
|
42717
42888
|
access: "view"
|
|
42718
42889
|
},
|
|
42890
|
+
"webrtcSession.getSessionDebug": {
|
|
42891
|
+
capName: "webrtc-session",
|
|
42892
|
+
capScope: "device",
|
|
42893
|
+
addonId: null,
|
|
42894
|
+
access: "view"
|
|
42895
|
+
},
|
|
42719
42896
|
"webrtcSession.getSessionState": {
|
|
42720
42897
|
capName: "webrtc-session",
|
|
42721
42898
|
capScope: "device",
|
|
@@ -44699,6 +44876,11 @@ Object.freeze({
|
|
|
44699
44876
|
form: "single",
|
|
44700
44877
|
optional: false
|
|
44701
44878
|
}],
|
|
44879
|
+
"webrtcSession.getSessionDebug": [{
|
|
44880
|
+
name: "deviceId",
|
|
44881
|
+
form: "single",
|
|
44882
|
+
optional: false
|
|
44883
|
+
}],
|
|
44702
44884
|
"webrtcSession.getSessionState": [{
|
|
44703
44885
|
name: "deviceId",
|
|
44704
44886
|
form: "single",
|
|
@@ -5359,7 +5359,7 @@ var ZodIssueCode = {
|
|
|
5359
5359
|
var ZodFirstPartyTypeKind;
|
|
5360
5360
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5361
5361
|
//#endregion
|
|
5362
|
-
//#region ../types/dist/sleep-
|
|
5362
|
+
//#region ../types/dist/sleep-CiR4z7r9.mjs
|
|
5363
5363
|
/**
|
|
5364
5364
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5365
5365
|
* window to float samples (D455).
|
|
@@ -6501,6 +6501,17 @@ var CamProfileSchema = _enum([
|
|
|
6501
6501
|
"mid",
|
|
6502
6502
|
"low"
|
|
6503
6503
|
]);
|
|
6504
|
+
/**
|
|
6505
|
+
* Which rung a per-camera consumer listens on: an explicit profile, or `auto`.
|
|
6506
|
+
*
|
|
6507
|
+
* `auto` is a REAL value, not an absent one — it means "resolve it by the rule
|
|
6508
|
+
* that consumer owns". It lives beside `CamProfile` because a rung is the only
|
|
6509
|
+
* operator-facing way to name a stream: a cam-stream id is broker-internal and
|
|
6510
|
+
* moves when a slot is reassigned, so a stored id silently stops meaning what
|
|
6511
|
+
* the operator picked. Today's one user is the audio analyser, whose rule is
|
|
6512
|
+
* "the lowest AVAILABLE native rung" (D537).
|
|
6513
|
+
*/
|
|
6514
|
+
var AudioStreamPickSchema = union([CamProfileSchema, literal("auto")]);
|
|
6504
6515
|
var CamStreamKindSchema = _enum([
|
|
6505
6516
|
"pull-rtsp",
|
|
6506
6517
|
"pull-rtmp",
|
|
@@ -7897,6 +7908,7 @@ var CameraSwitchIdSchema = _enum([
|
|
|
7897
7908
|
"device-audio",
|
|
7898
7909
|
"broker-audio",
|
|
7899
7910
|
"audio-analysis",
|
|
7911
|
+
"event-saving",
|
|
7900
7912
|
"recording",
|
|
7901
7913
|
"notifications"
|
|
7902
7914
|
]);
|
|
@@ -7923,6 +7935,7 @@ var CAMERA_SWITCH_ORDER = [
|
|
|
7923
7935
|
"device-audio",
|
|
7924
7936
|
"broker-audio",
|
|
7925
7937
|
"audio-analysis",
|
|
7938
|
+
"event-saving",
|
|
7926
7939
|
"recording",
|
|
7927
7940
|
"notifications"
|
|
7928
7941
|
];
|
|
@@ -20636,6 +20649,7 @@ var MediaFileKindEnum = _enum([
|
|
|
20636
20649
|
"keyFrameSmall",
|
|
20637
20650
|
"thumbnailSmall",
|
|
20638
20651
|
"confirmationCrop",
|
|
20652
|
+
"debugCrop",
|
|
20639
20653
|
"sceneFrame"
|
|
20640
20654
|
]);
|
|
20641
20655
|
/**
|
|
@@ -22300,6 +22314,18 @@ var RunnerCameraConfigSchema = object({
|
|
|
22300
22314
|
motionStreamId: string(),
|
|
22301
22315
|
detectionStreamId: string(),
|
|
22302
22316
|
/**
|
|
22317
|
+
* Which rung this camera's audio analysis listens on — `auto` (the lowest
|
|
22318
|
+
* AVAILABLE native rung) or an explicit one.
|
|
22319
|
+
*
|
|
22320
|
+
* Not consumed by the runner: carried here so it travels with the camera's
|
|
22321
|
+
* config the way `audioMotionWindowMs` does, and so the orchestrator's
|
|
22322
|
+
* watchdog resolves the same stream the audio subscription did. OPTIONAL
|
|
22323
|
+
* with no default — absent means `auto`, which is what every camera written
|
|
22324
|
+
* before this field had, and an absent optional adds nothing to the
|
|
22325
|
+
* generated cap-input defaults (D537).
|
|
22326
|
+
*/
|
|
22327
|
+
audioStreamPick: AudioStreamPickSchema.optional(),
|
|
22328
|
+
/**
|
|
22303
22329
|
* Motion sources that can activate the pipeline. At least one must
|
|
22304
22330
|
* be present. Multiple sources OR-ed: any one firing arms the
|
|
22305
22331
|
* cooldown timer and transitions watching → active. The same
|
|
@@ -24791,6 +24817,104 @@ var WebrtcStreamChoiceSchema = object({
|
|
|
24791
24817
|
/** Bitrate in kbps (null for adaptive). */
|
|
24792
24818
|
bitrateKbps: number().nullable()
|
|
24793
24819
|
});
|
|
24820
|
+
/**
|
|
24821
|
+
* The last move the adaptive ladder made on this session, and the numbers it
|
|
24822
|
+
* rested on. Mirrors `LadderDecision` in the stream-broker's
|
|
24823
|
+
* `adaptive-controller.ts` — the log line's evidence, made readable.
|
|
24824
|
+
*
|
|
24825
|
+
* Nullability is load-bearing: `lossRatio: null` means the loss tracker had
|
|
24826
|
+
* no window to report, which is NOT a link with zero loss.
|
|
24827
|
+
*/
|
|
24828
|
+
var WebrtcLadderDecisionSchema = object({
|
|
24829
|
+
/** `loss` — RTCP / client-reported packet loss; `stall` — the picture
|
|
24830
|
+
* stopped moving; `healthy` — a promotion. */
|
|
24831
|
+
cause: _enum([
|
|
24832
|
+
"loss",
|
|
24833
|
+
"stall",
|
|
24834
|
+
"healthy"
|
|
24835
|
+
]),
|
|
24836
|
+
/** The rung the ladder moved TO. Null for a move that changed no rung
|
|
24837
|
+
* (an MTU reduction, a forced transcode-down at the bottom). */
|
|
24838
|
+
tier: CamProfileSchema.nullable(),
|
|
24839
|
+
/** Whether that move forced the egress transcode-down rung. */
|
|
24840
|
+
forcedTranscodeDown: boolean(),
|
|
24841
|
+
/** Loss ratio over the tracker's window at the moment of the decision. */
|
|
24842
|
+
lossRatio: number().nullable(),
|
|
24843
|
+
/** The client's reported downlink at that moment, in kbps. */
|
|
24844
|
+
clientKbps: number().nullable(),
|
|
24845
|
+
/** How long the stall detector had been seeing nothing arrive. */
|
|
24846
|
+
stalledForMs: number().nullable(),
|
|
24847
|
+
/** Whether the broker WAS sending while that was measured — the half that
|
|
24848
|
+
* separates "the camera stopped" from "the link stopped". */
|
|
24849
|
+
serverSending: boolean().nullable(),
|
|
24850
|
+
/** Epoch ms. The overlay prints its age; a decision from 40 minutes ago
|
|
24851
|
+
* must not read like one from this second. */
|
|
24852
|
+
at: number()
|
|
24853
|
+
});
|
|
24854
|
+
/** Per-tier count of MEASURED downgrades this viewer session has suffered. */
|
|
24855
|
+
var WebrtcTierFailureSchema = object({
|
|
24856
|
+
tier: CamProfileSchema,
|
|
24857
|
+
failures: number().int().nonnegative()
|
|
24858
|
+
});
|
|
24859
|
+
/**
|
|
24860
|
+
* What the SERVER decided for one WebRTC session — the half the client cannot
|
|
24861
|
+
* measure. See `getSessionDebug`.
|
|
24862
|
+
*/
|
|
24863
|
+
var WebrtcSessionDebugSchema = object({
|
|
24864
|
+
sessionId: string(),
|
|
24865
|
+
/** Whether the session still exists here. Everything below is null when
|
|
24866
|
+
* it does not — a dead session has no measurements, not zero ones. */
|
|
24867
|
+
alive: boolean(),
|
|
24868
|
+
/** Epoch ms this snapshot was taken, so the overlay can show its age. */
|
|
24869
|
+
observedAt: number(),
|
|
24870
|
+
/** The broker actually feeding this session (`<deviceId>/<camStreamId>`). */
|
|
24871
|
+
brokerId: string().nullable(),
|
|
24872
|
+
camStreamId: string().nullable(),
|
|
24873
|
+
/** `derived` = this hub BUILT the rung being served (a transcode/scale of
|
|
24874
|
+
* another stream); `native` = the camera's own encode. */
|
|
24875
|
+
sourceKind: _enum([
|
|
24876
|
+
"native",
|
|
24877
|
+
"derived",
|
|
24878
|
+
"unknown"
|
|
24879
|
+
]),
|
|
24880
|
+
/** Whether the ladder is running at all (an `adaptive` target). */
|
|
24881
|
+
adaptive: boolean(),
|
|
24882
|
+
/** The rung `auto` ACTUALLY resolved to right now. Null when the broker
|
|
24883
|
+
* is bound to no profile slot (a manually-activated stream). */
|
|
24884
|
+
servedTier: CamProfileSchema.nullable(),
|
|
24885
|
+
/** Which node ingests the source. Null when unresolved. */
|
|
24886
|
+
ingestNodeId: string().nullable(),
|
|
24887
|
+
/** Whether that node is a remote one (the media crosses a node boundary
|
|
24888
|
+
* before it reaches this session). Null when unresolved. */
|
|
24889
|
+
ingestIsRemote: boolean().nullable(),
|
|
24890
|
+
/** Codec off the source, and the one negotiated with this client. */
|
|
24891
|
+
sourceCodec: string().nullable(),
|
|
24892
|
+
negotiatedCodec: string().nullable(),
|
|
24893
|
+
/** Whether the EGRESS leg re-encodes for this client or forwards the
|
|
24894
|
+
* source bytes untouched. */
|
|
24895
|
+
egressLeg: _enum([
|
|
24896
|
+
"copy",
|
|
24897
|
+
"transcode",
|
|
24898
|
+
"unknown"
|
|
24899
|
+
]),
|
|
24900
|
+
/** What the broker itself reports about the rung it is serving. */
|
|
24901
|
+
brokerStatus: string().nullable(),
|
|
24902
|
+
brokerCodec: string().nullable(),
|
|
24903
|
+
brokerBitrateKbps: number().nullable(),
|
|
24904
|
+
brokerInputFps: number().nullable(),
|
|
24905
|
+
brokerUptimeMs: number().nullable(),
|
|
24906
|
+
/** RTP packets this session has sent — the server's own "am I sending?". */
|
|
24907
|
+
rtpPacketsSent: number().nullable(),
|
|
24908
|
+
/** D42: the track may be carrying the recorded feeder, not live. The ladder
|
|
24909
|
+
* is suspended there, so a frozen picture in `recorded` mode means
|
|
24910
|
+
* something entirely different. */
|
|
24911
|
+
trackMode: _enum(["live", "recorded"]).nullable(),
|
|
24912
|
+
lastDecision: WebrtcLadderDecisionSchema.nullable(),
|
|
24913
|
+
tierFailures: array(WebrtcTierFailureSchema),
|
|
24914
|
+
/** Successful promotions in this viewer session (drives the progressive
|
|
24915
|
+
* healthy timer). Null when no ladder is running. */
|
|
24916
|
+
upgradeCount: number().nullable()
|
|
24917
|
+
});
|
|
24794
24918
|
DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(WebrtcStreamChoiceSchema)), method(object({
|
|
24795
24919
|
deviceId: number().int().nonnegative(),
|
|
24796
24920
|
target: WebrtcStreamTargetSchema,
|
|
@@ -24955,7 +25079,10 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
|
|
|
24955
25079
|
* the same answer as "it ended": either way nothing is holding it up.
|
|
24956
25080
|
*/
|
|
24957
25081
|
alive: boolean()
|
|
24958
|
-
}))
|
|
25082
|
+
})), method(object({
|
|
25083
|
+
deviceId: number().int().nonnegative(),
|
|
25084
|
+
sessionId: string()
|
|
25085
|
+
}), WebrtcSessionDebugSchema, { kind: "query" });
|
|
24959
25086
|
/**
|
|
24960
25087
|
* List of accessory children linked to a parent device. The cap surfaces
|
|
24961
25088
|
* the grouping and the per-child UI-visibility flag — the single source
|
|
@@ -29443,16 +29570,37 @@ var GridProfileOfferSchema = object({
|
|
|
29443
29570
|
]),
|
|
29444
29571
|
offered: boolean(),
|
|
29445
29572
|
/** Sources that cannot serve it. Empty when it is offered, or when there are no cells. */
|
|
29446
|
-
missingSources: array(number().int().positive())
|
|
29573
|
+
missingSources: array(number().int().positive()),
|
|
29574
|
+
/**
|
|
29575
|
+
* The canvas this profile composes onto, `WxH`, or empty when it is not
|
|
29576
|
+
* offered. DERIVED from the cells and the sources' own size at this profile —
|
|
29577
|
+
* it is reported because nothing else in the system would ever say what the
|
|
29578
|
+
* grid came out as, and because it is the number an operator would otherwise
|
|
29579
|
+
* expect to type.
|
|
29580
|
+
*/
|
|
29581
|
+
canvas: string(),
|
|
29582
|
+
/**
|
|
29583
|
+
* Whether this profile is PUBLISHED, of the ones the grid could serve.
|
|
29584
|
+
*
|
|
29585
|
+
* A grid's `high` is composed of its sources' `high`, so on a 4K fleet it is
|
|
29586
|
+
* a 4K canvas built from 4K decodes — something to opt into, not something a
|
|
29587
|
+
* viewer's adaptive should be handed by climbing to the top rung it can see.
|
|
29588
|
+
* Default is `mid` + `low`.
|
|
29589
|
+
*/
|
|
29590
|
+
published: boolean()
|
|
29447
29591
|
});
|
|
29448
29592
|
var GridLayoutViewSchema = object({
|
|
29449
29593
|
/** The persisted grid row this camera was declared from. */
|
|
29450
29594
|
instanceId: string(),
|
|
29451
29595
|
deviceId: number().int().nonnegative(),
|
|
29452
29596
|
name: string(),
|
|
29453
|
-
/**
|
|
29454
|
-
|
|
29455
|
-
|
|
29597
|
+
/**
|
|
29598
|
+
* NO canvas size. A grid's resolution is not authored: each profile derives
|
|
29599
|
+
* its own from the cells and its sources' dimensions. The two numbers that
|
|
29600
|
+
* used to be here were a text field that silently decided both how much the
|
|
29601
|
+
* composite cost and how sharp it was — see `profiles[].canvas` for what it
|
|
29602
|
+
* came out as.
|
|
29603
|
+
*/
|
|
29456
29604
|
fps: number().int(),
|
|
29457
29605
|
cells: array(GridLayoutCellSchema),
|
|
29458
29606
|
/** What the catalog will publish, and what it refuses to. Read-only. */
|
|
@@ -29461,9 +29609,13 @@ var GridLayoutViewSchema = object({
|
|
|
29461
29609
|
var GridLayoutPatchSchema = object({
|
|
29462
29610
|
deviceId: number().int().nonnegative(),
|
|
29463
29611
|
name: string().min(1).max(160).optional(),
|
|
29464
|
-
width: number().int().min(160).max(7680).optional(),
|
|
29465
|
-
height: number().int().min(120).max(4320).optional(),
|
|
29466
29612
|
fps: number().int().min(1).max(60).optional(),
|
|
29613
|
+
/** Which profiles to publish. See `GridProfileOffer.published`. */
|
|
29614
|
+
publishedProfiles: array(_enum([
|
|
29615
|
+
"high",
|
|
29616
|
+
"mid",
|
|
29617
|
+
"low"
|
|
29618
|
+
])).max(3).optional(),
|
|
29467
29619
|
/**
|
|
29468
29620
|
* The whole cell list at once. A per-cell patch would need an ordering the
|
|
29469
29621
|
* editor does not have, and a half-applied layout is a picture nobody asked
|
|
@@ -31746,6 +31898,25 @@ var ptzAutotrackCapability = {
|
|
|
31746
31898
|
scope: "device",
|
|
31747
31899
|
deviceNative: true,
|
|
31748
31900
|
mode: "singleton",
|
|
31901
|
+
/**
|
|
31902
|
+
* Two kinds of provider behind one name, the same shape `snapshot` and
|
|
31903
|
+
* `doorbell` use:
|
|
31904
|
+
*
|
|
31905
|
+
* - **Native**: the camera's own firmware autotrack, registered per device by
|
|
31906
|
+
* the driver when the probe says it exists (Reolink: yes; Hikvision:
|
|
31907
|
+
* model-dependent).
|
|
31908
|
+
* - **Wrapper**: the emulated implementation (`@camstack/system`'s `autotrack` builtin),
|
|
31909
|
+
* which follows a subject with `ptz.move` pulses driven by the detection
|
|
31910
|
+
* pipeline. `defaultActive: false` — the operator binds it per camera in
|
|
31911
|
+
* the bindings UI, exactly as `virtual-doorbell` is bound.
|
|
31912
|
+
*
|
|
31913
|
+
* The binding chooses WHICH implementation answers; `setEnabled` stays the
|
|
31914
|
+
* one switch for the function either way. A camera never has both driving its
|
|
31915
|
+
* head, which is the whole reason this is not a second cap with a second
|
|
31916
|
+
* toggle (D62, D520).
|
|
31917
|
+
*/
|
|
31918
|
+
kind: "wrapper",
|
|
31919
|
+
defaultActive: false,
|
|
31749
31920
|
deviceTypes: [DeviceType.Camera],
|
|
31750
31921
|
deviceConfig: { ui: {
|
|
31751
31922
|
kind: "widget",
|
|
@@ -42716,6 +42887,12 @@ Object.freeze({
|
|
|
42716
42887
|
addonId: null,
|
|
42717
42888
|
access: "view"
|
|
42718
42889
|
},
|
|
42890
|
+
"webrtcSession.getSessionDebug": {
|
|
42891
|
+
capName: "webrtc-session",
|
|
42892
|
+
capScope: "device",
|
|
42893
|
+
addonId: null,
|
|
42894
|
+
access: "view"
|
|
42895
|
+
},
|
|
42719
42896
|
"webrtcSession.getSessionState": {
|
|
42720
42897
|
capName: "webrtc-session",
|
|
42721
42898
|
capScope: "device",
|
|
@@ -44699,6 +44876,11 @@ Object.freeze({
|
|
|
44699
44876
|
form: "single",
|
|
44700
44877
|
optional: false
|
|
44701
44878
|
}],
|
|
44879
|
+
"webrtcSession.getSessionDebug": [{
|
|
44880
|
+
name: "deviceId",
|
|
44881
|
+
form: "single",
|
|
44882
|
+
optional: false
|
|
44883
|
+
}],
|
|
44702
44884
|
"webrtcSession.getSessionState": [{
|
|
44703
44885
|
name: "deviceId",
|
|
44704
44886
|
form: "single",
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
//#endregion
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-D1vVHAPR.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
//#region src/ha-export/topics.ts
|
|
9
9
|
/**
|
|
@@ -4071,6 +4071,8 @@ async function applyCameraFunctionWrite(api, deviceId, write, sourceNodeId) {
|
|
|
4071
4071
|
//#region src/ha-export/camera-functions.ts
|
|
4072
4072
|
var DETECTION_CAP = "detection-pipeline";
|
|
4073
4073
|
var AUDIO_ANALYSIS_CAP = "audio-analysis";
|
|
4074
|
+
/** Il wrapper che SCRIVE la storia: tracce, eventi e i loro media. */
|
|
4075
|
+
var PIPELINE_ANALYTICS_CAP = "pipeline-analytics";
|
|
4074
4076
|
/** Ordine di resa: raggio d'azione più ampio prima, sorgente prima del consumatore. */
|
|
4075
4077
|
var ORDER = [
|
|
4076
4078
|
"stream-broker",
|
|
@@ -4079,6 +4081,7 @@ var ORDER = [
|
|
|
4079
4081
|
"device-audio",
|
|
4080
4082
|
"broker-audio",
|
|
4081
4083
|
"audio-analysis",
|
|
4084
|
+
"event-saving",
|
|
4082
4085
|
"recording",
|
|
4083
4086
|
"notifications"
|
|
4084
4087
|
];
|
|
@@ -4089,9 +4092,20 @@ var LABELS = {
|
|
|
4089
4092
|
"device-audio": "Camera microphone",
|
|
4090
4093
|
"broker-audio": "Audio distribution",
|
|
4091
4094
|
"audio-analysis": "Audio detection & classification",
|
|
4095
|
+
"event-saving": "Event saving",
|
|
4092
4096
|
recording: "Recording",
|
|
4093
4097
|
notifications: "Notifications"
|
|
4094
4098
|
};
|
|
4099
|
+
/**
|
|
4100
|
+
* Quale cap sta dietro ogni switch wrapper-backed. Una mappa invece di un
|
|
4101
|
+
* ternario: con tre voci il ternario diventava una catena, e la prossima voce
|
|
4102
|
+
* sarebbe finita silenziosamente sul ramo sbagliato.
|
|
4103
|
+
*/
|
|
4104
|
+
var WRAPPER_CAP_BY_ID = {
|
|
4105
|
+
"object-detection": DETECTION_CAP,
|
|
4106
|
+
"audio-analysis": AUDIO_ANALYSIS_CAP,
|
|
4107
|
+
"event-saving": PIPELINE_ANALYTICS_CAP
|
|
4108
|
+
};
|
|
4095
4109
|
var UNAVAILABLE = {
|
|
4096
4110
|
available: false,
|
|
4097
4111
|
enabled: true
|
|
@@ -4113,6 +4127,7 @@ function resolveOne(id, reads) {
|
|
|
4113
4127
|
};
|
|
4114
4128
|
case "object-detection": return resolveWrapper(DETECTION_CAP, reads);
|
|
4115
4129
|
case "audio-analysis": return resolveWrapper(AUDIO_ANALYSIS_CAP, reads);
|
|
4130
|
+
case "event-saving": return resolveWrapper(PIPELINE_ANALYTICS_CAP, reads);
|
|
4116
4131
|
case "recording": return reads.recordingEnabled === null ? UNAVAILABLE : {
|
|
4117
4132
|
available: true,
|
|
4118
4133
|
enabled: reads.recordingEnabled
|
|
@@ -4151,8 +4166,9 @@ function resolveCameraFunctionWrite(id, on, reads) {
|
|
|
4151
4166
|
disabled: !on
|
|
4152
4167
|
};
|
|
4153
4168
|
case "object-detection":
|
|
4154
|
-
case "audio-analysis":
|
|
4155
|
-
|
|
4169
|
+
case "audio-analysis":
|
|
4170
|
+
case "event-saving": {
|
|
4171
|
+
const capName = WRAPPER_CAP_BY_ID[id];
|
|
4156
4172
|
const wrapperAddonId = reads.wrapperAddonIdByCap.get(capName);
|
|
4157
4173
|
if (wrapperAddonId === void 0) return null;
|
|
4158
4174
|
return {
|
|
@@ -4184,6 +4200,60 @@ function resolveCameraFunctionWrite(id, on, reads) {
|
|
|
4184
4200
|
};
|
|
4185
4201
|
}
|
|
4186
4202
|
}
|
|
4203
|
+
//#endregion
|
|
4204
|
+
//#region ../types/dist/node.mjs
|
|
4205
|
+
/**
|
|
4206
|
+
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
4207
|
+
*
|
|
4208
|
+
* ## Why a download, on every node, including the container
|
|
4209
|
+
*
|
|
4210
|
+
* The version of ffmpeg used to be CODE: baked into the image, changeable only
|
|
4211
|
+
* by rebuilding and rolling it. Now it is DATA — this file states the release,
|
|
4212
|
+
* and a node that boots with a different one downloads it. Changing ffmpeg is a
|
|
4213
|
+
* framework bump, not an image rebuild.
|
|
4214
|
+
*
|
|
4215
|
+
* It also settles the licence question by removing it. FFmpeg's licence is
|
|
4216
|
+
* chosen at compile time: the core is LGPL 2.1+, `--enable-gpl` makes it GPL
|
|
4217
|
+
* (for x264/x265), `--enable-version3` makes that v3, and `--enable-nonfree`
|
|
4218
|
+
* makes the result undistributable. These are GPL v3 builds, and we do not
|
|
4219
|
+
* distribute them: the user's own node fetches the archive from the project
|
|
4220
|
+
* that publishes it, so we convey no GPL work and inherit none of the
|
|
4221
|
+
* obligations that come with conveying one. That is the whole reason the image
|
|
4222
|
+
* no longer installs ffmpeg at all — not a technical preference.
|
|
4223
|
+
*
|
|
4224
|
+
* ## Why jellyfin-ffmpeg
|
|
4225
|
+
*
|
|
4226
|
+
* One vendor covering linux-amd64, linux-arm64, darwin-arm64 and darwin-x64
|
|
4227
|
+
* from one release, with the widest hardware surface of anything maintained:
|
|
4228
|
+
* VAAPI, QSV, NVENC/NVDEC, AMF, Vulkan, OpenCL and DRM on amd64, plus Rockchip
|
|
4229
|
+
* MPP on arm64. It reaches the vendor libraries through dlopen trampolines,
|
|
4230
|
+
* so the binary is not linked against a driver stack it may not find.
|
|
4231
|
+
*
|
|
4232
|
+
* **What it still needs from the host, measured 2026-09-19.** On the Unraid
|
|
4233
|
+
* HOST, which has no libva, `-vaapi_device` aborts the process outright —
|
|
4234
|
+
* `implib-gen: libva-drm.so.2: failed to load library ... Assertion '0 &&
|
|
4235
|
+
* "Assertion in generated code"' failed`. Inside our container, which installs
|
|
4236
|
+
* libva plus the iHD driver, the same command encodes: verified end to end with
|
|
4237
|
+
* `testsrc → format=nv12,hwupload → h264_vaapi`. So the image drops ffmpeg and
|
|
4238
|
+
* KEEPS the Intel media stack; the drivers are the part a binary cannot bring.
|
|
4239
|
+
*
|
|
4240
|
+
* ## Why each artifact carries a checksum and a claim
|
|
4241
|
+
*
|
|
4242
|
+
* The download is now the only source, so `sha256` is verified before anything
|
|
4243
|
+
* is extracted — jellyfin publishes no checksums on its release assets, so
|
|
4244
|
+
* these are ours, taken at the version bump. And `expectedHwaccels` is what the
|
|
4245
|
+
* build is supposed to be able to do: a binary that probes short of its own
|
|
4246
|
+
* claim is a source that changed under us, which is precisely what went
|
|
4247
|
+
* unnoticed for months when a static build turned out to carry no VAAPI at all
|
|
4248
|
+
* (D536).
|
|
4249
|
+
*/
|
|
4250
|
+
/** The pinned jellyfin-ffmpeg release. Changing this is the version change. */
|
|
4251
|
+
var FFMPEG_RELEASE = "8.1.2-5";
|
|
4252
|
+
var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFMPEG_RELEASE}`;
|
|
4253
|
+
function portable(target) {
|
|
4254
|
+
return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
|
|
4255
|
+
}
|
|
4256
|
+
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
|
|
4187
4257
|
/**
|
|
4188
4258
|
* The ONE derivation of "a signed, expiring URL".
|
|
4189
4259
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { At as EventCategory, C as buildAddonRouteProvider, Et as string, N as deviceExportCapability, X as oauthIntegrationCapability, a as COCO_TO_MACRO, c as FanDirectionSchema, i as CAMERA_SWITCH_ORDER, l as HvacModeSchema, o as CameraSwitchIdSchema, p as addonRoutesCapability, pt as BaseAddon, t as AlarmArmModeSchema, u as MediaPlayerRepeatSchema, vt as nodePin } from "../dist-
|
|
1
|
+
import { At as EventCategory, C as buildAddonRouteProvider, Et as string, N as deviceExportCapability, X as oauthIntegrationCapability, a as COCO_TO_MACRO, c as FanDirectionSchema, i as CAMERA_SWITCH_ORDER, l as HvacModeSchema, o as CameraSwitchIdSchema, p as addonRoutesCapability, pt as BaseAddon, t as AlarmArmModeSchema, u as MediaPlayerRepeatSchema, vt as nodePin } from "../dist-DEPiNusb.mjs";
|
|
2
2
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
3
3
|
//#region src/ha-export/topics.ts
|
|
4
4
|
/**
|
|
@@ -4066,6 +4066,8 @@ async function applyCameraFunctionWrite(api, deviceId, write, sourceNodeId) {
|
|
|
4066
4066
|
//#region src/ha-export/camera-functions.ts
|
|
4067
4067
|
var DETECTION_CAP = "detection-pipeline";
|
|
4068
4068
|
var AUDIO_ANALYSIS_CAP = "audio-analysis";
|
|
4069
|
+
/** Il wrapper che SCRIVE la storia: tracce, eventi e i loro media. */
|
|
4070
|
+
var PIPELINE_ANALYTICS_CAP = "pipeline-analytics";
|
|
4069
4071
|
/** Ordine di resa: raggio d'azione più ampio prima, sorgente prima del consumatore. */
|
|
4070
4072
|
var ORDER = [
|
|
4071
4073
|
"stream-broker",
|
|
@@ -4074,6 +4076,7 @@ var ORDER = [
|
|
|
4074
4076
|
"device-audio",
|
|
4075
4077
|
"broker-audio",
|
|
4076
4078
|
"audio-analysis",
|
|
4079
|
+
"event-saving",
|
|
4077
4080
|
"recording",
|
|
4078
4081
|
"notifications"
|
|
4079
4082
|
];
|
|
@@ -4084,9 +4087,20 @@ var LABELS = {
|
|
|
4084
4087
|
"device-audio": "Camera microphone",
|
|
4085
4088
|
"broker-audio": "Audio distribution",
|
|
4086
4089
|
"audio-analysis": "Audio detection & classification",
|
|
4090
|
+
"event-saving": "Event saving",
|
|
4087
4091
|
recording: "Recording",
|
|
4088
4092
|
notifications: "Notifications"
|
|
4089
4093
|
};
|
|
4094
|
+
/**
|
|
4095
|
+
* Quale cap sta dietro ogni switch wrapper-backed. Una mappa invece di un
|
|
4096
|
+
* ternario: con tre voci il ternario diventava una catena, e la prossima voce
|
|
4097
|
+
* sarebbe finita silenziosamente sul ramo sbagliato.
|
|
4098
|
+
*/
|
|
4099
|
+
var WRAPPER_CAP_BY_ID = {
|
|
4100
|
+
"object-detection": DETECTION_CAP,
|
|
4101
|
+
"audio-analysis": AUDIO_ANALYSIS_CAP,
|
|
4102
|
+
"event-saving": PIPELINE_ANALYTICS_CAP
|
|
4103
|
+
};
|
|
4090
4104
|
var UNAVAILABLE = {
|
|
4091
4105
|
available: false,
|
|
4092
4106
|
enabled: true
|
|
@@ -4108,6 +4122,7 @@ function resolveOne(id, reads) {
|
|
|
4108
4122
|
};
|
|
4109
4123
|
case "object-detection": return resolveWrapper(DETECTION_CAP, reads);
|
|
4110
4124
|
case "audio-analysis": return resolveWrapper(AUDIO_ANALYSIS_CAP, reads);
|
|
4125
|
+
case "event-saving": return resolveWrapper(PIPELINE_ANALYTICS_CAP, reads);
|
|
4111
4126
|
case "recording": return reads.recordingEnabled === null ? UNAVAILABLE : {
|
|
4112
4127
|
available: true,
|
|
4113
4128
|
enabled: reads.recordingEnabled
|
|
@@ -4146,8 +4161,9 @@ function resolveCameraFunctionWrite(id, on, reads) {
|
|
|
4146
4161
|
disabled: !on
|
|
4147
4162
|
};
|
|
4148
4163
|
case "object-detection":
|
|
4149
|
-
case "audio-analysis":
|
|
4150
|
-
|
|
4164
|
+
case "audio-analysis":
|
|
4165
|
+
case "event-saving": {
|
|
4166
|
+
const capName = WRAPPER_CAP_BY_ID[id];
|
|
4151
4167
|
const wrapperAddonId = reads.wrapperAddonIdByCap.get(capName);
|
|
4152
4168
|
if (wrapperAddonId === void 0) return null;
|
|
4153
4169
|
return {
|
|
@@ -4179,6 +4195,60 @@ function resolveCameraFunctionWrite(id, on, reads) {
|
|
|
4179
4195
|
};
|
|
4180
4196
|
}
|
|
4181
4197
|
}
|
|
4198
|
+
//#endregion
|
|
4199
|
+
//#region ../types/dist/node.mjs
|
|
4200
|
+
/**
|
|
4201
|
+
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
4202
|
+
*
|
|
4203
|
+
* ## Why a download, on every node, including the container
|
|
4204
|
+
*
|
|
4205
|
+
* The version of ffmpeg used to be CODE: baked into the image, changeable only
|
|
4206
|
+
* by rebuilding and rolling it. Now it is DATA — this file states the release,
|
|
4207
|
+
* and a node that boots with a different one downloads it. Changing ffmpeg is a
|
|
4208
|
+
* framework bump, not an image rebuild.
|
|
4209
|
+
*
|
|
4210
|
+
* It also settles the licence question by removing it. FFmpeg's licence is
|
|
4211
|
+
* chosen at compile time: the core is LGPL 2.1+, `--enable-gpl` makes it GPL
|
|
4212
|
+
* (for x264/x265), `--enable-version3` makes that v3, and `--enable-nonfree`
|
|
4213
|
+
* makes the result undistributable. These are GPL v3 builds, and we do not
|
|
4214
|
+
* distribute them: the user's own node fetches the archive from the project
|
|
4215
|
+
* that publishes it, so we convey no GPL work and inherit none of the
|
|
4216
|
+
* obligations that come with conveying one. That is the whole reason the image
|
|
4217
|
+
* no longer installs ffmpeg at all — not a technical preference.
|
|
4218
|
+
*
|
|
4219
|
+
* ## Why jellyfin-ffmpeg
|
|
4220
|
+
*
|
|
4221
|
+
* One vendor covering linux-amd64, linux-arm64, darwin-arm64 and darwin-x64
|
|
4222
|
+
* from one release, with the widest hardware surface of anything maintained:
|
|
4223
|
+
* VAAPI, QSV, NVENC/NVDEC, AMF, Vulkan, OpenCL and DRM on amd64, plus Rockchip
|
|
4224
|
+
* MPP on arm64. It reaches the vendor libraries through dlopen trampolines,
|
|
4225
|
+
* so the binary is not linked against a driver stack it may not find.
|
|
4226
|
+
*
|
|
4227
|
+
* **What it still needs from the host, measured 2026-09-19.** On the Unraid
|
|
4228
|
+
* HOST, which has no libva, `-vaapi_device` aborts the process outright —
|
|
4229
|
+
* `implib-gen: libva-drm.so.2: failed to load library ... Assertion '0 &&
|
|
4230
|
+
* "Assertion in generated code"' failed`. Inside our container, which installs
|
|
4231
|
+
* libva plus the iHD driver, the same command encodes: verified end to end with
|
|
4232
|
+
* `testsrc → format=nv12,hwupload → h264_vaapi`. So the image drops ffmpeg and
|
|
4233
|
+
* KEEPS the Intel media stack; the drivers are the part a binary cannot bring.
|
|
4234
|
+
*
|
|
4235
|
+
* ## Why each artifact carries a checksum and a claim
|
|
4236
|
+
*
|
|
4237
|
+
* The download is now the only source, so `sha256` is verified before anything
|
|
4238
|
+
* is extracted — jellyfin publishes no checksums on its release assets, so
|
|
4239
|
+
* these are ours, taken at the version bump. And `expectedHwaccels` is what the
|
|
4240
|
+
* build is supposed to be able to do: a binary that probes short of its own
|
|
4241
|
+
* claim is a source that changed under us, which is precisely what went
|
|
4242
|
+
* unnoticed for months when a static build turned out to carry no VAAPI at all
|
|
4243
|
+
* (D536).
|
|
4244
|
+
*/
|
|
4245
|
+
/** The pinned jellyfin-ffmpeg release. Changing this is the version change. */
|
|
4246
|
+
var FFMPEG_RELEASE = "8.1.2-5";
|
|
4247
|
+
var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFMPEG_RELEASE}`;
|
|
4248
|
+
function portable(target) {
|
|
4249
|
+
return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
|
|
4250
|
+
}
|
|
4251
|
+
portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
|
|
4182
4252
|
/**
|
|
4183
4253
|
* The ONE derivation of "a signed, expiring URL".
|
|
4184
4254
|
*
|