@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.mjs
CHANGED
|
@@ -15441,6 +15441,51 @@ var SourceInfoSchema = object({
|
|
|
15441
15441
|
raw: record(string(), unknown()).optional()
|
|
15442
15442
|
});
|
|
15443
15443
|
/**
|
|
15444
|
+
* Coerce common unit-of-measurement string variants to a canonical form
|
|
15445
|
+
* so the UI's display templates can rely on stable strings. Pure
|
|
15446
|
+
* passthrough for unknown units — kept conservative on purpose, the goal
|
|
15447
|
+
* is to normalise the long tail of HA `unit_of_measurement` values that
|
|
15448
|
+
* have multiple historical spellings (`'°C'` vs `'C'` vs `'celsius'`).
|
|
15449
|
+
*
|
|
15450
|
+
* Extend the map as we encounter real-world data. Returning the raw
|
|
15451
|
+
* input for unknown values is intentional: providers should never
|
|
15452
|
+
* lose information silently here.
|
|
15453
|
+
*/
|
|
15454
|
+
function normalizeUnit(raw) {
|
|
15455
|
+
if (raw == null) return void 0;
|
|
15456
|
+
const trimmed = raw.trim();
|
|
15457
|
+
if (trimmed === "") return void 0;
|
|
15458
|
+
const lower = trimmed.toLowerCase();
|
|
15459
|
+
if (lower === "°c" || lower === "c" || lower === "celsius") return "°C";
|
|
15460
|
+
if (lower === "°f" || lower === "f" || lower === "fahrenheit") return "°F";
|
|
15461
|
+
if (lower === "k" || lower === "kelvin") return "K";
|
|
15462
|
+
if (lower === "%" || lower === "percent" || lower === "percentage") return "%";
|
|
15463
|
+
if (lower === "lx" || lower === "lux") return "lx";
|
|
15464
|
+
if (lower === "hpa" || lower === "hectopascal") return "hPa";
|
|
15465
|
+
if (lower === "kpa") return "kPa";
|
|
15466
|
+
if (lower === "pa") return "Pa";
|
|
15467
|
+
if (lower === "mbar" || lower === "millibar") return "mbar";
|
|
15468
|
+
if (lower === "w" || lower === "watt") return "W";
|
|
15469
|
+
if (lower === "kw" || lower === "kilowatt") return "kW";
|
|
15470
|
+
if (lower === "wh" || lower === "watt-hour") return "Wh";
|
|
15471
|
+
if (lower === "kwh" || lower === "kilowatt-hour") return "kWh";
|
|
15472
|
+
if (lower === "v" || lower === "volt") return "V";
|
|
15473
|
+
if (lower === "a" || lower === "amp" || lower === "ampere") return "A";
|
|
15474
|
+
if (lower === "ma" || lower === "milliampere") return "mA";
|
|
15475
|
+
if (lower === "va") return "VA";
|
|
15476
|
+
if (lower === "ppm") return "ppm";
|
|
15477
|
+
if (lower === "ppb") return "ppb";
|
|
15478
|
+
if (lower === "µg/m³" || lower === "ug/m3" || lower === "µg/m3") return "µg/m³";
|
|
15479
|
+
if (lower === "km/h" || lower === "kph") return "km/h";
|
|
15480
|
+
if (lower === "mph") return "mph";
|
|
15481
|
+
if (lower === "m/s") return "m/s";
|
|
15482
|
+
if (lower === "m") return "m";
|
|
15483
|
+
if (lower === "km") return "km";
|
|
15484
|
+
if (lower === "kg") return "kg";
|
|
15485
|
+
if (lower === "g") return "g";
|
|
15486
|
+
return trimmed;
|
|
15487
|
+
}
|
|
15488
|
+
/**
|
|
15444
15489
|
* device-discovery — device-scoped capability for parents that host /
|
|
15445
15490
|
* enumerate child devices (Reolink Hub / NVR, ONVIF gateway, future
|
|
15446
15491
|
* integrations). The parent advertises a list of discoverable children,
|
|
@@ -39292,28 +39337,29 @@ setOverlay: method(object({
|
|
|
39292
39337
|
*
|
|
39293
39338
|
* This cap owns the other half: a per-(camera, slot) BINDING that says
|
|
39294
39339
|
* which value feeds the slot, how it is formatted, and under which
|
|
39295
|
-
* conditions it is shown at all.
|
|
39296
|
-
*
|
|
39297
|
-
|
|
39298
|
-
|
|
39299
|
-
|
|
39300
|
-
|
|
39301
|
-
|
|
39302
|
-
|
|
39303
|
-
|
|
39304
|
-
|
|
39305
|
-
|
|
39306
|
-
|
|
39307
|
-
|
|
39308
|
-
*
|
|
39309
|
-
*
|
|
39310
|
-
* 3. Because the renderer's facts are device STATE and not a detection
|
|
39311
|
-
* record, only a SUBSET of that vocabulary can be answered here.
|
|
39312
|
-
* `setSlotBinding` REJECTS the rest at write time (see
|
|
39313
|
-
* `getConditionSupport`). It does not accept-then-fail-closed: a
|
|
39314
|
-
* condition that can never be true renders a permanently blank
|
|
39315
|
-
* overlay, and a blank overlay looks exactly like a broken camera.
|
|
39340
|
+
* conditions it is shown at all. Named TEMPLATES combine many device
|
|
39341
|
+
* fields into one overlay string (Scrypted OSD Manager parity).
|
|
39342
|
+
*/
|
|
39343
|
+
/** One field reference inside a named multi-device template. */
|
|
39344
|
+
var OsdTemplateFieldSchema = object({
|
|
39345
|
+
deviceId: number().int(),
|
|
39346
|
+
capName: string().min(1).max(64),
|
|
39347
|
+
valuePath: string().min(1).max(64),
|
|
39348
|
+
/** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
|
|
39349
|
+
displayUnit: string().max(16).optional(),
|
|
39350
|
+
maxDecimals: number().int().min(0).max(4).default(1)
|
|
39351
|
+
});
|
|
39352
|
+
/**
|
|
39353
|
+
* A named template: several device fields → one parser string with
|
|
39354
|
+
* `{deviceId.capName.valuePath}` tokens (Scrypted `{deviceId.sensorId}`).
|
|
39316
39355
|
*/
|
|
39356
|
+
var OsdNamedTemplateSchema = object({
|
|
39357
|
+
id: string().min(1).max(64),
|
|
39358
|
+
name: string().min(1).max(64),
|
|
39359
|
+
fields: array(OsdTemplateFieldSchema).max(32).default([]),
|
|
39360
|
+
/** Parser with multi-token placeholders, e.g. `T {12.thermometer.value}°`. */
|
|
39361
|
+
parser: string().max(256).default("")
|
|
39362
|
+
});
|
|
39317
39363
|
/** Where a slot's value comes from. */
|
|
39318
39364
|
var OsdSourceSchema = discriminatedUnion("kind", [
|
|
39319
39365
|
object({
|
|
@@ -39332,11 +39378,17 @@ var OsdSourceSchema = discriminatedUnion("kind", [
|
|
|
39332
39378
|
deviceId: number().int().optional(),
|
|
39333
39379
|
capName: string().min(1).max(64),
|
|
39334
39380
|
/** Dot path inside the slice, e.g. `detected`, `value`, `mode`. */
|
|
39335
|
-
valuePath: string().min(1).max(64)
|
|
39381
|
+
valuePath: string().min(1).max(64),
|
|
39382
|
+
/** Convert TO this unit at render. Absent = no conversion. */
|
|
39383
|
+
displayUnit: string().max(16).optional()
|
|
39336
39384
|
}),
|
|
39337
39385
|
object({
|
|
39338
39386
|
kind: literal("latest-recognition"),
|
|
39339
39387
|
recognition: _enum(["person", "plate"])
|
|
39388
|
+
}),
|
|
39389
|
+
object({
|
|
39390
|
+
kind: literal("template"),
|
|
39391
|
+
templateId: string().min(1).max(64)
|
|
39340
39392
|
})
|
|
39341
39393
|
]);
|
|
39342
39394
|
var OsdSlotBindingSchema = object({
|
|
@@ -39352,7 +39404,10 @@ var OsdSlotBindingSchema = object({
|
|
|
39352
39404
|
* documented workaround for firmwares that reject `.` in overlay text.
|
|
39353
39405
|
*/
|
|
39354
39406
|
maxDecimals: number().int().min(0).max(4).default(1),
|
|
39355
|
-
/**
|
|
39407
|
+
/**
|
|
39408
|
+
* Appended via `${unit}` when no unit conversion produces a label.
|
|
39409
|
+
* Prefer `source.displayUnit` for converted device-state values.
|
|
39410
|
+
*/
|
|
39356
39411
|
unitLabel: string().max(8).optional(),
|
|
39357
39412
|
/** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
|
|
39358
39413
|
valueMap: record(string(), string()).optional(),
|
|
@@ -39506,6 +39561,21 @@ var osdManagerCapability = {
|
|
|
39506
39561
|
renderDevice: method(object({ deviceId: number().int() }), object({ results: array(OsdRenderResultSchema) }), {
|
|
39507
39562
|
kind: "mutation",
|
|
39508
39563
|
auth: "admin"
|
|
39564
|
+
}),
|
|
39565
|
+
/** Every named multi-device template. */
|
|
39566
|
+
listTemplates: method(object({}), object({ templates: array(OsdNamedTemplateSchema) }), { auth: "admin" }),
|
|
39567
|
+
/**
|
|
39568
|
+
* Create or replace a named template by `id`. Returns the stored record
|
|
39569
|
+
* after Zod normalisation.
|
|
39570
|
+
*/
|
|
39571
|
+
upsertTemplate: method(object({ template: OsdNamedTemplateSchema }), object({ template: OsdNamedTemplateSchema }), {
|
|
39572
|
+
kind: "mutation",
|
|
39573
|
+
auth: "admin"
|
|
39574
|
+
}),
|
|
39575
|
+
/** Drop a named template. Bindings that still reference it render unreadable. */
|
|
39576
|
+
deleteTemplate: method(object({ templateId: string().min(1).max(64) }), object({ success: literal(true) }), {
|
|
39577
|
+
kind: "mutation",
|
|
39578
|
+
auth: "admin"
|
|
39509
39579
|
})
|
|
39510
39580
|
}
|
|
39511
39581
|
};
|
|
@@ -45256,6 +45326,265 @@ var zoneRulesCapability = {
|
|
|
45256
45326
|
*/
|
|
45257
45327
|
durability: "restored"
|
|
45258
45328
|
};
|
|
45329
|
+
/**
|
|
45330
|
+
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
45331
|
+
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
45332
|
+
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
45333
|
+
* decimals). K derives from `°C = K − 273.15`.
|
|
45334
|
+
*/
|
|
45335
|
+
var UNIT_TABLE = {
|
|
45336
|
+
"°C": {
|
|
45337
|
+
dimension: "temperature",
|
|
45338
|
+
factor: 1,
|
|
45339
|
+
offset: 0
|
|
45340
|
+
},
|
|
45341
|
+
"°F": {
|
|
45342
|
+
dimension: "temperature",
|
|
45343
|
+
factor: 5 / 9,
|
|
45344
|
+
offset: -160 / 9
|
|
45345
|
+
},
|
|
45346
|
+
K: {
|
|
45347
|
+
dimension: "temperature",
|
|
45348
|
+
factor: 1,
|
|
45349
|
+
offset: -273.15
|
|
45350
|
+
},
|
|
45351
|
+
hPa: {
|
|
45352
|
+
dimension: "pressure",
|
|
45353
|
+
factor: 1,
|
|
45354
|
+
offset: 0
|
|
45355
|
+
},
|
|
45356
|
+
kPa: {
|
|
45357
|
+
dimension: "pressure",
|
|
45358
|
+
factor: 10,
|
|
45359
|
+
offset: 0
|
|
45360
|
+
},
|
|
45361
|
+
Pa: {
|
|
45362
|
+
dimension: "pressure",
|
|
45363
|
+
factor: .01,
|
|
45364
|
+
offset: 0
|
|
45365
|
+
},
|
|
45366
|
+
mbar: {
|
|
45367
|
+
dimension: "pressure",
|
|
45368
|
+
factor: 1,
|
|
45369
|
+
offset: 0
|
|
45370
|
+
},
|
|
45371
|
+
bar: {
|
|
45372
|
+
dimension: "pressure",
|
|
45373
|
+
factor: 1e3,
|
|
45374
|
+
offset: 0
|
|
45375
|
+
},
|
|
45376
|
+
inHg: {
|
|
45377
|
+
dimension: "pressure",
|
|
45378
|
+
factor: 33.8639,
|
|
45379
|
+
offset: 0
|
|
45380
|
+
},
|
|
45381
|
+
mmHg: {
|
|
45382
|
+
dimension: "pressure",
|
|
45383
|
+
factor: 1.33322,
|
|
45384
|
+
offset: 0
|
|
45385
|
+
},
|
|
45386
|
+
psi: {
|
|
45387
|
+
dimension: "pressure",
|
|
45388
|
+
factor: 68.9476,
|
|
45389
|
+
offset: 0
|
|
45390
|
+
},
|
|
45391
|
+
"m/s": {
|
|
45392
|
+
dimension: "speed",
|
|
45393
|
+
factor: 1,
|
|
45394
|
+
offset: 0
|
|
45395
|
+
},
|
|
45396
|
+
"km/h": {
|
|
45397
|
+
dimension: "speed",
|
|
45398
|
+
factor: 1 / 3.6,
|
|
45399
|
+
offset: 0
|
|
45400
|
+
},
|
|
45401
|
+
mph: {
|
|
45402
|
+
dimension: "speed",
|
|
45403
|
+
factor: .44704,
|
|
45404
|
+
offset: 0
|
|
45405
|
+
},
|
|
45406
|
+
kn: {
|
|
45407
|
+
dimension: "speed",
|
|
45408
|
+
factor: .514444,
|
|
45409
|
+
offset: 0
|
|
45410
|
+
},
|
|
45411
|
+
"mm/h": {
|
|
45412
|
+
dimension: "precipitation-rate",
|
|
45413
|
+
factor: 1,
|
|
45414
|
+
offset: 0
|
|
45415
|
+
},
|
|
45416
|
+
"in/h": {
|
|
45417
|
+
dimension: "precipitation-rate",
|
|
45418
|
+
factor: 25.4,
|
|
45419
|
+
offset: 0
|
|
45420
|
+
},
|
|
45421
|
+
m: {
|
|
45422
|
+
dimension: "length",
|
|
45423
|
+
factor: 1,
|
|
45424
|
+
offset: 0
|
|
45425
|
+
},
|
|
45426
|
+
mm: {
|
|
45427
|
+
dimension: "length",
|
|
45428
|
+
factor: .001,
|
|
45429
|
+
offset: 0
|
|
45430
|
+
},
|
|
45431
|
+
cm: {
|
|
45432
|
+
dimension: "length",
|
|
45433
|
+
factor: .01,
|
|
45434
|
+
offset: 0
|
|
45435
|
+
},
|
|
45436
|
+
km: {
|
|
45437
|
+
dimension: "length",
|
|
45438
|
+
factor: 1e3,
|
|
45439
|
+
offset: 0
|
|
45440
|
+
},
|
|
45441
|
+
in: {
|
|
45442
|
+
dimension: "length",
|
|
45443
|
+
factor: .0254,
|
|
45444
|
+
offset: 0
|
|
45445
|
+
},
|
|
45446
|
+
ft: {
|
|
45447
|
+
dimension: "length",
|
|
45448
|
+
factor: .3048,
|
|
45449
|
+
offset: 0
|
|
45450
|
+
},
|
|
45451
|
+
mi: {
|
|
45452
|
+
dimension: "length",
|
|
45453
|
+
factor: 1609.344,
|
|
45454
|
+
offset: 0
|
|
45455
|
+
},
|
|
45456
|
+
lx: {
|
|
45457
|
+
dimension: "illuminance",
|
|
45458
|
+
factor: 1,
|
|
45459
|
+
offset: 0
|
|
45460
|
+
},
|
|
45461
|
+
"W/m²": {
|
|
45462
|
+
dimension: "irradiance",
|
|
45463
|
+
factor: 1,
|
|
45464
|
+
offset: 0
|
|
45465
|
+
},
|
|
45466
|
+
W: {
|
|
45467
|
+
dimension: "power",
|
|
45468
|
+
factor: 1,
|
|
45469
|
+
offset: 0
|
|
45470
|
+
},
|
|
45471
|
+
kW: {
|
|
45472
|
+
dimension: "power",
|
|
45473
|
+
factor: 1e3,
|
|
45474
|
+
offset: 0
|
|
45475
|
+
},
|
|
45476
|
+
VA: {
|
|
45477
|
+
dimension: "apparent-power",
|
|
45478
|
+
factor: 1,
|
|
45479
|
+
offset: 0
|
|
45480
|
+
},
|
|
45481
|
+
Wh: {
|
|
45482
|
+
dimension: "energy",
|
|
45483
|
+
factor: 1,
|
|
45484
|
+
offset: 0
|
|
45485
|
+
},
|
|
45486
|
+
kWh: {
|
|
45487
|
+
dimension: "energy",
|
|
45488
|
+
factor: 1e3,
|
|
45489
|
+
offset: 0
|
|
45490
|
+
},
|
|
45491
|
+
MWh: {
|
|
45492
|
+
dimension: "energy",
|
|
45493
|
+
factor: 1e6,
|
|
45494
|
+
offset: 0
|
|
45495
|
+
},
|
|
45496
|
+
V: {
|
|
45497
|
+
dimension: "voltage",
|
|
45498
|
+
factor: 1,
|
|
45499
|
+
offset: 0
|
|
45500
|
+
},
|
|
45501
|
+
mV: {
|
|
45502
|
+
dimension: "voltage",
|
|
45503
|
+
factor: .001,
|
|
45504
|
+
offset: 0
|
|
45505
|
+
},
|
|
45506
|
+
A: {
|
|
45507
|
+
dimension: "current",
|
|
45508
|
+
factor: 1,
|
|
45509
|
+
offset: 0
|
|
45510
|
+
},
|
|
45511
|
+
mA: {
|
|
45512
|
+
dimension: "current",
|
|
45513
|
+
factor: .001,
|
|
45514
|
+
offset: 0
|
|
45515
|
+
},
|
|
45516
|
+
ppm: {
|
|
45517
|
+
dimension: "concentration-volume",
|
|
45518
|
+
factor: 1,
|
|
45519
|
+
offset: 0
|
|
45520
|
+
},
|
|
45521
|
+
ppb: {
|
|
45522
|
+
dimension: "concentration-volume",
|
|
45523
|
+
factor: .001,
|
|
45524
|
+
offset: 0
|
|
45525
|
+
},
|
|
45526
|
+
"µg/m³": {
|
|
45527
|
+
dimension: "concentration-mass",
|
|
45528
|
+
factor: 1,
|
|
45529
|
+
offset: 0
|
|
45530
|
+
},
|
|
45531
|
+
"mg/m³": {
|
|
45532
|
+
dimension: "concentration-mass",
|
|
45533
|
+
factor: 1e3,
|
|
45534
|
+
offset: 0
|
|
45535
|
+
},
|
|
45536
|
+
kg: {
|
|
45537
|
+
dimension: "mass",
|
|
45538
|
+
factor: 1,
|
|
45539
|
+
offset: 0
|
|
45540
|
+
},
|
|
45541
|
+
g: {
|
|
45542
|
+
dimension: "mass",
|
|
45543
|
+
factor: .001,
|
|
45544
|
+
offset: 0
|
|
45545
|
+
},
|
|
45546
|
+
lb: {
|
|
45547
|
+
dimension: "mass",
|
|
45548
|
+
factor: .453592,
|
|
45549
|
+
offset: 0
|
|
45550
|
+
},
|
|
45551
|
+
oz: {
|
|
45552
|
+
dimension: "mass",
|
|
45553
|
+
factor: .0283495,
|
|
45554
|
+
offset: 0
|
|
45555
|
+
},
|
|
45556
|
+
"%": {
|
|
45557
|
+
dimension: "percentage",
|
|
45558
|
+
factor: 1,
|
|
45559
|
+
offset: 0
|
|
45560
|
+
}
|
|
45561
|
+
};
|
|
45562
|
+
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
45563
|
+
function specFor(unit) {
|
|
45564
|
+
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
45565
|
+
}
|
|
45566
|
+
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
45567
|
+
function toCanonical(value, spec) {
|
|
45568
|
+
return value * spec.factor + spec.offset;
|
|
45569
|
+
}
|
|
45570
|
+
/** Canonical-unit value → the unit described by `spec`. */
|
|
45571
|
+
function fromCanonical(canonical, spec) {
|
|
45572
|
+
return (canonical - spec.offset) / spec.factor;
|
|
45573
|
+
}
|
|
45574
|
+
/**
|
|
45575
|
+
* Graceful variant for the render path: returns the converted value, or
|
|
45576
|
+
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
45577
|
+
* Never throws — a render caller falls back to the raw value + source unit.
|
|
45578
|
+
*/
|
|
45579
|
+
function tryConvertUnit(value, from, to) {
|
|
45580
|
+
if (from === to) return value;
|
|
45581
|
+
const fromSpec = specFor(from);
|
|
45582
|
+
if (!fromSpec) return void 0;
|
|
45583
|
+
const toSpec = specFor(to);
|
|
45584
|
+
if (!toSpec) return void 0;
|
|
45585
|
+
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
45586
|
+
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
45587
|
+
}
|
|
45259
45588
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
45260
45589
|
/**
|
|
45261
45590
|
* Most specific first. Extending this list is how a new device kind becomes
|
|
@@ -50624,6 +50953,12 @@ Object.freeze({
|
|
|
50624
50953
|
addonId: null,
|
|
50625
50954
|
access: "create"
|
|
50626
50955
|
},
|
|
50956
|
+
"osdManager.deleteTemplate": {
|
|
50957
|
+
capName: "osd-manager",
|
|
50958
|
+
capScope: "system",
|
|
50959
|
+
addonId: null,
|
|
50960
|
+
access: "delete"
|
|
50961
|
+
},
|
|
50627
50962
|
"osdManager.getConditionSupport": {
|
|
50628
50963
|
capName: "osd-manager",
|
|
50629
50964
|
capScope: "system",
|
|
@@ -50642,6 +50977,12 @@ Object.freeze({
|
|
|
50642
50977
|
addonId: null,
|
|
50643
50978
|
access: "view"
|
|
50644
50979
|
},
|
|
50980
|
+
"osdManager.listTemplates": {
|
|
50981
|
+
capName: "osd-manager",
|
|
50982
|
+
capScope: "system",
|
|
50983
|
+
addonId: null,
|
|
50984
|
+
access: "view"
|
|
50985
|
+
},
|
|
50645
50986
|
"osdManager.previewSlot": {
|
|
50646
50987
|
capName: "osd-manager",
|
|
50647
50988
|
capScope: "system",
|
|
@@ -50660,6 +51001,12 @@ Object.freeze({
|
|
|
50660
51001
|
addonId: null,
|
|
50661
51002
|
access: "create"
|
|
50662
51003
|
},
|
|
51004
|
+
"osdManager.upsertTemplate": {
|
|
51005
|
+
capName: "osd-manager",
|
|
51006
|
+
capScope: "system",
|
|
51007
|
+
addonId: null,
|
|
51008
|
+
access: "create"
|
|
51009
|
+
},
|
|
50663
51010
|
"petFeeder.callPet": {
|
|
50664
51011
|
capName: "pet-feeder",
|
|
50665
51012
|
capScope: "device",
|
|
@@ -57908,10 +58255,10 @@ function limitText(text, max) {
|
|
|
57908
58255
|
return `${text.slice(0, max - 3)}...`;
|
|
57909
58256
|
}
|
|
57910
58257
|
/** Turn a raw state value into the string that goes into `${value}`. */
|
|
57911
|
-
function displayValue(raw, binding) {
|
|
58258
|
+
function displayValue(raw, binding, maxDecimals = binding.maxDecimals) {
|
|
57912
58259
|
const mapped = binding.valueMap?.[String(raw)];
|
|
57913
58260
|
if (mapped !== void 0) return mapped;
|
|
57914
|
-
if (typeof raw === "number") return truncateDecimals(raw,
|
|
58261
|
+
if (typeof raw === "number") return truncateDecimals(raw, maxDecimals);
|
|
57915
58262
|
if (typeof raw === "boolean") return raw ? "ON" : "OFF";
|
|
57916
58263
|
if (raw === null || raw === void 0) return "";
|
|
57917
58264
|
if (typeof raw === "string") return raw;
|
|
@@ -57925,9 +58272,10 @@ function applyTemplate(template, value, unit) {
|
|
|
57925
58272
|
return template.replaceAll("${value}", value).replaceAll("${unit}", unit);
|
|
57926
58273
|
}
|
|
57927
58274
|
/** The whole formatting chain: raw value → the text to burn into the slot. */
|
|
57928
|
-
function formatSlotText(raw, binding) {
|
|
57929
|
-
const value = displayValue(raw, binding);
|
|
57930
|
-
|
|
58275
|
+
function formatSlotText(raw, binding, options = {}) {
|
|
58276
|
+
const value = displayValue(options.displayRaw ?? raw, binding);
|
|
58277
|
+
const unit = options.unit ?? binding.unitLabel ?? "";
|
|
58278
|
+
return limitText(applyTemplate(binding.template, value, unit).trim(), binding.maxCharacters);
|
|
57931
58279
|
}
|
|
57932
58280
|
//#endregion
|
|
57933
58281
|
//#region src/state-leaves.ts
|
|
@@ -58015,6 +58363,92 @@ function readStatePath(slice, path) {
|
|
|
58015
58363
|
return current;
|
|
58016
58364
|
}
|
|
58017
58365
|
//#endregion
|
|
58366
|
+
//#region src/template-tokens.ts
|
|
58367
|
+
/** Build the placeholder token for one template field. */
|
|
58368
|
+
function fieldToken(field) {
|
|
58369
|
+
return `{${field.deviceId}.${field.capName}.${field.valuePath}}`;
|
|
58370
|
+
}
|
|
58371
|
+
/** Replace every occurrence of `token` in `parser` with `value`. */
|
|
58372
|
+
function replaceToken(parser, token, value) {
|
|
58373
|
+
return parser.replaceAll(token, value);
|
|
58374
|
+
}
|
|
58375
|
+
//#endregion
|
|
58376
|
+
//#region src/unit-display.ts
|
|
58377
|
+
/**
|
|
58378
|
+
* Convert a numeric state value for OSD display using `@camstack/types`
|
|
58379
|
+
* UNIT_TABLE / tryConvertUnit (same set as D699).
|
|
58380
|
+
*/
|
|
58381
|
+
/**
|
|
58382
|
+
* Convert `raw` from `sourceUnit` to `displayUnit` when both are known and
|
|
58383
|
+
* same-dimension. On refusal, keep the raw value and the source unit so the
|
|
58384
|
+
* label never lies about the number.
|
|
58385
|
+
*/
|
|
58386
|
+
function convertForDisplay(raw, sourceUnit, displayUnit) {
|
|
58387
|
+
const from = sourceUnit !== void 0 ? normalizeUnit(sourceUnit) : void 0;
|
|
58388
|
+
const to = displayUnit !== void 0 ? normalizeUnit(displayUnit) : void 0;
|
|
58389
|
+
if (from === void 0 || to === void 0 || from === to) return {
|
|
58390
|
+
value: raw,
|
|
58391
|
+
unit: to ?? from ?? ""
|
|
58392
|
+
};
|
|
58393
|
+
const converted = tryConvertUnit(raw, from, to);
|
|
58394
|
+
if (converted === void 0) return {
|
|
58395
|
+
value: raw,
|
|
58396
|
+
unit: from
|
|
58397
|
+
};
|
|
58398
|
+
return {
|
|
58399
|
+
value: converted,
|
|
58400
|
+
unit: to
|
|
58401
|
+
};
|
|
58402
|
+
}
|
|
58403
|
+
/**
|
|
58404
|
+
* Best-effort unit from a state slice: sibling `unit` at the same depth as
|
|
58405
|
+
* `valuePath`, else top-level `unit`.
|
|
58406
|
+
*/
|
|
58407
|
+
function inferSourceUnit(slice, valuePath) {
|
|
58408
|
+
if (typeof slice !== "object" || slice === null || Array.isArray(slice)) return void 0;
|
|
58409
|
+
const root = { ...slice };
|
|
58410
|
+
const segments = valuePath.split(".");
|
|
58411
|
+
if (segments.length >= 1) {
|
|
58412
|
+
let parent = root;
|
|
58413
|
+
for (const segment of segments.slice(0, -1)) {
|
|
58414
|
+
if (typeof parent !== "object" || parent === null || Array.isArray(parent)) {
|
|
58415
|
+
parent = void 0;
|
|
58416
|
+
break;
|
|
58417
|
+
}
|
|
58418
|
+
parent = { ...parent }[segment];
|
|
58419
|
+
}
|
|
58420
|
+
if (typeof parent === "object" && parent !== null && !Array.isArray(parent)) {
|
|
58421
|
+
const unit = { ...parent }.unit;
|
|
58422
|
+
if (typeof unit === "string" && unit.length > 0) return unit;
|
|
58423
|
+
}
|
|
58424
|
+
}
|
|
58425
|
+
const top = root.unit;
|
|
58426
|
+
return typeof top === "string" && top.length > 0 ? top : void 0;
|
|
58427
|
+
}
|
|
58428
|
+
//#endregion
|
|
58429
|
+
//#region src/named-template-render.ts
|
|
58430
|
+
/** Render one named template's parser from live states. */
|
|
58431
|
+
function renderNamedTemplate(template, facts) {
|
|
58432
|
+
let text = template.parser;
|
|
58433
|
+
if (template.fields.length === 0) return text.trim() === "" ? { reason: `template "${template.id}" has no fields` } : { text };
|
|
58434
|
+
for (const field of template.fields) {
|
|
58435
|
+
const token = fieldToken(field);
|
|
58436
|
+
const deviceState = facts.stateByDevice.get(field.deviceId);
|
|
58437
|
+
if (deviceState === void 0) return { reason: `no state mirrored for device ${field.deviceId}` };
|
|
58438
|
+
const slice = deviceState[field.capName];
|
|
58439
|
+
if (slice === void 0) return { reason: `device ${field.deviceId} publishes no "${field.capName}" state` };
|
|
58440
|
+
const raw = readStatePath(slice, field.valuePath);
|
|
58441
|
+
if (raw === void 0) return { reason: `"${field.capName}.${field.valuePath}" is absent on device ${field.deviceId}` };
|
|
58442
|
+
let rendered;
|
|
58443
|
+
if (typeof raw === "number") rendered = truncateDecimals(convertForDisplay(raw, inferSourceUnit(slice, field.valuePath), field.displayUnit).value, field.maxDecimals);
|
|
58444
|
+
else if (typeof raw === "boolean") rendered = raw ? "ON" : "OFF";
|
|
58445
|
+
else if (raw === null) rendered = "";
|
|
58446
|
+
else rendered = String(raw);
|
|
58447
|
+
text = replaceToken(text, token, rendered);
|
|
58448
|
+
}
|
|
58449
|
+
return { text };
|
|
58450
|
+
}
|
|
58451
|
+
//#endregion
|
|
58018
58452
|
//#region src/render.ts
|
|
58019
58453
|
/**
|
|
58020
58454
|
* Read the source's value. `null` means "unreadable" — a missing device, a
|
|
@@ -58029,6 +58463,16 @@ function resolveSource(binding, cameraDeviceId, facts) {
|
|
|
58029
58463
|
const value = facts.recognitionByDevice.get(cameraDeviceId)?.[source.recognition];
|
|
58030
58464
|
return value === void 0 ? { reason: `no recognized ${source.recognition} has been persisted for device ${cameraDeviceId}` } : { value: { raw: value } };
|
|
58031
58465
|
}
|
|
58466
|
+
if (source.kind === "template") {
|
|
58467
|
+
const named = facts.templatesById.get(source.templateId);
|
|
58468
|
+
if (named === void 0) return { reason: `named template "${source.templateId}" is missing` };
|
|
58469
|
+
const rendered = renderNamedTemplate(named, { stateByDevice: facts.stateByDevice });
|
|
58470
|
+
if ("reason" in rendered) return rendered;
|
|
58471
|
+
return { value: {
|
|
58472
|
+
raw: rendered.text,
|
|
58473
|
+
preformatted: rendered.text
|
|
58474
|
+
} };
|
|
58475
|
+
}
|
|
58032
58476
|
const deviceId = source.deviceId ?? cameraDeviceId;
|
|
58033
58477
|
const deviceState = facts.stateByDevice.get(deviceId);
|
|
58034
58478
|
if (deviceState === void 0) return { reason: `no state mirrored for device ${deviceId}` };
|
|
@@ -58036,6 +58480,14 @@ function resolveSource(binding, cameraDeviceId, facts) {
|
|
|
58036
58480
|
if (slice === void 0) return { reason: `device ${deviceId} publishes no "${source.capName}" state` };
|
|
58037
58481
|
const raw = readStatePath(slice, source.valuePath);
|
|
58038
58482
|
if (raw === void 0) return { reason: `"${source.capName}.${source.valuePath}" is absent on device ${deviceId}` };
|
|
58483
|
+
if (typeof raw === "number") {
|
|
58484
|
+
const converted = convertForDisplay(raw, inferSourceUnit(slice, source.valuePath), source.displayUnit);
|
|
58485
|
+
return { value: {
|
|
58486
|
+
raw,
|
|
58487
|
+
displayRaw: converted.value,
|
|
58488
|
+
unit: converted.unit || binding.unitLabel
|
|
58489
|
+
} };
|
|
58490
|
+
}
|
|
58039
58491
|
return { value: { raw } };
|
|
58040
58492
|
}
|
|
58041
58493
|
/**
|
|
@@ -58068,7 +58520,12 @@ function renderSlot(slotId, binding, cameraDeviceId, facts, previousText) {
|
|
|
58068
58520
|
if (!gate.open) return settle(slotId, fallback, previousText, "gated", `condition "${gate.failedCondition}"`);
|
|
58069
58521
|
const resolved = resolveSource(binding, cameraDeviceId, facts);
|
|
58070
58522
|
if ("reason" in resolved) return settle(slotId, fallback, previousText, "unreadable", resolved.reason);
|
|
58071
|
-
return settle(slotId,
|
|
58523
|
+
if (resolved.value.preformatted !== void 0 && binding.source.kind === "template") return settle(slotId, limitText(resolved.value.preformatted.trim(), binding.maxCharacters), previousText, "written");
|
|
58524
|
+
const unit = resolved.value.unit ?? binding.unitLabel;
|
|
58525
|
+
return settle(slotId, formatSlotText(resolved.value.raw, binding, {
|
|
58526
|
+
...resolved.value.displayRaw !== void 0 ? { displayRaw: resolved.value.displayRaw } : {},
|
|
58527
|
+
...unit !== void 0 ? { unit } : {}
|
|
58528
|
+
}), previousText, "written");
|
|
58072
58529
|
}
|
|
58073
58530
|
function settle(slotId, text, previousText, outcome, reason) {
|
|
58074
58531
|
if (previousText !== void 0 && previousText === text) return {
|
|
@@ -58128,6 +58585,24 @@ function sourceOptionsFor(cameraDeviceId, devices, capsByDevice) {
|
|
|
58128
58585
|
return options;
|
|
58129
58586
|
}
|
|
58130
58587
|
//#endregion
|
|
58588
|
+
//#region src/templates-store.ts
|
|
58589
|
+
/**
|
|
58590
|
+
* Named OSD templates — durable in the addon's own (global) store.
|
|
58591
|
+
*/
|
|
58592
|
+
var OSD_TEMPLATES_KEY = "namedTemplates";
|
|
58593
|
+
var OsdNamedTemplatesSchema = array(OsdNamedTemplateSchema);
|
|
58594
|
+
var EMPTY_TEMPLATES = [];
|
|
58595
|
+
function upsertNamedTemplate(prev, template) {
|
|
58596
|
+
const parsed = OsdNamedTemplateSchema.parse(template);
|
|
58597
|
+
return [...prev.filter((t) => t.id !== parsed.id), parsed].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
58598
|
+
}
|
|
58599
|
+
function deleteNamedTemplate(prev, templateId) {
|
|
58600
|
+
return prev.filter((t) => t.id !== templateId);
|
|
58601
|
+
}
|
|
58602
|
+
function findNamedTemplate(templates, templateId) {
|
|
58603
|
+
return templates.find((t) => t.id === templateId);
|
|
58604
|
+
}
|
|
58605
|
+
//#endregion
|
|
58131
58606
|
//#region src/osd-manager.ts
|
|
58132
58607
|
/**
|
|
58133
58608
|
* The `osd-manager` provider — the render loop and the operator surface.
|
|
@@ -58491,6 +58966,26 @@ var OsdManager = class {
|
|
|
58491
58966
|
renderDevice = async ({ deviceId }) => {
|
|
58492
58967
|
return { results: [...await this.renderDeviceInternal(deviceId, null)] };
|
|
58493
58968
|
};
|
|
58969
|
+
listTemplates = async () => {
|
|
58970
|
+
return { templates: [...await this.deps.templates.get()] };
|
|
58971
|
+
};
|
|
58972
|
+
upsertTemplate = async ({ template }) => {
|
|
58973
|
+
const next = upsertNamedTemplate(await this.deps.templates.get(), template);
|
|
58974
|
+
await this.deps.templates.set(next);
|
|
58975
|
+
const stored = findNamedTemplate(next, template.id);
|
|
58976
|
+
if (stored === void 0) throw new Error(`osd-manager: template "${template.id}" missing after upsert`);
|
|
58977
|
+
this.logger.info("osd-manager: named template upserted", { meta: {
|
|
58978
|
+
templateId: stored.id,
|
|
58979
|
+
fieldCount: stored.fields.length
|
|
58980
|
+
} });
|
|
58981
|
+
return { template: stored };
|
|
58982
|
+
};
|
|
58983
|
+
deleteTemplate = async ({ templateId }) => {
|
|
58984
|
+
const prev = await this.deps.templates.get();
|
|
58985
|
+
await this.deps.templates.set(deleteNamedTemplate(prev, templateId));
|
|
58986
|
+
this.logger.info("osd-manager: named template deleted", { meta: { templateId } });
|
|
58987
|
+
return { success: true };
|
|
58988
|
+
};
|
|
58494
58989
|
async renderAllBound() {
|
|
58495
58990
|
for (const deviceId of [...this.boundDevices]) try {
|
|
58496
58991
|
await this.renderDeviceInternal(deviceId, null);
|
|
@@ -58558,11 +59053,19 @@ var OsdManager = class {
|
|
|
58558
59053
|
async collectFacts(cameraDeviceId, bindings) {
|
|
58559
59054
|
const wanted = new Set([cameraDeviceId]);
|
|
58560
59055
|
const needsRecognition = Object.values(bindings).some((binding) => binding.source.kind === "latest-recognition");
|
|
59056
|
+
const templateIds = /* @__PURE__ */ new Set();
|
|
58561
59057
|
for (const binding of Object.values(bindings)) {
|
|
58562
59058
|
if (binding.source.kind === "device-state" && binding.source.deviceId !== void 0) wanted.add(binding.source.deviceId);
|
|
59059
|
+
if (binding.source.kind === "template") templateIds.add(binding.source.templateId);
|
|
58563
59060
|
const gated = binding.conditions?.deviceState?.deviceId;
|
|
58564
59061
|
if (gated !== void 0) wanted.add(gated);
|
|
58565
59062
|
}
|
|
59063
|
+
const allTemplates = await this.deps.templates.get();
|
|
59064
|
+
const templatesById = /* @__PURE__ */ new Map();
|
|
59065
|
+
for (const named of allTemplates) {
|
|
59066
|
+
templatesById.set(named.id, named);
|
|
59067
|
+
if (templateIds.has(named.id)) for (const field of named.fields) wanted.add(field.deviceId);
|
|
59068
|
+
}
|
|
58566
59069
|
const stateByDevice = /* @__PURE__ */ new Map();
|
|
58567
59070
|
const gateState = /* @__PURE__ */ new Map();
|
|
58568
59071
|
for (const id of wanted) try {
|
|
@@ -58587,7 +59090,8 @@ var OsdManager = class {
|
|
|
58587
59090
|
atMs: this.now(),
|
|
58588
59091
|
stateByDevice,
|
|
58589
59092
|
recognitionByDevice,
|
|
58590
|
-
deviceState: (id) => gateState.get(id)
|
|
59093
|
+
deviceState: (id) => gateState.get(id),
|
|
59094
|
+
templatesById
|
|
58591
59095
|
};
|
|
58592
59096
|
}
|
|
58593
59097
|
async hydrateRecognitions(deviceId) {
|
|
@@ -58728,6 +59232,7 @@ var OsdManagerAddon = class extends BaseAddon {
|
|
|
58728
59232
|
const ctx = this.ctx;
|
|
58729
59233
|
const store = new OsdBindingsStore((deviceId, key, schema, fallback) => this.deviceState(deviceId, key, schema, fallback));
|
|
58730
59234
|
const boundIndex = this.state(OSD_BOUND_INDEX_KEY, OsdBoundIndexSchema, null);
|
|
59235
|
+
const templates = this.state(OSD_TEMPLATES_KEY, OsdNamedTemplatesSchema, EMPTY_TEMPLATES);
|
|
58731
59236
|
const manager = new OsdManager({
|
|
58732
59237
|
logger: ctx.logger,
|
|
58733
59238
|
readOsdStatus: async (deviceId) => {
|
|
@@ -58801,7 +59306,8 @@ var OsdManagerAddon = class extends BaseAddon {
|
|
|
58801
59306
|
};
|
|
58802
59307
|
},
|
|
58803
59308
|
bindings: (deviceId) => store.forDevice(deviceId),
|
|
58804
|
-
boundIndex
|
|
59309
|
+
boundIndex,
|
|
59310
|
+
templates
|
|
58805
59311
|
});
|
|
58806
59312
|
this.manager = manager;
|
|
58807
59313
|
this.subscribe({ category: EventCategory.PipelineAnalyticsTrackLifecycle }, (event) => {
|