@camstack/addon-pipeline-orchestrator 1.2.626 → 1.2.628

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/index.js CHANGED
@@ -30,7 +30,7 @@ node_fs = __toESM(node_fs);
30
30
  let node_path = require("node:path");
31
31
  node_path = __toESM(node_path);
32
32
  let node_url = require("node:url");
33
- //#region ../types/dist/event-category-Ceif8n-v.mjs
33
+ //#region ../types/dist/event-category-DHXxB8t9.mjs
34
34
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
35
35
  EventCategory["SystemBoot"] = "system.boot";
36
36
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -326,7 +326,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
326
326
  * TRANSITION into that state (never per dispatch), and the node is dropped
327
327
  * from the placement candidate set for as long as it holds.
328
328
  *
329
- * This exists because the state was previously invisible: little-unraid
329
+ * This exists because the state was previously invisible: agent-a
330
330
  * absorbed 283k inference errors in a day while still being handed cameras,
331
331
  * and nothing in the system said so.
332
332
  *
@@ -5598,7 +5598,7 @@ var ZodIssueCode = {
5598
5598
  var ZodFirstPartyTypeKind;
5599
5599
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5600
5600
  //#endregion
5601
- //#region ../types/dist/sleep-CYfIZzGv.mjs
5601
+ //#region ../types/dist/sleep-BErAxhul.mjs
5602
5602
  /**
5603
5603
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5604
5604
  * window to float samples (D455).
@@ -9260,7 +9260,7 @@ var viewerUiCapability = {
9260
9260
  * `getStaticDir` leaks a hub filesystem path.
9261
9261
  *
9262
9262
  * ⚠ Checked before gating: the VIEWER app IS used by a non-admin user
9263
- * (`roberta`), but it never calls `viewerUi.*` over tRPC. The static SPA
9263
+ * (`bob`), but it never calls `viewerUi.*` over tRPC. The static SPA
9264
9264
  * it loads is served by the plain `GET /viewer/*` Fastify route in
9265
9265
  * `main.ts`, which reads `staticDir`/`indexPath` from a module-level
9266
9266
  * cache populated ONCE at boot via `capRegistry.getSingletonForNode`
@@ -9335,6 +9335,1980 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
9335
9335
  kind: "mutation",
9336
9336
  auth: "admin"
9337
9337
  }), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
9338
+ Object.freeze({ "doorbell": "lastPressedAt" });
9339
+ /** Every capability name, so addon code can check a name without the schema graph. */
9340
+ var CAP_NAMES = Object.freeze([
9341
+ "accessories",
9342
+ "addon-pages",
9343
+ "addon-pages-source",
9344
+ "addon-routes",
9345
+ "addon-settings",
9346
+ "addon-widgets",
9347
+ "addon-widgets-source",
9348
+ "addons",
9349
+ "admin-ui",
9350
+ "air-quality-sensor",
9351
+ "alarm-panel",
9352
+ "alerts",
9353
+ "ambient-light-sensor",
9354
+ "audio-analysis",
9355
+ "audio-analyzer",
9356
+ "audio-codec",
9357
+ "audio-metrics",
9358
+ "auth-provider",
9359
+ "automation-control",
9360
+ "backup",
9361
+ "battery",
9362
+ "binary",
9363
+ "brightness",
9364
+ "broker",
9365
+ "button",
9366
+ "calendar-source",
9367
+ "calendars",
9368
+ "camera-credentials",
9369
+ "camera-grid-layout",
9370
+ "camera-pipeline-config",
9371
+ "camera-streams",
9372
+ "carbon-monoxide",
9373
+ "climate-control",
9374
+ "color",
9375
+ "composer",
9376
+ "connection-test",
9377
+ "connectivity",
9378
+ "consumables",
9379
+ "contact",
9380
+ "control",
9381
+ "core-blocks",
9382
+ "cover",
9383
+ "custom-model-registry",
9384
+ "data-store-provider",
9385
+ "database-snapshot",
9386
+ "day-night",
9387
+ "decoder",
9388
+ "detection-pipeline",
9389
+ "device-admin-link",
9390
+ "device-adoption",
9391
+ "device-credentials",
9392
+ "device-discovery",
9393
+ "device-export",
9394
+ "device-extension",
9395
+ "device-history",
9396
+ "device-manager",
9397
+ "device-ops",
9398
+ "device-provider",
9399
+ "device-removal-hook",
9400
+ "device-state",
9401
+ "device-status",
9402
+ "dishwasher",
9403
+ "display-palette",
9404
+ "doorbell",
9405
+ "embedding-encoder",
9406
+ "enum-sensor",
9407
+ "event-emitter",
9408
+ "face-gallery",
9409
+ "failure-contribution",
9410
+ "fan-control",
9411
+ "feature-probe",
9412
+ "filesystem-browse",
9413
+ "flood",
9414
+ "gas",
9415
+ "heating-valve",
9416
+ "humidifier",
9417
+ "humidity-sensor",
9418
+ "image",
9419
+ "image-settings",
9420
+ "integrations",
9421
+ "intercom",
9422
+ "keychain",
9423
+ "keychain-admin",
9424
+ "lawn-mower-control",
9425
+ "llm",
9426
+ "llm-runtime",
9427
+ "load-contribution",
9428
+ "local-network",
9429
+ "lock-control",
9430
+ "log-channels",
9431
+ "log-destination",
9432
+ "login-method",
9433
+ "media-player",
9434
+ "mesh-network",
9435
+ "metrics-provider",
9436
+ "model-convert",
9437
+ "model-distributor",
9438
+ "motion",
9439
+ "motion-detection",
9440
+ "motion-trigger",
9441
+ "motion-zones",
9442
+ "mqtt-broker",
9443
+ "native-object-detection",
9444
+ "navigation",
9445
+ "network-access",
9446
+ "network-link",
9447
+ "network-quality",
9448
+ "node-certificates",
9449
+ "nodes",
9450
+ "notification-output",
9451
+ "notification-rules",
9452
+ "notifier",
9453
+ "numeric-sensor",
9454
+ "oauth-integration",
9455
+ "oidc-connections",
9456
+ "osd",
9457
+ "osd-manager",
9458
+ "pet-feeder",
9459
+ "pipeline-analytics",
9460
+ "pipeline-executor",
9461
+ "pipeline-orchestrator",
9462
+ "pipeline-runner",
9463
+ "plate-gallery",
9464
+ "platform-probe",
9465
+ "power-meter",
9466
+ "presence",
9467
+ "pressure-sensor",
9468
+ "privacy-mask",
9469
+ "ptz",
9470
+ "ptz-autotrack",
9471
+ "reboot",
9472
+ "record-entries",
9473
+ "recording",
9474
+ "recording-archive",
9475
+ "recording-export",
9476
+ "recording-onboard",
9477
+ "recording-signal",
9478
+ "records",
9479
+ "running-state",
9480
+ "scene-monitor",
9481
+ "script-runner",
9482
+ "server-management",
9483
+ "settings-store",
9484
+ "smoke",
9485
+ "smtp-provider",
9486
+ "snapshot",
9487
+ "sso-bridge",
9488
+ "storage",
9489
+ "storage-admin",
9490
+ "storage-evictable",
9491
+ "storage-migration",
9492
+ "storage-occupancy",
9493
+ "storage-provider",
9494
+ "stream-broker",
9495
+ "stream-catalog",
9496
+ "stream-params",
9497
+ "stream-signals",
9498
+ "switch",
9499
+ "system",
9500
+ "tamper",
9501
+ "temperature-sensor",
9502
+ "terminal-session",
9503
+ "toast",
9504
+ "training",
9505
+ "training-provider",
9506
+ "turn-provider",
9507
+ "update",
9508
+ "user-management",
9509
+ "user-passkeys",
9510
+ "vacuum-control",
9511
+ "valve",
9512
+ "vector-store",
9513
+ "vibration",
9514
+ "videoclips",
9515
+ "viewer-ui",
9516
+ "water-heater",
9517
+ "weather",
9518
+ "webrtc-session",
9519
+ "zone-analytics",
9520
+ "zone-rules",
9521
+ "zones"
9522
+ ]);
9523
+ Object.freeze({
9524
+ "air-quality-sensor": [
9525
+ {
9526
+ "path": "co2Ppm",
9527
+ "valueType": "number"
9528
+ },
9529
+ {
9530
+ "path": "vocPpb",
9531
+ "valueType": "number"
9532
+ },
9533
+ {
9534
+ "path": "pm25",
9535
+ "valueType": "number"
9536
+ },
9537
+ {
9538
+ "path": "pm10",
9539
+ "valueType": "number"
9540
+ },
9541
+ {
9542
+ "path": "aqi",
9543
+ "valueType": "number"
9544
+ },
9545
+ {
9546
+ "path": "unit",
9547
+ "valueType": "string"
9548
+ },
9549
+ {
9550
+ "path": "precision",
9551
+ "valueType": "number"
9552
+ }
9553
+ ],
9554
+ "alarm-panel": [
9555
+ {
9556
+ "path": "state",
9557
+ "valueType": "enum",
9558
+ "enumValues": [
9559
+ "disarmed",
9560
+ "armed_home",
9561
+ "armed_away",
9562
+ "armed_night",
9563
+ "armed_vacation",
9564
+ "armed_custom_bypass",
9565
+ "arming",
9566
+ "disarming",
9567
+ "pending",
9568
+ "triggered"
9569
+ ]
9570
+ },
9571
+ {
9572
+ "path": "targetMode",
9573
+ "valueType": "enum",
9574
+ "enumValues": [
9575
+ "home",
9576
+ "away",
9577
+ "night",
9578
+ "vacation",
9579
+ "custom_bypass"
9580
+ ]
9581
+ },
9582
+ {
9583
+ "path": "requiresCode",
9584
+ "valueType": "boolean"
9585
+ }
9586
+ ],
9587
+ "ambient-light-sensor": [
9588
+ {
9589
+ "path": "lux",
9590
+ "valueType": "number"
9591
+ },
9592
+ {
9593
+ "path": "unit",
9594
+ "valueType": "string"
9595
+ },
9596
+ {
9597
+ "path": "precision",
9598
+ "valueType": "number"
9599
+ }
9600
+ ],
9601
+ "audio-metrics": [
9602
+ {
9603
+ "path": "ts",
9604
+ "valueType": "number"
9605
+ },
9606
+ {
9607
+ "path": "windowSec",
9608
+ "valueType": "number"
9609
+ },
9610
+ {
9611
+ "path": "level.rms",
9612
+ "valueType": "number"
9613
+ },
9614
+ {
9615
+ "path": "level.dbfs",
9616
+ "valueType": "number"
9617
+ },
9618
+ {
9619
+ "path": "peakDbfs",
9620
+ "valueType": "number"
9621
+ },
9622
+ {
9623
+ "path": "avgDbfs",
9624
+ "valueType": "number"
9625
+ },
9626
+ {
9627
+ "path": "current.className",
9628
+ "valueType": "string"
9629
+ },
9630
+ {
9631
+ "path": "current.score",
9632
+ "valueType": "number"
9633
+ },
9634
+ {
9635
+ "path": "current.timestamp",
9636
+ "valueType": "number"
9637
+ }
9638
+ ],
9639
+ "automation-control": [
9640
+ {
9641
+ "path": "enabled",
9642
+ "valueType": "boolean"
9643
+ },
9644
+ {
9645
+ "path": "isRunning",
9646
+ "valueType": "boolean"
9647
+ },
9648
+ {
9649
+ "path": "lastTriggeredAt",
9650
+ "valueType": "number"
9651
+ },
9652
+ {
9653
+ "path": "lastError",
9654
+ "valueType": "string"
9655
+ }
9656
+ ],
9657
+ "battery": [
9658
+ {
9659
+ "path": "percentage",
9660
+ "valueType": "number"
9661
+ },
9662
+ {
9663
+ "path": "charging",
9664
+ "valueType": "enum",
9665
+ "enumValues": [
9666
+ "dc",
9667
+ "solar",
9668
+ "none",
9669
+ "unknown"
9670
+ ]
9671
+ },
9672
+ {
9673
+ "path": "sleeping",
9674
+ "valueType": "boolean"
9675
+ },
9676
+ {
9677
+ "path": "lastUpdated",
9678
+ "valueType": "number"
9679
+ },
9680
+ {
9681
+ "path": "lastContactAt",
9682
+ "valueType": "number"
9683
+ },
9684
+ {
9685
+ "path": "lastFullRefreshAt",
9686
+ "valueType": "number"
9687
+ },
9688
+ {
9689
+ "path": "binary",
9690
+ "valueType": "boolean"
9691
+ }
9692
+ ],
9693
+ "binary": [{
9694
+ "path": "on",
9695
+ "valueType": "boolean"
9696
+ }],
9697
+ "brightness": [{
9698
+ "path": "percentage",
9699
+ "valueType": "number"
9700
+ }],
9701
+ "broker": [{
9702
+ "path": "brokerCount",
9703
+ "valueType": "number"
9704
+ }, {
9705
+ "path": "connectedCount",
9706
+ "valueType": "number"
9707
+ }],
9708
+ "camera-credentials": [
9709
+ {
9710
+ "path": "host",
9711
+ "valueType": "string"
9712
+ },
9713
+ {
9714
+ "path": "port",
9715
+ "valueType": "number"
9716
+ },
9717
+ {
9718
+ "path": "onvifPort",
9719
+ "valueType": "number"
9720
+ },
9721
+ {
9722
+ "path": "rtspPort",
9723
+ "valueType": "number"
9724
+ },
9725
+ {
9726
+ "path": "https",
9727
+ "valueType": "boolean"
9728
+ },
9729
+ {
9730
+ "path": "hasUsername",
9731
+ "valueType": "boolean"
9732
+ },
9733
+ {
9734
+ "path": "hasPassword",
9735
+ "valueType": "boolean"
9736
+ },
9737
+ {
9738
+ "path": "onvifProfile",
9739
+ "valueType": "string"
9740
+ }
9741
+ ],
9742
+ "camera-streams": [
9743
+ {
9744
+ "path": "online",
9745
+ "valueType": "boolean"
9746
+ },
9747
+ {
9748
+ "path": "slotStatuses.high",
9749
+ "valueType": "enum",
9750
+ "enumValues": [
9751
+ "unassigned",
9752
+ "idle",
9753
+ "connecting",
9754
+ "streaming",
9755
+ "error"
9756
+ ]
9757
+ },
9758
+ {
9759
+ "path": "slotStatuses.mid",
9760
+ "valueType": "enum",
9761
+ "enumValues": [
9762
+ "unassigned",
9763
+ "idle",
9764
+ "connecting",
9765
+ "streaming",
9766
+ "error"
9767
+ ]
9768
+ },
9769
+ {
9770
+ "path": "slotStatuses.low",
9771
+ "valueType": "enum",
9772
+ "enumValues": [
9773
+ "unassigned",
9774
+ "idle",
9775
+ "connecting",
9776
+ "streaming",
9777
+ "error"
9778
+ ]
9779
+ },
9780
+ {
9781
+ "path": "slotErrors.high",
9782
+ "valueType": "string"
9783
+ },
9784
+ {
9785
+ "path": "slotErrors.mid",
9786
+ "valueType": "string"
9787
+ },
9788
+ {
9789
+ "path": "slotErrors.low",
9790
+ "valueType": "string"
9791
+ }
9792
+ ],
9793
+ "carbon-monoxide": [{
9794
+ "path": "detected",
9795
+ "valueType": "boolean"
9796
+ }],
9797
+ "climate-control": [
9798
+ {
9799
+ "path": "mode",
9800
+ "valueType": "enum",
9801
+ "enumValues": [
9802
+ "off",
9803
+ "heat",
9804
+ "cool",
9805
+ "auto",
9806
+ "heat_cool",
9807
+ "fan_only",
9808
+ "dry"
9809
+ ]
9810
+ },
9811
+ {
9812
+ "path": "fanMode",
9813
+ "valueType": "string"
9814
+ },
9815
+ {
9816
+ "path": "preset",
9817
+ "valueType": "string"
9818
+ },
9819
+ {
9820
+ "path": "target",
9821
+ "valueType": "number"
9822
+ },
9823
+ {
9824
+ "path": "targetHigh",
9825
+ "valueType": "number"
9826
+ },
9827
+ {
9828
+ "path": "targetLow",
9829
+ "valueType": "number"
9830
+ },
9831
+ {
9832
+ "path": "targetHumidity",
9833
+ "valueType": "number"
9834
+ },
9835
+ {
9836
+ "path": "currentTemp",
9837
+ "valueType": "number"
9838
+ },
9839
+ {
9840
+ "path": "currentHumidity",
9841
+ "valueType": "number"
9842
+ },
9843
+ {
9844
+ "path": "swingVertical",
9845
+ "valueType": "boolean"
9846
+ },
9847
+ {
9848
+ "path": "swingHorizontal",
9849
+ "valueType": "boolean"
9850
+ },
9851
+ {
9852
+ "path": "minTemp",
9853
+ "valueType": "number"
9854
+ },
9855
+ {
9856
+ "path": "maxTemp",
9857
+ "valueType": "number"
9858
+ },
9859
+ {
9860
+ "path": "tempStep",
9861
+ "valueType": "number"
9862
+ }
9863
+ ],
9864
+ "color": [
9865
+ {
9866
+ "path": "mode",
9867
+ "valueType": "enum",
9868
+ "enumValues": [
9869
+ "rgb",
9870
+ "hsv",
9871
+ "mired"
9872
+ ]
9873
+ },
9874
+ {
9875
+ "path": "rgb.r",
9876
+ "valueType": "number"
9877
+ },
9878
+ {
9879
+ "path": "rgb.g",
9880
+ "valueType": "number"
9881
+ },
9882
+ {
9883
+ "path": "rgb.b",
9884
+ "valueType": "number"
9885
+ },
9886
+ {
9887
+ "path": "hsv.h",
9888
+ "valueType": "number"
9889
+ },
9890
+ {
9891
+ "path": "hsv.s",
9892
+ "valueType": "number"
9893
+ },
9894
+ {
9895
+ "path": "hsv.v",
9896
+ "valueType": "number"
9897
+ },
9898
+ {
9899
+ "path": "mireds",
9900
+ "valueType": "number"
9901
+ }
9902
+ ],
9903
+ "connectivity": [{
9904
+ "path": "connected",
9905
+ "valueType": "boolean"
9906
+ }],
9907
+ "contact": [{
9908
+ "path": "entryOpen",
9909
+ "valueType": "boolean"
9910
+ }],
9911
+ "control": [
9912
+ {
9913
+ "path": "kind",
9914
+ "valueType": "enum",
9915
+ "enumValues": [
9916
+ "numeric",
9917
+ "select",
9918
+ "text",
9919
+ "datetime"
9920
+ ]
9921
+ },
9922
+ {
9923
+ "path": "unit",
9924
+ "valueType": "string"
9925
+ },
9926
+ {
9927
+ "path": "min",
9928
+ "valueType": "number"
9929
+ },
9930
+ {
9931
+ "path": "max",
9932
+ "valueType": "number"
9933
+ },
9934
+ {
9935
+ "path": "step",
9936
+ "valueType": "number"
9937
+ },
9938
+ {
9939
+ "path": "precision",
9940
+ "valueType": "number"
9941
+ },
9942
+ {
9943
+ "path": "format",
9944
+ "valueType": "enum",
9945
+ "enumValues": [
9946
+ "date",
9947
+ "time",
9948
+ "datetime"
9949
+ ]
9950
+ }
9951
+ ],
9952
+ "cover": [
9953
+ {
9954
+ "path": "state",
9955
+ "valueType": "enum",
9956
+ "enumValues": [
9957
+ "open",
9958
+ "opening",
9959
+ "closing",
9960
+ "closed",
9961
+ "stopped"
9962
+ ]
9963
+ },
9964
+ {
9965
+ "path": "position",
9966
+ "valueType": "number"
9967
+ },
9968
+ {
9969
+ "path": "tiltPosition",
9970
+ "valueType": "number"
9971
+ }
9972
+ ],
9973
+ "day-night": [
9974
+ {
9975
+ "path": "mode",
9976
+ "valueType": "enum",
9977
+ "enumValues": [
9978
+ "auto",
9979
+ "day",
9980
+ "night",
9981
+ "schedule"
9982
+ ]
9983
+ },
9984
+ {
9985
+ "path": "sensitivity",
9986
+ "valueType": "number"
9987
+ },
9988
+ {
9989
+ "path": "switchDelaySec",
9990
+ "valueType": "number"
9991
+ }
9992
+ ],
9993
+ "device-adoption": [
9994
+ {
9995
+ "path": "lastDiscoveryAt",
9996
+ "valueType": "number"
9997
+ },
9998
+ {
9999
+ "path": "candidateCount",
10000
+ "valueType": "number"
10001
+ },
10002
+ {
10003
+ "path": "adoptedCount",
10004
+ "valueType": "number"
10005
+ },
10006
+ {
10007
+ "path": "lastError",
10008
+ "valueType": "string"
10009
+ }
10010
+ ],
10011
+ "device-discovery": [{
10012
+ "path": "lastDiscoveryAt",
10013
+ "valueType": "number"
10014
+ }, {
10015
+ "path": "lastError",
10016
+ "valueType": "string"
10017
+ }],
10018
+ "device-export": [
10019
+ {
10020
+ "path": "linkState",
10021
+ "valueType": "enum",
10022
+ "enumValues": [
10023
+ "unlinked",
10024
+ "linked",
10025
+ "error"
10026
+ ]
10027
+ },
10028
+ {
10029
+ "path": "exposedDeviceCount",
10030
+ "valueType": "number"
10031
+ },
10032
+ {
10033
+ "path": "error",
10034
+ "valueType": "string"
10035
+ },
10036
+ {
10037
+ "path": "setup.qr",
10038
+ "valueType": "string"
10039
+ },
10040
+ {
10041
+ "path": "setup.note",
10042
+ "valueType": "string"
10043
+ }
10044
+ ],
10045
+ "device-status": [{
10046
+ "path": "online",
10047
+ "valueType": "boolean"
10048
+ }],
10049
+ "dishwasher": [
10050
+ {
10051
+ "path": "availability",
10052
+ "valueType": "enum",
10053
+ "enumValues": [
10054
+ "available",
10055
+ "offline",
10056
+ "description-missing"
10057
+ ]
10058
+ },
10059
+ {
10060
+ "path": "state",
10061
+ "valueType": "enum",
10062
+ "enumValues": [
10063
+ "idle",
10064
+ "running",
10065
+ "paused",
10066
+ "finished",
10067
+ "error",
10068
+ "unavailable"
10069
+ ]
10070
+ },
10071
+ {
10072
+ "path": "operationState",
10073
+ "valueType": "enum",
10074
+ "enumValues": [
10075
+ "Inactive",
10076
+ "Ready",
10077
+ "DelayedStart",
10078
+ "Run",
10079
+ "Pause",
10080
+ "ActionRequired",
10081
+ "Finished",
10082
+ "Error",
10083
+ "Aborting"
10084
+ ]
10085
+ },
10086
+ {
10087
+ "path": "program",
10088
+ "valueType": "string"
10089
+ },
10090
+ {
10091
+ "path": "selectedProgram",
10092
+ "valueType": "string"
10093
+ },
10094
+ {
10095
+ "path": "remainingSeconds",
10096
+ "valueType": "number"
10097
+ },
10098
+ {
10099
+ "path": "delayedStartSeconds",
10100
+ "valueType": "number"
10101
+ },
10102
+ {
10103
+ "path": "progressPercent",
10104
+ "valueType": "number"
10105
+ },
10106
+ {
10107
+ "path": "doorOpen",
10108
+ "valueType": "boolean"
10109
+ },
10110
+ {
10111
+ "path": "doorLocked",
10112
+ "valueType": "boolean"
10113
+ },
10114
+ {
10115
+ "path": "remoteStartAllowed",
10116
+ "valueType": "boolean"
10117
+ },
10118
+ {
10119
+ "path": "remoteControlActive",
10120
+ "valueType": "boolean"
10121
+ },
10122
+ {
10123
+ "path": "localControlActive",
10124
+ "valueType": "boolean"
10125
+ },
10126
+ {
10127
+ "path": "powerOn",
10128
+ "valueType": "boolean"
10129
+ },
10130
+ {
10131
+ "path": "powerState",
10132
+ "valueType": "string"
10133
+ },
10134
+ {
10135
+ "path": "filterCleaningDue",
10136
+ "valueType": "boolean"
10137
+ },
10138
+ {
10139
+ "path": "machineCareDue",
10140
+ "valueType": "boolean"
10141
+ },
10142
+ {
10143
+ "path": "machineCareRemainingRuns",
10144
+ "valueType": "number"
10145
+ },
10146
+ {
10147
+ "path": "halfLoad",
10148
+ "valueType": "boolean"
10149
+ },
10150
+ {
10151
+ "path": "hygienePlus",
10152
+ "valueType": "boolean"
10153
+ },
10154
+ {
10155
+ "path": "extraDry",
10156
+ "valueType": "boolean"
10157
+ },
10158
+ {
10159
+ "path": "intensiveZone",
10160
+ "valueType": "boolean"
10161
+ },
10162
+ {
10163
+ "path": "varioSpeedPlus",
10164
+ "valueType": "boolean"
10165
+ },
10166
+ {
10167
+ "path": "childLock",
10168
+ "valueType": "boolean"
10169
+ },
10170
+ {
10171
+ "path": "errorCode",
10172
+ "valueType": "number"
10173
+ },
10174
+ {
10175
+ "path": "errorLabel",
10176
+ "valueType": "string"
10177
+ }
10178
+ ],
10179
+ "doorbell": [{
10180
+ "path": "lastPressedAt",
10181
+ "valueType": "number"
10182
+ }, {
10183
+ "path": "pressCountSinceStart",
10184
+ "valueType": "number"
10185
+ }],
10186
+ "enum-sensor": [{
10187
+ "path": "value",
10188
+ "valueType": "string"
10189
+ }, {
10190
+ "path": "format",
10191
+ "valueType": "enum",
10192
+ "enumValues": [
10193
+ "date",
10194
+ "time",
10195
+ "datetime"
10196
+ ]
10197
+ }],
10198
+ "event-emitter": [
10199
+ {
10200
+ "path": "lastEvent.deviceId",
10201
+ "valueType": "number"
10202
+ },
10203
+ {
10204
+ "path": "lastEvent.eventType",
10205
+ "valueType": "string"
10206
+ },
10207
+ {
10208
+ "path": "lastEvent.timestamp",
10209
+ "valueType": "number"
10210
+ },
10211
+ {
10212
+ "path": "lastEvent.seq",
10213
+ "valueType": "number"
10214
+ },
10215
+ {
10216
+ "path": "eventCountSinceStart",
10217
+ "valueType": "number"
10218
+ }
10219
+ ],
10220
+ "fan-control": [
10221
+ {
10222
+ "path": "percentage",
10223
+ "valueType": "number"
10224
+ },
10225
+ {
10226
+ "path": "percentageStep",
10227
+ "valueType": "number"
10228
+ },
10229
+ {
10230
+ "path": "preset",
10231
+ "valueType": "string"
10232
+ },
10233
+ {
10234
+ "path": "direction",
10235
+ "valueType": "enum",
10236
+ "enumValues": ["forward", "reverse"]
10237
+ },
10238
+ {
10239
+ "path": "oscillating",
10240
+ "valueType": "boolean"
10241
+ }
10242
+ ],
10243
+ "feature-probe": [
10244
+ {
10245
+ "path": "deviceType",
10246
+ "valueType": "string"
10247
+ },
10248
+ {
10249
+ "path": "model",
10250
+ "valueType": "string"
10251
+ },
10252
+ {
10253
+ "path": "channelCount",
10254
+ "valueType": "number"
10255
+ },
10256
+ {
10257
+ "path": "lastProbedAt",
10258
+ "valueType": "number"
10259
+ }
10260
+ ],
10261
+ "flood": [{
10262
+ "path": "flooded",
10263
+ "valueType": "boolean"
10264
+ }],
10265
+ "gas": [{
10266
+ "path": "detected",
10267
+ "valueType": "boolean"
10268
+ }],
10269
+ "heating-valve": [{
10270
+ "path": "position",
10271
+ "valueType": "number"
10272
+ }],
10273
+ "humidifier": [
10274
+ {
10275
+ "path": "on",
10276
+ "valueType": "boolean"
10277
+ },
10278
+ {
10279
+ "path": "currentHumidity",
10280
+ "valueType": "number"
10281
+ },
10282
+ {
10283
+ "path": "targetHumidity",
10284
+ "valueType": "number"
10285
+ },
10286
+ {
10287
+ "path": "mode",
10288
+ "valueType": "string"
10289
+ },
10290
+ {
10291
+ "path": "action",
10292
+ "valueType": "string"
10293
+ },
10294
+ {
10295
+ "path": "minHumidity",
10296
+ "valueType": "number"
10297
+ },
10298
+ {
10299
+ "path": "maxHumidity",
10300
+ "valueType": "number"
10301
+ }
10302
+ ],
10303
+ "humidity-sensor": [
10304
+ {
10305
+ "path": "percent",
10306
+ "valueType": "number"
10307
+ },
10308
+ {
10309
+ "path": "unit",
10310
+ "valueType": "string"
10311
+ },
10312
+ {
10313
+ "path": "precision",
10314
+ "valueType": "number"
10315
+ }
10316
+ ],
10317
+ "image": [{
10318
+ "path": "url",
10319
+ "valueType": "string"
10320
+ }, {
10321
+ "path": "lastUpdated",
10322
+ "valueType": "number"
10323
+ }],
10324
+ "image-settings": [
10325
+ {
10326
+ "path": "brightness",
10327
+ "valueType": "number"
10328
+ },
10329
+ {
10330
+ "path": "contrast",
10331
+ "valueType": "number"
10332
+ },
10333
+ {
10334
+ "path": "saturation",
10335
+ "valueType": "number"
10336
+ },
10337
+ {
10338
+ "path": "sharpness",
10339
+ "valueType": "number"
10340
+ },
10341
+ {
10342
+ "path": "mirror",
10343
+ "valueType": "boolean"
10344
+ },
10345
+ {
10346
+ "path": "flip",
10347
+ "valueType": "boolean"
10348
+ },
10349
+ {
10350
+ "path": "rotate",
10351
+ "valueType": "enum",
10352
+ "enumValues": [
10353
+ "0",
10354
+ "90",
10355
+ "180",
10356
+ "270"
10357
+ ]
10358
+ },
10359
+ {
10360
+ "path": "whiteBalance",
10361
+ "valueType": "enum",
10362
+ "enumValues": ["auto", "manual"]
10363
+ },
10364
+ {
10365
+ "path": "warmth",
10366
+ "valueType": "number"
10367
+ },
10368
+ {
10369
+ "path": "exposureMode",
10370
+ "valueType": "enum",
10371
+ "enumValues": ["auto", "manual"]
10372
+ },
10373
+ {
10374
+ "path": "backlightMode",
10375
+ "valueType": "enum",
10376
+ "enumValues": [
10377
+ "off",
10378
+ "blc",
10379
+ "wdr",
10380
+ "hlc"
10381
+ ]
10382
+ }
10383
+ ],
10384
+ "intercom": [
10385
+ {
10386
+ "path": "talking",
10387
+ "valueType": "boolean"
10388
+ },
10389
+ {
10390
+ "path": "lastSessionAt",
10391
+ "valueType": "number"
10392
+ },
10393
+ {
10394
+ "path": "ability.sampleRate",
10395
+ "valueType": "number"
10396
+ },
10397
+ {
10398
+ "path": "ability.duplex",
10399
+ "valueType": "enum",
10400
+ "enumValues": ["half", "full"]
10401
+ },
10402
+ {
10403
+ "path": "ability.maxBacklogMs",
10404
+ "valueType": "number"
10405
+ }
10406
+ ],
10407
+ "lawn-mower-control": [
10408
+ {
10409
+ "path": "activity",
10410
+ "valueType": "enum",
10411
+ "enumValues": [
10412
+ "idle",
10413
+ "mowing",
10414
+ "paused",
10415
+ "docked",
10416
+ "error"
10417
+ ]
10418
+ },
10419
+ {
10420
+ "path": "batteryLevel",
10421
+ "valueType": "number"
10422
+ },
10423
+ {
10424
+ "path": "progressPercent",
10425
+ "valueType": "number"
10426
+ },
10427
+ {
10428
+ "path": "currentCode",
10429
+ "valueType": "number"
10430
+ },
10431
+ {
10432
+ "path": "currentCodeLabel",
10433
+ "valueType": "string"
10434
+ },
10435
+ {
10436
+ "path": "severity",
10437
+ "valueType": "enum",
10438
+ "enumValues": [
10439
+ "info",
10440
+ "warning",
10441
+ "error"
10442
+ ]
10443
+ }
10444
+ ],
10445
+ "lock-control": [{
10446
+ "path": "state",
10447
+ "valueType": "enum",
10448
+ "enumValues": [
10449
+ "locked",
10450
+ "unlocked",
10451
+ "locking",
10452
+ "unlocking",
10453
+ "jammed"
10454
+ ]
10455
+ }],
10456
+ "media-player": [
10457
+ {
10458
+ "path": "state",
10459
+ "valueType": "enum",
10460
+ "enumValues": [
10461
+ "off",
10462
+ "on",
10463
+ "idle",
10464
+ "playing",
10465
+ "paused",
10466
+ "buffering",
10467
+ "standby"
10468
+ ]
10469
+ },
10470
+ {
10471
+ "path": "volumeLevel",
10472
+ "valueType": "number"
10473
+ },
10474
+ {
10475
+ "path": "isMuted",
10476
+ "valueType": "boolean"
10477
+ },
10478
+ {
10479
+ "path": "source",
10480
+ "valueType": "string"
10481
+ },
10482
+ {
10483
+ "path": "currentMedia.type",
10484
+ "valueType": "string"
10485
+ },
10486
+ {
10487
+ "path": "currentMedia.title",
10488
+ "valueType": "string"
10489
+ },
10490
+ {
10491
+ "path": "currentMedia.artist",
10492
+ "valueType": "string"
10493
+ },
10494
+ {
10495
+ "path": "currentMedia.album",
10496
+ "valueType": "string"
10497
+ },
10498
+ {
10499
+ "path": "currentMedia.imageUrl",
10500
+ "valueType": "string"
10501
+ },
10502
+ {
10503
+ "path": "positionMs",
10504
+ "valueType": "number"
10505
+ },
10506
+ {
10507
+ "path": "durationMs",
10508
+ "valueType": "number"
10509
+ },
10510
+ {
10511
+ "path": "shuffle",
10512
+ "valueType": "boolean"
10513
+ },
10514
+ {
10515
+ "path": "repeat",
10516
+ "valueType": "enum",
10517
+ "enumValues": [
10518
+ "off",
10519
+ "all",
10520
+ "one"
10521
+ ]
10522
+ }
10523
+ ],
10524
+ "motion": [
10525
+ {
10526
+ "path": "detected",
10527
+ "valueType": "boolean"
10528
+ },
10529
+ {
10530
+ "path": "lastDetectedAt",
10531
+ "valueType": "number"
10532
+ },
10533
+ {
10534
+ "path": "autoClearAfterMs",
10535
+ "valueType": "number"
10536
+ }
10537
+ ],
10538
+ "motion-trigger": [{
10539
+ "path": "enabled",
10540
+ "valueType": "boolean"
10541
+ }],
10542
+ "motion-zones": [{
10543
+ "path": "enabled",
10544
+ "valueType": "boolean"
10545
+ }, {
10546
+ "path": "sensitivity",
10547
+ "valueType": "number"
10548
+ }],
10549
+ "mqtt-broker": [{
10550
+ "path": "brokerCount",
10551
+ "valueType": "number"
10552
+ }, {
10553
+ "path": "embeddedRunning",
10554
+ "valueType": "boolean"
10555
+ }],
10556
+ "native-object-detection": [{
10557
+ "path": "enabled",
10558
+ "valueType": "boolean"
10559
+ }],
10560
+ "navigation": [
10561
+ {
10562
+ "path": "mode",
10563
+ "valueType": "enum",
10564
+ "enumValues": [
10565
+ "idle",
10566
+ "cleaning",
10567
+ "spot",
10568
+ "following",
10569
+ "goto",
10570
+ "returning",
10571
+ "paused",
10572
+ "unknown"
10573
+ ]
10574
+ },
10575
+ {
10576
+ "path": "following",
10577
+ "valueType": "boolean"
10578
+ },
10579
+ {
10580
+ "path": "flash",
10581
+ "valueType": "boolean"
10582
+ },
10583
+ {
10584
+ "path": "lightMode",
10585
+ "valueType": "enum",
10586
+ "enumValues": ["auto", "manual"]
10587
+ },
10588
+ {
10589
+ "path": "lightLevel",
10590
+ "valueType": "number"
10591
+ }
10592
+ ],
10593
+ "network-link": [
10594
+ {
10595
+ "path": "type",
10596
+ "valueType": "enum",
10597
+ "enumValues": [
10598
+ "wifi",
10599
+ "ethernet",
10600
+ "cellular",
10601
+ "unknown"
10602
+ ]
10603
+ },
10604
+ {
10605
+ "path": "signalPercent",
10606
+ "valueType": "number"
10607
+ },
10608
+ {
10609
+ "path": "rssiDbm",
10610
+ "valueType": "number"
10611
+ },
10612
+ {
10613
+ "path": "ssid",
10614
+ "valueType": "string"
10615
+ },
10616
+ {
10617
+ "path": "lastUpdated",
10618
+ "valueType": "number"
10619
+ }
10620
+ ],
10621
+ "notifier": [
10622
+ {
10623
+ "path": "lastSentAt",
10624
+ "valueType": "number"
10625
+ },
10626
+ {
10627
+ "path": "lastError",
10628
+ "valueType": "string"
10629
+ },
10630
+ {
10631
+ "path": "queueDepth",
10632
+ "valueType": "number"
10633
+ },
10634
+ {
10635
+ "path": "supports.image",
10636
+ "valueType": "boolean"
10637
+ },
10638
+ {
10639
+ "path": "supports.priority",
10640
+ "valueType": "boolean"
10641
+ },
10642
+ {
10643
+ "path": "supports.data",
10644
+ "valueType": "boolean"
10645
+ },
10646
+ {
10647
+ "path": "supports.actions",
10648
+ "valueType": "boolean"
10649
+ },
10650
+ {
10651
+ "path": "supports.recipients",
10652
+ "valueType": "boolean"
10653
+ }
10654
+ ],
10655
+ "numeric-sensor": [
10656
+ {
10657
+ "path": "value",
10658
+ "valueType": "number"
10659
+ },
10660
+ {
10661
+ "path": "unit",
10662
+ "valueType": "string"
10663
+ },
10664
+ {
10665
+ "path": "precision",
10666
+ "valueType": "number"
10667
+ }
10668
+ ],
10669
+ "pet-feeder": [
10670
+ {
10671
+ "path": "foodLevel",
10672
+ "valueType": "number"
10673
+ },
10674
+ {
10675
+ "path": "food1",
10676
+ "valueType": "number"
10677
+ },
10678
+ {
10679
+ "path": "food2",
10680
+ "valueType": "number"
10681
+ },
10682
+ {
10683
+ "path": "lowFood",
10684
+ "valueType": "boolean"
10685
+ },
10686
+ {
10687
+ "path": "batteryPower",
10688
+ "valueType": "number"
10689
+ },
10690
+ {
10691
+ "path": "desiccantLeftDays",
10692
+ "valueType": "number"
10693
+ },
10694
+ {
10695
+ "path": "feeding",
10696
+ "valueType": "boolean"
10697
+ },
10698
+ {
10699
+ "path": "status",
10700
+ "valueType": "enum",
10701
+ "enumValues": [
10702
+ "normal",
10703
+ "offline",
10704
+ "on_batteries"
10705
+ ]
10706
+ },
10707
+ {
10708
+ "path": "error",
10709
+ "valueType": "string"
10710
+ },
10711
+ {
10712
+ "path": "errorCode",
10713
+ "valueType": "number"
10714
+ },
10715
+ {
10716
+ "path": "isDualHopper",
10717
+ "valueType": "boolean"
10718
+ },
10719
+ {
10720
+ "path": "childLock",
10721
+ "valueType": "boolean"
10722
+ },
10723
+ {
10724
+ "path": "indicatorLight",
10725
+ "valueType": "boolean"
10726
+ },
10727
+ {
10728
+ "path": "feedSound",
10729
+ "valueType": "boolean"
10730
+ },
10731
+ {
10732
+ "path": "volume",
10733
+ "valueType": "number"
10734
+ }
10735
+ ],
10736
+ "power-meter": [
10737
+ {
10738
+ "path": "watts",
10739
+ "valueType": "number"
10740
+ },
10741
+ {
10742
+ "path": "kwhTotal",
10743
+ "valueType": "number"
10744
+ },
10745
+ {
10746
+ "path": "volts",
10747
+ "valueType": "number"
10748
+ },
10749
+ {
10750
+ "path": "amps",
10751
+ "valueType": "number"
10752
+ },
10753
+ {
10754
+ "path": "va",
10755
+ "valueType": "number"
10756
+ },
10757
+ {
10758
+ "path": "powerFactor",
10759
+ "valueType": "number"
10760
+ },
10761
+ {
10762
+ "path": "unit",
10763
+ "valueType": "string"
10764
+ },
10765
+ {
10766
+ "path": "precision",
10767
+ "valueType": "number"
10768
+ }
10769
+ ],
10770
+ "presence": [
10771
+ {
10772
+ "path": "state",
10773
+ "valueType": "string"
10774
+ },
10775
+ {
10776
+ "path": "location",
10777
+ "valueType": "string"
10778
+ },
10779
+ {
10780
+ "path": "gps.latitude",
10781
+ "valueType": "number"
10782
+ },
10783
+ {
10784
+ "path": "gps.longitude",
10785
+ "valueType": "number"
10786
+ },
10787
+ {
10788
+ "path": "gps.accuracyMeters",
10789
+ "valueType": "number"
10790
+ },
10791
+ {
10792
+ "path": "batteryPercent",
10793
+ "valueType": "number"
10794
+ }
10795
+ ],
10796
+ "pressure-sensor": [
10797
+ {
10798
+ "path": "hpa",
10799
+ "valueType": "number"
10800
+ },
10801
+ {
10802
+ "path": "unit",
10803
+ "valueType": "string"
10804
+ },
10805
+ {
10806
+ "path": "precision",
10807
+ "valueType": "number"
10808
+ }
10809
+ ],
10810
+ "privacy-mask": [{
10811
+ "path": "enabled",
10812
+ "valueType": "boolean"
10813
+ }, {
10814
+ "path": "audioEnabled",
10815
+ "valueType": "boolean"
10816
+ }],
10817
+ "ptz": [
10818
+ {
10819
+ "path": "pan",
10820
+ "valueType": "number"
10821
+ },
10822
+ {
10823
+ "path": "tilt",
10824
+ "valueType": "number"
10825
+ },
10826
+ {
10827
+ "path": "zoom",
10828
+ "valueType": "number"
10829
+ },
10830
+ {
10831
+ "path": "autofocus",
10832
+ "valueType": "boolean"
10833
+ }
10834
+ ],
10835
+ "ptz-autotrack": [
10836
+ {
10837
+ "path": "enabled",
10838
+ "valueType": "boolean"
10839
+ },
10840
+ {
10841
+ "path": "currentSettings.targetType",
10842
+ "valueType": "string"
10843
+ },
10844
+ {
10845
+ "path": "currentSettings.stopDelaySeconds",
10846
+ "valueType": "number"
10847
+ },
10848
+ {
10849
+ "path": "currentSettings.disappearDelaySeconds",
10850
+ "valueType": "number"
10851
+ }
10852
+ ],
10853
+ "recording-archive": [
10854
+ {
10855
+ "path": "deviceId",
10856
+ "valueType": "number"
10857
+ },
10858
+ {
10859
+ "path": "enabled",
10860
+ "valueType": "boolean"
10861
+ },
10862
+ {
10863
+ "path": "activeMode",
10864
+ "valueType": "enum",
10865
+ "enumValues": [
10866
+ "off",
10867
+ "events",
10868
+ "continuous",
10869
+ "on-device-decision"
10870
+ ]
10871
+ },
10872
+ {
10873
+ "path": "nodeId",
10874
+ "valueType": "string"
10875
+ },
10876
+ {
10877
+ "path": "storageBytes",
10878
+ "valueType": "number"
10879
+ }
10880
+ ],
10881
+ "recording-onboard": [
10882
+ {
10883
+ "path": "primaryTrackId",
10884
+ "valueType": "string"
10885
+ },
10886
+ {
10887
+ "path": "enabled",
10888
+ "valueType": "boolean"
10889
+ },
10890
+ {
10891
+ "path": "overwriteWhenFull",
10892
+ "valueType": "boolean"
10893
+ },
10894
+ {
10895
+ "path": "preRecordSec",
10896
+ "valueType": "number"
10897
+ },
10898
+ {
10899
+ "path": "postRecordSec",
10900
+ "valueType": "number"
10901
+ },
10902
+ {
10903
+ "path": "segmentMinutes",
10904
+ "valueType": "number"
10905
+ },
10906
+ {
10907
+ "path": "unreadableWindows",
10908
+ "valueType": "number"
10909
+ },
10910
+ {
10911
+ "path": "recordingToNowhere",
10912
+ "valueType": "boolean"
10913
+ }
10914
+ ],
10915
+ "recording-signal": [{
10916
+ "path": "active",
10917
+ "valueType": "boolean"
10918
+ }, {
10919
+ "path": "reason",
10920
+ "valueType": "string"
10921
+ }],
10922
+ "running-state": [
10923
+ {
10924
+ "path": "running",
10925
+ "valueType": "boolean"
10926
+ },
10927
+ {
10928
+ "path": "since",
10929
+ "valueType": "number"
10930
+ },
10931
+ {
10932
+ "path": "source",
10933
+ "valueType": "enum",
10934
+ "enumValues": ["native", "assigned"]
10935
+ },
10936
+ {
10937
+ "path": "phase",
10938
+ "valueType": "string"
10939
+ }
10940
+ ],
10941
+ "script-runner": [
10942
+ {
10943
+ "path": "isRunning",
10944
+ "valueType": "boolean"
10945
+ },
10946
+ {
10947
+ "path": "lastRunAt",
10948
+ "valueType": "number"
10949
+ },
10950
+ {
10951
+ "path": "lastRunSuccess",
10952
+ "valueType": "boolean"
10953
+ },
10954
+ {
10955
+ "path": "lastError",
10956
+ "valueType": "string"
10957
+ }
10958
+ ],
10959
+ "smoke": [{
10960
+ "path": "detected",
10961
+ "valueType": "boolean"
10962
+ }],
10963
+ "snapshot": [
10964
+ {
10965
+ "path": "lastCapturedAt",
10966
+ "valueType": "number"
10967
+ },
10968
+ {
10969
+ "path": "cacheAgeMs",
10970
+ "valueType": "number"
10971
+ },
10972
+ {
10973
+ "path": "lastBytes",
10974
+ "valueType": "number"
10975
+ },
10976
+ {
10977
+ "path": "lastStreamId",
10978
+ "valueType": "string"
10979
+ }
10980
+ ],
10981
+ "stream-params": [
10982
+ {
10983
+ "path": "main.width",
10984
+ "valueType": "number"
10985
+ },
10986
+ {
10987
+ "path": "main.height",
10988
+ "valueType": "number"
10989
+ },
10990
+ {
10991
+ "path": "main.codec",
10992
+ "valueType": "enum",
10993
+ "enumValues": ["h264", "h265"]
10994
+ },
10995
+ {
10996
+ "path": "main.framerate",
10997
+ "valueType": "number"
10998
+ },
10999
+ {
11000
+ "path": "main.bitrate",
11001
+ "valueType": "number"
11002
+ },
11003
+ {
11004
+ "path": "main.bitrateMode",
11005
+ "valueType": "enum",
11006
+ "enumValues": ["vbr", "cbr"]
11007
+ },
11008
+ {
11009
+ "path": "main.encoderProfile",
11010
+ "valueType": "enum",
11011
+ "enumValues": [
11012
+ "high",
11013
+ "main",
11014
+ "baseline"
11015
+ ]
11016
+ },
11017
+ {
11018
+ "path": "main.gop",
11019
+ "valueType": "number"
11020
+ },
11021
+ {
11022
+ "path": "main.audio",
11023
+ "valueType": "boolean"
11024
+ },
11025
+ {
11026
+ "path": "sub.width",
11027
+ "valueType": "number"
11028
+ },
11029
+ {
11030
+ "path": "sub.height",
11031
+ "valueType": "number"
11032
+ },
11033
+ {
11034
+ "path": "sub.codec",
11035
+ "valueType": "enum",
11036
+ "enumValues": ["h264", "h265"]
11037
+ },
11038
+ {
11039
+ "path": "sub.framerate",
11040
+ "valueType": "number"
11041
+ },
11042
+ {
11043
+ "path": "sub.bitrate",
11044
+ "valueType": "number"
11045
+ },
11046
+ {
11047
+ "path": "sub.bitrateMode",
11048
+ "valueType": "enum",
11049
+ "enumValues": ["vbr", "cbr"]
11050
+ },
11051
+ {
11052
+ "path": "sub.encoderProfile",
11053
+ "valueType": "enum",
11054
+ "enumValues": [
11055
+ "high",
11056
+ "main",
11057
+ "baseline"
11058
+ ]
11059
+ },
11060
+ {
11061
+ "path": "sub.gop",
11062
+ "valueType": "number"
11063
+ },
11064
+ {
11065
+ "path": "sub.audio",
11066
+ "valueType": "boolean"
11067
+ },
11068
+ {
11069
+ "path": "ext.width",
11070
+ "valueType": "number"
11071
+ },
11072
+ {
11073
+ "path": "ext.height",
11074
+ "valueType": "number"
11075
+ },
11076
+ {
11077
+ "path": "ext.codec",
11078
+ "valueType": "enum",
11079
+ "enumValues": ["h264", "h265"]
11080
+ },
11081
+ {
11082
+ "path": "ext.framerate",
11083
+ "valueType": "number"
11084
+ },
11085
+ {
11086
+ "path": "ext.bitrate",
11087
+ "valueType": "number"
11088
+ },
11089
+ {
11090
+ "path": "ext.bitrateMode",
11091
+ "valueType": "enum",
11092
+ "enumValues": ["vbr", "cbr"]
11093
+ },
11094
+ {
11095
+ "path": "ext.encoderProfile",
11096
+ "valueType": "enum",
11097
+ "enumValues": [
11098
+ "high",
11099
+ "main",
11100
+ "baseline"
11101
+ ]
11102
+ },
11103
+ {
11104
+ "path": "ext.gop",
11105
+ "valueType": "number"
11106
+ },
11107
+ {
11108
+ "path": "ext.audio",
11109
+ "valueType": "boolean"
11110
+ }
11111
+ ],
11112
+ "switch": [{
11113
+ "path": "on",
11114
+ "valueType": "boolean"
11115
+ }],
11116
+ "tamper": [{
11117
+ "path": "tampered",
11118
+ "valueType": "boolean"
11119
+ }],
11120
+ "temperature-sensor": [
11121
+ {
11122
+ "path": "celsius",
11123
+ "valueType": "number"
11124
+ },
11125
+ {
11126
+ "path": "unit",
11127
+ "valueType": "string"
11128
+ },
11129
+ {
11130
+ "path": "precision",
11131
+ "valueType": "number"
11132
+ }
11133
+ ],
11134
+ "update": [
11135
+ {
11136
+ "path": "currentVersion",
11137
+ "valueType": "string"
11138
+ },
11139
+ {
11140
+ "path": "availableVersion",
11141
+ "valueType": "string"
11142
+ },
11143
+ {
11144
+ "path": "updatable",
11145
+ "valueType": "boolean"
11146
+ },
11147
+ {
11148
+ "path": "state",
11149
+ "valueType": "string"
11150
+ },
11151
+ {
11152
+ "path": "inProgress",
11153
+ "valueType": "boolean"
11154
+ }
11155
+ ],
11156
+ "vacuum-control": [
11157
+ {
11158
+ "path": "state",
11159
+ "valueType": "enum",
11160
+ "enumValues": [
11161
+ "idle",
11162
+ "cleaning",
11163
+ "paused",
11164
+ "returning",
11165
+ "docked",
11166
+ "drying",
11167
+ "error"
11168
+ ]
11169
+ },
11170
+ {
11171
+ "path": "batteryLevel",
11172
+ "valueType": "number"
11173
+ },
11174
+ {
11175
+ "path": "fanSpeed",
11176
+ "valueType": "string"
11177
+ },
11178
+ {
11179
+ "path": "progressPercent",
11180
+ "valueType": "number"
11181
+ },
11182
+ {
11183
+ "path": "errorCode",
11184
+ "valueType": "number"
11185
+ },
11186
+ {
11187
+ "path": "errorLabel",
11188
+ "valueType": "string"
11189
+ }
11190
+ ],
11191
+ "valve": [{
11192
+ "path": "state",
11193
+ "valueType": "enum",
11194
+ "enumValues": [
11195
+ "open",
11196
+ "opening",
11197
+ "closing",
11198
+ "closed",
11199
+ "stopped"
11200
+ ]
11201
+ }, {
11202
+ "path": "position",
11203
+ "valueType": "number"
11204
+ }],
11205
+ "vibration": [{
11206
+ "path": "detected",
11207
+ "valueType": "boolean"
11208
+ }],
11209
+ "water-heater": [
11210
+ {
11211
+ "path": "currentTemp",
11212
+ "valueType": "number"
11213
+ },
11214
+ {
11215
+ "path": "targetTemp",
11216
+ "valueType": "number"
11217
+ },
11218
+ {
11219
+ "path": "operationMode",
11220
+ "valueType": "string"
11221
+ },
11222
+ {
11223
+ "path": "away",
11224
+ "valueType": "boolean"
11225
+ },
11226
+ {
11227
+ "path": "minTemp",
11228
+ "valueType": "number"
11229
+ },
11230
+ {
11231
+ "path": "maxTemp",
11232
+ "valueType": "number"
11233
+ }
11234
+ ],
11235
+ "weather": [
11236
+ {
11237
+ "path": "condition",
11238
+ "valueType": "string"
11239
+ },
11240
+ {
11241
+ "path": "temperature",
11242
+ "valueType": "number"
11243
+ },
11244
+ {
11245
+ "path": "temperatureUnit",
11246
+ "valueType": "string"
11247
+ },
11248
+ {
11249
+ "path": "humidity",
11250
+ "valueType": "number"
11251
+ },
11252
+ {
11253
+ "path": "pressure",
11254
+ "valueType": "number"
11255
+ },
11256
+ {
11257
+ "path": "pressureUnit",
11258
+ "valueType": "string"
11259
+ },
11260
+ {
11261
+ "path": "windSpeed",
11262
+ "valueType": "number"
11263
+ },
11264
+ {
11265
+ "path": "windSpeedUnit",
11266
+ "valueType": "string"
11267
+ },
11268
+ {
11269
+ "path": "windBearing",
11270
+ "valueType": "number"
11271
+ }
11272
+ ],
11273
+ "zone-analytics": [
11274
+ {
11275
+ "path": "ts",
11276
+ "valueType": "number"
11277
+ },
11278
+ {
11279
+ "path": "frameWidth",
11280
+ "valueType": "number"
11281
+ },
11282
+ {
11283
+ "path": "frameHeight",
11284
+ "valueType": "number"
11285
+ },
11286
+ {
11287
+ "path": "frame.totalObjects",
11288
+ "valueType": "number"
11289
+ },
11290
+ {
11291
+ "path": "frame.standingObjects",
11292
+ "valueType": "number"
11293
+ },
11294
+ {
11295
+ "path": "frame.transientObjects",
11296
+ "valueType": "number"
11297
+ },
11298
+ {
11299
+ "path": "unzoned.totalObjects",
11300
+ "valueType": "number"
11301
+ },
11302
+ {
11303
+ "path": "unzoned.standingObjects",
11304
+ "valueType": "number"
11305
+ },
11306
+ {
11307
+ "path": "unzoned.transientObjects",
11308
+ "valueType": "number"
11309
+ }
11310
+ ]
11311
+ });
9338
11312
  object({
9339
11313
  code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
9340
11314
  params: record(string(), union([string(), number()])).optional()
@@ -9717,7 +11691,7 @@ function errMsg$2(err) {
9717
11691
  return String(err);
9718
11692
  }
9719
11693
  //#endregion
9720
- //#region ../types/dist/composition-BJ8iAZb3.mjs
11694
+ //#region ../types/dist/composition-kEl9mevu.mjs
9721
11695
  /**
9722
11696
  * The ffmpeg build every CamStack node downloads, per platform and arch.
9723
11697
  *
@@ -13393,7 +15367,7 @@ var LogChannelDescriptorSchema = object({
13393
15367
  * `true` is a PROMISE with two halves, and both must hold: the gate is
13394
15368
  * consulted with the numeric device id, AND every line the channel admits
13395
15369
  * carries `tags: { deviceId }` with that same numeric id. The second half is
13396
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
15370
+ * what makes `| json | deviceId="102"` work in Loki — `loki-payload.ts`
13397
15371
  * keeps `deviceId` out of the stream labels for cardinality, so the tag in
13398
15372
  * the body is the only way to filter.
13399
15373
  *
@@ -13600,7 +15574,7 @@ var ModelVariantGroupSchema = object({
13600
15574
  precision: _enum(["fp32", "int8"]).optional(),
13601
15575
  /**
13602
15576
  * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
13603
- * an alternate training of the same family (Scrypted `relu_test`); chip
15577
+ * an alternate training of the same family (plugin catalog `relu_test`); chip
13604
15578
  * label is "Test".
13605
15579
  */
13606
15580
  optimization: _enum(["standard", "test"]).optional(),
@@ -13852,8 +15826,8 @@ var ModelCatalogEntrySchema = object({
13852
15826
  provider: ModelProviderIdSchema.optional(),
13853
15827
  /**
13854
15828
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
13855
- * applies (Frigate / COCO public catalog). Set on a custom model whose raw
13856
- * labels already ARE the CamStack macros (Scrypted identity map).
15829
+ * applies (plus catalog / COCO public catalog). Set on a custom model whose raw
15830
+ * labels already ARE the CamStack macros (plugin catalog identity map).
13857
15831
  */
13858
15832
  classMap: DetectionCatalogClassMapSchema.optional(),
13859
15833
  /**
@@ -16879,7 +18853,7 @@ method(object({
16879
18853
  * `1` is real time and is the ONLY value that allows the copy branch —
16880
18854
  * anything else forces `libx264` over the window. That was priced
16881
18855
  * before it was chosen: a per-event burst measured at 0.23 s and 254 KB
16882
- * on a real 615 720p cut, against 922 KB for the copy it replaces. A
18856
+ * on a real 101 720p cut, against 922 KB for the copy it replaces. A
16883
18857
  * re-encode is capped at 720p (`EVENT_CLIP_ENCODE_MAX_WIDTH`), because
16884
18858
  * once the decode is forced the width stops being free.
16885
18859
  */
@@ -17299,6 +19273,7 @@ var CompositionRunStatusSchema = _enum([
17299
19273
  "starting",
17300
19274
  "running",
17301
19275
  "degraded",
19276
+ "incomplete",
17302
19277
  "failed"
17303
19278
  ]);
17304
19279
  var CompositionFieldStateKindSchema = _enum([
@@ -17512,6 +19487,7 @@ var CoreBlockStatusSchema = _enum([
17512
19487
  "starting",
17513
19488
  "running",
17514
19489
  "degraded",
19490
+ "incomplete",
17515
19491
  "failed"
17516
19492
  ]);
17517
19493
  /** path → TypeScript source for a multi-file block. */
@@ -39435,7 +41411,7 @@ var PRIMARY_CATEGORIES = new Set(ENTRIES.map((e) => e.primaryCategory));
39435
41411
  * `locationKey` is ALWAYS {@link locationIconKey} of the label — never the
39436
41412
  * operator's casing. The registry matches labels trimmed + case-insensitively
39437
41413
  * everywhere (`addLocation`, `removeLocation`, `renameLocation`), and a
39438
- * case-only rename (`cucina` → `Cucina`) is a real edit this repo supports; a
41414
+ * case-only rename (`kitchen` → `Kitchen`) is a real edit this repo supports; a
39439
41415
  * map keyed on the displayed casing would orphan its entry on exactly that
39440
41416
  * edit. Consumers join by keying their own label through `locationIconKey`.
39441
41417
  */
@@ -39460,7 +41436,7 @@ var LocationDescriptionAssignmentSchema = object({
39460
41436
  /**
39461
41437
  * A SITE ZONE — a named part of the property that several locations belong to.
39462
41438
  *
39463
- * "Zona notte" holds every bedroom; "Zona giorno" the living room and the
41439
+ * "Night zone" holds every bedroom; "Zona giorno" the living room and the
39464
41440
  * kitchen. A camera belongs to exactly one location, and a location to at most
39465
41441
  * one zone, so a camera's zone is derived and never stored on the device: the
39466
41442
  * whole point of this entity is that grouping rooms touches no device row.
@@ -39489,7 +41465,7 @@ var LocationDescriptionAssignmentSchema = object({
39489
41465
  * the locations inside it.
39490
41466
  *
39491
41467
  * The LABEL is carried alongside the key because the key is lossy on purpose —
39492
- * a surface must show "Zona notte", not "zona notte". Locations are carried
41468
+ * a surface must show "Night zone", not "night zone". Locations are carried
39493
41469
  * here, rather than left to a second call, because every consumer wants both
39494
41470
  * at once and a viewer that asked twice could render a zone whose membership
39495
41471
  * it has not fetched yet.
@@ -41602,8 +43578,8 @@ var LoadContributionSchema = object({
41602
43578
  "unattributable"
41603
43579
  ]),
41604
43580
  /**
41605
- * What ONE entry is, in the contributor's own words — `615/high`,
41606
- * `617/native`, `cuda:0 shared pool`. Free text because the unit differs per
43581
+ * What ONE entry is, in the contributor's own words — `101/high`,
43582
+ * `102/native`, `cuda:0 shared pool`. Free text because the unit differs per
41607
43583
  * family and inventing a common one would lose the only information that
41608
43584
  * makes two entries for the same camera distinguishable.
41609
43585
  */
@@ -43251,6 +45227,649 @@ var _macroLookup = /* @__PURE__ */ new Map();
43251
45227
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
43252
45228
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
43253
45229
  /**
45230
+ * Palette entry ids (D939). Audio is the one gradient; `audio-*` leaves are
45231
+ * not entries. `heatmapScrub` is a mark, not an event kind.
45232
+ */
45233
+ var PALETTE_ENTRY_IDS = [
45234
+ "motion",
45235
+ "audio",
45236
+ "recording",
45237
+ "live",
45238
+ "heatmapScrub",
45239
+ "person",
45240
+ "vehicle",
45241
+ "animal",
45242
+ "package",
45243
+ "sensor",
45244
+ "control",
45245
+ "face",
45246
+ "plate",
45247
+ "car",
45248
+ "truck",
45249
+ "bus",
45250
+ "motorcycle",
45251
+ "bicycle",
45252
+ "airplane",
45253
+ "boat",
45254
+ "train",
45255
+ "bird",
45256
+ "dog",
45257
+ "cat",
45258
+ "horse",
45259
+ "sheep",
45260
+ "cow",
45261
+ "elephant",
45262
+ "bear",
45263
+ "zebra",
45264
+ "giraffe",
45265
+ "package-delivered",
45266
+ "package-picked-up",
45267
+ "suitcase",
45268
+ "backpack",
45269
+ "handbag",
45270
+ "contact",
45271
+ "motion-sensor",
45272
+ "smoke",
45273
+ "flood",
45274
+ "gas",
45275
+ "carbon-monoxide",
45276
+ "vibration",
45277
+ "tamper",
45278
+ "presence",
45279
+ "enum-sensor",
45280
+ "device-event",
45281
+ "lock",
45282
+ "cover",
45283
+ "switch",
45284
+ "siren",
45285
+ "button",
45286
+ "doorbell"
45287
+ ];
45288
+ var AUDIO_ENTRY_ID = "audio";
45289
+ /** Six rungs, quiet → loud. The opaque end is the entry's hex. */
45290
+ var AUDIO_GRADIENT_ALPHAS = [
45291
+ .3,
45292
+ .42,
45293
+ .55,
45294
+ .7,
45295
+ .85,
45296
+ 1
45297
+ ];
45298
+ var PALETTE_PRESET_IDS = [
45299
+ "camstack",
45300
+ "high-contrast",
45301
+ "okabe-ito"
45302
+ ];
45303
+ function isPaletteEntryId(id) {
45304
+ return PALETTE_ENTRY_IDS.includes(id);
45305
+ }
45306
+ function isPalettePresetId(id) {
45307
+ return PALETTE_PRESET_IDS.includes(id);
45308
+ }
45309
+ /**
45310
+ * CIEDE2000 and WCAG contrast for the display palette (D939).
45311
+ *
45312
+ * One implementation, shared by the hub validator, the admin editor and
45313
+ * (phase 2) the viewer. The arithmetic matches the viewer's `src/theme/cie.ts`.
45314
+ */
45315
+ /** Page backgrounds the contrast rule is measured against. */
45316
+ var PALETTE_BACKGROUNDS = {
45317
+ dark: "#0c0a09",
45318
+ light: "#fafaf9"
45319
+ };
45320
+ /** Marks that are not event kinds and share the raised distance floor. */
45321
+ var PALETTE_MARK_PAIR = ["live", "heatmapScrub"];
45322
+ var HEX = /^#[0-9a-fA-F]{6}$/;
45323
+ function srgbChannel(hex, start) {
45324
+ return Number.parseInt(hex.slice(start, start + 2), 16);
45325
+ }
45326
+ function srgbToLin(channel) {
45327
+ const x = channel / 255;
45328
+ return x <= .04045 ? x / 12.92 : ((x + .055) / 1.055) ** 2.4;
45329
+ }
45330
+ function relativeLuminance(hex) {
45331
+ const r = srgbToLin(srgbChannel(hex, 1));
45332
+ const g = srgbToLin(srgbChannel(hex, 3));
45333
+ const b = srgbToLin(srgbChannel(hex, 5));
45334
+ return .2126 * r + .7152 * g + .0722 * b;
45335
+ }
45336
+ /** WCAG contrast ratio of two `#rrggbb` colours. Non-hex returns 0. */
45337
+ function contrastRatio(a, b) {
45338
+ if (!HEX.test(a) || !HEX.test(b)) return 0;
45339
+ const hi = Math.max(relativeLuminance(a), relativeLuminance(b));
45340
+ const lo = Math.min(relativeLuminance(a), relativeLuminance(b));
45341
+ return (hi + .05) / (lo + .05);
45342
+ }
45343
+ function xyzToLab(hex) {
45344
+ const r = srgbToLin(srgbChannel(hex, 1));
45345
+ const g = srgbToLin(srgbChannel(hex, 3));
45346
+ const bl = srgbToLin(srgbChannel(hex, 5));
45347
+ const x = .4124564 * r + .3575761 * g + .1804375 * bl;
45348
+ const y = .2126729 * r + .7151522 * g + .072175 * bl;
45349
+ const z = .0193339 * r + .119192 * g + .9503041 * bl;
45350
+ const f = (t) => t > .008856 ? Math.cbrt(t) : 7.787 * t + 16 / 116;
45351
+ const fx = f(x / .95047);
45352
+ const fy = f(y);
45353
+ const fz = f(z / 1.08883);
45354
+ return {
45355
+ L: 116 * fy - 16,
45356
+ a: 500 * (fx - fy),
45357
+ b: 200 * (fy - fz)
45358
+ };
45359
+ }
45360
+ function hueDeg(ap, b) {
45361
+ if (ap === 0 && b === 0) return 0;
45362
+ const ang = Math.atan2(b, ap) * 180 / Math.PI;
45363
+ return ang < 0 ? ang + 360 : ang;
45364
+ }
45365
+ /** CIEDE2000. Non-hex returns 0 so a bad swatch fails the distinctness rule. */
45366
+ function deltaE00(hex1, hex2) {
45367
+ if (!HEX.test(hex1) || !HEX.test(hex2)) return 0;
45368
+ const lab1 = xyzToLab(hex1);
45369
+ const lab2 = xyzToLab(hex2);
45370
+ const cab7 = ((Math.hypot(lab1.a, lab1.b) + Math.hypot(lab2.a, lab2.b)) / 2) ** 7;
45371
+ const g = .5 * (1 - Math.sqrt(cab7 / (cab7 + 25 ** 7)));
45372
+ const a1p = (1 + g) * lab1.a;
45373
+ const a2p = (1 + g) * lab2.a;
45374
+ const c1p = Math.hypot(a1p, lab1.b);
45375
+ const c2p = Math.hypot(a2p, lab2.b);
45376
+ const h1p = hueDeg(a1p, lab1.b);
45377
+ const h2p = hueDeg(a2p, lab2.b);
45378
+ let dhp = 0;
45379
+ if (c1p * c2p !== 0) {
45380
+ dhp = h2p - h1p;
45381
+ if (dhp > 180) dhp -= 360;
45382
+ else if (dhp < -180) dhp += 360;
45383
+ }
45384
+ const dHp = 2 * Math.sqrt(c1p * c2p) * Math.sin(dhp * Math.PI / 360);
45385
+ const lbp = (lab1.L + lab2.L) / 2;
45386
+ const cbp = (c1p + c2p) / 2;
45387
+ let hbp = h1p + h2p;
45388
+ if (c1p * c2p !== 0) {
45389
+ if (Math.abs(h1p - h2p) > 180) hbp += hbp < 360 ? 360 : -360;
45390
+ hbp /= 2;
45391
+ }
45392
+ const t = 1 - .17 * Math.cos((hbp - 30) * Math.PI / 180) + .24 * Math.cos(2 * hbp * Math.PI / 180) + .32 * Math.cos((3 * hbp + 6) * Math.PI / 180) - .2 * Math.cos((4 * hbp - 63) * Math.PI / 180);
45393
+ const dTheta = 30 * Math.exp(-(((hbp - 275) / 25) ** 2));
45394
+ const cbp7 = cbp ** 7;
45395
+ const rc = 2 * Math.sqrt(cbp7 / (cbp7 + 25 ** 7));
45396
+ const sl = 1 + .015 * (lbp - 50) ** 2 / Math.sqrt(20 + (lbp - 50) ** 2);
45397
+ const sc = 1 + .045 * cbp;
45398
+ const sh = 1 + .015 * cbp * t;
45399
+ const rt = -Math.sin(2 * dTheta * Math.PI / 180) * rc;
45400
+ const dLp = lab2.L - lab1.L;
45401
+ const dCp = c2p - c1p;
45402
+ return Math.sqrt((dLp / sl) ** 2 + (dCp / sc) ** 2 + (dHp / sh) ** 2 + rt * (dCp / sc) * (dHp / sh));
45403
+ }
45404
+ /** Frozen palette tables (D939). The spec refuses a table that breaks the rule. */
45405
+ var CAMSTACK_HEX = {
45406
+ motion: "#d946ef",
45407
+ audio: "#059eb8",
45408
+ recording: "#ef4444",
45409
+ live: "#8d7a00",
45410
+ heatmapScrub: "#4b32ff",
45411
+ person: "#11a64c",
45412
+ vehicle: "#3b82f6",
45413
+ animal: "#ea6a09",
45414
+ package: "#924667",
45415
+ sensor: "#624bbc",
45416
+ control: "#944d24",
45417
+ face: "#ed4f8d",
45418
+ plate: "#4b7027",
45419
+ car: "#9684cf",
45420
+ truck: "#0f9b89",
45421
+ bus: "#a734a9",
45422
+ motorcycle: "#c1133a",
45423
+ bicycle: "#0e6a9b",
45424
+ airplane: "#7c9928",
45425
+ boat: "#b17933",
45426
+ train: "#ad6ca3",
45427
+ bird: "#0f7c55",
45428
+ dog: "#c82700",
45429
+ cat: "#7268f2",
45430
+ horse: "#cb795e",
45431
+ sheep: "#b8636b",
45432
+ cow: "#cb2969",
45433
+ elephant: "#1158d2",
45434
+ bear: "#8d5e00",
45435
+ zebra: "#0097d6",
45436
+ giraffe: "#8f61bb",
45437
+ "package-delivered": "#cd3f9b",
45438
+ "package-picked-up": "#388a08",
45439
+ suitcase: "#666800",
45440
+ backpack: "#ec5022",
45441
+ handbag: "#a76ef8",
45442
+ contact: "#ab5a49",
45443
+ "motion-sensor": "#b95d1a",
45444
+ smoke: "#dd3858",
45445
+ flood: "#b31e7d",
45446
+ gas: "#5b70b5",
45447
+ "carbon-monoxide": "#7b4e8e",
45448
+ vibration: "#e65bba",
45449
+ tamper: "#98464b",
45450
+ presence: "#3a9468",
45451
+ "enum-sensor": "#7885f8",
45452
+ "device-event": "#8c43c8",
45453
+ lock: "#649653",
45454
+ cover: "#c67597",
45455
+ switch: "#7a812a",
45456
+ siren: "#ac5d7f",
45457
+ button: "#b276cd",
45458
+ doorbell: "#0087ab"
45459
+ };
45460
+ var CAMSTACK_FALLBACK = [
45461
+ "#b34dc7",
45462
+ "#1d7d34",
45463
+ "#db6b6c",
45464
+ "#a43d29",
45465
+ "#0e6ee6",
45466
+ "#29a10b",
45467
+ "#a98731",
45468
+ "#0181c6"
45469
+ ];
45470
+ var HIGH_CONTRAST_HEX = {
45471
+ motion: "#964360",
45472
+ audio: "#23a132",
45473
+ recording: "#2494ca",
45474
+ live: "#bd7e2b",
45475
+ heatmapScrub: "#ad6eea",
45476
+ person: "#62611e",
45477
+ vehicle: "#f4534c",
45478
+ animal: "#1556cd",
45479
+ package: "#9c4415",
45480
+ sensor: "#066c48",
45481
+ control: "#f2479f",
45482
+ face: "#139d8a",
45483
+ plate: "#909024",
45484
+ car: "#8e35ab",
45485
+ truck: "#bd0b2b",
45486
+ bus: "#7a88ca",
45487
+ motorcycle: "#e26725",
45488
+ bicycle: "#a6679b",
45489
+ airplane: "#7c5720",
45490
+ boat: "#467a1b",
45491
+ train: "#6f65a4",
45492
+ bird: "#cb717f",
45493
+ dog: "#c62490",
45494
+ cat: "#b36850",
45495
+ horse: "#1872ac",
45496
+ sheep: "#927939",
45497
+ cow: "#428a5c",
45498
+ elephant: "#d41160",
45499
+ bear: "#799453",
45500
+ zebra: "#a85455",
45501
+ giraffe: "#746be8",
45502
+ "package-delivered": "#824a80",
45503
+ "package-picked-up": "#614ac1",
45504
+ suitcase: "#2f77f3",
45505
+ backpack: "#fa476d",
45506
+ handbag: "#c14cbe",
45507
+ contact: "#c83921",
45508
+ "motion-sensor": "#a07ebd",
45509
+ smoke: "#a25b13",
45510
+ flood: "#526bab",
45511
+ gas: "#924938",
45512
+ "carbon-monoxide": "#bb7e53",
45513
+ vibration: "#b66182",
45514
+ tamper: "#3c6931",
45515
+ presence: "#6c851d",
45516
+ "enum-sensor": "#ad2d48",
45517
+ "device-event": "#5e9b04",
45518
+ lock: "#00a062",
45519
+ cover: "#ad8624",
45520
+ switch: "#d05259",
45521
+ siren: "#cd67b3",
45522
+ button: "#c9746a",
45523
+ doorbell: "#338eeb"
45524
+ };
45525
+ var HIGH_CONTRAST_FALLBACK = [
45526
+ "#817f3b",
45527
+ "#a0733e",
45528
+ "#8658b2",
45529
+ "#956301",
45530
+ "#bc652d",
45531
+ "#647331",
45532
+ "#a14c9e",
45533
+ "#d14e71"
45534
+ ];
45535
+ var OKABE_ITO_HEX = {
45536
+ motion: "#b88128",
45537
+ audio: "#1096c8",
45538
+ recording: "#009e73",
45539
+ live: "#9c8c22",
45540
+ heatmapScrub: "#0072b2",
45541
+ person: "#d55e00",
45542
+ vehicle: "#c0739f",
45543
+ animal: "#226d18",
45544
+ package: "#91446c",
45545
+ sensor: "#217b56",
45546
+ control: "#648647",
45547
+ face: "#c22998",
45548
+ plate: "#fa4575",
45549
+ car: "#956301",
45550
+ truck: "#2f77f3",
45551
+ bus: "#7a4e86",
45552
+ motorcycle: "#f54597",
45553
+ bicycle: "#bb3e75",
45554
+ airplane: "#e054c0",
45555
+ boat: "#457942",
45556
+ train: "#867afa",
45557
+ bird: "#88914c",
45558
+ dog: "#1556cd",
45559
+ cat: "#71702c",
45560
+ horse: "#9b4fa4",
45561
+ sheep: "#c0559d",
45562
+ cow: "#d14f65",
45563
+ elephant: "#98470f",
45564
+ bear: "#595896",
45565
+ zebra: "#96790e",
45566
+ giraffe: "#cb717b",
45567
+ "package-delivered": "#139d8a",
45568
+ "package-picked-up": "#906ead",
45569
+ suitcase: "#d02939",
45570
+ backpack: "#d41160",
45571
+ handbag: "#f64f54",
45572
+ contact: "#7258d2",
45573
+ "motion-sensor": "#b66182",
45574
+ smoke: "#627abb",
45575
+ flood: "#b86ae4",
45576
+ gas: "#a95261",
45577
+ "carbon-monoxide": "#c65d3c",
45578
+ vibration: "#7a40b7",
45579
+ tamper: "#914b34",
45580
+ presence: "#9481c3",
45581
+ "enum-sensor": "#ac6d47",
45582
+ "device-event": "#7b6fce",
45583
+ lock: "#c67662",
45584
+ cover: "#a6564d",
45585
+ switch: "#ad2c4e",
45586
+ siren: "#348e08",
45587
+ button: "#d27303",
45588
+ doorbell: "#55770f"
45589
+ };
45590
+ var OKABE_ITO_FALLBACK = [
45591
+ "#9a3784",
45592
+ "#a7392c",
45593
+ "#409d50",
45594
+ "#6987e7",
45595
+ "#ee5b3d",
45596
+ "#c0430d",
45597
+ "#af79b3",
45598
+ "#4b6628"
45599
+ ];
45600
+ /**
45601
+ * The display palette (D939). One table of colours, three presets, one
45602
+ * validator. Taxonomy colours and every consumer read this — there is no
45603
+ * second hex table.
45604
+ */
45605
+ var paletteHexSchema = string().regex(/^#[0-9a-f]{6}$/);
45606
+ var paletteSwatchSchema = discriminatedUnion("form", [object({
45607
+ form: literal("solid"),
45608
+ hex: paletteHexSchema
45609
+ }), object({
45610
+ form: literal("gradient"),
45611
+ hex: paletteHexSchema,
45612
+ alphas: array(number().gt(0).lte(1)).length(AUDIO_GRADIENT_ALPHAS.length)
45613
+ })]);
45614
+ var paletteReadySchema = object({
45615
+ status: literal("ready"),
45616
+ presetId: _enum(PALETTE_PRESET_IDS),
45617
+ customized: boolean(),
45618
+ source: _enum(["stored", "unconfigured"]),
45619
+ swatches: record(string(), paletteSwatchSchema),
45620
+ fallbacks: array(paletteHexSchema).length(8)
45621
+ });
45622
+ var paletteResultSchema = discriminatedUnion("status", [paletteReadySchema, object({
45623
+ status: literal("unknown"),
45624
+ reason: string()
45625
+ })]);
45626
+ var setPaletteInputSchema = union([object({ presetId: string() }), object({
45627
+ presetId: string(),
45628
+ overrides: record(string(), paletteSwatchSchema)
45629
+ })]);
45630
+ var storedPaletteSchema = object({
45631
+ presetId: string(),
45632
+ overrides: record(string(), paletteSwatchSchema)
45633
+ });
45634
+ function solid(hex) {
45635
+ return {
45636
+ form: "solid",
45637
+ hex
45638
+ };
45639
+ }
45640
+ function audioGradient(hex) {
45641
+ return {
45642
+ form: "gradient",
45643
+ hex,
45644
+ alphas: [...AUDIO_GRADIENT_ALPHAS]
45645
+ };
45646
+ }
45647
+ function swatchesFrom(hexes) {
45648
+ const out = {};
45649
+ for (const id of PALETTE_ENTRY_IDS) {
45650
+ const hex = hexes[id];
45651
+ if (hex === void 0) throw new Error(`palette table missing ${id}`);
45652
+ out[id] = id === "audio" ? audioGradient(hex) : solid(hex);
45653
+ }
45654
+ return out;
45655
+ }
45656
+ function preset(id, name, hexes, fallbacks) {
45657
+ return {
45658
+ id,
45659
+ name,
45660
+ swatches: swatchesFrom(hexes),
45661
+ fallbacks
45662
+ };
45663
+ }
45664
+ var DISPLAY_PRESETS = [
45665
+ preset("camstack", "CamStack", CAMSTACK_HEX, CAMSTACK_FALLBACK),
45666
+ preset("high-contrast", "High contrast", HIGH_CONTRAST_HEX, HIGH_CONTRAST_FALLBACK),
45667
+ preset("okabe-ito", "Colour-blind safe", OKABE_ITO_HEX, OKABE_ITO_FALLBACK)
45668
+ ];
45669
+ function presetById(id) {
45670
+ return DISPLAY_PRESETS.find((item) => item.id === id);
45671
+ }
45672
+ function bundledCamstack() {
45673
+ const presetRow = presetById("camstack");
45674
+ if (presetRow === void 0) throw new Error("CamStack preset missing");
45675
+ return readyOf(presetRow, "unconfigured", false);
45676
+ }
45677
+ function readyOf(row, source, customized) {
45678
+ return {
45679
+ status: "ready",
45680
+ presetId: row.id,
45681
+ customized,
45682
+ source,
45683
+ swatches: row.swatches,
45684
+ fallbacks: [...row.fallbacks]
45685
+ };
45686
+ }
45687
+ function namedColours(swatches, fallbacks) {
45688
+ const rows = [];
45689
+ for (const id of PALETTE_ENTRY_IDS) {
45690
+ const swatch = swatches[id];
45691
+ if (swatch !== void 0) rows.push({
45692
+ name: id,
45693
+ hex: swatch.hex
45694
+ });
45695
+ }
45696
+ fallbacks.forEach((hex, index) => {
45697
+ rows.push({
45698
+ name: `fallback-${index}`,
45699
+ hex
45700
+ });
45701
+ });
45702
+ return rows;
45703
+ }
45704
+ function gradientViolations(swatches) {
45705
+ const audio = swatches[AUDIO_ENTRY_ID];
45706
+ if (audio === void 0) return [];
45707
+ if (audio.form !== "gradient") return [{
45708
+ code: "bad-gradient",
45709
+ entry: AUDIO_ENTRY_ID
45710
+ }];
45711
+ return AUDIO_GRADIENT_ALPHAS.every((alpha, index) => audio.alphas[index] === alpha) ? [] : [{
45712
+ code: "bad-gradient",
45713
+ entry: AUDIO_ENTRY_ID
45714
+ }];
45715
+ }
45716
+ function isMarkPair(left, right) {
45717
+ const marks = PALETTE_MARK_PAIR;
45718
+ return marks.includes(left) && marks.includes(right);
45719
+ }
45720
+ /** Refuse a palette whose entries are not pairwise distinct on both themes. */
45721
+ function validatePalette(swatches, fallbacks) {
45722
+ const violations = [];
45723
+ for (const id of PALETTE_ENTRY_IDS) if (swatches[id] === void 0) violations.push({
45724
+ code: "missing-entry",
45725
+ entry: id
45726
+ });
45727
+ for (const key of Object.keys(swatches)) if (!isPaletteEntryId(key)) violations.push({
45728
+ code: "unknown-entry",
45729
+ entry: key
45730
+ });
45731
+ if (fallbacks.length !== 8) violations.push({ code: "bad-fallbacks" });
45732
+ violations.push(...gradientViolations(swatches));
45733
+ const rows = namedColours(swatches, fallbacks);
45734
+ for (let i = 0; i < rows.length; i++) for (let j = i + 1; j < rows.length; j++) {
45735
+ const left = rows[i];
45736
+ const right = rows[j];
45737
+ if (left === void 0 || right === void 0) continue;
45738
+ const deltaE = deltaE00(left.hex, right.hex);
45739
+ if (deltaE < (isMarkPair(left.name, right.name) ? 20 : 7)) violations.push({
45740
+ code: "too-close",
45741
+ left: left.name,
45742
+ right: right.name,
45743
+ deltaE
45744
+ });
45745
+ }
45746
+ for (const row of rows) for (const background of ["dark", "light"]) {
45747
+ const contrast = contrastRatio(row.hex, PALETTE_BACKGROUNDS[background]);
45748
+ if (contrast < 3) violations.push({
45749
+ code: "low-contrast",
45750
+ entry: row.name,
45751
+ background,
45752
+ contrast
45753
+ });
45754
+ }
45755
+ return violations.length === 0 ? { ok: true } : {
45756
+ ok: false,
45757
+ violations
45758
+ };
45759
+ }
45760
+ function formatPaletteViolation(violation) {
45761
+ switch (violation.code) {
45762
+ case "too-close": return `${violation.left} vs ${violation.right} (ΔE ${violation.deltaE.toFixed(1)})`;
45763
+ case "low-contrast": return `${violation.entry} on ${violation.background} (${violation.contrast.toFixed(1)}:1)`;
45764
+ case "missing-entry": return `${violation.entry} is missing`;
45765
+ case "unknown-entry": return `${violation.entry} is not a palette entry`;
45766
+ case "bad-gradient": return `${violation.entry} gradient is not the six canonical steps`;
45767
+ case "bad-fallbacks": return "fallback list must hold eight colours";
45768
+ case "unknown-preset": return `${violation.presetId} is not a preset`;
45769
+ }
45770
+ }
45771
+ function formatPaletteViolations(violations) {
45772
+ return violations.map(formatPaletteViolation).join("; ");
45773
+ }
45774
+ var PaletteRejectedError = class extends Error {
45775
+ violations;
45776
+ constructor(violations) {
45777
+ super(formatPaletteViolations(violations));
45778
+ this.name = "PaletteRejectedError";
45779
+ this.violations = violations;
45780
+ }
45781
+ };
45782
+ function hashFallback(kind, fallbacks) {
45783
+ let hash = 0;
45784
+ for (let i = 0; i < kind.length; i++) hash = hash * 31 + (kind.codePointAt(i) ?? 0) >>> 0;
45785
+ const hex = fallbacks[hash % fallbacks.length];
45786
+ if (hex === void 0) throw new Error("display palette has no fallback colour");
45787
+ return hex;
45788
+ }
45789
+ /** Colour a kind paints with. Audio leaves share the audio gradient's hex. */
45790
+ function paintHex(swatches, fallbacks, kind) {
45791
+ const key = kind.trim().toLowerCase();
45792
+ if (key.startsWith("audio-")) {
45793
+ const audio = swatches[AUDIO_ENTRY_ID];
45794
+ return audio !== void 0 ? audio.hex : hashFallback(key, fallbacks);
45795
+ }
45796
+ const direct = swatches[key];
45797
+ if (direct !== void 0) return direct.hex;
45798
+ return hashFallback(key, fallbacks);
45799
+ }
45800
+ function mergeOverrides(base, overrides) {
45801
+ return {
45802
+ ...base,
45803
+ ...overrides
45804
+ };
45805
+ }
45806
+ function overridesOf(input) {
45807
+ return "overrides" in input ? input.overrides : {};
45808
+ }
45809
+ /** The palette a `setPalette` input would store. Invalid input is a refusal. */
45810
+ function paletteFromInput(input) {
45811
+ const row = presetById(input.presetId);
45812
+ if (row === void 0 || !isPalettePresetId(input.presetId)) throw new PaletteRejectedError([{
45813
+ code: "unknown-preset",
45814
+ presetId: input.presetId
45815
+ }]);
45816
+ const overrides = overridesOf(input);
45817
+ const swatches = mergeOverrides(row.swatches, overrides);
45818
+ const verdict = validatePalette(swatches, row.fallbacks);
45819
+ if (!verdict.ok) throw new PaletteRejectedError(verdict.violations);
45820
+ const customized = Object.keys(overrides).length > 0;
45821
+ return {
45822
+ status: "ready",
45823
+ presetId: row.id,
45824
+ customized,
45825
+ source: "stored",
45826
+ swatches,
45827
+ fallbacks: [...row.fallbacks]
45828
+ };
45829
+ }
45830
+ function isEmptyRecord(value) {
45831
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length === 0;
45832
+ }
45833
+ /**
45834
+ * Interpret a settings blob. A missing row is the bundled preset and says so.
45835
+ * A blob that does not parse, names an unknown preset, or fails the rule is
45836
+ * `unknown` — never the default wearing the stored preset's name.
45837
+ */
45838
+ function paletteFromStored(value) {
45839
+ if (value === void 0 || value === null || isEmptyRecord(value)) return bundledCamstack();
45840
+ const parsed = storedPaletteSchema.safeParse(value);
45841
+ if (!parsed.success) return {
45842
+ status: "unknown",
45843
+ reason: "stored palette is unreadable"
45844
+ };
45845
+ const row = presetById(parsed.data.presetId);
45846
+ if (row === void 0) return {
45847
+ status: "unknown",
45848
+ reason: `unknown preset ${parsed.data.presetId}`
45849
+ };
45850
+ const swatches = mergeOverrides(row.swatches, parsed.data.overrides);
45851
+ const verdict = validatePalette(swatches, row.fallbacks);
45852
+ if (!verdict.ok) return {
45853
+ status: "unknown",
45854
+ reason: formatPaletteViolations(verdict.violations)
45855
+ };
45856
+ const customized = Object.keys(parsed.data.overrides).length > 0;
45857
+ return {
45858
+ status: "ready",
45859
+ presetId: row.id,
45860
+ customized,
45861
+ source: "stored",
45862
+ swatches,
45863
+ fallbacks: [...row.fallbacks]
45864
+ };
45865
+ }
45866
+ function storedFromReady(ready, overrides) {
45867
+ return {
45868
+ presetId: ready.presetId,
45869
+ overrides: { ...overrides }
45870
+ };
45871
+ }
45872
+ /**
43254
45873
  * Unified event-kind taxonomy — THE single source of truth for
43255
45874
  * `kind → { parentKind, category, level, color, iconId, labelKey, label,
43256
45875
  * icon }`.
@@ -43264,109 +45883,88 @@ for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set
43264
45883
  * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
43265
45884
  * - the COCO / audio class maps (macro ↔ sub relationships)
43266
45885
  *
43267
- * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
43268
- * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
43269
- * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
43270
- * color or an icon: they read this dictionary (server descriptors carry the
43271
- * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
45886
+ * The DATA (serializable — parentKind / iconId / labelKey) lives here in
45887
+ * `@camstack/types`. Colour is NOT a second table: each entry's `color` is
45888
+ * the bundled CamStack display palette (D939). The effective cluster palette
45889
+ * is the `display-palette` capability. The UI-side mapping `iconId → lucide
45890
+ * component` and `labelKey → t()` lives in `@camstack/ui-library`.
43272
45891
  *
43273
45892
  * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
43274
45893
  */
43275
- var TAXONOMY_COLORS = {
43276
- motion: "#f59e0b",
43277
- audio: "#06b6d4",
43278
- person: "#22c55e",
43279
- vehicle: "#3b82f6",
43280
- animal: "#f97316",
43281
- package: "#a855f7",
43282
- sensor: "#8b5cf6",
43283
- control: "#10b981",
43284
- genericDetection: "#64748b"
43285
- };
43286
- var DETECTION_SUB_COLORS = {
43287
- car: "#f59e0b",
43288
- truck: "#d97706",
43289
- bus: "#b45309",
43290
- motorcycle: "#eab308",
43291
- bicycle: "#ca8a04",
43292
- airplane: "#60a5fa",
43293
- boat: "#2563eb",
43294
- train: "#1d4ed8",
43295
- bird: "#14b8a6",
43296
- dog: "#84cc16",
43297
- cat: "#f97316",
43298
- horse: "#a16207",
43299
- sheep: "#a3a3a3",
43300
- cow: "#78716c",
43301
- elephant: "#6b7280",
43302
- bear: "#7c2d12",
43303
- zebra: "#404040",
43304
- giraffe: "#d4a373"
43305
- };
45894
+ var BUNDLED_PALETTE = bundledCamstack();
45895
+ function kindColor(kind) {
45896
+ return paintHex(BUNDLED_PALETTE.swatches, BUNDLED_PALETTE.fallbacks, kind);
45897
+ }
45898
+ function firstFallback(fallbacks) {
45899
+ const hex = fallbacks[0];
45900
+ if (hex === void 0) throw new Error("display palette has no fallback colour");
45901
+ return hex;
45902
+ }
45903
+ firstFallback(BUNDLED_PALETTE.fallbacks);
43306
45904
  function titleCase(id) {
43307
45905
  return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
43308
45906
  }
43309
45907
  var entries = /* @__PURE__ */ new Map();
43310
- function macro(kind, category, color, iconId, label) {
45908
+ function macro(kind, category, iconId, label) {
43311
45909
  entries.set(kind, {
43312
45910
  kind,
43313
45911
  parentKind: null,
43314
45912
  level: "macro",
43315
45913
  category,
43316
- color,
45914
+ color: kindColor(kind),
43317
45915
  iconId,
43318
45916
  labelKey: `eventKind.${kind}`,
43319
45917
  label
43320
45918
  });
43321
45919
  }
43322
- function sub(kind, parentKind, category, color, iconId, label) {
45920
+ function sub(kind, parentKind, category, iconId, label) {
43323
45921
  entries.set(kind, {
43324
45922
  kind,
43325
45923
  parentKind,
43326
45924
  level: "sub",
43327
45925
  category,
43328
- color,
45926
+ color: kindColor(kind),
43329
45927
  iconId,
43330
45928
  labelKey: `eventKind.${kind}`,
43331
45929
  label
43332
45930
  });
43333
45931
  }
43334
- macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
43335
- macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
43336
- macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
43337
- macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
43338
- macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
43339
- macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
43340
- macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
43341
- macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
45932
+ macro("motion", "motion", "motion", "Motion");
45933
+ macro("audio", "audio", "audio", "Audio");
45934
+ macro("person", "detection", "person", "Person");
45935
+ macro("vehicle", "detection", "vehicle", "Vehicle");
45936
+ macro("animal", "detection", "animal", "Animal");
45937
+ macro("package", "package", "package", "Package");
45938
+ macro("sensor", "sensor", "sensor", "Sensor");
45939
+ macro("control", "control", "control", "Control");
43342
45940
  for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
43343
45941
  if (macroClass !== "vehicle" && macroClass !== "animal") continue;
43344
45942
  if (entries.has(cocoClass)) continue;
43345
- sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
43346
- }
43347
- sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
43348
- sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
43349
- sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
43350
- sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
43351
- sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
43352
- sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
43353
- sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
43354
- sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
43355
- sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
43356
- sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
43357
- sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
43358
- sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
43359
- sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
43360
- sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
43361
- sub("cover", "control", "control", "#0ea5e9", "door", "Cover");
43362
- sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
43363
- sub("siren", "control", "control", "#dc2626", "siren", "Siren");
43364
- sub("button", "control", "control", "#10b981", "button", "Button");
43365
- sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
45943
+ sub(cocoClass, macroClass, "detection", cocoClass, titleCase(cocoClass));
45944
+ }
45945
+ sub("package-delivered", "package", "package", "package", "Package delivered");
45946
+ sub("package-picked-up", "package", "package", "package", "Package picked up");
45947
+ sub("contact", "sensor", "sensor", "door", "Contact");
45948
+ sub("motion-sensor", "sensor", "sensor", "pir", "Motion sensor");
45949
+ sub("smoke", "sensor", "sensor", "smoke", "Smoke");
45950
+ sub("flood", "sensor", "sensor", "water", "Water leak");
45951
+ sub("gas", "sensor", "sensor", "gas", "Gas");
45952
+ sub("carbon-monoxide", "sensor", "sensor", "smoke", "Carbon monoxide");
45953
+ sub("vibration", "sensor", "sensor", "vibration", "Vibration");
45954
+ sub("tamper", "sensor", "sensor", "tamper", "Tamper");
45955
+ sub("presence", "sensor", "sensor", "presence", "Presence");
45956
+ sub("enum-sensor", "sensor", "sensor", "generic", "Sensor state");
45957
+ sub("device-event", "sensor", "sensor", "button", "Device event");
45958
+ sub("lock", "control", "control", "lock", "Lock");
45959
+ sub("cover", "control", "control", "door", "Cover");
45960
+ sub("switch", "control", "control", "switch", "Switch");
45961
+ sub("siren", "control", "control", "siren", "Siren");
45962
+ sub("button", "control", "control", "button", "Button");
45963
+ sub("doorbell", "control", "control", "doorbell", "Doorbell");
43366
45964
  for (const l of AUDIO_MACRO_LABELS) {
43367
45965
  const kind = `audio-${l.id}`;
43368
45966
  if (entries.has(kind)) continue;
43369
- sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
45967
+ sub(kind, "audio", "audio", kind, l.name);
43370
45968
  }
43371
45969
  /** The complete taxonomy dictionary, keyed by kind. */
43372
45970
  var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
@@ -44209,7 +46807,7 @@ var NcConditionsSchema = object({
44209
46807
  * with the list under it is worse than no switch (D62).
44210
46808
  *
44211
46809
  * MEMBERS ARE FACE-GALLERY `Identity.id`s (uuid), not display names. A name is
44212
- * renameable, and a rule authored on "Gianluca" went silently dark the moment
46810
+ * renameable, and a rule authored on "Alice" went silently dark the moment
44213
46811
  * the operator fixed the spelling. The id reaches the record on
44214
46812
  * `LabelAttribution.identityId`; the name is what the editor shows and what
44215
46813
  * `{{label}}` renders.
@@ -44760,7 +47358,7 @@ var NcRulePatchSchema = NcRuleInputSchema.partial().extend({
44760
47358
  * A rule scoped to one camera and one zone silently became a rule that
44761
47359
  * matches EVERY event on EVERY camera, and lost its `media` policy
44762
47360
  * (zoneCrop / gif / clip / frame) and its `priority` at the same time. Seen
44763
- * live on 2026-08-12: a rule scoped to device 617 fired on 590 and 615
47361
+ * live on 2026-08-12: a rule scoped to device 102 fired on 104 and 101
44764
47362
  * within a minute of a two-field patch.
44765
47363
  *
44766
47364
  * So every defaulted field is re-declared here WITHOUT its default. The
@@ -46623,7 +49221,7 @@ var LabelAttributionSchema = object({
46623
49221
  * `Identity.id` or a plate-gallery `Vehicle.id` (both `randomUUID`).
46624
49222
  *
46625
49223
  * The text alone is a DISPLAY NAME, and a display name is renameable: a
46626
- * notification rule authored on "Gianluca" stopped matching the moment the
49224
+ * notification rule authored on "Alice" stopped matching the moment the
46627
49225
  * operator fixed the spelling in the gallery, and nothing said so. The id is
46628
49226
  * the thing that does not move, so it is what a rule matches on
46629
49227
  * (`NcConditions.identities`) and the text is what a human is shown.
@@ -49275,7 +51873,7 @@ var pipelineExecutorCapability = {
49275
51873
  /**
49276
51874
  * Root-supplied detections for the tree's ROOT step (D730). Set by
49277
51875
  * `runDetailSubtree` when the camera's root model supplies a crop-kind
49278
- * step (a Frigate+ `license_plate` for `plate-detection`): the step is
51876
+ * step (a plus catalog `license_plate` for `plate-detection`): the step is
49279
51877
  * NOT inferred, these boxes are its output, and its children (the plate
49280
51878
  * reader) run on them through their own crop policy. Boxes are in the
49281
51879
  * PIXELS of the image this call carries. Absent ⇒ the root step runs.
@@ -51043,7 +53641,7 @@ var RunnerCameraConfigSchema = object({
51043
53641
  * detection session that asks for history. What shipped was a last-moment
51044
53642
  * discard — `FrameSlot`'s throttle compares wall clocks, and ~4 s of media
51045
53643
  * arriving inside a few hundred ms of wall clock looks to it like one frame's
51046
- * worth. Device 617, 2026-09-08: a re-run of the SAME model over the stored
53644
+ * worth. Device 102, 2026-09-08: a re-run of the SAME model over the stored
51047
53645
  * recording scored the subject `vehicle 0.5669` — above the 0.50 floor —
51048
53646
  * 1.68 s BEFORE the live track was born, on a frame that was in the pre-roll,
51049
53647
  * was decoded, and was freed.
@@ -53698,7 +56296,7 @@ method(_void(), ProviderInfoSchema, { auth: "admin" }), method(object({ config:
53698
56296
  * The signals a device can emit to WAKE its own stream.
53699
56297
  *
53700
56298
  * A camera whose stream is built on demand sleeps until something asks for it,
53701
- * and "something" cannot be a consumer that is merely attached — a Frigate-style
56299
+ * and "something" cannot be a consumer that is merely attached — a external-catalog
53702
56300
  * puller holds a session open for ever, and treating that as demand would keep
53703
56301
  * a battery camera awake for ever, which is the whole thing the battery is for
53704
56302
  * (D173). So the wake has to come from the CAMERA: an event it noticed by
@@ -54385,7 +56983,7 @@ var ClipSchema = object({
54385
56983
  * {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
54386
56984
  *
54387
56985
  * Bounded because it is not a payload the surface pages through: one visit on
54388
- * device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
56986
+ * device 101 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
54389
56987
  * camera-day. Read {@link eventCount} for the true total.
54390
56988
  */
54391
56989
  eventIds: array(string()).optional(),
@@ -54530,7 +57128,7 @@ var ClipPlaybackSchema = object({
54530
57128
  * camera has no recordings", which is the defect this whole line of work is
54531
57129
  * about: a battery camera nobody woke (`sleeping`), a camera that could not be
54532
57130
  * reached at all (`unreachable`, also what a provider that THREW reports), a
54533
- * camera whose SD card is not mounted (`no-storage`, measured on 640 —
57131
+ * camera whose SD card is not mounted (`no-storage`, measured on 106 —
54534
57132
  * `HddInfo mount 0`, honestly nothing to list rather than "no clips"), and a
54535
57133
  * camera whose OWN index disagrees with its OWN calendar (`index-empty`,
54536
57134
  * measured on 618: the calendar marks 3–20 September, days 6–20 list zero
@@ -54795,7 +57393,7 @@ DeviceType.Camera, method(object({
54795
57393
  *
54796
57394
  * It was optional, documented as "absent means the device's BOUND
54797
57395
  * provider, which is CamStack on every camera". No code implemented
54798
- * that. Measured on the live hub 2026-09-20 — device 592, bound to
57396
+ * that. Measured on the live hub 2026-09-20 — device 107, bound to
54799
57397
  * `recorder` AND `provider-reolink` — a bare call with `limit: 3`
54800
57398
  * answered SIX rows, three from each source, merged newest-first:
54801
57399
  * `device-collection-dispatch.ts` simply left the fan-out un-narrowed,
@@ -55873,7 +58471,10 @@ object({
55873
58471
  DeviceType.Sensor;
55874
58472
  object({
55875
58473
  /** Current illuminance in lux (lx). */
55876
- lux: number().min(0),
58474
+ lux: number().min(0).meta({
58475
+ unit: "lx",
58476
+ label: "Illuminance"
58477
+ }),
55877
58478
  /** Ms epoch when the slice was last updated. */
55878
58479
  lastFetchedAt: number(),
55879
58480
  /** Live display unit from the upstream source (e.g. HA
@@ -56029,7 +58630,10 @@ var BatteryStatusSchema = object({
56029
58630
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
56030
58631
  * declines to band a non-finite reading, which is the correct shape.
56031
58632
  */
56032
- percentage: number().min(0).max(100).nullable(),
58633
+ percentage: number().min(0).max(100).nullable().meta({
58634
+ unit: "%",
58635
+ label: "Charge"
58636
+ }),
56033
58637
  /**
56034
58638
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
56035
58639
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -56048,13 +58652,13 @@ var BatteryStatusSchema = object({
56048
58652
  "solar",
56049
58653
  "none",
56050
58654
  "unknown"
56051
- ]),
58655
+ ]).meta({ label: "Charging source" }),
56052
58656
  /**
56053
58657
  * True when the camera firmware has gone into low-power mode. Battery
56054
58658
  * providers MUST avoid polling during sleep — reading the battery
56055
58659
  * wakes the camera up and drains charge.
56056
58660
  */
56057
- sleeping: boolean(),
58661
+ sleeping: boolean().meta({ label: "Sleeping" }),
56058
58662
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
56059
58663
  lastUpdated: number(),
56060
58664
  /**
@@ -56094,7 +58698,7 @@ var BatteryStatusSchema = object({
56094
58698
  * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
56095
58699
  * misleading exact percentage. Absent/false → genuine 0–100 % reading.
56096
58700
  */
56097
- binary: boolean().optional()
58701
+ binary: boolean().optional().meta({ label: "Binary indicator" })
56098
58702
  });
56099
58703
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat;
56100
58704
  method(object({
@@ -56109,7 +58713,7 @@ method(object({
56109
58713
  status: BatteryStatusSchema
56110
58714
  });
56111
58715
  object({
56112
- on: boolean(),
58716
+ on: boolean().meta({ label: "On" }),
56113
58717
  /** Ms epoch of the last transition. 0 if never observed. */
56114
58718
  lastChangedAt: number()
56115
58719
  });
@@ -56367,7 +58971,7 @@ var GridLayoutCellSchema = object({
56367
58971
  *
56368
58972
  * A grid's `high` composes its sources' `high` and its `low` their `low`, so a
56369
58973
  * profile is on offer only when EVERY source can serve it. The refusal NAMES
56370
- * the sources, because "this grid has no low" is not a finding — "615 has no
58974
+ * the sources, because "this grid has no low" is not a finding — "101 has no
56371
58975
  * low" is, and it is the one an operator can act on.
56372
58976
  */
56373
58977
  var GridProfileOfferSchema = object({
@@ -56999,6 +59603,39 @@ DeviceType.Camera, method(object({ deviceId: number() }), DayNightOptionsSchema)
56999
59603
  auth: "admin"
57000
59604
  });
57001
59605
  /**
59606
+ * display-palette — the cluster's one colour table (D939).
59607
+ *
59608
+ * Hub singleton. `getPalette` reports `unknown` when the settings store
59609
+ * cannot be read; it does not pretend the bundled preset was stored.
59610
+ * `setPalette` refuses a palette that fails the ΔE / contrast rule and names
59611
+ * the pairs.
59612
+ */
59613
+ var presetSummarySchema = object({
59614
+ id: string(),
59615
+ name: string(),
59616
+ swatches: record(string(), paletteSwatchSchema),
59617
+ fallbacks: array(string())
59618
+ });
59619
+ var displayPaletteCapability = {
59620
+ name: "display-palette",
59621
+ scope: "system",
59622
+ mode: "singleton",
59623
+ methods: {
59624
+ getPalette: method(_void(), paletteResultSchema, {
59625
+ auth: "protected",
59626
+ role: "viewer"
59627
+ }),
59628
+ listPresets: method(_void(), array(presetSummarySchema).readonly(), {
59629
+ auth: "protected",
59630
+ role: "viewer"
59631
+ }),
59632
+ setPalette: method(setPaletteInputSchema, paletteReadySchema, {
59633
+ kind: "mutation",
59634
+ auth: "admin"
59635
+ })
59636
+ }
59637
+ };
59638
+ /**
57002
59639
  * Generic device-level status snapshot. Auto-registered by `BaseDevice`
57003
59640
  * for every device, regardless of provider — the kernel needs a uniform
57004
59641
  * cap-keyed slice for the basic device flags every consumer expects to
@@ -57894,7 +60531,10 @@ DeviceType.Humidifier, method(object({
57894
60531
  });
57895
60532
  object({
57896
60533
  /** Current relative humidity, 0..100. */
57897
- percent: number().min(0).max(100),
60534
+ percent: number().min(0).max(100).meta({
60535
+ unit: "%",
60536
+ label: "Humidity"
60537
+ }),
57898
60538
  /** Ms epoch when the slice was last updated. */
57899
60539
  lastFetchedAt: number(),
57900
60540
  /** Live display unit from the upstream source (e.g. HA
@@ -61412,9 +64052,15 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
61412
64052
  });
61413
64053
  object({
61414
64054
  /** Instantaneous power draw in watts. */
61415
- watts: number().optional(),
64055
+ watts: number().optional().meta({
64056
+ unit: "W",
64057
+ label: "Power"
64058
+ }),
61416
64059
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
61417
- kwhTotal: number().optional(),
64060
+ kwhTotal: number().optional().meta({
64061
+ unit: "kWh",
64062
+ label: "Energy"
64063
+ }),
61418
64064
  /** Voltage in volts. */
61419
64065
  volts: number().optional(),
61420
64066
  /** Current in amperes. */
@@ -61733,9 +64379,9 @@ var PtzOptionsSchema = object({
61733
64379
  * Reolink preset 0, Hikvision preset '1', Dahua preset 1 -- and on this fleet
61734
64380
  * not one of the three cameras honours its own:
61735
64381
  *
61736
- * 592 Videocamera camera Daniel (reolink) preset 0 holds "stanza"
61737
- * 1438 Videocamera dispensa (hikvision) preset 1 holds "Credenza"
61738
- * 3836 Videocamera studio (amcrest) preset 1 holds "Preset1"
64382
+ * 107 Camera camera Carol (reolink) preset 0 holds "stanza"
64383
+ * 1438 Camera dispensa (hikvision) preset 1 holds "Credenza"
64384
+ * 3836 Camera studio (amcrest) preset 1 holds "Preset1"
61739
64385
  *
61740
64386
  * So `goHome` meant "go wherever the operator happened to save in slot 0 or 1",
61741
64387
  * and two of the three swallowed the failure in a bare `catch`. For autotrack
@@ -61882,7 +64528,7 @@ object({
61882
64528
  running: boolean().nullable(),
61883
64529
  /** Epoch ms the current boolean began; null while `running` is null. */
61884
64530
  since: number().nullable(),
61885
- source: RunningStateSourceSchema,
64531
+ source: RunningStateSourceSchema.meta({ label: "Source" }),
61886
64532
  phase: string().nullable(),
61887
64533
  /** null until a reading has been published (D393). */
61888
64534
  lastFetchedAt: number().nullable()
@@ -61935,7 +64581,7 @@ var RecordingAvailabilitySchema = object({
61935
64581
  * would look identical. But the answer used to say nothing about that, so a
61936
64582
  * caller asking "what can I export?" read the single preferred profile as
61937
64583
  * the only one that exists — on this deployment every camera records `high`
61938
- * AND `low`, the low tree holding 14 GB for camera 615 alone, and the export
64584
+ * AND `low`, the low tree holding 14 GB for camera 101 alone, and the export
61939
64585
  * UI could not see it.
61940
64586
  *
61941
64587
  * Cheap: it is the same shallow directory read the profile CHOICE already
@@ -62034,7 +64680,7 @@ var RecordingSourceOptionsSchema = object({
62034
64680
  * `sparse` = gaps are the absence of one, and must be drawn as such.
62035
64681
  *
62036
64682
  * Not an onboard-only concession: measured 2026-09-24, OUR bar covers 98.8 %
62037
- * of 592's day and 1.2 % of 1436's. It is a fact about a (source, camera)
64683
+ * of 107's day and 1.2 % of 1436's. It is a fact about a (source, camera)
62038
64684
  * pair, and ours answers it per camera from `deriveRecordingMode`. */
62039
64685
  coverage: _enum(["continuous", "sparse"]),
62040
64686
  /** Where the playhead may be put.
@@ -62110,7 +64756,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(RecordingSourceS
62110
64756
  * merely discouraged (D554 amended).
62111
64757
  *
62112
64758
  * It was learned the expensive way on `videoclips.listClips`: measured
62113
- * on the live hub 2026-09-20, device 592 bound to `recorder` AND
64759
+ * on the live hub 2026-09-20, device 107 bound to `recorder` AND
62114
64760
  * `provider-reolink`, a bare call with `limit: 3` answered SIX rows,
62115
64761
  * three from each source, merged — `device-collection-dispatch.ts`
62116
64762
  * leaves an unpinned fan-out un-narrowed, so absence buys the union the
@@ -62711,7 +65357,7 @@ var ExportSpeedSchema = number().min(.25).max(32);
62711
65357
  * on a 10 h window holding 29,393 s of footage every range landed late by the
62712
65358
  * gap accumulated before it — up to 6,607 s, well past EOF. Nothing matched,
62713
65359
  * the video was a uniform timelapse, and the log line reported the five ranges
62714
- * that had been ASKED for (2026-08-13, export `57d14363`, camera 615).
65360
+ * that had been ASKED for (2026-08-13, export `57d14363`, camera 101).
62715
65361
  *
62716
65362
  * Relative and not absolute epoch, because an absolute epoch would make every
62717
65363
  * call site responsible for the same subtraction.
@@ -63379,7 +66025,7 @@ getStatus: method(object({ deviceId: number() }), RecordingSignalStatusSchema) }
63379
66025
  * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
63380
66026
  * for the thing: a scene is a standing question about the property ("is the bin
63381
66027
  * still out"), and the operator's question is "which of my scenes have tripped",
63382
- * across every camera at once — not "what does camera 617 think". Buried one
66028
+ * across every camera at once — not "what does camera 102 think". Buried one
63383
66029
  * camera deep it also could not be found. The surface is now a top-level admin
63384
66030
  * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
63385
66031
  * picks the camera inside the create flow, the same shape Events and Faces have.
@@ -63962,7 +66608,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), StreamParamsOptionsSch
63962
66608
  auth: "admin"
63963
66609
  }), method(object({ deviceId: number() }), unknown().nullable());
63964
66610
  object({
63965
- on: boolean(),
66611
+ on: boolean().meta({ label: "On" }),
63966
66612
  /** Ms epoch of the last state change. Useful for UI "X minutes ago". */
63967
66613
  lastChangedAt: number()
63968
66614
  });
@@ -64525,7 +67171,10 @@ object({
64525
67171
  DeviceType.Sensor;
64526
67172
  object({
64527
67173
  /** Current temperature in Celsius. */
64528
- celsius: number(),
67174
+ celsius: number().meta({
67175
+ unit: "°C",
67176
+ label: "Temperature"
67177
+ }),
64529
67178
  /** Ms epoch when the slice was last updated (push or poll). */
64530
67179
  lastFetchedAt: number(),
64531
67180
  /** Live display unit from the upstream source (e.g. HA
@@ -65733,6 +68382,10 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
65733
68382
  auth: "admin"
65734
68383
  });
65735
68384
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
68385
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Camera, DeviceType.Camera, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
68386
+ String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
68387
+ DeviceRole.BinarySensor, DeviceRole.MotionSensor;
68388
+ new Set(CAP_NAMES);
65736
68389
  Object.freeze({
65737
68390
  person: .5,
65738
68391
  vehicle: .5,
@@ -68732,6 +71385,27 @@ Object.freeze({
68732
71385
  access: "create",
68733
71386
  role: "viewer"
68734
71387
  },
71388
+ "displayPalette.getPalette": {
71389
+ capName: "display-palette",
71390
+ capScope: "system",
71391
+ addonId: null,
71392
+ access: "view",
71393
+ role: "viewer"
71394
+ },
71395
+ "displayPalette.listPresets": {
71396
+ capName: "display-palette",
71397
+ capScope: "system",
71398
+ addonId: null,
71399
+ access: "view",
71400
+ role: "viewer"
71401
+ },
71402
+ "displayPalette.setPalette": {
71403
+ capName: "display-palette",
71404
+ capScope: "system",
71405
+ addonId: null,
71406
+ access: "create",
71407
+ role: "admin"
71408
+ },
68735
71409
  "doorbell.getStatus": {
68736
71410
  capName: "doorbell",
68737
71411
  capScope: "device",
@@ -78649,7 +81323,7 @@ var SENSOR_CAP_VOCABULARY = {
78649
81323
  state: [{
78650
81324
  kind: "boolean",
78651
81325
  field: "on",
78652
- label: t("Active", "Attivo"),
81326
+ label: t("On", "Acceso"),
78653
81327
  whenTrue: t("When {device} turns active", "Quando {device} si attiva"),
78654
81328
  whenFalse: t("When {device} turns inactive", "Quando {device} si disattiva"),
78655
81329
  icon: "toggle-right"
@@ -79188,6 +81862,7 @@ var SYSTEM_CAP_VOCABULARY = {
79188
81862
  "device-provider": system(INTERNAL),
79189
81863
  "device-removal-hook": system(INTERNAL),
79190
81864
  "device-state": system("the runtime-state mirror every trigger already reads through"),
81865
+ "display-palette": system("the cluster display palette — one colour table for every client, not a device action"),
79191
81866
  "embedding-encoder": system(ML),
79192
81867
  "face-gallery": system("the face gallery — curated on its own page"),
79193
81868
  "failure-contribution": system(INTERNAL),
@@ -82197,7 +84872,6 @@ var BucketAccumulator = class {
82197
84872
  }
82198
84873
  };
82199
84874
  DeviceType.Camera;
82200
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Camera, DeviceType.Camera, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
82201
84875
  /**
82202
84876
  * A saved Customize assignment (D936). The schema is the type: callers use
82203
84877
  * `z.infer`, they do not restate the shape.
@@ -82286,7 +84960,6 @@ object({
82286
84960
  ]),
82287
84961
  reason: string().nullable()
82288
84962
  });
82289
- String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
82290
84963
  var G711_SCALE_CORRECTION_DB = {
82291
84964
  PCMU: 20 * Math.log10(4),
82292
84965
  PCMA: 20 * Math.log10(8)
@@ -82659,7 +85332,7 @@ var STATE_SUMMARY_SECTION_VARS = [
82659
85332
  {
82660
85333
  name: "summaryStreams",
82661
85334
  label: "Summary: streams offline",
82662
- example: "Ingresso (high) — since 06:40",
85335
+ example: "Front door (high) — since 06:40",
82663
85336
  kinds: ["stream-offline", "stream-online"]
82664
85337
  },
82665
85338
  {
@@ -82677,13 +85350,13 @@ var STATE_SUMMARY_SECTION_VARS = [
82677
85350
  {
82678
85351
  name: "summaryDetection",
82679
85352
  label: "Summary: cameras detecting nothing",
82680
- example: "Ingresso — since 05:10",
85353
+ example: "Front door — since 05:10",
82681
85354
  kinds: ["detection-blind"]
82682
85355
  },
82683
85356
  {
82684
85357
  name: "summaryNodes",
82685
85358
  label: "Summary: nodes",
82686
- example: "little-unraid — offline since 03:00",
85359
+ example: "agent-a — offline since 03:00",
82687
85360
  kinds: [
82688
85361
  "node-offline",
82689
85362
  "node-online",
@@ -87139,7 +89812,7 @@ function validateJumpTargets(inferenceDevices, enabledAvailableKeys) {
87139
89812
  * A **Coral Edge TPU (`edgetpu`)** is an accelerator like the rest and is
87140
89813
  * enabled by default (D912, superseding D39). The 2026-08-01 incident that made
87141
89814
  * it opt-in (a Coral nobody enabled took an equal share of the rotation, ran a
87142
- * weaker SSD and left camera 615 at 2.4 fps) is answered by speed-proportional
89815
+ * weaker SSD and left camera 101 at 2.4 fps) is answered by speed-proportional
87143
89816
  * automatic weights — Auto tune sizes the Coral at its default YOLOv9-s 320
87144
89817
  * Edge TPU build with the measured latency — and by that default model, which
87145
89818
  * is YOLOv9 like the rest. An explicit stored `enabled:false` still wins.
@@ -90789,7 +93462,7 @@ var CameraStatusService = class {
90789
93462
  * means "we could not look" from a `null` that means "there is nothing
90790
93463
  * there"; the `warn` is what makes the next hang findable at all. The line
90791
93464
  * carries `tags.deviceId` with the numeric id because the question is always
90792
- * per-camera ("why is 614 worse than 615?") and a line without the tag
93465
+ * per-camera ("why is 105 worse than 101?") and a line without the tag
90793
93466
  * cannot be grouped to answer it.
90794
93467
  */
90795
93468
  recordDegraded(sink, deviceId, stage, reason, elapsedMs, extraMeta = {}) {
@@ -91089,7 +93762,7 @@ var CameraStatusService = class {
91089
93762
  *
91090
93763
  * In the last two the returned list is `[]`, and `[]` was once handed to the
91091
93764
  * payload as the positive claim "the operator has switched nothing off".
91092
- * Measured on the live hub 2026-08-08: `getCameraStatus(614)` said
93765
+ * Measured on the live hub 2026-08-08: `getCameraStatus(105)` said
91093
93766
  * `switchedOff: []` at the very moment `stream-broker` AND `recording` were
91094
93767
  * switched off by the operator — the camera read as BROKEN because it was
91095
93768
  * switched OFF. The list stays empty (a guess would put a permanent "off"
@@ -91276,7 +93949,7 @@ function readPrivacyMaskFacts(v) {
91276
93949
  *
91277
93950
  * Measured on the live hub, 2026-08-08: seven of the eight sources answered in
91278
93951
  * 5–15ms on every camera. The eighth, `privacyMask.getStatus`, probes the
91279
- * camera itself and took 3.1–3.6s on device 614 on every single call (18s under
93952
+ * camera itself and took 3.1–3.6s on device 105 on every single call (18s under
91280
93953
  * load) — so a bound generous enough to make the fast sources safe still has to
91281
93954
  * be willing to drop the slow one, because waiting for it costs the other six.
91282
93955
  */
@@ -92582,7 +95255,7 @@ async function reconcileOnboardForwarding(input, deps) {
92582
95255
  * never cause a video transcode, and on this fleet the motion rung IS a
92583
95256
  * `derived:*` transcode. The caller therefore hands the resolved audio stream
92584
95257
  * in {@link WatchdogAudioSource} — reading `motionStreamId` here would label
92585
- * and RECOVER the wrong stream, which is how camera 618's audio stage watched
95258
+ * and RECOVER the wrong stream, which is how camera 108's audio stage watched
92586
95259
  * a plane the analyser was not listening to.
92587
95260
  *
92588
95261
  * `pipelineEnabled === false` (the `object-detection` switch, D62/ADR-0109)
@@ -92711,7 +95384,7 @@ var DetectionWiringController = class {
92711
95384
  * silently loses `BATTERY_DEVICE_PROFILE` and `audioMode` falls back to
92712
95385
  * `'always-on'` — which subscribes the audio-analyzer, which is demand on
92713
95386
  * the substream, which dials the camera and WAKES it. Measured on the live
92714
- * hub: 3 of 12 detection passes on device 640 did exactly that, each right
95387
+ * hub: 3 of 12 detection passes on device 106 did exactly that, each right
92715
95388
  * after a `provider-reolink` restart.
92716
95389
  *
92717
95390
  * So this reports UNKNOWN (`null`) distinctly from "no features" (`[]`), and
@@ -94170,7 +96843,7 @@ function decideSessionClose(input) {
94170
96843
  * straight to the floor check; the hold, the cap and the track-freshness read
94171
96844
  * all lived inside the COOLDOWN TIMER's body. On a camera that emits an
94172
96845
  * explicit low edge — which is every camera — the timer body never ran.
94173
- * Measured on device 617 over 24 h, 503 paired sessions: **502 closed on
96846
+ * Measured on device 102 over 24 h, 503 paired sessions: **502 closed on
94174
96847
  * `motion_cleared`**, 1 on the cooldown, 0 on `maxSessionHoldMs`, and the
94175
96848
  * "held open" branch fired **0 times**. Median session life 10 221 ms against a
94176
96849
  * 10 000 ms floor — not a duration, a floor. 71.5% of the tracks that died in
@@ -99030,7 +101703,7 @@ var RECHECK_REASON = "orchestrator-recheck";
99030
101703
  /**
99031
101704
  * D691 — the EARLY LOOK. An occupancy edge commits only on a sample that lands
99032
101705
  * after its sustain window, and a departure's session usually closes before
99033
- * that (the car drives off, motion clears): 617 on 2026-10-01 closed 1.3 s
101706
+ * that (the car drives off, motion clears): 102 on 2026-10-01 closed 1.3 s
99034
101707
  * before its 29 s sustain ended and the edge waited 4 min 52 s for the cadence
99035
101708
  * above. The notification centre, which alone knows a flip is pending, asks
99036
101709
  * for ONE look at the sustain's end ({@link OccupancyRecheckController.requestEarlyLook});
@@ -99802,7 +102475,7 @@ function countRunningByNode(placements) {
99802
102475
  * 57.6% under 5 s, and **820 of 822 closes were the hub's explicit
99803
102476
  * `motion_cleared` falling edge** — which closed the session instantly, so
99804
102477
  * `motionCooldownMs` / the track hold / `maxSessionHoldMs` decided 2 closes in
99805
- * total. Camera 590 opened and closed 249 sessions in 25 minutes; every close
102478
+ * total. Camera 104 opened and closed 249 sessions in 25 minutes; every close
99806
102479
  * dropped its assignment and every reopen re-elected a node, which is the
99807
102480
  * 2-3 second flicker the operator reported on the placement column — and a full
99808
102481
  * detach+attach round trip per flicker.
@@ -100173,7 +102846,7 @@ var SessionDispatchController = class {
100173
102846
  * Close an ACTIVE detection session so the next motion event re-dispatches
100174
102847
  * it fresh — used by `assignPipeline` when the operator pins a session-mode
100175
102848
  * camera to a node OTHER than the one its session currently runs on (the
100176
- * cam-590 case: a busy camera's session never cools down, so without this
102849
+ * cam-104 case: a busy camera's session never cools down, so without this
100177
102850
  * the stale pre-pin placement survived indefinitely).
100178
102851
  *
100179
102852
  * Deliberately NOT `stopForDevice`: the camera must stay ARMED —
@@ -100279,7 +102952,7 @@ var SessionDispatchController = class {
100279
102952
  * SAME rule the fallback timer uses ({@link resolveSessionLifecycleLocked}).
100280
102953
  * It no longer bypasses the hold and the cap: it used to, and that made
100281
102954
  * both unreachable on every camera that emits a falling edge — 502 of
100282
- * 503 closes on device 617 in 24 h. An explicit clear still wins over
102955
+ * 503 closes on device 102 in 24 h. An explicit clear still wins over
100283
102956
  * the cooldown; it does not win over a subject that is still there.
100284
102957
  *
100285
102958
  * LIVE as of Task 2: `startDetection` no longer standing-attaches on-motion
@@ -100728,7 +103401,7 @@ var SessionDispatchController = class {
100728
103401
  * falling edge, the `cooldown_expired` fallback timer, and the re-ask of a
100729
103402
  * close this method itself deferred. That is the fix — the hold, the
100730
103403
  * freshness read and `maxSessionHoldMs` used to live inside the COOLDOWN
100731
- * TIMER's body, so a falling edge reached none of them. On device 617, 24 h,
103404
+ * TIMER's body, so a falling edge reached none of them. On device 102, 24 h,
100732
103405
  * 503 sessions: 502 closes were falling edges, the hold branch fired 0
100733
103406
  * times, `maxSessionHoldMs` fired 0 times, and the session-duration
100734
103407
  * distribution collapsed onto {@link MIN_SESSION_OPEN_MS} — median 10 221 ms
@@ -100803,7 +103476,7 @@ var SessionDispatchController = class {
100803
103476
  * **This branch drops work, so it says so.** Closing a session detaches the
100804
103477
  * runner: every track still live on that camera dies here, mid-life, with no
100805
103478
  * `end` key-event and no chance of ever reaching the stationary registry's
100806
- * promotion window. It used to log one bare sentence. Measured on device 617
103479
+ * promotion window. It used to log one bare sentence. Measured on device 102
100807
103480
  * over 24 h it killed **223 live tracks across 503 closes (0.44 per close)**,
100808
103481
  * 95% of which had been motionless for at least a second, and not one line
100809
103482
  * named a single one of them — the gap had to be reconstructed by
@@ -101416,9 +104089,9 @@ var ZoneRulesProvider = class {
101416
104089
  * so a camera whose runtime-state row had never been written (or had
101417
104090
  * been reset) had no `zones` slice at all, and every mirror-only
101418
104091
  * consumer concluded "this camera has no zones" — forever, because no
101419
- * mutation was coming. Live cost (camera 617 'Parcheggio',
101420
- * 2026-08-12): `zones.listZones {617}` returned 'Parcheggio papà'
101421
- * while `deviceState.getCapSlice {617,'zones'}` returned `null`;
104092
+ * mutation was coming. Live cost (camera 102 'Parking',
104093
+ * 2026-08-12): `zones.listZones {102}` returned 'Parking pat'
104094
+ * while `deviceState.getCapSlice {102,'zones'}` returned `null`;
101422
104095
  * occupancy dropped all three parked cars into `unzoned`, its zone
101423
104096
  * rule could never fire, and the admin Zones tab — which reads the
101424
104097
  * same mirror — showed "No zones yet".
@@ -101882,7 +104555,7 @@ async function buildOrchestratorControllers(deps) {
101882
104555
  * false negative on every COLD broker: `getStats()` answers `audio: null`
101883
104556
  * until a track is observed, and dispatch normally runs while the stream is
101884
104557
  * idle. So the audio stage armed only for cameras that happened to be hot at
101885
- * dispatch — which is why camera 618's derived stream delivered zero audio
104558
+ * dispatch — which is why camera 108's derived stream delivered zero audio
101886
104559
  * chunks for four days without a single watchdog line. A stage armed on a
101887
104560
  * camera that turns out to have no audio is BOUNDED: the watchdog disarms a
101888
104561
  * stage that never produced one signal, with one line.
@@ -103064,6 +105737,112 @@ function deriveRuntimeSettings(config) {
103064
105737
  };
103065
105738
  }
103066
105739
  //#endregion
105740
+ //#region src/display-palette-provider.ts
105741
+ /**
105742
+ * Display palette provider (D939, D940). The pipeline orchestrator owns
105743
+ * this cluster setting. The document is one key in the addon's store, so
105744
+ * a write does not replace the orchestrator's other settings. An
105745
+ * unreadable store is `unknown`, never the bundled preset.
105746
+ */
105747
+ /** Key inside the orchestrator's addon store. Not a settings-schema field. */
105748
+ var DISPLAY_PALETTE_STORE_KEY = "displayPalette";
105749
+ function createDisplayPaletteProvider(settings, logger) {
105750
+ return {
105751
+ getPalette: async () => readPalette(settings, logger),
105752
+ listPresets: async () => DISPLAY_PRESETS.map((row) => ({
105753
+ id: row.id,
105754
+ name: row.name,
105755
+ swatches: row.swatches,
105756
+ fallbacks: [...row.fallbacks]
105757
+ })),
105758
+ setPalette: async (input) => writePalette(settings, logger, input)
105759
+ };
105760
+ }
105761
+ async function readPalette(settings, logger) {
105762
+ if (settings === void 0) {
105763
+ logger.warn("display-palette: settings store cannot be read three ways");
105764
+ return {
105765
+ status: "unknown",
105766
+ reason: "settings store cannot be read"
105767
+ };
105768
+ }
105769
+ const readKey = settings.readAddonStoreKeyResult;
105770
+ if (readKey !== void 0) return readByKey(settings, readKey, logger);
105771
+ const read = settings.readAddonStoreResult;
105772
+ if (read === void 0) {
105773
+ logger.warn("display-palette: settings store cannot be read three ways");
105774
+ return {
105775
+ status: "unknown",
105776
+ reason: "settings store cannot be read"
105777
+ };
105778
+ }
105779
+ return readByStore(settings, read, logger);
105780
+ }
105781
+ async function readByKey(settings, readKey, logger) {
105782
+ let result;
105783
+ try {
105784
+ result = await readKey.call(settings, DISPLAY_PALETTE_STORE_KEY);
105785
+ } catch (err) {
105786
+ return readThrew(logger, err);
105787
+ }
105788
+ if (result.kind === "unreadable") {
105789
+ logger.warn("display-palette: settings unreadable", { meta: { error: result.error } });
105790
+ return {
105791
+ status: "unknown",
105792
+ reason: result.error ?? "settings store cannot be read"
105793
+ };
105794
+ }
105795
+ if (result.kind === "absent") return bundledCamstack();
105796
+ return interpretStored(logger, result.value);
105797
+ }
105798
+ async function readByStore(settings, read, logger) {
105799
+ let result;
105800
+ try {
105801
+ result = await read.call(settings);
105802
+ } catch (err) {
105803
+ return readThrew(logger, err);
105804
+ }
105805
+ if (result.kind === "failed") {
105806
+ logger.warn("display-palette: settings unreadable", { meta: { error: result.error } });
105807
+ return {
105808
+ status: "unknown",
105809
+ reason: result.error
105810
+ };
105811
+ }
105812
+ if (result.kind === "missing") return bundledCamstack();
105813
+ return interpretStored(logger, result.value[DISPLAY_PALETTE_STORE_KEY]);
105814
+ }
105815
+ function readThrew(logger, err) {
105816
+ const error = err instanceof Error ? err.message : String(err);
105817
+ logger.warn("display-palette: settings read threw", { meta: { error } });
105818
+ return {
105819
+ status: "unknown",
105820
+ reason: "settings store read failed"
105821
+ };
105822
+ }
105823
+ function interpretStored(logger, value) {
105824
+ const palette = paletteFromStored(value);
105825
+ if (palette.status === "unknown") logger.warn("display-palette: stored palette is not usable", { meta: { reason: palette.reason } });
105826
+ return palette;
105827
+ }
105828
+ async function writePalette(settings, logger, input) {
105829
+ let ready;
105830
+ try {
105831
+ ready = paletteFromInput(input);
105832
+ } catch (err) {
105833
+ if (err instanceof PaletteRejectedError) logger.info("display-palette: refused", { meta: { reason: err.message } });
105834
+ throw err;
105835
+ }
105836
+ const write = settings?.writeAddonStore;
105837
+ if (settings === void 0 || write === void 0) {
105838
+ logger.warn("display-palette: settings store cannot be written");
105839
+ throw new Error("display-palette: settings store cannot be written");
105840
+ }
105841
+ const overrides = "overrides" in input ? input.overrides : {};
105842
+ await write.call(settings, { [DISPLAY_PALETTE_STORE_KEY]: storedFromReady(ready, overrides) });
105843
+ return ready;
105844
+ }
105845
+ //#endregion
103067
105846
  //#region src/viewer-ui-provider.ts
103068
105847
  /**
103069
105848
  * viewer-ui provider — the pipeline-orchestrator serves the CamStack viewer web
@@ -104520,6 +107299,10 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
104520
107299
  {
104521
107300
  capability: viewerUiCapability,
104522
107301
  provider: createViewerUiProvider()
107302
+ },
107303
+ {
107304
+ capability: displayPaletteCapability,
107305
+ provider: createDisplayPaletteProvider(this.ctx.settings, this.ctx.logger)
104523
107306
  }
104524
107307
  ],
104525
107308
  customActions: pipelineOrchestratorActions,
@@ -105414,7 +108197,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
105414
108197
  * Which nodes can still infer. Refreshed by
105415
108198
  * {@link resolveEligibleInferenceDevices}; read synchronously by both
105416
108199
  * dispatch paths so a node whose every enabled accelerator vanished stops
105417
- * receiving cameras. little-unraid was handed cameras for a whole day in
108200
+ * receiving cameras. agent-a was handed cameras for a whole day in
105418
108201
  * exactly that state.
105419
108202
  */
105420
108203
  nodeInferenceUsability = new NodeInferenceUsabilityMirror();