@camstack/addon-osd-manager 0.1.156 → 0.1.157
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/{MaskShapeCanvas-BEVxAjNq.mjs → MaskShapeCanvas-BT714ljC.mjs} +113 -113
- package/dist/MotionZonesSettings-vYt4dibj.mjs +310 -0
- package/dist/{PrivacyMaskSettings-Ci65qYWB.mjs → PrivacyMaskSettings-BiQ78hst.mjs} +35 -60
- package/dist/{SceneMonitorEditor-BG9mHyrC.mjs → SceneMonitorEditor-CJtJIyMf.mjs} +307 -307
- package/dist/_stub.js +11764 -4485
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-BDKvyj3r.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-DdAtpC7z.mjs} +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BtgV-Ym5.mjs +26 -0
- package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-CHxDN0gE.mjs +26 -0
- package/dist/{_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react__loadShare__.js-Bs1t18EM.mjs → _virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react__loadShare__.js-C5jUMd7X.mjs} +1 -1
- package/dist/{_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react_mf_2_dom__loadShare__.js-C0K4h_0e.mjs → _virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react_mf_2_dom__loadShare__.js-qB5uHZAd.mjs} +8 -7
- package/dist/addon-osd-manager.css +1 -1
- package/dist/{hostInit-DdhUwSkK.mjs → hostInit-0Mc7Fm1R.mjs} +2 -2
- package/dist/index.js +537 -31
- package/dist/index.mjs +537 -31
- package/dist/{player-overlays-C_Gmuzev.mjs → player-overlays-BPyna-eI.mjs} +14 -14
- package/dist/remoteEntry.js +1 -1
- package/dist/{responsive-C-9Ko8kt.mjs → responsive-B0yDP9vf.mjs} +31 -27
- package/dist/{scene-monitor-copy-DudW1LZg.mjs → scene-monitor-copy-BzX20O6q.mjs} +13 -13
- package/dist/{square-CpweDmax.mjs → square-BOMOn_6j.mjs} +29 -4
- package/dist/{trash-2-cb_YtxOy.mjs → trash-2-BolGRl2P.mjs} +1 -1
- package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-Dly_n9Qj.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-UY9P_4V2.mjs} +1 -1
- package/package.json +1 -1
- package/dist/MotionZonesSettings-DaDfau7A.mjs +0 -310
- package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BTkfkwrM.mjs +0 -26
- package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DmzTg7Gl.mjs +0 -25
package/dist/index.js
CHANGED
|
@@ -15445,6 +15445,51 @@ var SourceInfoSchema = object({
|
|
|
15445
15445
|
raw: record(string(), unknown()).optional()
|
|
15446
15446
|
});
|
|
15447
15447
|
/**
|
|
15448
|
+
* Coerce common unit-of-measurement string variants to a canonical form
|
|
15449
|
+
* so the UI's display templates can rely on stable strings. Pure
|
|
15450
|
+
* passthrough for unknown units — kept conservative on purpose, the goal
|
|
15451
|
+
* is to normalise the long tail of HA `unit_of_measurement` values that
|
|
15452
|
+
* have multiple historical spellings (`'°C'` vs `'C'` vs `'celsius'`).
|
|
15453
|
+
*
|
|
15454
|
+
* Extend the map as we encounter real-world data. Returning the raw
|
|
15455
|
+
* input for unknown values is intentional: providers should never
|
|
15456
|
+
* lose information silently here.
|
|
15457
|
+
*/
|
|
15458
|
+
function normalizeUnit(raw) {
|
|
15459
|
+
if (raw == null) return void 0;
|
|
15460
|
+
const trimmed = raw.trim();
|
|
15461
|
+
if (trimmed === "") return void 0;
|
|
15462
|
+
const lower = trimmed.toLowerCase();
|
|
15463
|
+
if (lower === "°c" || lower === "c" || lower === "celsius") return "°C";
|
|
15464
|
+
if (lower === "°f" || lower === "f" || lower === "fahrenheit") return "°F";
|
|
15465
|
+
if (lower === "k" || lower === "kelvin") return "K";
|
|
15466
|
+
if (lower === "%" || lower === "percent" || lower === "percentage") return "%";
|
|
15467
|
+
if (lower === "lx" || lower === "lux") return "lx";
|
|
15468
|
+
if (lower === "hpa" || lower === "hectopascal") return "hPa";
|
|
15469
|
+
if (lower === "kpa") return "kPa";
|
|
15470
|
+
if (lower === "pa") return "Pa";
|
|
15471
|
+
if (lower === "mbar" || lower === "millibar") return "mbar";
|
|
15472
|
+
if (lower === "w" || lower === "watt") return "W";
|
|
15473
|
+
if (lower === "kw" || lower === "kilowatt") return "kW";
|
|
15474
|
+
if (lower === "wh" || lower === "watt-hour") return "Wh";
|
|
15475
|
+
if (lower === "kwh" || lower === "kilowatt-hour") return "kWh";
|
|
15476
|
+
if (lower === "v" || lower === "volt") return "V";
|
|
15477
|
+
if (lower === "a" || lower === "amp" || lower === "ampere") return "A";
|
|
15478
|
+
if (lower === "ma" || lower === "milliampere") return "mA";
|
|
15479
|
+
if (lower === "va") return "VA";
|
|
15480
|
+
if (lower === "ppm") return "ppm";
|
|
15481
|
+
if (lower === "ppb") return "ppb";
|
|
15482
|
+
if (lower === "µg/m³" || lower === "ug/m3" || lower === "µg/m3") return "µg/m³";
|
|
15483
|
+
if (lower === "km/h" || lower === "kph") return "km/h";
|
|
15484
|
+
if (lower === "mph") return "mph";
|
|
15485
|
+
if (lower === "m/s") return "m/s";
|
|
15486
|
+
if (lower === "m") return "m";
|
|
15487
|
+
if (lower === "km") return "km";
|
|
15488
|
+
if (lower === "kg") return "kg";
|
|
15489
|
+
if (lower === "g") return "g";
|
|
15490
|
+
return trimmed;
|
|
15491
|
+
}
|
|
15492
|
+
/**
|
|
15448
15493
|
* device-discovery — device-scoped capability for parents that host /
|
|
15449
15494
|
* enumerate child devices (Reolink Hub / NVR, ONVIF gateway, future
|
|
15450
15495
|
* integrations). The parent advertises a list of discoverable children,
|
|
@@ -39296,28 +39341,29 @@ setOverlay: method(object({
|
|
|
39296
39341
|
*
|
|
39297
39342
|
* This cap owns the other half: a per-(camera, slot) BINDING that says
|
|
39298
39343
|
* which value feeds the slot, how it is formatted, and under which
|
|
39299
|
-
* conditions it is shown at all.
|
|
39300
|
-
*
|
|
39301
|
-
|
|
39302
|
-
|
|
39303
|
-
|
|
39304
|
-
|
|
39305
|
-
|
|
39306
|
-
|
|
39307
|
-
|
|
39308
|
-
|
|
39309
|
-
|
|
39310
|
-
|
|
39311
|
-
|
|
39312
|
-
*
|
|
39313
|
-
*
|
|
39314
|
-
* 3. Because the renderer's facts are device STATE and not a detection
|
|
39315
|
-
* record, only a SUBSET of that vocabulary can be answered here.
|
|
39316
|
-
* `setSlotBinding` REJECTS the rest at write time (see
|
|
39317
|
-
* `getConditionSupport`). It does not accept-then-fail-closed: a
|
|
39318
|
-
* condition that can never be true renders a permanently blank
|
|
39319
|
-
* overlay, and a blank overlay looks exactly like a broken camera.
|
|
39344
|
+
* conditions it is shown at all. Named TEMPLATES combine many device
|
|
39345
|
+
* fields into one overlay string (Scrypted OSD Manager parity).
|
|
39346
|
+
*/
|
|
39347
|
+
/** One field reference inside a named multi-device template. */
|
|
39348
|
+
var OsdTemplateFieldSchema = object({
|
|
39349
|
+
deviceId: number().int(),
|
|
39350
|
+
capName: string().min(1).max(64),
|
|
39351
|
+
valuePath: string().min(1).max(64),
|
|
39352
|
+
/** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
|
|
39353
|
+
displayUnit: string().max(16).optional(),
|
|
39354
|
+
maxDecimals: number().int().min(0).max(4).default(1)
|
|
39355
|
+
});
|
|
39356
|
+
/**
|
|
39357
|
+
* A named template: several device fields → one parser string with
|
|
39358
|
+
* `{deviceId.capName.valuePath}` tokens (Scrypted `{deviceId.sensorId}`).
|
|
39320
39359
|
*/
|
|
39360
|
+
var OsdNamedTemplateSchema = object({
|
|
39361
|
+
id: string().min(1).max(64),
|
|
39362
|
+
name: string().min(1).max(64),
|
|
39363
|
+
fields: array(OsdTemplateFieldSchema).max(32).default([]),
|
|
39364
|
+
/** Parser with multi-token placeholders, e.g. `T {12.thermometer.value}°`. */
|
|
39365
|
+
parser: string().max(256).default("")
|
|
39366
|
+
});
|
|
39321
39367
|
/** Where a slot's value comes from. */
|
|
39322
39368
|
var OsdSourceSchema = discriminatedUnion("kind", [
|
|
39323
39369
|
object({
|
|
@@ -39336,11 +39382,17 @@ var OsdSourceSchema = discriminatedUnion("kind", [
|
|
|
39336
39382
|
deviceId: number().int().optional(),
|
|
39337
39383
|
capName: string().min(1).max(64),
|
|
39338
39384
|
/** Dot path inside the slice, e.g. `detected`, `value`, `mode`. */
|
|
39339
|
-
valuePath: string().min(1).max(64)
|
|
39385
|
+
valuePath: string().min(1).max(64),
|
|
39386
|
+
/** Convert TO this unit at render. Absent = no conversion. */
|
|
39387
|
+
displayUnit: string().max(16).optional()
|
|
39340
39388
|
}),
|
|
39341
39389
|
object({
|
|
39342
39390
|
kind: literal("latest-recognition"),
|
|
39343
39391
|
recognition: _enum(["person", "plate"])
|
|
39392
|
+
}),
|
|
39393
|
+
object({
|
|
39394
|
+
kind: literal("template"),
|
|
39395
|
+
templateId: string().min(1).max(64)
|
|
39344
39396
|
})
|
|
39345
39397
|
]);
|
|
39346
39398
|
var OsdSlotBindingSchema = object({
|
|
@@ -39356,7 +39408,10 @@ var OsdSlotBindingSchema = object({
|
|
|
39356
39408
|
* documented workaround for firmwares that reject `.` in overlay text.
|
|
39357
39409
|
*/
|
|
39358
39410
|
maxDecimals: number().int().min(0).max(4).default(1),
|
|
39359
|
-
/**
|
|
39411
|
+
/**
|
|
39412
|
+
* Appended via `${unit}` when no unit conversion produces a label.
|
|
39413
|
+
* Prefer `source.displayUnit` for converted device-state values.
|
|
39414
|
+
*/
|
|
39360
39415
|
unitLabel: string().max(8).optional(),
|
|
39361
39416
|
/** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
|
|
39362
39417
|
valueMap: record(string(), string()).optional(),
|
|
@@ -39510,6 +39565,21 @@ var osdManagerCapability = {
|
|
|
39510
39565
|
renderDevice: method(object({ deviceId: number().int() }), object({ results: array(OsdRenderResultSchema) }), {
|
|
39511
39566
|
kind: "mutation",
|
|
39512
39567
|
auth: "admin"
|
|
39568
|
+
}),
|
|
39569
|
+
/** Every named multi-device template. */
|
|
39570
|
+
listTemplates: method(object({}), object({ templates: array(OsdNamedTemplateSchema) }), { auth: "admin" }),
|
|
39571
|
+
/**
|
|
39572
|
+
* Create or replace a named template by `id`. Returns the stored record
|
|
39573
|
+
* after Zod normalisation.
|
|
39574
|
+
*/
|
|
39575
|
+
upsertTemplate: method(object({ template: OsdNamedTemplateSchema }), object({ template: OsdNamedTemplateSchema }), {
|
|
39576
|
+
kind: "mutation",
|
|
39577
|
+
auth: "admin"
|
|
39578
|
+
}),
|
|
39579
|
+
/** Drop a named template. Bindings that still reference it render unreadable. */
|
|
39580
|
+
deleteTemplate: method(object({ templateId: string().min(1).max(64) }), object({ success: literal(true) }), {
|
|
39581
|
+
kind: "mutation",
|
|
39582
|
+
auth: "admin"
|
|
39513
39583
|
})
|
|
39514
39584
|
}
|
|
39515
39585
|
};
|
|
@@ -45260,6 +45330,265 @@ var zoneRulesCapability = {
|
|
|
45260
45330
|
*/
|
|
45261
45331
|
durability: "restored"
|
|
45262
45332
|
};
|
|
45333
|
+
/**
|
|
45334
|
+
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
45335
|
+
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
45336
|
+
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
45337
|
+
* decimals). K derives from `°C = K − 273.15`.
|
|
45338
|
+
*/
|
|
45339
|
+
var UNIT_TABLE = {
|
|
45340
|
+
"°C": {
|
|
45341
|
+
dimension: "temperature",
|
|
45342
|
+
factor: 1,
|
|
45343
|
+
offset: 0
|
|
45344
|
+
},
|
|
45345
|
+
"°F": {
|
|
45346
|
+
dimension: "temperature",
|
|
45347
|
+
factor: 5 / 9,
|
|
45348
|
+
offset: -160 / 9
|
|
45349
|
+
},
|
|
45350
|
+
K: {
|
|
45351
|
+
dimension: "temperature",
|
|
45352
|
+
factor: 1,
|
|
45353
|
+
offset: -273.15
|
|
45354
|
+
},
|
|
45355
|
+
hPa: {
|
|
45356
|
+
dimension: "pressure",
|
|
45357
|
+
factor: 1,
|
|
45358
|
+
offset: 0
|
|
45359
|
+
},
|
|
45360
|
+
kPa: {
|
|
45361
|
+
dimension: "pressure",
|
|
45362
|
+
factor: 10,
|
|
45363
|
+
offset: 0
|
|
45364
|
+
},
|
|
45365
|
+
Pa: {
|
|
45366
|
+
dimension: "pressure",
|
|
45367
|
+
factor: .01,
|
|
45368
|
+
offset: 0
|
|
45369
|
+
},
|
|
45370
|
+
mbar: {
|
|
45371
|
+
dimension: "pressure",
|
|
45372
|
+
factor: 1,
|
|
45373
|
+
offset: 0
|
|
45374
|
+
},
|
|
45375
|
+
bar: {
|
|
45376
|
+
dimension: "pressure",
|
|
45377
|
+
factor: 1e3,
|
|
45378
|
+
offset: 0
|
|
45379
|
+
},
|
|
45380
|
+
inHg: {
|
|
45381
|
+
dimension: "pressure",
|
|
45382
|
+
factor: 33.8639,
|
|
45383
|
+
offset: 0
|
|
45384
|
+
},
|
|
45385
|
+
mmHg: {
|
|
45386
|
+
dimension: "pressure",
|
|
45387
|
+
factor: 1.33322,
|
|
45388
|
+
offset: 0
|
|
45389
|
+
},
|
|
45390
|
+
psi: {
|
|
45391
|
+
dimension: "pressure",
|
|
45392
|
+
factor: 68.9476,
|
|
45393
|
+
offset: 0
|
|
45394
|
+
},
|
|
45395
|
+
"m/s": {
|
|
45396
|
+
dimension: "speed",
|
|
45397
|
+
factor: 1,
|
|
45398
|
+
offset: 0
|
|
45399
|
+
},
|
|
45400
|
+
"km/h": {
|
|
45401
|
+
dimension: "speed",
|
|
45402
|
+
factor: 1 / 3.6,
|
|
45403
|
+
offset: 0
|
|
45404
|
+
},
|
|
45405
|
+
mph: {
|
|
45406
|
+
dimension: "speed",
|
|
45407
|
+
factor: .44704,
|
|
45408
|
+
offset: 0
|
|
45409
|
+
},
|
|
45410
|
+
kn: {
|
|
45411
|
+
dimension: "speed",
|
|
45412
|
+
factor: .514444,
|
|
45413
|
+
offset: 0
|
|
45414
|
+
},
|
|
45415
|
+
"mm/h": {
|
|
45416
|
+
dimension: "precipitation-rate",
|
|
45417
|
+
factor: 1,
|
|
45418
|
+
offset: 0
|
|
45419
|
+
},
|
|
45420
|
+
"in/h": {
|
|
45421
|
+
dimension: "precipitation-rate",
|
|
45422
|
+
factor: 25.4,
|
|
45423
|
+
offset: 0
|
|
45424
|
+
},
|
|
45425
|
+
m: {
|
|
45426
|
+
dimension: "length",
|
|
45427
|
+
factor: 1,
|
|
45428
|
+
offset: 0
|
|
45429
|
+
},
|
|
45430
|
+
mm: {
|
|
45431
|
+
dimension: "length",
|
|
45432
|
+
factor: .001,
|
|
45433
|
+
offset: 0
|
|
45434
|
+
},
|
|
45435
|
+
cm: {
|
|
45436
|
+
dimension: "length",
|
|
45437
|
+
factor: .01,
|
|
45438
|
+
offset: 0
|
|
45439
|
+
},
|
|
45440
|
+
km: {
|
|
45441
|
+
dimension: "length",
|
|
45442
|
+
factor: 1e3,
|
|
45443
|
+
offset: 0
|
|
45444
|
+
},
|
|
45445
|
+
in: {
|
|
45446
|
+
dimension: "length",
|
|
45447
|
+
factor: .0254,
|
|
45448
|
+
offset: 0
|
|
45449
|
+
},
|
|
45450
|
+
ft: {
|
|
45451
|
+
dimension: "length",
|
|
45452
|
+
factor: .3048,
|
|
45453
|
+
offset: 0
|
|
45454
|
+
},
|
|
45455
|
+
mi: {
|
|
45456
|
+
dimension: "length",
|
|
45457
|
+
factor: 1609.344,
|
|
45458
|
+
offset: 0
|
|
45459
|
+
},
|
|
45460
|
+
lx: {
|
|
45461
|
+
dimension: "illuminance",
|
|
45462
|
+
factor: 1,
|
|
45463
|
+
offset: 0
|
|
45464
|
+
},
|
|
45465
|
+
"W/m²": {
|
|
45466
|
+
dimension: "irradiance",
|
|
45467
|
+
factor: 1,
|
|
45468
|
+
offset: 0
|
|
45469
|
+
},
|
|
45470
|
+
W: {
|
|
45471
|
+
dimension: "power",
|
|
45472
|
+
factor: 1,
|
|
45473
|
+
offset: 0
|
|
45474
|
+
},
|
|
45475
|
+
kW: {
|
|
45476
|
+
dimension: "power",
|
|
45477
|
+
factor: 1e3,
|
|
45478
|
+
offset: 0
|
|
45479
|
+
},
|
|
45480
|
+
VA: {
|
|
45481
|
+
dimension: "apparent-power",
|
|
45482
|
+
factor: 1,
|
|
45483
|
+
offset: 0
|
|
45484
|
+
},
|
|
45485
|
+
Wh: {
|
|
45486
|
+
dimension: "energy",
|
|
45487
|
+
factor: 1,
|
|
45488
|
+
offset: 0
|
|
45489
|
+
},
|
|
45490
|
+
kWh: {
|
|
45491
|
+
dimension: "energy",
|
|
45492
|
+
factor: 1e3,
|
|
45493
|
+
offset: 0
|
|
45494
|
+
},
|
|
45495
|
+
MWh: {
|
|
45496
|
+
dimension: "energy",
|
|
45497
|
+
factor: 1e6,
|
|
45498
|
+
offset: 0
|
|
45499
|
+
},
|
|
45500
|
+
V: {
|
|
45501
|
+
dimension: "voltage",
|
|
45502
|
+
factor: 1,
|
|
45503
|
+
offset: 0
|
|
45504
|
+
},
|
|
45505
|
+
mV: {
|
|
45506
|
+
dimension: "voltage",
|
|
45507
|
+
factor: .001,
|
|
45508
|
+
offset: 0
|
|
45509
|
+
},
|
|
45510
|
+
A: {
|
|
45511
|
+
dimension: "current",
|
|
45512
|
+
factor: 1,
|
|
45513
|
+
offset: 0
|
|
45514
|
+
},
|
|
45515
|
+
mA: {
|
|
45516
|
+
dimension: "current",
|
|
45517
|
+
factor: .001,
|
|
45518
|
+
offset: 0
|
|
45519
|
+
},
|
|
45520
|
+
ppm: {
|
|
45521
|
+
dimension: "concentration-volume",
|
|
45522
|
+
factor: 1,
|
|
45523
|
+
offset: 0
|
|
45524
|
+
},
|
|
45525
|
+
ppb: {
|
|
45526
|
+
dimension: "concentration-volume",
|
|
45527
|
+
factor: .001,
|
|
45528
|
+
offset: 0
|
|
45529
|
+
},
|
|
45530
|
+
"µg/m³": {
|
|
45531
|
+
dimension: "concentration-mass",
|
|
45532
|
+
factor: 1,
|
|
45533
|
+
offset: 0
|
|
45534
|
+
},
|
|
45535
|
+
"mg/m³": {
|
|
45536
|
+
dimension: "concentration-mass",
|
|
45537
|
+
factor: 1e3,
|
|
45538
|
+
offset: 0
|
|
45539
|
+
},
|
|
45540
|
+
kg: {
|
|
45541
|
+
dimension: "mass",
|
|
45542
|
+
factor: 1,
|
|
45543
|
+
offset: 0
|
|
45544
|
+
},
|
|
45545
|
+
g: {
|
|
45546
|
+
dimension: "mass",
|
|
45547
|
+
factor: .001,
|
|
45548
|
+
offset: 0
|
|
45549
|
+
},
|
|
45550
|
+
lb: {
|
|
45551
|
+
dimension: "mass",
|
|
45552
|
+
factor: .453592,
|
|
45553
|
+
offset: 0
|
|
45554
|
+
},
|
|
45555
|
+
oz: {
|
|
45556
|
+
dimension: "mass",
|
|
45557
|
+
factor: .0283495,
|
|
45558
|
+
offset: 0
|
|
45559
|
+
},
|
|
45560
|
+
"%": {
|
|
45561
|
+
dimension: "percentage",
|
|
45562
|
+
factor: 1,
|
|
45563
|
+
offset: 0
|
|
45564
|
+
}
|
|
45565
|
+
};
|
|
45566
|
+
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
45567
|
+
function specFor(unit) {
|
|
45568
|
+
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
45569
|
+
}
|
|
45570
|
+
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
45571
|
+
function toCanonical(value, spec) {
|
|
45572
|
+
return value * spec.factor + spec.offset;
|
|
45573
|
+
}
|
|
45574
|
+
/** Canonical-unit value → the unit described by `spec`. */
|
|
45575
|
+
function fromCanonical(canonical, spec) {
|
|
45576
|
+
return (canonical - spec.offset) / spec.factor;
|
|
45577
|
+
}
|
|
45578
|
+
/**
|
|
45579
|
+
* Graceful variant for the render path: returns the converted value, or
|
|
45580
|
+
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
45581
|
+
* Never throws — a render caller falls back to the raw value + source unit.
|
|
45582
|
+
*/
|
|
45583
|
+
function tryConvertUnit(value, from, to) {
|
|
45584
|
+
if (from === to) return value;
|
|
45585
|
+
const fromSpec = specFor(from);
|
|
45586
|
+
if (!fromSpec) return void 0;
|
|
45587
|
+
const toSpec = specFor(to);
|
|
45588
|
+
if (!toSpec) return void 0;
|
|
45589
|
+
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
45590
|
+
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
45591
|
+
}
|
|
45263
45592
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
45264
45593
|
/**
|
|
45265
45594
|
* Most specific first. Extending this list is how a new device kind becomes
|
|
@@ -50628,6 +50957,12 @@ Object.freeze({
|
|
|
50628
50957
|
addonId: null,
|
|
50629
50958
|
access: "create"
|
|
50630
50959
|
},
|
|
50960
|
+
"osdManager.deleteTemplate": {
|
|
50961
|
+
capName: "osd-manager",
|
|
50962
|
+
capScope: "system",
|
|
50963
|
+
addonId: null,
|
|
50964
|
+
access: "delete"
|
|
50965
|
+
},
|
|
50631
50966
|
"osdManager.getConditionSupport": {
|
|
50632
50967
|
capName: "osd-manager",
|
|
50633
50968
|
capScope: "system",
|
|
@@ -50646,6 +50981,12 @@ Object.freeze({
|
|
|
50646
50981
|
addonId: null,
|
|
50647
50982
|
access: "view"
|
|
50648
50983
|
},
|
|
50984
|
+
"osdManager.listTemplates": {
|
|
50985
|
+
capName: "osd-manager",
|
|
50986
|
+
capScope: "system",
|
|
50987
|
+
addonId: null,
|
|
50988
|
+
access: "view"
|
|
50989
|
+
},
|
|
50649
50990
|
"osdManager.previewSlot": {
|
|
50650
50991
|
capName: "osd-manager",
|
|
50651
50992
|
capScope: "system",
|
|
@@ -50664,6 +51005,12 @@ Object.freeze({
|
|
|
50664
51005
|
addonId: null,
|
|
50665
51006
|
access: "create"
|
|
50666
51007
|
},
|
|
51008
|
+
"osdManager.upsertTemplate": {
|
|
51009
|
+
capName: "osd-manager",
|
|
51010
|
+
capScope: "system",
|
|
51011
|
+
addonId: null,
|
|
51012
|
+
access: "create"
|
|
51013
|
+
},
|
|
50667
51014
|
"petFeeder.callPet": {
|
|
50668
51015
|
capName: "pet-feeder",
|
|
50669
51016
|
capScope: "device",
|
|
@@ -57912,10 +58259,10 @@ function limitText(text, max) {
|
|
|
57912
58259
|
return `${text.slice(0, max - 3)}...`;
|
|
57913
58260
|
}
|
|
57914
58261
|
/** Turn a raw state value into the string that goes into `${value}`. */
|
|
57915
|
-
function displayValue(raw, binding) {
|
|
58262
|
+
function displayValue(raw, binding, maxDecimals = binding.maxDecimals) {
|
|
57916
58263
|
const mapped = binding.valueMap?.[String(raw)];
|
|
57917
58264
|
if (mapped !== void 0) return mapped;
|
|
57918
|
-
if (typeof raw === "number") return truncateDecimals(raw,
|
|
58265
|
+
if (typeof raw === "number") return truncateDecimals(raw, maxDecimals);
|
|
57919
58266
|
if (typeof raw === "boolean") return raw ? "ON" : "OFF";
|
|
57920
58267
|
if (raw === null || raw === void 0) return "";
|
|
57921
58268
|
if (typeof raw === "string") return raw;
|
|
@@ -57929,9 +58276,10 @@ function applyTemplate(template, value, unit) {
|
|
|
57929
58276
|
return template.replaceAll("${value}", value).replaceAll("${unit}", unit);
|
|
57930
58277
|
}
|
|
57931
58278
|
/** The whole formatting chain: raw value → the text to burn into the slot. */
|
|
57932
|
-
function formatSlotText(raw, binding) {
|
|
57933
|
-
const value = displayValue(raw, binding);
|
|
57934
|
-
|
|
58279
|
+
function formatSlotText(raw, binding, options = {}) {
|
|
58280
|
+
const value = displayValue(options.displayRaw ?? raw, binding);
|
|
58281
|
+
const unit = options.unit ?? binding.unitLabel ?? "";
|
|
58282
|
+
return limitText(applyTemplate(binding.template, value, unit).trim(), binding.maxCharacters);
|
|
57935
58283
|
}
|
|
57936
58284
|
//#endregion
|
|
57937
58285
|
//#region src/state-leaves.ts
|
|
@@ -58019,6 +58367,92 @@ function readStatePath(slice, path) {
|
|
|
58019
58367
|
return current;
|
|
58020
58368
|
}
|
|
58021
58369
|
//#endregion
|
|
58370
|
+
//#region src/template-tokens.ts
|
|
58371
|
+
/** Build the placeholder token for one template field. */
|
|
58372
|
+
function fieldToken(field) {
|
|
58373
|
+
return `{${field.deviceId}.${field.capName}.${field.valuePath}}`;
|
|
58374
|
+
}
|
|
58375
|
+
/** Replace every occurrence of `token` in `parser` with `value`. */
|
|
58376
|
+
function replaceToken(parser, token, value) {
|
|
58377
|
+
return parser.replaceAll(token, value);
|
|
58378
|
+
}
|
|
58379
|
+
//#endregion
|
|
58380
|
+
//#region src/unit-display.ts
|
|
58381
|
+
/**
|
|
58382
|
+
* Convert a numeric state value for OSD display using `@camstack/types`
|
|
58383
|
+
* UNIT_TABLE / tryConvertUnit (same set as D699).
|
|
58384
|
+
*/
|
|
58385
|
+
/**
|
|
58386
|
+
* Convert `raw` from `sourceUnit` to `displayUnit` when both are known and
|
|
58387
|
+
* same-dimension. On refusal, keep the raw value and the source unit so the
|
|
58388
|
+
* label never lies about the number.
|
|
58389
|
+
*/
|
|
58390
|
+
function convertForDisplay(raw, sourceUnit, displayUnit) {
|
|
58391
|
+
const from = sourceUnit !== void 0 ? normalizeUnit(sourceUnit) : void 0;
|
|
58392
|
+
const to = displayUnit !== void 0 ? normalizeUnit(displayUnit) : void 0;
|
|
58393
|
+
if (from === void 0 || to === void 0 || from === to) return {
|
|
58394
|
+
value: raw,
|
|
58395
|
+
unit: to ?? from ?? ""
|
|
58396
|
+
};
|
|
58397
|
+
const converted = tryConvertUnit(raw, from, to);
|
|
58398
|
+
if (converted === void 0) return {
|
|
58399
|
+
value: raw,
|
|
58400
|
+
unit: from
|
|
58401
|
+
};
|
|
58402
|
+
return {
|
|
58403
|
+
value: converted,
|
|
58404
|
+
unit: to
|
|
58405
|
+
};
|
|
58406
|
+
}
|
|
58407
|
+
/**
|
|
58408
|
+
* Best-effort unit from a state slice: sibling `unit` at the same depth as
|
|
58409
|
+
* `valuePath`, else top-level `unit`.
|
|
58410
|
+
*/
|
|
58411
|
+
function inferSourceUnit(slice, valuePath) {
|
|
58412
|
+
if (typeof slice !== "object" || slice === null || Array.isArray(slice)) return void 0;
|
|
58413
|
+
const root = { ...slice };
|
|
58414
|
+
const segments = valuePath.split(".");
|
|
58415
|
+
if (segments.length >= 1) {
|
|
58416
|
+
let parent = root;
|
|
58417
|
+
for (const segment of segments.slice(0, -1)) {
|
|
58418
|
+
if (typeof parent !== "object" || parent === null || Array.isArray(parent)) {
|
|
58419
|
+
parent = void 0;
|
|
58420
|
+
break;
|
|
58421
|
+
}
|
|
58422
|
+
parent = { ...parent }[segment];
|
|
58423
|
+
}
|
|
58424
|
+
if (typeof parent === "object" && parent !== null && !Array.isArray(parent)) {
|
|
58425
|
+
const unit = { ...parent }.unit;
|
|
58426
|
+
if (typeof unit === "string" && unit.length > 0) return unit;
|
|
58427
|
+
}
|
|
58428
|
+
}
|
|
58429
|
+
const top = root.unit;
|
|
58430
|
+
return typeof top === "string" && top.length > 0 ? top : void 0;
|
|
58431
|
+
}
|
|
58432
|
+
//#endregion
|
|
58433
|
+
//#region src/named-template-render.ts
|
|
58434
|
+
/** Render one named template's parser from live states. */
|
|
58435
|
+
function renderNamedTemplate(template, facts) {
|
|
58436
|
+
let text = template.parser;
|
|
58437
|
+
if (template.fields.length === 0) return text.trim() === "" ? { reason: `template "${template.id}" has no fields` } : { text };
|
|
58438
|
+
for (const field of template.fields) {
|
|
58439
|
+
const token = fieldToken(field);
|
|
58440
|
+
const deviceState = facts.stateByDevice.get(field.deviceId);
|
|
58441
|
+
if (deviceState === void 0) return { reason: `no state mirrored for device ${field.deviceId}` };
|
|
58442
|
+
const slice = deviceState[field.capName];
|
|
58443
|
+
if (slice === void 0) return { reason: `device ${field.deviceId} publishes no "${field.capName}" state` };
|
|
58444
|
+
const raw = readStatePath(slice, field.valuePath);
|
|
58445
|
+
if (raw === void 0) return { reason: `"${field.capName}.${field.valuePath}" is absent on device ${field.deviceId}` };
|
|
58446
|
+
let rendered;
|
|
58447
|
+
if (typeof raw === "number") rendered = truncateDecimals(convertForDisplay(raw, inferSourceUnit(slice, field.valuePath), field.displayUnit).value, field.maxDecimals);
|
|
58448
|
+
else if (typeof raw === "boolean") rendered = raw ? "ON" : "OFF";
|
|
58449
|
+
else if (raw === null) rendered = "";
|
|
58450
|
+
else rendered = String(raw);
|
|
58451
|
+
text = replaceToken(text, token, rendered);
|
|
58452
|
+
}
|
|
58453
|
+
return { text };
|
|
58454
|
+
}
|
|
58455
|
+
//#endregion
|
|
58022
58456
|
//#region src/render.ts
|
|
58023
58457
|
/**
|
|
58024
58458
|
* Read the source's value. `null` means "unreadable" — a missing device, a
|
|
@@ -58033,6 +58467,16 @@ function resolveSource(binding, cameraDeviceId, facts) {
|
|
|
58033
58467
|
const value = facts.recognitionByDevice.get(cameraDeviceId)?.[source.recognition];
|
|
58034
58468
|
return value === void 0 ? { reason: `no recognized ${source.recognition} has been persisted for device ${cameraDeviceId}` } : { value: { raw: value } };
|
|
58035
58469
|
}
|
|
58470
|
+
if (source.kind === "template") {
|
|
58471
|
+
const named = facts.templatesById.get(source.templateId);
|
|
58472
|
+
if (named === void 0) return { reason: `named template "${source.templateId}" is missing` };
|
|
58473
|
+
const rendered = renderNamedTemplate(named, { stateByDevice: facts.stateByDevice });
|
|
58474
|
+
if ("reason" in rendered) return rendered;
|
|
58475
|
+
return { value: {
|
|
58476
|
+
raw: rendered.text,
|
|
58477
|
+
preformatted: rendered.text
|
|
58478
|
+
} };
|
|
58479
|
+
}
|
|
58036
58480
|
const deviceId = source.deviceId ?? cameraDeviceId;
|
|
58037
58481
|
const deviceState = facts.stateByDevice.get(deviceId);
|
|
58038
58482
|
if (deviceState === void 0) return { reason: `no state mirrored for device ${deviceId}` };
|
|
@@ -58040,6 +58484,14 @@ function resolveSource(binding, cameraDeviceId, facts) {
|
|
|
58040
58484
|
if (slice === void 0) return { reason: `device ${deviceId} publishes no "${source.capName}" state` };
|
|
58041
58485
|
const raw = readStatePath(slice, source.valuePath);
|
|
58042
58486
|
if (raw === void 0) return { reason: `"${source.capName}.${source.valuePath}" is absent on device ${deviceId}` };
|
|
58487
|
+
if (typeof raw === "number") {
|
|
58488
|
+
const converted = convertForDisplay(raw, inferSourceUnit(slice, source.valuePath), source.displayUnit);
|
|
58489
|
+
return { value: {
|
|
58490
|
+
raw,
|
|
58491
|
+
displayRaw: converted.value,
|
|
58492
|
+
unit: converted.unit || binding.unitLabel
|
|
58493
|
+
} };
|
|
58494
|
+
}
|
|
58043
58495
|
return { value: { raw } };
|
|
58044
58496
|
}
|
|
58045
58497
|
/**
|
|
@@ -58072,7 +58524,12 @@ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
|
|
|
58072
58524
|
if (!gate.open) return settle(slotId, fallback, previousText, "gated", `condition "${gate.failedCondition}"`);
|
|
58073
58525
|
const resolved = resolveSource(binding, cameraDeviceId, facts);
|
|
58074
58526
|
if ("reason" in resolved) return settle(slotId, fallback, previousText, "unreadable", resolved.reason);
|
|
58075
|
-
return settle(slotId,
|
|
58527
|
+
if (resolved.value.preformatted !== void 0 && binding.source.kind === "template") return settle(slotId, limitText(resolved.value.preformatted.trim(), binding.maxCharacters), previousText, "written");
|
|
58528
|
+
const unit = resolved.value.unit ?? binding.unitLabel;
|
|
58529
|
+
return settle(slotId, formatSlotText(resolved.value.raw, binding, {
|
|
58530
|
+
...resolved.value.displayRaw !== void 0 ? { displayRaw: resolved.value.displayRaw } : {},
|
|
58531
|
+
...unit !== void 0 ? { unit } : {}
|
|
58532
|
+
}), previousText, "written");
|
|
58076
58533
|
}
|
|
58077
58534
|
function settle(slotId, text, previousText, outcome, reason) {
|
|
58078
58535
|
if (previousText !== void 0 && previousText === text) return {
|
|
@@ -58132,6 +58589,24 @@ function sourceOptionsFor(cameraDeviceId, devices, capsByDevice) {
|
|
|
58132
58589
|
return options;
|
|
58133
58590
|
}
|
|
58134
58591
|
//#endregion
|
|
58592
|
+
//#region src/templates-store.ts
|
|
58593
|
+
/**
|
|
58594
|
+
* Named OSD templates — durable in the addon's own (global) store.
|
|
58595
|
+
*/
|
|
58596
|
+
var OSD_TEMPLATES_KEY = "namedTemplates";
|
|
58597
|
+
var OsdNamedTemplatesSchema = array(OsdNamedTemplateSchema);
|
|
58598
|
+
var EMPTY_TEMPLATES = [];
|
|
58599
|
+
function upsertNamedTemplate(prev, template) {
|
|
58600
|
+
const parsed = OsdNamedTemplateSchema.parse(template);
|
|
58601
|
+
return [...prev.filter((t) => t.id !== parsed.id), parsed].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
58602
|
+
}
|
|
58603
|
+
function deleteNamedTemplate(prev, templateId) {
|
|
58604
|
+
return prev.filter((t) => t.id !== templateId);
|
|
58605
|
+
}
|
|
58606
|
+
function findNamedTemplate(templates, templateId) {
|
|
58607
|
+
return templates.find((t) => t.id === templateId);
|
|
58608
|
+
}
|
|
58609
|
+
//#endregion
|
|
58135
58610
|
//#region src/osd-manager.ts
|
|
58136
58611
|
/**
|
|
58137
58612
|
* The `osd-manager` provider — the render loop and the operator surface.
|
|
@@ -58495,6 +58970,26 @@ var OsdManager = class {
|
|
|
58495
58970
|
renderDevice = async ({ deviceId }) => {
|
|
58496
58971
|
return { results: [...await this.renderDeviceInternal(deviceId, null)] };
|
|
58497
58972
|
};
|
|
58973
|
+
listTemplates = async () => {
|
|
58974
|
+
return { templates: [...await this.deps.templates.get()] };
|
|
58975
|
+
};
|
|
58976
|
+
upsertTemplate = async ({ template }) => {
|
|
58977
|
+
const next = upsertNamedTemplate(await this.deps.templates.get(), template);
|
|
58978
|
+
await this.deps.templates.set(next);
|
|
58979
|
+
const stored = findNamedTemplate(next, template.id);
|
|
58980
|
+
if (stored === void 0) throw new Error(`osd-manager: template "${template.id}" missing after upsert`);
|
|
58981
|
+
this.logger.info("osd-manager: named template upserted", { meta: {
|
|
58982
|
+
templateId: stored.id,
|
|
58983
|
+
fieldCount: stored.fields.length
|
|
58984
|
+
} });
|
|
58985
|
+
return { template: stored };
|
|
58986
|
+
};
|
|
58987
|
+
deleteTemplate = async ({ templateId }) => {
|
|
58988
|
+
const prev = await this.deps.templates.get();
|
|
58989
|
+
await this.deps.templates.set(deleteNamedTemplate(prev, templateId));
|
|
58990
|
+
this.logger.info("osd-manager: named template deleted", { meta: { templateId } });
|
|
58991
|
+
return { success: true };
|
|
58992
|
+
};
|
|
58498
58993
|
async renderAllBound() {
|
|
58499
58994
|
for (const deviceId of [...this.boundDevices]) try {
|
|
58500
58995
|
await this.renderDeviceInternal(deviceId, null);
|
|
@@ -58562,11 +59057,19 @@ var OsdManager = class {
|
|
|
58562
59057
|
async collectFacts(cameraDeviceId, bindings) {
|
|
58563
59058
|
const wanted = new Set([cameraDeviceId]);
|
|
58564
59059
|
const needsRecognition = Object.values(bindings).some((binding) => binding.source.kind === "latest-recognition");
|
|
59060
|
+
const templateIds = /* @__PURE__ */ new Set();
|
|
58565
59061
|
for (const binding of Object.values(bindings)) {
|
|
58566
59062
|
if (binding.source.kind === "device-state" && binding.source.deviceId !== void 0) wanted.add(binding.source.deviceId);
|
|
59063
|
+
if (binding.source.kind === "template") templateIds.add(binding.source.templateId);
|
|
58567
59064
|
const gated = binding.conditions?.deviceState?.deviceId;
|
|
58568
59065
|
if (gated !== void 0) wanted.add(gated);
|
|
58569
59066
|
}
|
|
59067
|
+
const allTemplates = await this.deps.templates.get();
|
|
59068
|
+
const templatesById = /* @__PURE__ */ new Map();
|
|
59069
|
+
for (const named of allTemplates) {
|
|
59070
|
+
templatesById.set(named.id, named);
|
|
59071
|
+
if (templateIds.has(named.id)) for (const field of named.fields) wanted.add(field.deviceId);
|
|
59072
|
+
}
|
|
58570
59073
|
const stateByDevice = /* @__PURE__ */ new Map();
|
|
58571
59074
|
const gateState = /* @__PURE__ */ new Map();
|
|
58572
59075
|
for (const id of wanted) try {
|
|
@@ -58591,7 +59094,8 @@ var OsdManager = class {
|
|
|
58591
59094
|
atMs: this.now(),
|
|
58592
59095
|
stateByDevice,
|
|
58593
59096
|
recognitionByDevice,
|
|
58594
|
-
deviceState: (id) => gateState.get(id)
|
|
59097
|
+
deviceState: (id) => gateState.get(id),
|
|
59098
|
+
templatesById
|
|
58595
59099
|
};
|
|
58596
59100
|
}
|
|
58597
59101
|
async hydrateRecognitions(deviceId) {
|
|
@@ -58732,6 +59236,7 @@ var OsdManagerAddon = class extends BaseAddon {
|
|
|
58732
59236
|
const ctx = this.ctx;
|
|
58733
59237
|
const store = new OsdBindingsStore((deviceId, key, schema, fallback) => this.deviceState(deviceId, key, schema, fallback));
|
|
58734
59238
|
const boundIndex = this.state(OSD_BOUND_INDEX_KEY, OsdBoundIndexSchema, null);
|
|
59239
|
+
const templates = this.state(OSD_TEMPLATES_KEY, OsdNamedTemplatesSchema, EMPTY_TEMPLATES);
|
|
58735
59240
|
const manager = new OsdManager({
|
|
58736
59241
|
logger: ctx.logger,
|
|
58737
59242
|
readOsdStatus: async (deviceId) => {
|
|
@@ -58805,7 +59310,8 @@ var OsdManagerAddon = class extends BaseAddon {
|
|
|
58805
59310
|
};
|
|
58806
59311
|
},
|
|
58807
59312
|
bindings: (deviceId) => store.forDevice(deviceId),
|
|
58808
|
-
boundIndex
|
|
59313
|
+
boundIndex,
|
|
59314
|
+
templates
|
|
58809
59315
|
});
|
|
58810
59316
|
this.manager = manager;
|
|
58811
59317
|
this.subscribe({ category: EventCategory.PipelineAnalyticsTrackLifecycle }, (event) => {
|