@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 CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("./dist-CwIqCT2h.js");
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-mrfDukqc.mjs";
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-GU_us3DG.mjs
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
- /** The canvas the operator laid out — this grid's `high`. mid/low are scaled from it. */
29454
- width: number().int(),
29455
- height: number().int(),
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-GU_us3DG.mjs
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
- /** The canvas the operator laid out — this grid's `high`. mid/low are scaled from it. */
29454
- width: number().int(),
29455
- height: number().int(),
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-CwIqCT2h.js");
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
- const capName = id === "object-detection" ? DETECTION_CAP : AUDIO_ANALYSIS_CAP;
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-mrfDukqc.mjs";
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
- const capName = id === "object-detection" ? DETECTION_CAP : AUDIO_ANALYSIS_CAP;
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
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homeassistant",
3
- "version": "1.2.138",
3
+ "version": "1.2.140",
4
4
  "description": "Home Assistant device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",