@camstack/addon-post-analysis 1.2.75 → 1.2.77
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/{dist-DseTbokb.js → dist-Bs6oboqZ.js} +354 -50
- package/dist/{dist-BA7ThdEJ.mjs → dist-CxeFHSSV.mjs} +349 -51
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-YItCXmVN.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BP6Na43Q.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DFckpC3K.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-DRCS32f_.mjs → hostInit-DTI0jC8p.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +2697 -1090
- package/dist/pipeline-analytics/index.mjs +2660 -1090
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BgpX7t50.mjs +0 -26
|
@@ -5427,12 +5427,6 @@ Object.fromEntries([
|
|
|
5427
5427
|
icon: "shapes",
|
|
5428
5428
|
order: 38
|
|
5429
5429
|
},
|
|
5430
|
-
{
|
|
5431
|
-
id: "scenes",
|
|
5432
|
-
label: "Scenes",
|
|
5433
|
-
icon: "scan-eye",
|
|
5434
|
-
order: 36
|
|
5435
|
-
},
|
|
5436
5430
|
{
|
|
5437
5431
|
id: "analytics",
|
|
5438
5432
|
label: "Analytics",
|
|
@@ -12706,6 +12700,17 @@ var LlmImageSchema = object({
|
|
|
12706
12700
|
bytes: _instanceof(Uint8Array),
|
|
12707
12701
|
mimeType: string()
|
|
12708
12702
|
});
|
|
12703
|
+
/**
|
|
12704
|
+
* Retry policy. `enabled: false` is NOT the same as `maxAttempts: 1` in intent —
|
|
12705
|
+
* the flag is what a consumer table flips, the count is what the operator tunes.
|
|
12706
|
+
* A retry doubles the wall time of a call, so the two gates that run inside a
|
|
12707
|
+
* notification's budget keep it off (see `CONSUMER_RETRY_POLICY` in addon-ai).
|
|
12708
|
+
*/
|
|
12709
|
+
var LlmRetryPolicySchema = object({
|
|
12710
|
+
enabled: boolean().default(false),
|
|
12711
|
+
/** Total attempts INCLUDING the first. 1 = no retry. */
|
|
12712
|
+
maxAttempts: number().int().min(1).max(5).default(1)
|
|
12713
|
+
});
|
|
12709
12714
|
var LlmGenerateBaseInputSchema = object({
|
|
12710
12715
|
/** Collection routing (the notification-output posture). */
|
|
12711
12716
|
addonId: string().optional(),
|
|
@@ -12720,7 +12725,28 @@ var LlmGenerateBaseInputSchema = object({
|
|
|
12720
12725
|
jsonSchema: record(string(), unknown()).optional(),
|
|
12721
12726
|
/** Per-call override of the profile default. */
|
|
12722
12727
|
maxTokens: number().int().positive().optional(),
|
|
12723
|
-
temperature: number().optional()
|
|
12728
|
+
temperature: number().optional(),
|
|
12729
|
+
/** Per-call override of the profile default (nucleus sampling). */
|
|
12730
|
+
topP: number().min(0).max(1).optional(),
|
|
12731
|
+
/** Per-call override of the profile default (top-k sampling). */
|
|
12732
|
+
topK: number().int().positive().optional(),
|
|
12733
|
+
/** Per-call override of `profile.timeoutMs` — the total generation bound. */
|
|
12734
|
+
timeoutMs: number().int().positive().optional(),
|
|
12735
|
+
/** Per-call override; beats both the consumer table and the profile. */
|
|
12736
|
+
retry: LlmRetryPolicySchema.optional(),
|
|
12737
|
+
/**
|
|
12738
|
+
* Caller-minted id that makes this generation CANCELLABLE.
|
|
12739
|
+
*
|
|
12740
|
+
* Without it a caller that stops waiting cannot stop the work: the gates race
|
|
12741
|
+
* the call against 8 s and free their own slot when the timer wins, while the
|
|
12742
|
+
* generation upstream keeps running to `profile.timeoutMs` — 60 s by default,
|
|
12743
|
+
* on a single-threaded local model. The per-camera bound then counts WAITS,
|
|
12744
|
+
* not generations, and the real load is unbounded.
|
|
12745
|
+
*
|
|
12746
|
+
* `AbortSignal` cannot cross a process boundary; an id can. Pass one here and
|
|
12747
|
+
* `llm.cancel({ requestId })` tears the socket down.
|
|
12748
|
+
*/
|
|
12749
|
+
requestId: string().optional()
|
|
12724
12750
|
});
|
|
12725
12751
|
/**
|
|
12726
12752
|
* `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
|
|
@@ -12759,8 +12785,49 @@ var ManagedRuntimeConfigSchema = object({
|
|
|
12759
12785
|
gpuLayers: number().int().default(0),
|
|
12760
12786
|
/** Default: cpus-2, clamped ≥1 (resolved node-side). */
|
|
12761
12787
|
threads: number().int().optional(),
|
|
12762
|
-
/** Concurrent slots. */
|
|
12788
|
+
/** Concurrent slots (`--parallel`). */
|
|
12763
12789
|
parallel: number().int().default(1),
|
|
12790
|
+
/** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
|
|
12791
|
+
batchSize: number().int().positive().optional(),
|
|
12792
|
+
/** Physical batch / micro-batch (`-ub`). */
|
|
12793
|
+
ubatchSize: number().int().positive().optional(),
|
|
12794
|
+
/**
|
|
12795
|
+
* `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
|
|
12796
|
+
* is a no-op elsewhere, so it is offered rather than assumed.
|
|
12797
|
+
*/
|
|
12798
|
+
flashAttention: boolean().default(false),
|
|
12799
|
+
/**
|
|
12800
|
+
* `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
|
|
12801
|
+
* inference. Costs the full model size in resident memory — which is exactly
|
|
12802
|
+
* what the RAM budget is counting.
|
|
12803
|
+
*/
|
|
12804
|
+
mlock: boolean().default(false),
|
|
12805
|
+
/**
|
|
12806
|
+
* `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
|
|
12807
|
+
* start, but avoids the page-fault stalls a network or spinning-disk model
|
|
12808
|
+
* store produces on every first token.
|
|
12809
|
+
*/
|
|
12810
|
+
noMmap: boolean().default(false),
|
|
12811
|
+
/** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
|
|
12812
|
+
* cheapest way to fit a longer context in the same RAM. */
|
|
12813
|
+
cacheTypeK: _enum([
|
|
12814
|
+
"f32",
|
|
12815
|
+
"f16",
|
|
12816
|
+
"q8_0",
|
|
12817
|
+
"q5_1",
|
|
12818
|
+
"q5_0",
|
|
12819
|
+
"q4_1",
|
|
12820
|
+
"q4_0"
|
|
12821
|
+
]).optional(),
|
|
12822
|
+
cacheTypeV: _enum([
|
|
12823
|
+
"f32",
|
|
12824
|
+
"f16",
|
|
12825
|
+
"q8_0",
|
|
12826
|
+
"q5_1",
|
|
12827
|
+
"q5_0",
|
|
12828
|
+
"q4_1",
|
|
12829
|
+
"q4_0"
|
|
12830
|
+
]).optional(),
|
|
12764
12831
|
/** Else lazy: first generate boots it. */
|
|
12765
12832
|
autoStart: boolean().default(false),
|
|
12766
12833
|
/** 0 = never; frees RAM after quiet periods. */
|
|
@@ -12848,10 +12915,44 @@ var LlmProfileSchema = object({
|
|
|
12848
12915
|
baseUrl: string().optional(),
|
|
12849
12916
|
/** ConfigUISchema type:'password' — never round-trips (spec §5). */
|
|
12850
12917
|
apiKey: string().optional(),
|
|
12918
|
+
/** Vision on/off. A vision call against a `false` profile is REFUSED, never
|
|
12919
|
+
* degraded to text — that shipped once and produced a confident answer to a
|
|
12920
|
+
* question about a picture nobody sent. */
|
|
12851
12921
|
supportsVision: boolean(),
|
|
12852
12922
|
temperature: number().min(0).max(2).optional(),
|
|
12923
|
+
/** Nucleus sampling. Every wire we speak has it. */
|
|
12924
|
+
topP: number().min(0).max(1).optional(),
|
|
12925
|
+
/** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
|
|
12926
|
+
* wire does, and the client drops it there (measured: the request body gets
|
|
12927
|
+
* `top_p` and no `top_k`). The profile editor hides the field wherever it
|
|
12928
|
+
* would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
|
|
12929
|
+
topK: number().int().positive().optional(),
|
|
12853
12930
|
maxTokens: number().int().positive().optional(),
|
|
12931
|
+
/** Prompt context window. Advisory for cloud kinds (they enforce their own);
|
|
12932
|
+
* for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
|
|
12933
|
+
* the model with, so it is the one field that changes a PROCESS. */
|
|
12934
|
+
contextLength: number().int().positive().optional(),
|
|
12935
|
+
/** Default system prompt. A caller's `system` REPLACES it (never appends —
|
|
12936
|
+
* two system prompts fighting is worse than either alone). */
|
|
12937
|
+
systemPrompt: string().optional(),
|
|
12938
|
+
/** Total generation bound — the only one a unary call has. */
|
|
12854
12939
|
timeoutMs: number().int().positive().default(6e4),
|
|
12940
|
+
/** Wait for response headers only. */
|
|
12941
|
+
connectTimeoutMs: number().int().positive().default(1e4),
|
|
12942
|
+
/** Accepted, but no output yet — a cold GPU load lives here. */
|
|
12943
|
+
firstTokenTimeoutMs: number().int().positive().default(12e4),
|
|
12944
|
+
/** Output started then stopped. */
|
|
12945
|
+
idleTimeoutMs: number().int().positive().default(6e4),
|
|
12946
|
+
/** Profile-level default. The per-consumer table and a per-call override
|
|
12947
|
+
* both beat it — see `resolveRetryPolicy`. */
|
|
12948
|
+
retry: LlmRetryPolicySchema.default({
|
|
12949
|
+
enabled: false,
|
|
12950
|
+
maxAttempts: 1
|
|
12951
|
+
}),
|
|
12952
|
+
/** Whether this profile may use tools. The tool-call plumbing rides the
|
|
12953
|
+
* library; the REGISTRY of callable tools is ours and is empty in v1, so a
|
|
12954
|
+
* `true` here buys the wiring, not behaviour, until tools are registered. */
|
|
12955
|
+
toolsEnabled: boolean().default(false),
|
|
12855
12956
|
extraHeaders: record(string(), string()).optional(),
|
|
12856
12957
|
/** kind === 'managed-local' only (spec §4). */
|
|
12857
12958
|
runtime: ManagedRuntimeConfigSchema.optional()
|
|
@@ -12913,7 +13014,10 @@ var ProfileRefInputSchema = object({
|
|
|
12913
13014
|
addonId: string(),
|
|
12914
13015
|
profileId: string()
|
|
12915
13016
|
});
|
|
12916
|
-
method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({
|
|
13017
|
+
method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({
|
|
13018
|
+
addonId: string().optional(),
|
|
13019
|
+
requestId: string()
|
|
13020
|
+
}), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
|
|
12917
13021
|
kind: "mutation",
|
|
12918
13022
|
auth: "admin"
|
|
12919
13023
|
}), method(ProfileRefInputSchema, _void(), {
|
|
@@ -14755,28 +14859,36 @@ var NcOccupancyConditionSchema = object({
|
|
|
14755
14859
|
/**
|
|
14756
14860
|
* Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
|
|
14757
14861
|
*
|
|
14758
|
-
*
|
|
14759
|
-
*
|
|
14760
|
-
*
|
|
14761
|
-
*
|
|
14762
|
-
*
|
|
14763
|
-
*
|
|
14764
|
-
*
|
|
14765
|
-
*
|
|
14766
|
-
*
|
|
14767
|
-
*
|
|
14768
|
-
*
|
|
14769
|
-
*
|
|
14770
|
-
*
|
|
14771
|
-
*
|
|
14772
|
-
*
|
|
14773
|
-
*
|
|
14774
|
-
*
|
|
14775
|
-
*
|
|
14776
|
-
*
|
|
14777
|
-
*
|
|
14778
|
-
*
|
|
14779
|
-
* `
|
|
14862
|
+
* **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
|
|
14863
|
+
* rule is in is not a stored field — it is WHICH FILTER the rule carries, so
|
|
14864
|
+
* there is no second switch that can disagree with the first and every rule
|
|
14865
|
+
* authored before the decision migrates for free (`audioModeOf`):
|
|
14866
|
+
*
|
|
14867
|
+
* - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
|
|
14868
|
+
* classifier labels with one of them. No window, no percentage:
|
|
14869
|
+
* `hitPercent` and `samplingSeconds` are ignored, and the rule's own
|
|
14870
|
+
* `throttle` cooldown is the only brake. The per-label confidence floor is
|
|
14871
|
+
* the analyzer's (`classificationMinScore`, per device) — a label only
|
|
14872
|
+
* reaches this condition if the classifier was already confident enough.
|
|
14873
|
+
* - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
|
|
14874
|
+
* the condition: at least `hitPercent`% of the samples over
|
|
14875
|
+
* `samplingSeconds` must be at or above `dbThreshold` dBFS (see
|
|
14876
|
+
* {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
|
|
14877
|
+
* must be FULL before it can match — a window open for two of its ten
|
|
14878
|
+
* seconds is 100% of nothing.
|
|
14879
|
+
*
|
|
14880
|
+
* **Why label mode has no window.** It had one, and it never fired: the
|
|
14881
|
+
* analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
|
|
14882
|
+
* of them per episode, even through continuous crying. The measured maximum
|
|
14883
|
+
* `hitPercent` over the whole live history was 40 — under the shipped default
|
|
14884
|
+
* of 60, so a label rule could not fire at all, ever. A percentage of frames is
|
|
14885
|
+
* the wrong question to ask of a sparse classifier.
|
|
14886
|
+
*
|
|
14887
|
+
* **Fail-closed when NEITHER is given** — every sample would be a trivial hit
|
|
14888
|
+
* and the rule would fire on silence. The schema cannot express "exactly one
|
|
14889
|
+
* of" without becoming a ZodEffects the cap path would have to special-case, so
|
|
14890
|
+
* the exclusivity is enforced where every editor writes (`patchAudio`) and a
|
|
14891
|
+
* legacy rule carrying both resolves to LABEL (the mode that fires).
|
|
14780
14892
|
*
|
|
14781
14893
|
* Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
|
|
14782
14894
|
* `audio-*` ids). Both spellings are accepted — the matcher normalizes the
|
|
@@ -14784,13 +14896,13 @@ var NcOccupancyConditionSchema = object({
|
|
|
14784
14896
|
* an operator who typed `dog` mean the same thing.
|
|
14785
14897
|
*/
|
|
14786
14898
|
var NcAudioConditionSchema = object({
|
|
14787
|
-
/**
|
|
14899
|
+
/** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
|
|
14788
14900
|
labels: array(string().min(1)).min(1).optional(),
|
|
14789
|
-
/**
|
|
14901
|
+
/** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
|
|
14790
14902
|
dbThreshold: number().min(-96).max(0).optional(),
|
|
14791
|
-
/**
|
|
14903
|
+
/** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
|
|
14792
14904
|
hitPercent: number().int().min(1).max(100).default(60),
|
|
14793
|
-
/**
|
|
14905
|
+
/** LEVEL MODE ONLY: length of the sampling window in seconds. */
|
|
14794
14906
|
samplingSeconds: number().int().min(1).max(300).default(10)
|
|
14795
14907
|
});
|
|
14796
14908
|
/**
|
|
@@ -14928,13 +15040,81 @@ var NcRuleActionsSchema = object({
|
|
|
14928
15040
|
*/
|
|
14929
15041
|
buttons: array(NcRuleNotificationButtonSchema).max(8).optional()
|
|
14930
15042
|
});
|
|
15043
|
+
/**
|
|
15044
|
+
* "This rule applies only while `deviceId` is in one of `states`."
|
|
15045
|
+
*
|
|
15046
|
+
* The states are the DEVICE's own vocabulary — `AlarmState` for a panel,
|
|
15047
|
+
* `on`/`off` for a switch — not a normalised set, because normalising would
|
|
15048
|
+
* make the condition lie about devices whose states have no equivalent.
|
|
15049
|
+
*
|
|
15050
|
+
* An unreadable state does NOT match: see the engine's fail-closed gate. A
|
|
15051
|
+
* condition that fired on "I could not read it" would be worse than no gate.
|
|
15052
|
+
*/
|
|
15053
|
+
var NcDeviceStateConditionSchema = object({
|
|
15054
|
+
deviceId: number().int(),
|
|
15055
|
+
/** Any of these matches. */
|
|
15056
|
+
states: array(string().min(1)).min(1)
|
|
15057
|
+
});
|
|
15058
|
+
/**
|
|
15059
|
+
* "This rule applies only while scene `sceneId` is `matched` / `diverged`."
|
|
15060
|
+
*
|
|
15061
|
+
* A GATE, not a trigger. `occupancy` and `audio` each DISCRIMINATE their rule —
|
|
15062
|
+
* carrying one makes the rule fire on that subject and nothing else. Scene is
|
|
15063
|
+
* the other shape entirely, the `deviceState` shape: it narrows a rule that
|
|
15064
|
+
* already has a trigger ("tell me about a person at the front door, but only
|
|
15065
|
+
* while the bin is still out"). That is why it composes with every delivery
|
|
15066
|
+
* instead of owning one, and why no new `NcDelivery` member and no new subject
|
|
15067
|
+
* kind exist for it — see D159.
|
|
15068
|
+
*
|
|
15069
|
+
* ── Identity ───────────────────────────────────────────────────────────────
|
|
15070
|
+
* `sceneId` is `SceneMonitor.id`, a `randomUUID()` minted by `createScene` —
|
|
15071
|
+
* globally unique, so it needs no device to disambiguate it. `deviceId` is
|
|
15072
|
+
* carried as a HINT for the editor and for the log line, never as part of the
|
|
15073
|
+
* lookup key: a rule whose hint drifted must still gate correctly.
|
|
15074
|
+
*
|
|
15075
|
+
* ── Which boolean ──────────────────────────────────────────────────────────
|
|
15076
|
+
* `latched` ABSENT means "whatever the scene itself says" — `SceneMonitor.emit`
|
|
15077
|
+
* already declares which boolean drives notification rules, and a second knob
|
|
15078
|
+
* that could disagree with it is exactly the D62 failure. Set it only to
|
|
15079
|
+
* override one rule against the scene's own default.
|
|
15080
|
+
*
|
|
15081
|
+
* - LIVE reading (`emit`/`latched` resolve to live): passes iff
|
|
15082
|
+
* `verdict === requiredState`. `unknown` — no reference for this light, view
|
|
15083
|
+
* shifted, no snapshot — passes NEITHER. A scene that cannot judge is not
|
|
15084
|
+
* evidence, in either direction.
|
|
15085
|
+
* - LATCHED reading: passes iff `latched === (requiredState === 'diverged')`.
|
|
15086
|
+
* The latch is a durable fact about the past ("it has diverged since I armed
|
|
15087
|
+
* it"), so a camera that has gone dark does not clear it — that is the whole
|
|
15088
|
+
* reason the operator asked for a latch.
|
|
15089
|
+
*
|
|
15090
|
+
* The gate reads an in-memory mirror (`NcSceneStateCache`) refreshed OFF the
|
|
15091
|
+
* event path, never the cap: D49. A mirror that has never loaded, or a scene it
|
|
15092
|
+
* does not carry, reads absent and the rule does NOT fire — fail closed, and
|
|
15093
|
+
* said out loud in the log rather than dropped in silence.
|
|
15094
|
+
*/
|
|
15095
|
+
var NcSceneConditionSchema = object({
|
|
15096
|
+
/** `SceneMonitor.id` — the uuid the cap mints. The whole lookup key. */
|
|
15097
|
+
sceneId: string().min(1),
|
|
15098
|
+
/** The camera the scene lives on. A hint for the editor and the log line. */
|
|
15099
|
+
deviceId: number().int().optional(),
|
|
15100
|
+
/** The state the scene must be in for the rule to fire. */
|
|
15101
|
+
requiredState: _enum(["matched", "diverged"]),
|
|
15102
|
+
/**
|
|
15103
|
+
* Read the LATCH (`true`) or the LIVE verdict (`false`). Absent = follow the
|
|
15104
|
+
* scene's own `emit` field, which is the only place that decision belongs.
|
|
15105
|
+
*/
|
|
15106
|
+
latched: boolean().optional()
|
|
15107
|
+
});
|
|
14931
15108
|
var NcConditionsSchema = object({
|
|
14932
15109
|
/** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
|
|
14933
|
-
deviceState:
|
|
14934
|
-
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
|
|
15110
|
+
deviceState: NcDeviceStateConditionSchema.optional(),
|
|
15111
|
+
/**
|
|
15112
|
+
* Gate on a SCENE's state — "only while the bin is still out". Composes with
|
|
15113
|
+
* every trigger (detection, occupancy, audio, sensor, package, track-end);
|
|
15114
|
+
* unlike `occupancy`/`audio` it discriminates nothing. See
|
|
15115
|
+
* {@link NcSceneCondition} and D159.
|
|
15116
|
+
*/
|
|
15117
|
+
scene: NcSceneConditionSchema.optional(),
|
|
14938
15118
|
/** Device scope — absent = all devices. */
|
|
14939
15119
|
devices: array(number()).optional(),
|
|
14940
15120
|
/** Detector class names (any overlap with the record's class set). */
|
|
@@ -15572,6 +15752,7 @@ var NcConditionDescriptorSchema = object({
|
|
|
15572
15752
|
"occupancy",
|
|
15573
15753
|
"audio",
|
|
15574
15754
|
"deviceState",
|
|
15755
|
+
"scene",
|
|
15575
15756
|
"systemEvent"
|
|
15576
15757
|
]),
|
|
15577
15758
|
operator: _enum([
|
|
@@ -15926,6 +16107,21 @@ var NC_CONDITION_CATALOG = [
|
|
|
15926
16107
|
phase: "P2",
|
|
15927
16108
|
description: "Only fire while another device is in one of the chosen states — the alarm armed, a switch on, a contact closed. A state that cannot be read does NOT fire."
|
|
15928
16109
|
},
|
|
16110
|
+
{
|
|
16111
|
+
id: "scene",
|
|
16112
|
+
group: "scope",
|
|
16113
|
+
label: "Scene state",
|
|
16114
|
+
valueType: "scene",
|
|
16115
|
+
operator: "in",
|
|
16116
|
+
appliesTo: [
|
|
16117
|
+
"immediate",
|
|
16118
|
+
"track-end",
|
|
16119
|
+
"device-event",
|
|
16120
|
+
"package-event"
|
|
16121
|
+
],
|
|
16122
|
+
phase: "P3",
|
|
16123
|
+
description: "Only fire while a scene is matched (the baseline is what we see) or diverged (it demonstrably is not) — “tell me about a person at the door, but only while the bin is still out”. A scene that cannot judge, or one this hub has not read yet, does NOT fire."
|
|
16124
|
+
},
|
|
15929
16125
|
{
|
|
15930
16126
|
id: "sensorKinds",
|
|
15931
16127
|
group: "device",
|
|
@@ -27505,10 +27701,22 @@ method(object({
|
|
|
27505
27701
|
* thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
|
|
27506
27702
|
* vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
|
|
27507
27703
|
*
|
|
27508
|
-
*
|
|
27509
|
-
*
|
|
27510
|
-
*
|
|
27511
|
-
*
|
|
27704
|
+
* **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
|
|
27705
|
+
* which put a "Scenes" tab on one camera's detail page. That is the wrong shape
|
|
27706
|
+
* for the thing: a scene is a standing question about the property ("is the bin
|
|
27707
|
+
* still out"), and the operator's question is "which of my scenes have tripped",
|
|
27708
|
+
* across every camera at once — not "what does camera 617 think". Buried one
|
|
27709
|
+
* camera deep it also could not be found. The surface is now a top-level admin
|
|
27710
|
+
* page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
|
|
27711
|
+
* picks the camera inside the create flow, the same shape Events and Faces have.
|
|
27712
|
+
*
|
|
27713
|
+
* The consequence to keep in mind: `host/scene-monitor-editor` is gone from
|
|
27714
|
+
* `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
|
|
27715
|
+
* directions, so a registration nobody declares fails exactly as loudly as a
|
|
27716
|
+
* declaration nobody registers. The editor is imported directly by the page.
|
|
27717
|
+
*
|
|
27718
|
+
* `status.kind:'push'` — the engine pushes on every hysteresis flip /
|
|
27719
|
+
* availability change; consumers never poll.
|
|
27512
27720
|
*/
|
|
27513
27721
|
/** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
|
|
27514
27722
|
* captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
|
|
@@ -27663,12 +27871,6 @@ var sceneMonitorCapability = {
|
|
|
27663
27871
|
kind: "wrapper",
|
|
27664
27872
|
defaultActive: true,
|
|
27665
27873
|
deviceTypes: [DeviceType.Camera],
|
|
27666
|
-
deviceConfig: { ui: {
|
|
27667
|
-
kind: "widget",
|
|
27668
|
-
widgetId: "host/scene-monitor-editor",
|
|
27669
|
-
tab: "scenes",
|
|
27670
|
-
label: "Scenes"
|
|
27671
|
-
} },
|
|
27672
27874
|
methods: {
|
|
27673
27875
|
listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
|
|
27674
27876
|
createScene: method(object({
|
|
@@ -28257,12 +28459,64 @@ var NetworkAddressSchema = object({
|
|
|
28257
28459
|
family: string(),
|
|
28258
28460
|
internal: boolean()
|
|
28259
28461
|
});
|
|
28462
|
+
/**
|
|
28463
|
+
* Provenance of the site coordinates, and the whole reason this is not just two
|
|
28464
|
+
* numbers.
|
|
28465
|
+
*
|
|
28466
|
+
* - `operator-set` — a human typed it, or accepted a detection. Authoritative;
|
|
28467
|
+
* nothing overwrites it.
|
|
28468
|
+
* - `derived-from-ip` — the hub geolocated its own public IP once, because a
|
|
28469
|
+
* default that is right to a few kilometres beats the coarse UTC clock split
|
|
28470
|
+
* the sun-times consumers otherwise fall back to.
|
|
28471
|
+
*
|
|
28472
|
+
* The UI shows which one it is. An operator who cannot tell a guess from their
|
|
28473
|
+
* own input will eventually trust the guess.
|
|
28474
|
+
*/
|
|
28475
|
+
var SiteLocationSourceSchema = _enum(["operator-set", "derived-from-ip"]);
|
|
28476
|
+
/**
|
|
28477
|
+
* The read shape: the location plus the honest state of the one-shot derivation.
|
|
28478
|
+
*
|
|
28479
|
+
* `derivationAttemptedAt` is what makes the "one call, ever" contract
|
|
28480
|
+
* inspectable. When it is set and `location` is null, the geo-IP lookup ran and
|
|
28481
|
+
* failed; the hub will NOT try again on its own — the fallback is declared
|
|
28482
|
+
* (consumers degrade to their own last resort) and the operator either types the
|
|
28483
|
+
* coordinates or presses detect.
|
|
28484
|
+
*/
|
|
28485
|
+
var SiteLocationStatusSchema = object({
|
|
28486
|
+
location: object({
|
|
28487
|
+
/** WGS84 decimal degrees. */
|
|
28488
|
+
latitude: number().min(-90).max(90),
|
|
28489
|
+
longitude: number().min(-180).max(180),
|
|
28490
|
+
source: SiteLocationSourceSchema,
|
|
28491
|
+
/** Epoch ms the value was last written. */
|
|
28492
|
+
updatedAt: number(),
|
|
28493
|
+
/**
|
|
28494
|
+
* Human-readable place the geo-IP service reported ("Napoli, IT"). Display
|
|
28495
|
+
* only — never parsed, never matched on. Absent for an operator-typed value.
|
|
28496
|
+
*/
|
|
28497
|
+
label: string().optional()
|
|
28498
|
+
}).nullable(),
|
|
28499
|
+
derivationAttemptedAt: number().nullable(),
|
|
28500
|
+
/** Why the last derivation failed, for the UI to show instead of a shrug. */
|
|
28501
|
+
derivationError: string().nullable()
|
|
28502
|
+
});
|
|
28503
|
+
/** `null` clears the location and re-arms nothing — the derivation stays spent. */
|
|
28504
|
+
var SetSiteLocationInputSchema = object({
|
|
28505
|
+
latitude: number().min(-90).max(90),
|
|
28506
|
+
longitude: number().min(-180).max(180)
|
|
28507
|
+
}).nullable();
|
|
28260
28508
|
method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
|
|
28261
28509
|
kind: "mutation",
|
|
28262
28510
|
auth: "admin"
|
|
28263
28511
|
}), method(_void(), _void(), {
|
|
28264
28512
|
kind: "mutation",
|
|
28265
28513
|
auth: "admin"
|
|
28514
|
+
}), method(_void(), SiteLocationStatusSchema), method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
|
|
28515
|
+
kind: "mutation",
|
|
28516
|
+
auth: "admin"
|
|
28517
|
+
}), method(_void(), SiteLocationStatusSchema, {
|
|
28518
|
+
kind: "mutation",
|
|
28519
|
+
auth: "admin"
|
|
28266
28520
|
});
|
|
28267
28521
|
/**
|
|
28268
28522
|
* Tamper / case-open detection sensor. Drives Home Assistant
|
|
@@ -32451,6 +32705,12 @@ Object.freeze({
|
|
|
32451
32705
|
addonId: null,
|
|
32452
32706
|
access: "create"
|
|
32453
32707
|
},
|
|
32708
|
+
"llm.cancel": {
|
|
32709
|
+
capName: "llm",
|
|
32710
|
+
capScope: "system",
|
|
32711
|
+
addonId: null,
|
|
32712
|
+
access: "create"
|
|
32713
|
+
},
|
|
32454
32714
|
"llm.deleteModel": {
|
|
32455
32715
|
capName: "llm",
|
|
32456
32716
|
capScope: "system",
|
|
@@ -35385,6 +35645,12 @@ Object.freeze({
|
|
|
35385
35645
|
addonId: null,
|
|
35386
35646
|
access: "create"
|
|
35387
35647
|
},
|
|
35648
|
+
"system.detectSiteLocation": {
|
|
35649
|
+
capName: "system",
|
|
35650
|
+
capScope: "system",
|
|
35651
|
+
addonId: null,
|
|
35652
|
+
access: "create"
|
|
35653
|
+
},
|
|
35388
35654
|
"system.featureFlags": {
|
|
35389
35655
|
capName: "system",
|
|
35390
35656
|
capScope: "system",
|
|
@@ -35403,6 +35669,12 @@ Object.freeze({
|
|
|
35403
35669
|
addonId: null,
|
|
35404
35670
|
access: "view"
|
|
35405
35671
|
},
|
|
35672
|
+
"system.getSiteLocation": {
|
|
35673
|
+
capName: "system",
|
|
35674
|
+
capScope: "system",
|
|
35675
|
+
addonId: null,
|
|
35676
|
+
access: "view"
|
|
35677
|
+
},
|
|
35406
35678
|
"system.health": {
|
|
35407
35679
|
capName: "system",
|
|
35408
35680
|
capScope: "system",
|
|
@@ -35427,6 +35699,12 @@ Object.freeze({
|
|
|
35427
35699
|
addonId: null,
|
|
35428
35700
|
access: "create"
|
|
35429
35701
|
},
|
|
35702
|
+
"system.setSiteLocation": {
|
|
35703
|
+
capName: "system",
|
|
35704
|
+
capScope: "system",
|
|
35705
|
+
addonId: null,
|
|
35706
|
+
access: "create"
|
|
35707
|
+
},
|
|
35430
35708
|
"terminalSession.adoptLegacyMonitor": {
|
|
35431
35709
|
capName: "terminal-session",
|
|
35432
35710
|
capScope: "system",
|
|
@@ -37684,6 +37962,26 @@ Object.freeze({
|
|
|
37684
37962
|
"network-access": "ingress",
|
|
37685
37963
|
"smtp-provider": "email"
|
|
37686
37964
|
});
|
|
37965
|
+
/**
|
|
37966
|
+
* Which mode this condition is in — the ONE place that question is answered.
|
|
37967
|
+
*
|
|
37968
|
+
* The mode is NOT a stored field, deliberately. It is which filter the rule
|
|
37969
|
+
* carries, so every rule authored before the modes existed migrates for free
|
|
37970
|
+
* and there is no second switch that can disagree with the first (the failure
|
|
37971
|
+
* this repo has shipped twice). `null` = neither filter, which the engine
|
|
37972
|
+
* refuses (see {@link audioIsFailClosed}).
|
|
37973
|
+
*
|
|
37974
|
+
* A LEGACY rule carrying BOTH resolves to `label`: it is the mode that fires,
|
|
37975
|
+
* and the alternative is silently keeping a window the operator can no longer
|
|
37976
|
+
* see in the editor. Nothing new can reach this branch — {@link patchAudio}
|
|
37977
|
+
* clears the other filter on every write.
|
|
37978
|
+
*/
|
|
37979
|
+
function audioModeOf(value) {
|
|
37980
|
+
if (value === void 0) return null;
|
|
37981
|
+
if (value.labels !== void 0 && value.labels.length > 0) return "label";
|
|
37982
|
+
if (value.dbThreshold !== void 0) return "level";
|
|
37983
|
+
return null;
|
|
37984
|
+
}
|
|
37687
37985
|
new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
|
|
37688
37986
|
new Set(["devices", "classes"]);
|
|
37689
37987
|
var WEEKDAY_TO_DAY = {
|
|
@@ -38522,6 +38820,12 @@ Object.defineProperty(exports, "audioMetricsCapability", {
|
|
|
38522
38820
|
return audioMetricsCapability;
|
|
38523
38821
|
}
|
|
38524
38822
|
});
|
|
38823
|
+
Object.defineProperty(exports, "audioModeOf", {
|
|
38824
|
+
enumerable: true,
|
|
38825
|
+
get: function() {
|
|
38826
|
+
return audioModeOf;
|
|
38827
|
+
}
|
|
38828
|
+
});
|
|
38525
38829
|
Object.defineProperty(exports, "boolean", {
|
|
38526
38830
|
enumerable: true,
|
|
38527
38831
|
get: function() {
|