@camstack/addon-provider-homematic 1.2.15 → 1.2.16

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
@@ -25216,13 +25216,24 @@ method(object({
25216
25216
  /** Playback-speed multiplier for the render (1 = realtime). */
25217
25217
  var ExportSpeedSchema = number().min(.25).max(32);
25218
25218
  /**
25219
- * One dense interval, in SECONDS FROM THE EXPORT'S OWN `fromMs`.
25219
+ * One dense interval, in WALL-CLOCK SECONDS FROM THE EXPORT'S OWN `fromMs`.
25220
25220
  *
25221
- * Relative and not absolute epoch on purpose: the renderer's frame-select
25222
- * expression sees ffmpeg's `t`, which starts at 0 for the export's source
25223
- * playlist. Handing it absolute epochs would make every call site responsible
25224
- * for the same subtraction, and the one that forgot would emit a filter that
25225
- * selects nothing — silently, as a uniform timelapse.
25221
+ * **Wall clock, not ffmpeg's `t`** — and the recorder translates. A caller
25222
+ * derives these bounds from things that happened at a TIME (a track's
25223
+ * `firstSeen`), while `t` runs over the source playlist: the concatenation of
25224
+ * every segment present for the range, with each recording GAP removed. The
25225
+ * two agree only on a window that recorded without one interruption, and only
25226
+ * the render side knows the segments, so the translation lives there
25227
+ * (`export-dense-map.ts`, addon-pipeline).
25228
+ *
25229
+ * It was not always so. These seconds were fed to `between(t,…)` verbatim, and
25230
+ * on a 10 h window holding 29,393 s of footage every range landed late by the
25231
+ * gap accumulated before it — up to 6,607 s, well past EOF. Nothing matched,
25232
+ * the video was a uniform timelapse, and the log line reported the five ranges
25233
+ * that had been ASKED for (2026-08-13, export `57d14363`, camera 615).
25234
+ *
25235
+ * Relative and not absolute epoch, because an absolute epoch would make every
25236
+ * call site responsible for the same subtraction.
25226
25237
  */
25227
25238
  var ExportDenseRangeSchema = object({
25228
25239
  fromSec: number().nonnegative(),
@@ -33836,25 +33847,32 @@ object({
33836
33847
  var NativeLeaseAdmissionSchema = _enum(["all", "inferred"]);
33837
33848
  object({
33838
33849
  /**
33839
- * How long a retained native frame is served before it counts as a miss.
33850
+ * How many delivered frames the worker HOLDS at once, waiting for each one's
33851
+ * detection result.
33840
33852
  *
33841
- * Must cover the FULL late-crop horizon: detection inference + the
33842
- * cross-process inference-result hop to hub post-analysis + tracking + the
33843
- * tRPC crop round-trip back. Below ~500 ms the busiest cameras' subject crops
33844
- * outrun it and fall back to the ≤640 detection frame; above ~3 s the resident
33845
- * RAM per busy camera grows linearly with no measured hit-rate gain.
33853
+ * This replaced a TTL on 2026-08-13, and the replacement is the whole point:
33854
+ * a time window was never related to the event the pixels were waiting for.
33855
+ * A held frame now lives from delivery until the runner has its `FrameResult`
33856
+ * — at which moment the runner cuts the subject tiles it actually wanted and
33857
+ * releases the frame. The bound exists only so a runner that stops answering
33858
+ * cannot pin RAM: above it the OLDEST held frame is dropped and counted.
33859
+ *
33860
+ * Sizing: the steady state is `inferenceLatency × deliveredFps`, measured at
33861
+ * 40-160 ms × ≤25 fps = 1-4 frames. The default leaves headroom for a hiccup
33862
+ * without ever approaching the old resident set (43 frames × 24.9 MB at 4K).
33863
+ * Raising it does not buy hit rate — it buys tolerance for a slow runner, and
33864
+ * `holdOverflow` on the metrics line is what says you need it.
33846
33865
  */
33847
- ttlMs: number().int().min(250).max(1e4),
33866
+ holdFrames: number().int().min(1).max(64),
33848
33867
  /**
33849
33868
  * Hard per-decode-worker RAM ceiling for retained native frames, in MB.
33850
33869
  *
33851
- * Intended as a SAFETY ceiling with the TTL as the effective cap — but check
33852
- * which one is actually binding before reasoning from that. At the shipped
33853
- * 1024 MB and a 2 800 ms TTL, a 4K camera hits the CEILING first (~43 frames
33854
- * at ~24 MB each) and the TTL never gets to expire anything; `leaseMb` /
33855
- * `leaseFrames` on the metrics line say which. When the ceiling binds, a
33856
- * change that admits fewer frames buys retention WINDOW at constant RAM
33857
- * rather than giving RAM back — lower this knob if RAM is what you wanted.
33870
+ * Since 2026-08-13 this is a SAFETY ceiling and nothing else: `holdFrames`
33871
+ * is what decides how much is held, and the ceiling is the number above which
33872
+ * something is wrong. Before that it was the effective cap — at 1024 MB with
33873
+ * a 2 800 ms TTL a 4K camera sat pinned at `leaseMb:1020, leaseFrames:43`
33874
+ * with the TTL expiring nothing, which is exactly the confusion the hold
33875
+ * removes. `leaseMb` / `leaseFrames` still say what is resident.
33858
33876
  * `0` DISABLES the lease entirely and falls the worker back to the tiny
33859
33877
  * leak-prone GPU surface ring (~85% crop miss; that is what the lease exists
33860
33878
  * to replace).
@@ -33880,22 +33898,45 @@ object({
33880
33898
  * there is the signal that some caller names frames outside the inference set
33881
33899
  * and that this must go back to `all`.
33882
33900
  */
33883
- admission: NativeLeaseAdmissionSchema
33901
+ admission: NativeLeaseAdmissionSchema,
33902
+ /**
33903
+ * RAM ceiling per decode worker, in MB, for the SUBJECT TILES — the
33904
+ * compressed native crops the worker cuts at the moment a frame's detection
33905
+ * result arrives, and keeps long after the frame itself is freed.
33906
+ *
33907
+ * This is the knob that replaced the old retention window, and it buys about
33908
+ * three orders of magnitude more of it: a tile is one subject at native
33909
+ * resolution, JPEG-encoded (~60-120 KB on a 4K person), against ~24.9 MB for
33910
+ * the frame it was cut from. A frame on which nothing was detected costs
33911
+ * nothing at all, which is the real change — the old lease paid per FRAME and
33912
+ * was interrogated per SUBJECT.
33913
+ *
33914
+ * `0` DISABLES tiles, leaving only the hold window and the ≤640 RAM
33915
+ * fallback — i.e. the pre-2026-08-13 miss profile. Set it there only to
33916
+ * reproduce that.
33917
+ */
33918
+ tileBudgetMb: number().int().min(0).max(1024)
33884
33919
  });
33885
33920
  /**
33886
- * The values in force when the operator has set nothing — byte-for-byte the
33887
- * constants the decode worker shipped with as env-var defaults, so making these
33888
- * settings changed no behaviour on the day it landed.
33921
+ * The values in force when the operator has set nothing.
33922
+ *
33923
+ * `budgetMb` stays at 1024 on the day the hold landed, deliberately: it stopped
33924
+ * being the retention window and became the OOM ceiling, and lowering a ceiling
33925
+ * in the same change that redefines it would make a regression and a retune
33926
+ * indistinguishable. Cut it once `tileHits` / `holdOverflow` have been read on
33927
+ * live traffic.
33889
33928
  */
33890
33929
  var DEFAULT_NATIVE_LEASE_SETTINGS = {
33891
- ttlMs: 1200,
33930
+ holdFrames: 8,
33892
33931
  budgetMb: 1024,
33893
33932
  activityMs: 15e3,
33933
+ tileBudgetMb: 64,
33894
33934
  admission: "inferred"
33895
33935
  };
33896
- DEFAULT_NATIVE_LEASE_SETTINGS.ttlMs;
33936
+ DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames;
33897
33937
  DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
33898
33938
  DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
33939
+ DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
33899
33940
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
33900
33941
  /**
33901
33942
  * Pure fuzzy matcher for adoption location import. Normalized
package/dist/addon.mjs CHANGED
@@ -25217,13 +25217,24 @@ method(object({
25217
25217
  /** Playback-speed multiplier for the render (1 = realtime). */
25218
25218
  var ExportSpeedSchema = number().min(.25).max(32);
25219
25219
  /**
25220
- * One dense interval, in SECONDS FROM THE EXPORT'S OWN `fromMs`.
25220
+ * One dense interval, in WALL-CLOCK SECONDS FROM THE EXPORT'S OWN `fromMs`.
25221
25221
  *
25222
- * Relative and not absolute epoch on purpose: the renderer's frame-select
25223
- * expression sees ffmpeg's `t`, which starts at 0 for the export's source
25224
- * playlist. Handing it absolute epochs would make every call site responsible
25225
- * for the same subtraction, and the one that forgot would emit a filter that
25226
- * selects nothing — silently, as a uniform timelapse.
25222
+ * **Wall clock, not ffmpeg's `t`** — and the recorder translates. A caller
25223
+ * derives these bounds from things that happened at a TIME (a track's
25224
+ * `firstSeen`), while `t` runs over the source playlist: the concatenation of
25225
+ * every segment present for the range, with each recording GAP removed. The
25226
+ * two agree only on a window that recorded without one interruption, and only
25227
+ * the render side knows the segments, so the translation lives there
25228
+ * (`export-dense-map.ts`, addon-pipeline).
25229
+ *
25230
+ * It was not always so. These seconds were fed to `between(t,…)` verbatim, and
25231
+ * on a 10 h window holding 29,393 s of footage every range landed late by the
25232
+ * gap accumulated before it — up to 6,607 s, well past EOF. Nothing matched,
25233
+ * the video was a uniform timelapse, and the log line reported the five ranges
25234
+ * that had been ASKED for (2026-08-13, export `57d14363`, camera 615).
25235
+ *
25236
+ * Relative and not absolute epoch, because an absolute epoch would make every
25237
+ * call site responsible for the same subtraction.
25227
25238
  */
25228
25239
  var ExportDenseRangeSchema = object({
25229
25240
  fromSec: number().nonnegative(),
@@ -33837,25 +33848,32 @@ object({
33837
33848
  var NativeLeaseAdmissionSchema = _enum(["all", "inferred"]);
33838
33849
  object({
33839
33850
  /**
33840
- * How long a retained native frame is served before it counts as a miss.
33851
+ * How many delivered frames the worker HOLDS at once, waiting for each one's
33852
+ * detection result.
33841
33853
  *
33842
- * Must cover the FULL late-crop horizon: detection inference + the
33843
- * cross-process inference-result hop to hub post-analysis + tracking + the
33844
- * tRPC crop round-trip back. Below ~500 ms the busiest cameras' subject crops
33845
- * outrun it and fall back to the ≤640 detection frame; above ~3 s the resident
33846
- * RAM per busy camera grows linearly with no measured hit-rate gain.
33854
+ * This replaced a TTL on 2026-08-13, and the replacement is the whole point:
33855
+ * a time window was never related to the event the pixels were waiting for.
33856
+ * A held frame now lives from delivery until the runner has its `FrameResult`
33857
+ * — at which moment the runner cuts the subject tiles it actually wanted and
33858
+ * releases the frame. The bound exists only so a runner that stops answering
33859
+ * cannot pin RAM: above it the OLDEST held frame is dropped and counted.
33860
+ *
33861
+ * Sizing: the steady state is `inferenceLatency × deliveredFps`, measured at
33862
+ * 40-160 ms × ≤25 fps = 1-4 frames. The default leaves headroom for a hiccup
33863
+ * without ever approaching the old resident set (43 frames × 24.9 MB at 4K).
33864
+ * Raising it does not buy hit rate — it buys tolerance for a slow runner, and
33865
+ * `holdOverflow` on the metrics line is what says you need it.
33847
33866
  */
33848
- ttlMs: number().int().min(250).max(1e4),
33867
+ holdFrames: number().int().min(1).max(64),
33849
33868
  /**
33850
33869
  * Hard per-decode-worker RAM ceiling for retained native frames, in MB.
33851
33870
  *
33852
- * Intended as a SAFETY ceiling with the TTL as the effective cap — but check
33853
- * which one is actually binding before reasoning from that. At the shipped
33854
- * 1024 MB and a 2 800 ms TTL, a 4K camera hits the CEILING first (~43 frames
33855
- * at ~24 MB each) and the TTL never gets to expire anything; `leaseMb` /
33856
- * `leaseFrames` on the metrics line say which. When the ceiling binds, a
33857
- * change that admits fewer frames buys retention WINDOW at constant RAM
33858
- * rather than giving RAM back — lower this knob if RAM is what you wanted.
33871
+ * Since 2026-08-13 this is a SAFETY ceiling and nothing else: `holdFrames`
33872
+ * is what decides how much is held, and the ceiling is the number above which
33873
+ * something is wrong. Before that it was the effective cap — at 1024 MB with
33874
+ * a 2 800 ms TTL a 4K camera sat pinned at `leaseMb:1020, leaseFrames:43`
33875
+ * with the TTL expiring nothing, which is exactly the confusion the hold
33876
+ * removes. `leaseMb` / `leaseFrames` still say what is resident.
33859
33877
  * `0` DISABLES the lease entirely and falls the worker back to the tiny
33860
33878
  * leak-prone GPU surface ring (~85% crop miss; that is what the lease exists
33861
33879
  * to replace).
@@ -33881,22 +33899,45 @@ object({
33881
33899
  * there is the signal that some caller names frames outside the inference set
33882
33900
  * and that this must go back to `all`.
33883
33901
  */
33884
- admission: NativeLeaseAdmissionSchema
33902
+ admission: NativeLeaseAdmissionSchema,
33903
+ /**
33904
+ * RAM ceiling per decode worker, in MB, for the SUBJECT TILES — the
33905
+ * compressed native crops the worker cuts at the moment a frame's detection
33906
+ * result arrives, and keeps long after the frame itself is freed.
33907
+ *
33908
+ * This is the knob that replaced the old retention window, and it buys about
33909
+ * three orders of magnitude more of it: a tile is one subject at native
33910
+ * resolution, JPEG-encoded (~60-120 KB on a 4K person), against ~24.9 MB for
33911
+ * the frame it was cut from. A frame on which nothing was detected costs
33912
+ * nothing at all, which is the real change — the old lease paid per FRAME and
33913
+ * was interrogated per SUBJECT.
33914
+ *
33915
+ * `0` DISABLES tiles, leaving only the hold window and the ≤640 RAM
33916
+ * fallback — i.e. the pre-2026-08-13 miss profile. Set it there only to
33917
+ * reproduce that.
33918
+ */
33919
+ tileBudgetMb: number().int().min(0).max(1024)
33885
33920
  });
33886
33921
  /**
33887
- * The values in force when the operator has set nothing — byte-for-byte the
33888
- * constants the decode worker shipped with as env-var defaults, so making these
33889
- * settings changed no behaviour on the day it landed.
33922
+ * The values in force when the operator has set nothing.
33923
+ *
33924
+ * `budgetMb` stays at 1024 on the day the hold landed, deliberately: it stopped
33925
+ * being the retention window and became the OOM ceiling, and lowering a ceiling
33926
+ * in the same change that redefines it would make a regression and a retune
33927
+ * indistinguishable. Cut it once `tileHits` / `holdOverflow` have been read on
33928
+ * live traffic.
33890
33929
  */
33891
33930
  var DEFAULT_NATIVE_LEASE_SETTINGS = {
33892
- ttlMs: 1200,
33931
+ holdFrames: 8,
33893
33932
  budgetMb: 1024,
33894
33933
  activityMs: 15e3,
33934
+ tileBudgetMb: 64,
33895
33935
  admission: "inferred"
33896
33936
  };
33897
- DEFAULT_NATIVE_LEASE_SETTINGS.ttlMs;
33937
+ DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames;
33898
33938
  DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
33899
33939
  DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
33940
+ DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
33900
33941
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
33901
33942
  /**
33902
33943
  * Pure fuzzy matcher for adoption location import. Normalized
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homematic",
3
- "version": "1.2.15",
3
+ "version": "1.2.16",
4
4
  "description": "Homematic / HomematicIP (CCU3 / RaspberryMatic) device-provider addon for CamStack — wraps the nodehomematic library",
5
5
  "keywords": [
6
6
  "camstack",