@camstack/addon-provider-unifi 0.2.149 → 0.2.150

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
2
+ //#region ../types/dist/event-category-DmZDPDf_.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -626,7 +626,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
626
626
  * (a track was promoted to a stationary-registry entry) or departs (the
627
627
  * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
628
628
  * the dedicated "Stationary" section — never the live event feed. Payload:
629
- * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
629
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
630
630
  */
631
631
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
632
632
  /**
@@ -764,6 +764,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
764
764
  */
765
765
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
766
766
  /**
767
+ * An action run started or ended. Emitted by the `actions` surface.
768
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
769
+ */
770
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
771
+ /**
767
772
  * Progress update from a running model conversion job.
768
773
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
769
774
  * Emitted by `addon-model-studio` on the converting node.
@@ -5442,7 +5447,7 @@ var ZodIssueCode = {
5442
5447
  var ZodFirstPartyTypeKind;
5443
5448
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5444
5449
  //#endregion
5445
- //#region ../types/dist/sleep--jXS9hWo.mjs
5450
+ //#region ../types/dist/sleep-Crlmu_tO.mjs
5446
5451
  /**
5447
5452
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5448
5453
  * window to float samples (D455).
@@ -8562,6 +8567,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
8562
8567
  /** Every capability name, so addon code can check a name without the schema graph. */
8563
8568
  var CAP_NAMES = Object.freeze([
8564
8569
  "accessories",
8570
+ "action-source",
8571
+ "actions",
8565
8572
  "addon-pages",
8566
8573
  "addon-pages-source",
8567
8574
  "addon-routes",
@@ -8750,11 +8757,11 @@ var CAP_NAMES = Object.freeze([
8750
8757
  Object.freeze({
8751
8758
  "air-quality-sensor": [
8752
8759
  {
8753
- "path": "co2Ppm",
8760
+ "path": "co2",
8754
8761
  "valueType": "number"
8755
8762
  },
8756
8763
  {
8757
- "path": "vocPpb",
8764
+ "path": "voc",
8758
8765
  "valueType": "number"
8759
8766
  },
8760
8767
  {
@@ -8804,7 +8811,7 @@ Object.freeze({
8804
8811
  }
8805
8812
  ],
8806
8813
  "ambient-light-sensor": [{
8807
- "path": "lux",
8814
+ "path": "illuminance",
8808
8815
  "valueType": "number"
8809
8816
  }],
8810
8817
  "audio-metrics": [
@@ -8865,7 +8872,7 @@ Object.freeze({
8865
8872
  ],
8866
8873
  "battery": [
8867
8874
  {
8868
- "path": "percentage",
8875
+ "path": "level",
8869
8876
  "valueType": "number"
8870
8877
  },
8871
8878
  {
@@ -8887,7 +8894,7 @@ Object.freeze({
8887
8894
  "valueType": "number"
8888
8895
  },
8889
8896
  {
8890
- "path": "volts",
8897
+ "path": "voltage",
8891
8898
  "valueType": "number"
8892
8899
  },
8893
8900
  {
@@ -8908,7 +8915,7 @@ Object.freeze({
8908
8915
  "valueType": "boolean"
8909
8916
  }],
8910
8917
  "brightness": [{
8911
- "path": "percentage",
8918
+ "path": "level",
8912
8919
  "valueType": "number"
8913
8920
  }],
8914
8921
  "broker": [{
@@ -9109,7 +9116,7 @@ Object.freeze({
9109
9116
  "valueType": "number"
9110
9117
  },
9111
9118
  {
9112
- "path": "mireds",
9119
+ "path": "colorTemperature",
9113
9120
  "valueType": "number"
9114
9121
  }
9115
9122
  ],
@@ -9288,15 +9295,15 @@ Object.freeze({
9288
9295
  "valueType": "string"
9289
9296
  },
9290
9297
  {
9291
- "path": "remainingSeconds",
9298
+ "path": "remaining",
9292
9299
  "valueType": "number"
9293
9300
  },
9294
9301
  {
9295
- "path": "delayedStartSeconds",
9302
+ "path": "delayedStart",
9296
9303
  "valueType": "number"
9297
9304
  },
9298
9305
  {
9299
- "path": "progressPercent",
9306
+ "path": "progress",
9300
9307
  "valueType": "number"
9301
9308
  },
9302
9309
  {
@@ -9410,7 +9417,7 @@ Object.freeze({
9410
9417
  ],
9411
9418
  "fan-control": [
9412
9419
  {
9413
- "path": "percentage",
9420
+ "path": "speed",
9414
9421
  "valueType": "number"
9415
9422
  },
9416
9423
  {
@@ -9510,7 +9517,7 @@ Object.freeze({
9510
9517
  }
9511
9518
  ],
9512
9519
  "humidity-sensor": [{
9513
- "path": "percent",
9520
+ "path": "humidity",
9514
9521
  "valueType": "number"
9515
9522
  }],
9516
9523
  "image": [{
@@ -9620,7 +9627,7 @@ Object.freeze({
9620
9627
  "valueType": "number"
9621
9628
  },
9622
9629
  {
9623
- "path": "progressPercent",
9630
+ "path": "progress",
9624
9631
  "valueType": "number"
9625
9632
  },
9626
9633
  {
@@ -9699,11 +9706,11 @@ Object.freeze({
9699
9706
  "valueType": "string"
9700
9707
  },
9701
9708
  {
9702
- "path": "positionMs",
9709
+ "path": "position",
9703
9710
  "valueType": "number"
9704
9711
  },
9705
9712
  {
9706
- "path": "durationMs",
9713
+ "path": "duration",
9707
9714
  "valueType": "number"
9708
9715
  },
9709
9716
  {
@@ -9805,11 +9812,11 @@ Object.freeze({
9805
9812
  ]
9806
9813
  },
9807
9814
  {
9808
- "path": "signalPercent",
9815
+ "path": "signal",
9809
9816
  "valueType": "number"
9810
9817
  },
9811
9818
  {
9812
- "path": "rssiDbm",
9819
+ "path": "rssi",
9813
9820
  "valueType": "number"
9814
9821
  },
9815
9822
  {
@@ -9864,6 +9871,18 @@ Object.freeze({
9864
9871
  "path": "foodLevel",
9865
9872
  "valueType": "number"
9866
9873
  },
9874
+ {
9875
+ "path": "bowlFood",
9876
+ "valueType": "number"
9877
+ },
9878
+ {
9879
+ "path": "eating",
9880
+ "valueType": "boolean"
9881
+ },
9882
+ {
9883
+ "path": "supportsMarkRefilled",
9884
+ "valueType": "boolean"
9885
+ },
9867
9886
  {
9868
9887
  "path": "food1",
9869
9888
  "valueType": "number"
@@ -9928,23 +9947,23 @@ Object.freeze({
9928
9947
  ],
9929
9948
  "power-meter": [
9930
9949
  {
9931
- "path": "watts",
9950
+ "path": "power",
9932
9951
  "valueType": "number"
9933
9952
  },
9934
9953
  {
9935
- "path": "kwhTotal",
9954
+ "path": "energy",
9936
9955
  "valueType": "number"
9937
9956
  },
9938
9957
  {
9939
- "path": "volts",
9958
+ "path": "voltage",
9940
9959
  "valueType": "number"
9941
9960
  },
9942
9961
  {
9943
- "path": "amps",
9962
+ "path": "current",
9944
9963
  "valueType": "number"
9945
9964
  },
9946
9965
  {
9947
- "path": "va",
9966
+ "path": "apparentPower",
9948
9967
  "valueType": "number"
9949
9968
  },
9950
9969
  {
@@ -9972,14 +9991,10 @@ Object.freeze({
9972
9991
  {
9973
9992
  "path": "gps.accuracyMeters",
9974
9993
  "valueType": "number"
9975
- },
9976
- {
9977
- "path": "batteryPercent",
9978
- "valueType": "number"
9979
9994
  }
9980
9995
  ],
9981
9996
  "pressure-sensor": [{
9982
- "path": "hpa",
9997
+ "path": "pressure",
9983
9998
  "valueType": "number"
9984
9999
  }],
9985
10000
  "privacy-mask": [{
@@ -10293,7 +10308,7 @@ Object.freeze({
10293
10308
  "valueType": "boolean"
10294
10309
  }],
10295
10310
  "temperature-sensor": [{
10296
- "path": "celsius",
10311
+ "path": "temperature",
10297
10312
  "valueType": "number"
10298
10313
  }],
10299
10314
  "update": [
@@ -10341,7 +10356,7 @@ Object.freeze({
10341
10356
  "valueType": "string"
10342
10357
  },
10343
10358
  {
10344
- "path": "progressPercent",
10359
+ "path": "progress",
10345
10360
  "valueType": "number"
10346
10361
  },
10347
10362
  {
@@ -10840,7 +10855,7 @@ function errMsg(err) {
10840
10855
  return String(err);
10841
10856
  }
10842
10857
  //#endregion
10843
- //#region ../types/dist/composition-Bb8iwHEJ.mjs
10858
+ //#region ../types/dist/composition-CZImLpOI.mjs
10844
10859
  /**
10845
10860
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10846
10861
  *
@@ -15360,6 +15375,14 @@ var AutomationConditionLeafSchema = object({
15360
15375
  array(union([string(), number()]))
15361
15376
  ])
15362
15377
  });
15378
+ var AutomationConditionStationaryPresentSchema = object({
15379
+ kind: literal("stationary-present"),
15380
+ deviceId: number().int().nonnegative(),
15381
+ /** Class names; a macro class matches every raw class that maps to it. */
15382
+ classes: array(string().min(1)).min(1).optional(),
15383
+ /** The parked object must be in at least one of these zones. */
15384
+ zones: array(string().min(1)).min(1).optional()
15385
+ });
15363
15386
  /**
15364
15387
  * The expression leaf, declared as a plain object rather than an intersection
15365
15388
  * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
@@ -15393,7 +15416,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
15393
15416
  child: AutomationConditionSchema
15394
15417
  }),
15395
15418
  AutomationConditionLeafSchema,
15396
- AutomationConditionExpressionSchema
15419
+ AutomationConditionExpressionSchema,
15420
+ AutomationConditionStationaryPresentSchema
15397
15421
  ]));
15398
15422
  /**
15399
15423
  * What starts a run.
@@ -15434,7 +15458,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15434
15458
  kind: literal("schedule"),
15435
15459
  cron: string().min(1).max(120)
15436
15460
  }),
15437
- object({ kind: literal("manual") })
15461
+ object({ kind: literal("manual") }),
15462
+ object({
15463
+ kind: literal("detection"),
15464
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
15465
+ * matches every raw class that maps to it; any other name matches
15466
+ * exactly. */
15467
+ classes: array(string().min(1)).min(1),
15468
+ deviceIds: array(number().int().nonnegative()).min(1),
15469
+ /** The track must have visited at least one of these zones. */
15470
+ zones: array(string().min(1)).min(1).optional(),
15471
+ /** Best-so-far confidence the track must reach. */
15472
+ minScore: number().min(0).max(1).optional()
15473
+ }),
15474
+ object({
15475
+ kind: literal("stationary"),
15476
+ deviceIds: array(number().int().nonnegative()).min(1),
15477
+ /** Class names; a macro class matches like the detection trigger. */
15478
+ classes: array(string().min(1)).min(1).optional(),
15479
+ /** The parked object must be in at least one of these zones. */
15480
+ zones: array(string().min(1)).min(1).optional(),
15481
+ /** `appeared`: it became parked. `departed`: the registry retired it.
15482
+ * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
15483
+ phase: _enum(["appeared", "departed"])
15484
+ }),
15485
+ object({
15486
+ kind: literal("interval"),
15487
+ minutes: number().int().min(1).max(1440)
15488
+ })
15438
15489
  ]);
15439
15490
  /**
15440
15491
  * One action step.
@@ -15454,28 +15505,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15454
15505
  * is called. The runner still receives a number and is untouched; the
15455
15506
  * resolution is the recipe's job, not the runner's.
15456
15507
  */
15508
+ var AutomationWaitStepSchema = object({
15509
+ kind: literal("wait"),
15510
+ seconds: number().min(0).max(300)
15511
+ });
15512
+ var AutomationCapStepSchema = object({
15513
+ kind: literal("cap"),
15514
+ /**
15515
+ * One device, several same-family devices (locks / climate family / …),
15516
+ * or `{ $var }` for the device that triggered the run. An array is
15517
+ * fan-out at codegen — each id gets the same cap.method call.
15518
+ */
15519
+ deviceId: union([
15520
+ number().int(),
15521
+ array(number().int()).min(1),
15522
+ object({ $var: string().min(1) })
15523
+ ]),
15524
+ cap: string().min(1),
15525
+ method: string().min(1),
15526
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
15527
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
15528
+ * the expression leaf. */
15529
+ args: record(string(), unknown()).optional()
15530
+ });
15531
+ /**
15532
+ * The steps an internal Action may contain: cap and wait, and nothing that can
15533
+ * call back into an Action — so an Action can never reach itself (no cycles).
15534
+ */
15535
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
15457
15536
  var AutomationActionSchema = discriminatedUnion("kind", [
15537
+ AutomationWaitStepSchema,
15538
+ AutomationCapStepSchema,
15458
15539
  object({
15459
- kind: literal("wait"),
15460
- seconds: number().min(0).max(300)
15461
- }),
15462
- object({
15463
- kind: literal("cap"),
15464
- /**
15465
- * One device, several same-family devices (locks / climate family / …),
15466
- * or `{ $var }` for the device that triggered the run. An array is
15467
- * fan-out at codegen — each id gets the same cap.method call.
15468
- */
15469
- deviceId: union([
15470
- number().int(),
15471
- array(number().int()).min(1),
15472
- object({ $var: string().min(1) })
15473
- ]),
15474
- cap: string().min(1),
15475
- method: string().min(1),
15476
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
15477
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
15478
- args: record(string(), unknown()).optional()
15540
+ kind: literal("action"),
15541
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
15542
+ actionId: string().min(1),
15543
+ /** Values for the action's declared variables; the others take their default. */
15544
+ variables: record(string(), union([
15545
+ string(),
15546
+ number(),
15547
+ boolean()
15548
+ ])).optional()
15479
15549
  }),
15480
15550
  object({
15481
15551
  kind: literal("code"),
@@ -15484,10 +15554,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
15484
15554
  code: string().min(1).max(2e4)
15485
15555
  })
15486
15556
  ]);
15557
+ /**
15558
+ * How a trigger that arrives while the automation is busy (or cooling down) is
15559
+ * handled. One of the automation's execution modes, never a property of its
15560
+ * actions. Absent means `single`.
15561
+ *
15562
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
15563
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
15564
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
15565
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
15566
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
15567
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
15568
+ * wait, never the single/queued limits.
15569
+ */
15570
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
15571
+ object({ mode: literal("single") }),
15572
+ object({ mode: literal("restart") }),
15573
+ object({
15574
+ mode: literal("queued"),
15575
+ max: number().int().min(1).max(10)
15576
+ }),
15577
+ object({
15578
+ mode: literal("parallel"),
15579
+ max: number().int().min(1).max(10)
15580
+ }),
15581
+ object({
15582
+ mode: literal("cooldown"),
15583
+ minutes: number().int().min(1).max(1440)
15584
+ })
15585
+ ]);
15487
15586
  var AutomationRecipeSchema = object({
15488
15587
  triggers: array(AutomationTriggerSchema),
15489
15588
  conditions: AutomationConditionSchema.optional(),
15490
- actions: array(AutomationActionSchema)
15589
+ actions: array(AutomationActionSchema),
15590
+ runMode: AutomationRunModeSchema.optional(),
15591
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
15592
+ keepTraces: number().int().min(1).max(100).optional()
15593
+ });
15594
+ new Set([
15595
+ {
15596
+ id: "person",
15597
+ name: "Person"
15598
+ },
15599
+ {
15600
+ id: "vehicle",
15601
+ name: "Vehicle"
15602
+ },
15603
+ {
15604
+ id: "animal",
15605
+ name: "Animal"
15606
+ },
15607
+ {
15608
+ id: "package",
15609
+ name: "Package"
15610
+ }
15611
+ ].map((l) => l.id));
15612
+ var COCO_TO_MACRO = {
15613
+ mapping: {
15614
+ person: "person",
15615
+ bicycle: "vehicle",
15616
+ car: "vehicle",
15617
+ motorcycle: "vehicle",
15618
+ airplane: "vehicle",
15619
+ bus: "vehicle",
15620
+ train: "vehicle",
15621
+ truck: "vehicle",
15622
+ boat: "vehicle",
15623
+ bird: "animal",
15624
+ cat: "animal",
15625
+ dog: "animal",
15626
+ horse: "animal",
15627
+ sheep: "animal",
15628
+ cow: "animal",
15629
+ elephant: "animal",
15630
+ bear: "animal",
15631
+ zebra: "animal",
15632
+ giraffe: "animal",
15633
+ suitcase: "package",
15634
+ backpack: "package",
15635
+ handbag: "package"
15636
+ },
15637
+ preserveOriginal: false
15638
+ };
15639
+ new Set(Object.values(COCO_TO_MACRO.mapping));
15640
+ /**
15641
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
15642
+ * so the editor can paint every block green or red with its reason.
15643
+ *
15644
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
15645
+ * every condition node (containers and leaves, including the ones a
15646
+ * short-circuit never reached), the run-mode verdict and every action step —
15647
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
15648
+ * for an Action run). Skipped
15649
+ * evaluations are traced too, and identical consecutive skips collapse into a
15650
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
15651
+ *
15652
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
15653
+ * vocabulary so an Action run records the same step shape.
15654
+ */
15655
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
15656
+ var TraceValueSchema = union([
15657
+ string(),
15658
+ number(),
15659
+ boolean(),
15660
+ _null(),
15661
+ array(union([string(), number()]))
15662
+ ]);
15663
+ var AutomationRunOutcomeSchema = _enum([
15664
+ "ran",
15665
+ "skipped-trigger",
15666
+ "skipped-conditions",
15667
+ "skipped-cooldown",
15668
+ "skipped-overlap",
15669
+ "failed",
15670
+ "aborted"
15671
+ ]);
15672
+ /** `not-evaluated` = a short-circuit never reached the node. */
15673
+ var TraceNodeOutcomeSchema = _enum([
15674
+ "pass",
15675
+ "fail",
15676
+ "not-evaluated"
15677
+ ]);
15678
+ var AutomationTriggerTraceSchema = object({
15679
+ kind: string().min(1),
15680
+ outcome: _enum(["pass", "fail"]),
15681
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
15682
+ reason: string(),
15683
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
15684
+ vars: record(string(), TraceValueSchema)
15685
+ });
15686
+ var AutomationConditionTraceNodeSchema = object({
15687
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
15688
+ path: string().min(1),
15689
+ kind: _enum([
15690
+ "all",
15691
+ "any",
15692
+ "not",
15693
+ "condition",
15694
+ "expression",
15695
+ "stationary-present"
15696
+ ]),
15697
+ outcome: TraceNodeOutcomeSchema,
15698
+ reason: string(),
15699
+ deviceId: number().int().optional(),
15700
+ cap: string().optional(),
15701
+ fieldPath: string().optional(),
15702
+ operator: string().optional(),
15703
+ expected: TraceValueSchema.optional(),
15704
+ /** What the mirror held; `null` when it held nothing (unknown). */
15705
+ observed: TraceValueSchema.optional()
15706
+ });
15707
+ var AutomationRunVerdictSchema = object({
15708
+ mode: AutomationRunModeSchema,
15709
+ decision: _enum([
15710
+ "run",
15711
+ "queued",
15712
+ "restart",
15713
+ "drop",
15714
+ "cooldown",
15715
+ "bypass"
15716
+ ]),
15717
+ reason: string()
15718
+ });
15719
+ /** One executed (or not-run) step — shared by automations and Action runs. */
15720
+ var AutomationStepTraceSchema = object({
15721
+ /** `actions.<i>` */
15722
+ path: string().min(1),
15723
+ kind: string().min(1),
15724
+ cap: string().optional(),
15725
+ method: string().optional(),
15726
+ deviceId: number().int().optional(),
15727
+ startedAt: number().optional(),
15728
+ endedAt: number().optional(),
15729
+ outcome: _enum([
15730
+ "ok",
15731
+ "error",
15732
+ "not-run"
15733
+ ]),
15734
+ message: string().optional()
15735
+ });
15736
+ var AutomationRunTraceSchema = object({
15737
+ runId: string().min(1),
15738
+ startedAt: number(),
15739
+ endedAt: number(),
15740
+ trigger: AutomationTriggerTraceSchema,
15741
+ conditions: array(AutomationConditionTraceNodeSchema),
15742
+ verdict: AutomationRunVerdictSchema.optional(),
15743
+ steps: array(AutomationStepTraceSchema),
15744
+ outcome: AutomationRunOutcomeSchema,
15745
+ /** How many identical consecutive skips this trace stands for (>= 1). */
15746
+ repeat: number().int().min(1),
15747
+ /** When the latest of those repeats happened. */
15748
+ lastAt: number()
15749
+ });
15750
+ /**
15751
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
15752
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
15753
+ */
15754
+ var AutomationStateDocSchema = object({
15755
+ version: literal(1),
15756
+ lastRunStartedAt: number().nullable(),
15757
+ traces: array(AutomationRunTraceSchema)
15758
+ });
15759
+ /**
15760
+ * Actions — named, runnable things an operator (or an automation) can start,
15761
+ * declared as LIST ENTRIES, never as devices.
15762
+ *
15763
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
15764
+ * hub's `actions` record kind) or declared by an addon through the
15765
+ * `action-source` collection (a Home Assistant script, a scene …). The one
15766
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
15767
+ * catalog of its own (D3) — every `list` asks the providers live.
15768
+ *
15769
+ * This file is the data half: the entry schema, the variables an entry may
15770
+ * declare, and the pure resolution of the values a run was given. It holds no
15771
+ * runtime.
15772
+ */
15773
+ /** What a variable's value is. `select` takes one of `options`. */
15774
+ var ActionVariableTypeSchema = _enum([
15775
+ "string",
15776
+ "number",
15777
+ "boolean",
15778
+ "select"
15779
+ ]);
15780
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
15781
+ var ActionVariableValueSchema = union([
15782
+ string(),
15783
+ number(),
15784
+ boolean()
15785
+ ]);
15786
+ /**
15787
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
15788
+ * value) or `{{vars.name}}` (inside a string) in their args.
15789
+ */
15790
+ var ActionVariableSchema = object({
15791
+ name: string().regex(ACTION_VARIABLE_NAME_RE),
15792
+ label: string().min(1).max(120).optional(),
15793
+ type: ActionVariableTypeSchema,
15794
+ required: boolean().optional(),
15795
+ default: ActionVariableValueSchema.optional(),
15796
+ /** The allowed values of a `select`. */
15797
+ options: array(string().min(1)).min(1).optional()
15798
+ }).superRefine((v, ctx) => {
15799
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
15800
+ code: "custom",
15801
+ message: "a select variable needs options",
15802
+ path: ["options"]
15803
+ });
15804
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
15805
+ code: "custom",
15806
+ message: variableValueError(v, v.default) ?? "invalid default",
15807
+ path: ["default"]
15808
+ });
15809
+ });
15810
+ /** Why `value` is not a legal value for the variable, or null when it is. */
15811
+ function variableValueError(variable, value) {
15812
+ switch (variable.type) {
15813
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
15814
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
15815
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
15816
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
15817
+ }
15818
+ }
15819
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
15820
+ function isInternalActionId(actionId) {
15821
+ return actionId.startsWith("internal:") && actionId.length > 9;
15822
+ }
15823
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
15824
+ function addonIdOfActionId(actionId) {
15825
+ if (isInternalActionId(actionId)) return null;
15826
+ const at = actionId.indexOf(":");
15827
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
15828
+ }
15829
+ var ActionStatusSchema = object({
15830
+ isRunning: boolean(),
15831
+ /** Ms epoch the latest run STARTED; null when it never ran. */
15832
+ lastRunAt: number().nullable(),
15833
+ /** The latest run's failure; null on success or when it never ran. */
15834
+ lastError: string().nullable()
15835
+ });
15836
+ var ActionEntrySchema = object({
15837
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
15838
+ id: string().min(3),
15839
+ name: string().min(1).max(120),
15840
+ /** The addon that declared it; `core-blocks` for an internal one. */
15841
+ addonId: string().min(1),
15842
+ source: ActionSourceKindSchema,
15843
+ /** An addon-declared entry is edited where it was declared. */
15844
+ readOnly: boolean(),
15845
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
15846
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
15847
+ variables: array(ActionVariableSchema).max(20).optional(),
15848
+ status: ActionStatusSchema
15849
+ }).superRefine((entry, ctx) => {
15850
+ if (entry.source === "internal") {
15851
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
15852
+ code: "custom",
15853
+ message: "an internal action id is internal:<uuid>",
15854
+ path: ["id"]
15855
+ });
15856
+ return;
15857
+ }
15858
+ if (entry.steps !== void 0) ctx.addIssue({
15859
+ code: "custom",
15860
+ message: "only an internal action carries steps",
15861
+ path: ["steps"]
15862
+ });
15863
+ if (!entry.readOnly) ctx.addIssue({
15864
+ code: "custom",
15865
+ message: "an addon action is read-only here",
15866
+ path: ["readOnly"]
15867
+ });
15868
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
15869
+ code: "custom",
15870
+ message: "an addon action id is <addonId>:<localId>",
15871
+ path: ["id"]
15872
+ });
15873
+ });
15874
+ /** What an operator writes for an internal action. */
15875
+ var ActionInputSchema = object({
15876
+ name: string().min(1).max(120),
15877
+ steps: array(ActionStepSchema).min(1).max(50),
15878
+ variables: array(ActionVariableSchema).max(20).optional()
15879
+ });
15880
+ /** Who started a run. Absent = the operator pressed it. */
15881
+ var ActionRunOriginSchema = object({
15882
+ /** The automation block whose step started it. */
15883
+ blockId: string().min(1).optional() });
15884
+ /** `started` = the caller did not ask to wait. */
15885
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
15886
+ var ActionRunResultSchema = object({
15887
+ runId: string().min(1),
15888
+ outcome: ActionRunOutcomeSchema,
15889
+ /** Why it did not run, or what failed. */
15890
+ message: string().optional()
15891
+ });
15892
+ _enum([
15893
+ "not-found",
15894
+ "read-only",
15895
+ "invalid-variables",
15896
+ "invalid-steps",
15897
+ "not-linked",
15898
+ "busy"
15899
+ ]);
15900
+ object({
15901
+ actionId: string(),
15902
+ runId: string(),
15903
+ isRunning: boolean(),
15904
+ /** Set when the run ended. */
15905
+ outcome: AutomationRunOutcomeSchema.optional(),
15906
+ lastRunAt: number().nullable(),
15907
+ lastError: string().nullable()
15491
15908
  });
15492
15909
  /**
15493
15910
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -18174,7 +18591,7 @@ var CompositionEventPlanSchema = object({
18174
18591
  stateField: string(),
18175
18592
  fieldKey: string()
18176
18593
  });
18177
- /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
18594
+ /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
18178
18595
  var CompositionProblemSchema = object({
18179
18596
  code: _enum([
18180
18597
  "reserved-variant",
@@ -18493,7 +18910,7 @@ var GROUP_TEMPLATES = [
18493
18910
  summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
18494
18911
  role: DeviceRole.TemperatureSensor,
18495
18912
  outputCap: "temperature-sensor",
18496
- outputField: "celsius",
18913
+ outputField: "temperature",
18497
18914
  quantity: "temperature",
18498
18915
  defaultAggregate: "mean",
18499
18916
  empty: "null",
@@ -18504,7 +18921,7 @@ var GROUP_TEMPLATES = [
18504
18921
  summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
18505
18922
  role: DeviceRole.HumiditySensor,
18506
18923
  outputCap: "humidity-sensor",
18507
- outputField: "percent",
18924
+ outputField: "humidity",
18508
18925
  quantity: "humidity",
18509
18926
  defaultAggregate: "mean",
18510
18927
  empty: "null",
@@ -18515,7 +18932,7 @@ var GROUP_TEMPLATES = [
18515
18932
  summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
18516
18933
  role: DeviceRole.PressureSensor,
18517
18934
  outputCap: "pressure-sensor",
18518
- outputField: "hpa",
18935
+ outputField: "pressure",
18519
18936
  quantity: "pressure",
18520
18937
  defaultAggregate: "mean",
18521
18938
  empty: "null",
@@ -18526,7 +18943,7 @@ var GROUP_TEMPLATES = [
18526
18943
  summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
18527
18944
  role: DeviceRole.AmbientLightSensor,
18528
18945
  outputCap: "ambient-light-sensor",
18529
- outputField: "lux",
18946
+ outputField: "illuminance",
18530
18947
  quantity: "illuminance",
18531
18948
  defaultAggregate: "mean",
18532
18949
  empty: "null",
@@ -18537,7 +18954,7 @@ var GROUP_TEMPLATES = [
18537
18954
  summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
18538
18955
  role: DeviceRole.PowerSensor,
18539
18956
  outputCap: "power-meter",
18540
- outputField: "watts",
18957
+ outputField: "power",
18541
18958
  quantity: "power",
18542
18959
  defaultAggregate: "sum",
18543
18960
  empty: "omit",
@@ -18548,7 +18965,7 @@ var GROUP_TEMPLATES = [
18548
18965
  summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
18549
18966
  role: DeviceRole.EnergySensor,
18550
18967
  outputCap: "power-meter",
18551
- outputField: "kwhTotal",
18968
+ outputField: "energy",
18552
18969
  quantity: "energy",
18553
18970
  defaultAggregate: "sum",
18554
18971
  empty: "omit",
@@ -18559,7 +18976,7 @@ var GROUP_TEMPLATES = [
18559
18976
  summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
18560
18977
  role: DeviceRole.BatterySensor,
18561
18978
  outputCap: "battery",
18562
- outputField: "percentage",
18979
+ outputField: "level",
18563
18980
  quantity: "percentage",
18564
18981
  defaultAggregate: "mean",
18565
18982
  empty: "null",
@@ -19137,7 +19554,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
19137
19554
  kind: "mutation",
19138
19555
  auth: "admin",
19139
19556
  access: "view"
19140
- });
19557
+ }), method(object({
19558
+ blockId: string().min(1),
19559
+ trace: AutomationRunTraceSchema.optional(),
19560
+ runStartedAt: number().optional(),
19561
+ keepTraces: number().int().min(1).max(100).optional()
19562
+ }), _void(), {
19563
+ kind: "mutation",
19564
+ auth: "admin"
19565
+ }), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
19566
+ traces: array(AutomationRunTraceSchema),
19567
+ lastError: string().nullable()
19568
+ }), { auth: "admin" });
19141
19569
  /**
19142
19570
  * `custom-model-registry` — collection cap exposing operator-registered
19143
19571
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -44535,51 +44963,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
44535
44963
  kind: "mutation",
44536
44964
  auth: "admin"
44537
44965
  });
44538
- new Set([
44539
- {
44540
- id: "person",
44541
- name: "Person"
44542
- },
44543
- {
44544
- id: "vehicle",
44545
- name: "Vehicle"
44546
- },
44547
- {
44548
- id: "animal",
44549
- name: "Animal"
44550
- },
44551
- {
44552
- id: "package",
44553
- name: "Package"
44554
- }
44555
- ].map((l) => l.id));
44556
- var COCO_TO_MACRO = {
44557
- mapping: {
44558
- person: "person",
44559
- bicycle: "vehicle",
44560
- car: "vehicle",
44561
- motorcycle: "vehicle",
44562
- airplane: "vehicle",
44563
- bus: "vehicle",
44564
- train: "vehicle",
44565
- truck: "vehicle",
44566
- boat: "vehicle",
44567
- bird: "animal",
44568
- cat: "animal",
44569
- dog: "animal",
44570
- horse: "animal",
44571
- sheep: "animal",
44572
- cow: "animal",
44573
- elephant: "animal",
44574
- bear: "animal",
44575
- zebra: "animal",
44576
- giraffe: "animal",
44577
- suitcase: "package",
44578
- backpack: "package",
44579
- handbag: "package"
44580
- },
44581
- preserveOriginal: false
44582
- };
44583
44966
  var AUDIO_MACRO_LABELS = [
44584
44967
  {
44585
44968
  id: "speech",
@@ -57146,9 +57529,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
57146
57529
  */
57147
57530
  var AirQualitySensorStatusSchema = object({
57148
57531
  /** Carbon dioxide concentration in ppm. */
57149
- co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
57532
+ co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
57150
57533
  /** Total volatile organic compounds in ppb. */
57151
- vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
57534
+ voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
57152
57535
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
57153
57536
  pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
57154
57537
  /** Particulate matter ≤ 10 μm in µg/m³. */
@@ -57181,12 +57564,12 @@ var airQualitySensorCapability = {
57181
57564
  * History (D850): recorded series.
57182
57565
  */
57183
57566
  history: { fields: {
57184
- co2Ppm: {
57567
+ co2: {
57185
57568
  kind: "gauge",
57186
57569
  unit: "ppm",
57187
57570
  deadband: 5
57188
57571
  },
57189
- vocPpb: {
57572
+ voc: {
57190
57573
  kind: "gauge",
57191
57574
  unit: "ppb",
57192
57575
  deadband: 5
@@ -57217,7 +57600,7 @@ var airQualitySensorCapability = {
57217
57600
  */
57218
57601
  var AmbientLightSensorStatusSchema = object({
57219
57602
  /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
57220
- lux: number().min(0).nullable().meta(readingMeta("illuminance", {
57603
+ illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
57221
57604
  precision: 0,
57222
57605
  label: "Illuminance"
57223
57606
  })),
@@ -57246,7 +57629,7 @@ var ambientLightSensorCapability = {
57246
57629
  /**
57247
57630
  * History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
57248
57631
  */
57249
- history: { fields: { lux: {
57632
+ history: { fields: { illuminance: {
57250
57633
  kind: "gauge",
57251
57634
  unit: "lx",
57252
57635
  deadbandPct: 10,
@@ -57385,6 +57768,84 @@ var audioMetricsCapability = {
57385
57768
  */
57386
57769
  history: "none"
57387
57770
  };
57771
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
57772
+ actionId: string().min(1),
57773
+ variables: record(string(), ActionVariableValueSchema).optional()
57774
+ }), _void(), {
57775
+ kind: "mutation",
57776
+ auth: "admin"
57777
+ }), method(object({ actionId: string().min(1) }), _void(), {
57778
+ kind: "mutation",
57779
+ auth: "admin"
57780
+ });
57781
+ /**
57782
+ * actions — the ONE surface for runnable actions, for the UI and for
57783
+ * automations. Provided by the core-blocks builtin on the hub.
57784
+ *
57785
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
57786
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
57787
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
57788
+ * the run status from the hub's `runs` ledger; there is no catalog here to
57789
+ * drift (D3).
57790
+ *
57791
+ * **Permissions follow the camera, as linked devices do.** An action is linked
57792
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
57793
+ * non-admin may `get` / `run` an action only through a camera they may see that
57794
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
57795
+ * gate sees it) and the provider refuses an action that camera does not link.
57796
+ * An admin, and an in-process caller (an automation block), may run any action.
57797
+ * Every refusal is typed (`[not-linked] …`) and logged.
57798
+ *
57799
+ * A second `run` of an action that is already running is REFUSED — traced and
57800
+ * logged — never queued.
57801
+ */
57802
+ var ActionIdInput = object({ actionId: string().min(1) });
57803
+ var ActionLinksSchema = object({
57804
+ deviceId: number().int().nonnegative(),
57805
+ actionIds: array(string().min(1)).max(100)
57806
+ });
57807
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
57808
+ actionId: string().min(1),
57809
+ deviceId: number().int().nonnegative().optional()
57810
+ }), object({ action: ActionEntrySchema.nullable() }), {
57811
+ role: "viewer",
57812
+ auth: "protected",
57813
+ kind: "query",
57814
+ caller: "required"
57815
+ }), method(object({
57816
+ actionId: string().min(1),
57817
+ variables: record(string(), ActionVariableValueSchema).optional(),
57818
+ wait: boolean().optional(),
57819
+ origin: ActionRunOriginSchema.optional(),
57820
+ deviceId: number().int().nonnegative().optional()
57821
+ }), ActionRunResultSchema, {
57822
+ role: "viewer",
57823
+ auth: "protected",
57824
+ kind: "mutation",
57825
+ caller: "required"
57826
+ }), method(ActionIdInput, _void(), {
57827
+ kind: "mutation",
57828
+ auth: "admin"
57829
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
57830
+ kind: "mutation",
57831
+ auth: "admin"
57832
+ }), method(object({
57833
+ actionId: string().min(1),
57834
+ action: ActionInputSchema.partial()
57835
+ }), object({ action: ActionEntrySchema }), {
57836
+ kind: "mutation",
57837
+ auth: "admin"
57838
+ }), method(ActionIdInput, _void(), {
57839
+ kind: "mutation",
57840
+ auth: "admin"
57841
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
57842
+ role: "viewer",
57843
+ auth: "protected",
57844
+ kind: "query"
57845
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
57846
+ kind: "mutation",
57847
+ auth: "admin"
57848
+ });
57388
57849
  /**
57389
57850
  * Automation-control cap. Models HA `automation.*` entities on
57390
57851
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -57481,7 +57942,7 @@ var BatteryStatusSchema = object({
57481
57942
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
57482
57943
  * declines to band a non-finite reading, which is the correct shape.
57483
57944
  */
57484
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
57945
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
57485
57946
  /**
57486
57947
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
57487
57948
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -57491,7 +57952,7 @@ var BatteryStatusSchema = object({
57491
57952
  * `'unknown'` is the absence of an answer: the provider has not read the
57492
57953
  * power fields yet, or the firmware reported neither. Before it existed,
57493
57954
  * two unreadable fields produced `'none'`, and a camera nobody had reached
57494
- * was drawn "on battery alone" — the same D315 defect `percentage` closed
57955
+ * was drawn "on battery alone" — the same D315 defect `level` closed
57495
57956
  * by going nullable. Consumers SKIP it (no charger state published, no
57496
57957
  * glyph, no HomeKit ChargingState) rather than coerce it either way.
57497
57958
  */
@@ -57515,7 +57976,7 @@ var BatteryStatusSchema = object({
57515
57976
  * that only report charge. A second reading on this cap, not a second
57516
57977
  * `numeric-sensor` on the same device.
57517
57978
  */
57518
- volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
57979
+ voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
57519
57980
  /**
57520
57981
  * Ms epoch of the last time the device PROVED it was reachable — a
57521
57982
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -57549,7 +58010,7 @@ var BatteryStatusSchema = object({
57549
58010
  /**
57550
58011
  * True when the source is a BINARY low-battery indicator (HA
57551
58012
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
57552
- * charge level — `percentage` is then a coarse stand-in (100 = normal,
58013
+ * charge level — `level` is then a coarse stand-in (100 = normal,
57553
58014
  * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
57554
58015
  * misleading exact percentage. Absent/false → genuine 0–100 % reading.
57555
58016
  */
@@ -57569,15 +58030,15 @@ var BATTERY_DEVICE_TYPES = [
57569
58030
  var batteryCustomize = {
57570
58031
  version: 1,
57571
58032
  targets: [{
57572
- id: "percentage",
58033
+ id: "level",
57573
58034
  cardinality: "scalar",
57574
58035
  vocabulary: {
57575
58036
  cap: "battery",
57576
- stateField: "percentage"
58037
+ stateField: "level"
57577
58038
  },
57578
58039
  deviceTypes: BATTERY_DEVICE_TYPES,
57579
58040
  apply: "add",
57580
- path: "percentage",
58041
+ path: "level",
57581
58042
  sources: [{
57582
58043
  kind: "field",
57583
58044
  valueKinds: ["number", "string"],
@@ -57671,7 +58132,7 @@ onStatusChanged: { data: object({
57671
58132
  schema: BatteryStatusSchema,
57672
58133
  kind: "push",
57673
58134
  empty: {
57674
- percentage: null,
58135
+ level: null,
57675
58136
  charging: "unknown",
57676
58137
  sleeping: false,
57677
58138
  lastUpdated: 0
@@ -57698,11 +58159,11 @@ onStatusChanged: { data: object({
57698
58159
  * History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
57699
58160
  */
57700
58161
  history: { fields: {
57701
- percentage: {
58162
+ level: {
57702
58163
  kind: "gauge",
57703
58164
  unit: "%"
57704
58165
  },
57705
- volts: {
58166
+ voltage: {
57706
58167
  kind: "gauge",
57707
58168
  unit: "V"
57708
58169
  }
@@ -57762,14 +58223,14 @@ var binaryCapability = {
57762
58223
  * controls to this single-method surface.
57763
58224
  *
57764
58225
  * The cap is intentionally minimal: a single `setBrightness({deviceId,
57765
- * percentage})` mutation plus the auto-injected `getStatus`. Drivers
58226
+ * level})` mutation plus the auto-injected `getStatus`. Drivers
57766
58227
  * that expose richer controls (color temperature, scenes, schedules)
57767
58228
  * should surface those via the device's `getSettingsUISchema()`
57768
58229
  * instead of bloating this cap.
57769
58230
  */
57770
58231
  var BrightnessStatusSchema = object({
57771
58232
  /** Current level as 0..100 inclusive. Firmware-reported. */
57772
- percentage: number().min(0).max(100).meta(readingMeta("percentage")),
58233
+ level: number().min(0).max(100).meta(readingMeta("percentage")),
57773
58234
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
57774
58235
  lastChangedAt: number()
57775
58236
  });
@@ -57781,7 +58242,7 @@ var brightnessCapability = {
57781
58242
  deviceTypes: [DeviceType.Light],
57782
58243
  methods: { setBrightness: method(object({
57783
58244
  deviceId: number().int().nonnegative(),
57784
- percentage: number().min(0).max(100)
58245
+ level: number().min(0).max(100)
57785
58246
  }), _void(), {
57786
58247
  kind: "mutation",
57787
58248
  auth: "protected"
@@ -57794,7 +58255,7 @@ var brightnessCapability = {
57794
58255
  */
57795
58256
  onBrightnessChanged: { data: object({
57796
58257
  deviceId: number(),
57797
- percentage: number().min(0).max(100),
58258
+ level: number().min(0).max(100),
57798
58259
  lastChangedAt: number()
57799
58260
  }) } },
57800
58261
  status: {
@@ -57817,7 +58278,7 @@ onBrightnessChanged: { data: object({
57817
58278
  /**
57818
58279
  * History (D850): recorded series. Actuator tier: level keeps every change.
57819
58280
  */
57820
- history: { fields: { percentage: {
58281
+ history: { fields: { level: {
57821
58282
  kind: "gauge",
57822
58283
  unit: "%",
57823
58284
  minIntervalMs: 0
@@ -58431,11 +58892,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
58431
58892
  }),
58432
58893
  object({
58433
58894
  mode: literal("mired"),
58434
- mireds: number().int().min(50).max(1e3)
58895
+ colorTemperature: number().int().min(50).max(1e3)
58435
58896
  })
58436
58897
  ]);
58437
58898
  var ColorStatusSchema = object({
58438
- /** Active color mode — which of `rgb` / `hsv` / `mireds` reflects
58899
+ /** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
58439
58900
  * the bulb's current state. */
58440
58901
  mode: _enum([
58441
58902
  "rgb",
@@ -58447,7 +58908,7 @@ var ColorStatusSchema = object({
58447
58908
  /** Populated when `mode === 'hsv'`. */
58448
58909
  hsv: HsvTripletSchema.optional(),
58449
58910
  /** Populated when `mode === 'mired'`. */
58450
- mireds: number().int().optional().meta(readingMeta("colour-temperature")),
58911
+ colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
58451
58912
  /** Ms epoch of the last operator-driven change. */
58452
58913
  lastChangedAt: number()
58453
58914
  });
@@ -58479,7 +58940,7 @@ onColorChanged: { data: object({
58479
58940
  ]),
58480
58941
  rgb: RgbTripletSchema.optional(),
58481
58942
  hsv: HsvTripletSchema.optional(),
58482
- mireds: number().int().optional(),
58943
+ colorTemperature: number().int().optional(),
58483
58944
  lastChangedAt: number()
58484
58945
  }) } },
58485
58946
  status: {
@@ -59356,11 +59817,11 @@ var DishwasherStatusSchema = object({
59356
59817
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
59357
59818
  availablePrograms: array(string()),
59358
59819
  /** Seconds remaining on the active cycle, or null when not running / not known. */
59359
- remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59820
+ remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59360
59821
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
59361
- delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59822
+ delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59362
59823
  /** Cycle completion 0..100, or null when not applicable. */
59363
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59824
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59364
59825
  /** True when the door is reported open; null when the door state is not known. */
59365
59826
  doorOpen: boolean().nullable(),
59366
59827
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -59579,14 +60040,14 @@ var dishwasherCapability = {
59579
60040
  runtimeState: DishwasherStatusSchema,
59580
60041
  durability: "session",
59581
60042
  history: { fields: {
59582
- progressPercent: {
60043
+ progress: {
59583
60044
  kind: "gauge",
59584
60045
  unit: "%",
59585
60046
  deadband: 2
59586
60047
  },
59587
60048
  state: { kind: "enum" },
59588
60049
  operationState: { kind: "enum" },
59589
- remainingSeconds: {
60050
+ remaining: {
59590
60051
  kind: "gauge",
59591
60052
  unit: "s",
59592
60053
  minIntervalMs: 6e4
@@ -60177,7 +60638,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
60177
60638
  var FanControlStatusSchema = object({
60178
60639
  /** Active speed as 0..100 inclusive. Null when the device has no
60179
60640
  * speed surface (single-speed fan). */
60180
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60641
+ speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60181
60642
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
60182
60643
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
60183
60644
  percentageStep: number().positive().optional().meta(readingMeta("percentage")),
@@ -60204,7 +60665,7 @@ var fanControlCapability = {
60204
60665
  methods: {
60205
60666
  setPercentage: method(object({
60206
60667
  deviceId: number().int().nonnegative(),
60207
- percentage: number().min(0).max(100)
60668
+ speed: number().min(0).max(100)
60208
60669
  }), _void(), {
60209
60670
  kind: "mutation",
60210
60671
  auth: "protected"
@@ -60237,7 +60698,7 @@ var fanControlCapability = {
60237
60698
  },
60238
60699
  /**
60239
60700
  * Runtime-state slice — mirrored by the kernel. UI fan speed
60240
- * sliders read `percentage` for live updates.
60701
+ * sliders read `speed` for live updates.
60241
60702
  */
60242
60703
  runtimeState: FanControlStatusSchema,
60243
60704
  /**
@@ -60251,7 +60712,7 @@ var fanControlCapability = {
60251
60712
  * History (D850): recorded series. Actuator tier: speed keeps every change.
60252
60713
  */
60253
60714
  history: { fields: {
60254
- percentage: {
60715
+ speed: {
60255
60716
  kind: "gauge",
60256
60717
  unit: "%",
60257
60718
  minIntervalMs: 0
@@ -60696,7 +61157,7 @@ var humidifierCapability = {
60696
61157
  */
60697
61158
  var HumiditySensorStatusSchema = object({
60698
61159
  /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
60699
- percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
61160
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
60700
61161
  precision: 0,
60701
61162
  label: "Humidity"
60702
61163
  })),
@@ -60725,7 +61186,7 @@ var humiditySensorCapability = {
60725
61186
  /**
60726
61187
  * History (D850): recorded series.
60727
61188
  */
60728
- history: { fields: { percent: {
61189
+ history: { fields: { humidity: {
60729
61190
  kind: "gauge",
60730
61191
  unit: "%",
60731
61192
  deadband: .5
@@ -61365,7 +61826,7 @@ var LawnMowerControlStatusSchema = object({
61365
61826
  batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61366
61827
  /** 0..100 mowing-completion percentage of the current task, or null when no
61367
61828
  * task is active / progress is unavailable. */
61368
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61829
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61369
61830
  /** Current device/event code (dynamic — mostly status, sometimes an error),
61370
61831
  * or null when unknown. */
61371
61832
  currentCode: number().nullable().meta(readingMeta("none")),
@@ -61865,7 +62326,7 @@ var lockControlCapability = {
61865
62326
  volatileStateFields: ["lastChangedAt"]
61866
62327
  };
61867
62328
  /**
61868
- * A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
62329
+ * A monotonic total between resets (D944). Energy uses `power-meter.energy`
61869
62330
  * so dashboards keep one series; every other instantaneous reading lands here.
61870
62331
  * `value` is the time integral in the source quantity's accumulated base
61871
62332
  * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
@@ -61959,10 +62420,10 @@ var MediaPlayerStatusSchema = object({
61959
62420
  currentMedia: MediaInfoSchema.nullable(),
61960
62421
  /** Current playback position in ms. Null when not seekable or
61961
62422
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
61962
- positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62423
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
61963
62424
  /** Total duration of the current media in ms. Null when unknown
61964
62425
  * (live stream) or nothing is playing. */
61965
- durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62426
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
61966
62427
  /** Shuffle toggle. Null when no shuffle surface.
61967
62428
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
61968
62429
  shuffle: boolean().nullable(),
@@ -62001,7 +62462,7 @@ var mediaPlayerCapability = {
62001
62462
  }),
62002
62463
  seek: method(object({
62003
62464
  deviceId: number().int().nonnegative(),
62004
- positionMs: number().int().nonnegative()
62465
+ position: number().int().nonnegative()
62005
62466
  }), _void(), {
62006
62467
  kind: "mutation",
62007
62468
  auth: "protected"
@@ -62073,7 +62534,7 @@ var mediaPlayerCapability = {
62073
62534
  /**
62074
62535
  * History (D850): volume (a level an operator or an automation sets; the
62075
62536
  * default 10 s floor folds a slider drag into its end value) and mute.
62076
- * `positionMs` is not recorded: it moves continuously while playing.
62537
+ * `position` is not recorded: it moves continuously while playing.
62077
62538
  */
62078
62539
  history: { fields: {
62079
62540
  volumeLevel: {
@@ -63035,9 +63496,9 @@ var NetworkLinkStatusSchema = object({
63035
63496
  * one whose reading has not landed must not be drawn at 0 %. Consumers
63036
63497
  * SKIP a null rather than coerce it.
63037
63498
  */
63038
- signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63499
+ signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63039
63500
  /** Raw received signal strength in dBm, when the firmware reports one. */
63040
- rssiDbm: number().optional().meta(readingMeta("signal-level")),
63501
+ rssi: number().optional().meta(readingMeta("signal-level")),
63041
63502
  /** Network name of a wireless link, when the firmware reports it. */
63042
63503
  ssid: string().optional(),
63043
63504
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
@@ -63073,7 +63534,7 @@ onStatusChanged: { data: object({
63073
63534
  kind: "push",
63074
63535
  empty: {
63075
63536
  type: "unknown",
63076
- signalPercent: null,
63537
+ signal: null,
63077
63538
  lastUpdated: 0
63078
63539
  }
63079
63540
  },
@@ -63096,13 +63557,13 @@ onStatusChanged: { data: object({
63096
63557
  * History (D850): recorded series.
63097
63558
  */
63098
63559
  history: { fields: {
63099
- signalPercent: {
63560
+ signal: {
63100
63561
  kind: "gauge",
63101
63562
  unit: "%",
63102
63563
  deadband: 3,
63103
63564
  minIntervalMs: 6e4
63104
63565
  },
63105
- rssiDbm: {
63566
+ rssi: {
63106
63567
  kind: "gauge",
63107
63568
  unit: "dBm",
63108
63569
  deadband: 2,
@@ -63847,10 +64308,20 @@ var PetFeederDeviceStatusSchema = _enum([
63847
64308
  ]);
63848
64309
  var gramsPortion = number().int().min(4).max(200);
63849
64310
  var PetFeederStatusSchema = object({
63850
- /** Food currently in the bowl (grams). Null when the device has not
63851
- * reported a reading yet. On dual-hopper models this is the combined
63852
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
64311
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
64312
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
64313
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
64314
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
64315
+ * the device has not reported yet. */
63853
64316
  foodLevel: number().nullable().meta(readingMeta("none")),
64317
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
64318
+ * feeder's own units. Null when the model reports none. */
64319
+ bowlFood: number().nullable().meta(readingMeta("none")),
64320
+ /** True while the pet is eating (`state.eating`). */
64321
+ eating: boolean(),
64322
+ /** Whether `markFoodReplenished` does anything on this model. When false
64323
+ * the client must not offer it. */
64324
+ supportsMarkRefilled: boolean(),
63854
64325
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
63855
64326
  * single-hopper models. */
63856
64327
  food1: number().nullable().meta(readingMeta("none")),
@@ -63858,8 +64329,8 @@ var PetFeederStatusSchema = object({
63858
64329
  * single-hopper models. */
63859
64330
  food2: number().nullable().meta(readingMeta("none")),
63860
64331
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
63861
- * (`device_class: problem`, on = low). True when the bowl is empty /
63862
- * below the feeder's low threshold. */
64332
+ * (`device_class: problem`, on = low). True when the tank reports empty,
64333
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
63863
64334
  lowFood: boolean(),
63864
64335
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
63865
64336
  * device has no battery reading. */
@@ -63927,7 +64398,9 @@ var petFeederCapability = {
63927
64398
  kind: "mutation",
63928
64399
  auth: "protected"
63929
64400
  }),
63930
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
64401
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
64402
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
64403
+ * change any sensor reading. */
63931
64404
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
63932
64405
  kind: "mutation",
63933
64406
  auth: "protected"
@@ -64000,6 +64473,7 @@ var petFeederCapability = {
64000
64473
  */
64001
64474
  history: { fields: {
64002
64475
  foodLevel: { kind: "gauge" },
64476
+ bowlFood: { kind: "gauge" },
64003
64477
  food1: { kind: "gauge" },
64004
64478
  food2: { kind: "gauge" },
64005
64479
  batteryPower: {
@@ -64409,16 +64883,16 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
64409
64883
  * in real-world devices.
64410
64884
  */
64411
64885
  var PowerMeterStatusSchema = object({
64412
- /** Instantaneous power draw in watts. */
64413
- watts: number().optional().meta(readingMeta("power", { label: "Power" })),
64886
+ /** Instantaneous power draw (watts). */
64887
+ power: number().optional().meta(readingMeta("power", { label: "Power" })),
64414
64888
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64415
- kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64416
- /** Voltage in volts. */
64417
- volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64889
+ energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64890
+ /** Voltage (volts). */
64891
+ voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64418
64892
  /** Current in amperes. */
64419
- amps: number().optional().meta(readingMeta("current", { label: "Current" })),
64893
+ current: number().optional().meta(readingMeta("current", { label: "Current" })),
64420
64894
  /** Apparent power in volt-amperes. */
64421
- va: number().optional().meta(readingMeta("apparent-power")),
64895
+ apparentPower: number().optional().meta(readingMeta("apparent-power")),
64422
64896
  /** Power factor (0..1) if reported. */
64423
64897
  powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
64424
64898
  /** Ms epoch when the slice was last updated. */
@@ -64476,14 +64950,14 @@ function powerTarget(id, unit) {
64476
64950
  advancedExpression: true
64477
64951
  };
64478
64952
  }
64479
- /** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
64953
+ /** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
64480
64954
  var powerMeterCustomize = {
64481
64955
  version: 1,
64482
64956
  targets: [
64483
- powerTarget("watts", "W"),
64484
- powerTarget("kwhTotal", "kWh"),
64485
- powerTarget("volts", "V"),
64486
- powerTarget("amps", "A")
64957
+ powerTarget("power", "W"),
64958
+ powerTarget("energy", "kWh"),
64959
+ powerTarget("voltage", "V"),
64960
+ powerTarget("current", "A")
64487
64961
  ]
64488
64962
  };
64489
64963
  var powerMeterCapability = {
@@ -64499,41 +64973,41 @@ var powerMeterCapability = {
64499
64973
  },
64500
64974
  runtimeState: PowerMeterStatusSchema,
64501
64975
  /**
64502
- * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
64976
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
64503
64977
  *
64504
64978
  * See `RuntimeStateDurability`. Enforced by
64505
64979
  * `scripts/check-runtime-state-durability.ts`.
64506
64980
  */
64507
64981
  durability: "restored",
64508
64982
  /**
64509
- * History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
64983
+ * History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
64510
64984
  */
64511
64985
  history: { fields: {
64512
- watts: {
64986
+ power: {
64513
64987
  kind: "gauge",
64514
64988
  unit: "W",
64515
64989
  deadband: 1,
64516
64990
  minIntervalMs: 3e4
64517
64991
  },
64518
- kwhTotal: {
64992
+ energy: {
64519
64993
  kind: "counter",
64520
64994
  unit: "kWh",
64521
64995
  deadband: .01,
64522
64996
  minIntervalMs: 6e4
64523
64997
  },
64524
- volts: {
64998
+ voltage: {
64525
64999
  kind: "gauge",
64526
65000
  unit: "V",
64527
65001
  deadband: 1,
64528
65002
  minIntervalMs: 3e4
64529
65003
  },
64530
- amps: {
65004
+ current: {
64531
65005
  kind: "gauge",
64532
65006
  unit: "A",
64533
65007
  deadband: .05,
64534
65008
  minIntervalMs: 3e4
64535
65009
  },
64536
- va: {
65010
+ apparentPower: {
64537
65011
  kind: "gauge",
64538
65012
  unit: "VA",
64539
65013
  deadband: 1,
@@ -64583,9 +65057,6 @@ var PresenceStatusSchema = object({
64583
65057
  location: string().nullable(),
64584
65058
  /** GPS coordinates when available. Null otherwise. */
64585
65059
  gps: GpsLocationSchema.nullable(),
64586
- /** Optional battery level of the tracking device (0..100). Null
64587
- * when not reported. */
64588
- batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64589
65060
  /** Ms epoch when the slice was last updated. */
64590
65061
  lastChangedAt: number()
64591
65062
  });
@@ -64614,14 +65085,7 @@ var presenceCapability = {
64614
65085
  * `scripts/check-runtime-state-durability.ts`.
64615
65086
  */
64616
65087
  durability: "restored",
64617
- /**
64618
- * History (D850): recorded series.
64619
- */
64620
- history: { fields: { batteryPercent: {
64621
- kind: "gauge",
64622
- unit: "%",
64623
- deadband: 1
64624
- } } },
65088
+ history: "none",
64625
65089
  /** Clock fields: written, but excluded from the compare that decides
64626
65090
  * whether persisting is worth a SQLite commit. */
64627
65091
  volatileStateFields: ["lastChangedAt"]
@@ -64636,7 +65100,7 @@ var presenceCapability = {
64636
65100
  */
64637
65101
  var PressureSensorStatusSchema = object({
64638
65102
  /** Current pressure in hPa. `null` when the sample was refused (D393). */
64639
- hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
65103
+ pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
64640
65104
  /** Ms epoch when the slice was last updated. */
64641
65105
  lastFetchedAt: number()
64642
65106
  });
@@ -64662,7 +65126,7 @@ var pressureSensorCapability = {
64662
65126
  /**
64663
65127
  * History (D850): recorded series.
64664
65128
  */
64665
- history: { fields: { hpa: {
65129
+ history: { fields: { pressure: {
64666
65130
  kind: "gauge",
64667
65131
  unit: "hPa",
64668
65132
  deadband: .1
@@ -68361,7 +68825,7 @@ var tamperCapability = {
68361
68825
  */
68362
68826
  var TemperatureSensorStatusSchema = object({
68363
68827
  /** Current temperature in Celsius. `null` when the sample was refused (D393). */
68364
- celsius: number().nullable().meta(readingMeta("temperature", {
68828
+ temperature: number().nullable().meta(readingMeta("temperature", {
68365
68829
  precision: 1,
68366
68830
  label: "Temperature"
68367
68831
  })),
@@ -68390,7 +68854,7 @@ var temperatureSensorCapability = {
68390
68854
  /**
68391
68855
  * History (D850): recorded series.
68392
68856
  */
68393
- history: { fields: { celsius: {
68857
+ history: { fields: { temperature: {
68394
68858
  kind: "gauge",
68395
68859
  unit: "°C",
68396
68860
  deadband: .1
@@ -68687,7 +69151,7 @@ var VacuumControlStatusSchema = object({
68687
69151
  /** Speed tokens the hardware accepts — drives the UI selector. */
68688
69152
  availableFanSpeeds: array(string()),
68689
69153
  /** 0..100 cleaning-completion percentage of the current task, or null. */
68690
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
69154
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
68691
69155
  /** Current error code (0 / null = no error). */
68692
69156
  errorCode: number().nullable().meta(readingMeta("none")),
68693
69157
  /** Human label for {@link errorCode}, or null when none / undecodable. */
@@ -70341,7 +70805,7 @@ var BaseDevice = class {
70341
70805
  * `device-local-state.ts`. Drivers access via:
70342
70806
  *
70343
70807
  * `this.state.battery.sleeping = true` // patches the battery slice
70344
- * `const pct = this.state.battery.percentage` // reads the battery slice
70808
+ * `const pct = this.state.battery.level` // reads the battery slice
70345
70809
  * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
70346
70810
  *
70347
70811
  * Adding a new cap with `runtimeState:` automatically extends this
@@ -70381,7 +70845,7 @@ var BaseDevice = class {
70381
70845
  * shape; drivers don't redeclare it, they just write through.
70382
70846
  *
70383
70847
  * Read: `this.runtimeState.getCapState('battery')` →
70384
- * `{percentage, charging, sleeping, lastUpdated}` for any
70848
+ * `{level, charging, sleeping, lastUpdated}` for any
70385
70849
  * provider that registers `batteryCapability`.
70386
70850
  * Write: `this.runtimeState.setCapState('battery', { … })`.
70387
70851
  *
@@ -71555,6 +72019,104 @@ Object.freeze({
71555
72019
  access: "create",
71556
72020
  role: "admin"
71557
72021
  },
72022
+ "actions.create": {
72023
+ capName: "actions",
72024
+ capScope: "system",
72025
+ addonId: null,
72026
+ access: "create",
72027
+ role: "admin"
72028
+ },
72029
+ "actions.delete": {
72030
+ capName: "actions",
72031
+ capScope: "system",
72032
+ addonId: null,
72033
+ access: "delete",
72034
+ role: "admin"
72035
+ },
72036
+ "actions.get": {
72037
+ capName: "actions",
72038
+ capScope: "system",
72039
+ addonId: null,
72040
+ access: "view",
72041
+ role: "viewer"
72042
+ },
72043
+ "actions.getLinks": {
72044
+ capName: "actions",
72045
+ capScope: "system",
72046
+ addonId: null,
72047
+ access: "view",
72048
+ role: "admin"
72049
+ },
72050
+ "actions.getTraces": {
72051
+ capName: "actions",
72052
+ capScope: "system",
72053
+ addonId: null,
72054
+ access: "view",
72055
+ role: "admin"
72056
+ },
72057
+ "actions.list": {
72058
+ capName: "actions",
72059
+ capScope: "system",
72060
+ addonId: null,
72061
+ access: "view",
72062
+ role: "admin"
72063
+ },
72064
+ "actions.listForCamera": {
72065
+ capName: "actions",
72066
+ capScope: "system",
72067
+ addonId: null,
72068
+ access: "view",
72069
+ role: "viewer"
72070
+ },
72071
+ "actions.run": {
72072
+ capName: "actions",
72073
+ capScope: "system",
72074
+ addonId: null,
72075
+ access: "create",
72076
+ role: "viewer"
72077
+ },
72078
+ "actions.setLinks": {
72079
+ capName: "actions",
72080
+ capScope: "system",
72081
+ addonId: null,
72082
+ access: "create",
72083
+ role: "admin"
72084
+ },
72085
+ "actions.stop": {
72086
+ capName: "actions",
72087
+ capScope: "system",
72088
+ addonId: null,
72089
+ access: "create",
72090
+ role: "admin"
72091
+ },
72092
+ "actions.update": {
72093
+ capName: "actions",
72094
+ capScope: "system",
72095
+ addonId: null,
72096
+ access: "create",
72097
+ role: "admin"
72098
+ },
72099
+ "actionSource.listActions": {
72100
+ capName: "action-source",
72101
+ capScope: "system",
72102
+ addonId: null,
72103
+ access: "view",
72104
+ role: "admin"
72105
+ },
72106
+ "actionSource.runAction": {
72107
+ capName: "action-source",
72108
+ capScope: "system",
72109
+ addonId: null,
72110
+ access: "create",
72111
+ role: "admin"
72112
+ },
72113
+ "actionSource.stopAction": {
72114
+ capName: "action-source",
72115
+ capScope: "system",
72116
+ addonId: null,
72117
+ access: "create",
72118
+ role: "admin"
72119
+ },
71558
72120
  "addonPages.listPages": {
71559
72121
  capName: "addon-pages",
71560
72122
  capScope: "system",
@@ -72836,6 +73398,13 @@ Object.freeze({
72836
73398
  access: "view",
72837
73399
  role: "admin"
72838
73400
  },
73401
+ "coreBlocks.getAutomationRunState": {
73402
+ capName: "core-blocks",
73403
+ capScope: "system",
73404
+ addonId: null,
73405
+ access: "view",
73406
+ role: "admin"
73407
+ },
72839
73408
  "coreBlocks.getBlockSettings": {
72840
73409
  capName: "core-blocks",
72841
73410
  capScope: "system",
@@ -72857,6 +73426,13 @@ Object.freeze({
72857
73426
  access: "view",
72858
73427
  role: "admin"
72859
73428
  },
73429
+ "coreBlocks.getTraces": {
73430
+ capName: "core-blocks",
73431
+ capScope: "system",
73432
+ addonId: null,
73433
+ access: "view",
73434
+ role: "admin"
73435
+ },
72860
73436
  "coreBlocks.getTypeDefs": {
72861
73437
  capName: "core-blocks",
72862
73438
  capScope: "system",
@@ -72892,6 +73468,13 @@ Object.freeze({
72892
73468
  access: "create",
72893
73469
  role: "admin"
72894
73470
  },
73471
+ "coreBlocks.reportAutomationRun": {
73472
+ capName: "core-blocks",
73473
+ capScope: "system",
73474
+ addonId: null,
73475
+ access: "create",
73476
+ role: "admin"
73477
+ },
72895
73478
  "coreBlocks.restart": {
72896
73479
  capName: "core-blocks",
72897
73480
  capScope: "system",
@@ -81465,7 +82048,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81465
82048
  label: t("Full brightness", "Luminosità massima"),
81466
82049
  phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
81467
82050
  icon: "sun",
81468
- args: { percentage: 100 }
82051
+ args: { level: 100 }
81469
82052
  },
81470
82053
  {
81471
82054
  id: "brightness.half",
@@ -81473,7 +82056,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81473
82056
  label: t("Half brightness", "Luminosità a metà"),
81474
82057
  phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
81475
82058
  icon: "sun-dim",
81476
- args: { percentage: 50 }
82059
+ args: { level: 50 }
81477
82060
  },
81478
82061
  {
81479
82062
  id: "brightness.off",
@@ -81481,16 +82064,16 @@ var ACTUATOR_CAP_VOCABULARY = {
81481
82064
  label: t("Brightness off", "Luminosità a zero"),
81482
82065
  phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
81483
82066
  icon: "lightbulb-off",
81484
- args: { percentage: 0 }
82067
+ args: { level: 0 }
81485
82068
  },
81486
82069
  {
81487
82070
  id: "brightness.set",
81488
82071
  method: "setBrightness",
81489
82072
  label: t("Set brightness", "Imposta la luminosità"),
81490
- phrase: t("Set the brightness of {device} to {percentage}", "Imposta la luminosità di {device} a {percentage}"),
82073
+ phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
81491
82074
  icon: "sun-medium",
81492
82075
  openArgs: [{
81493
- key: "percentage",
82076
+ key: "level",
81494
82077
  label: PERCENT,
81495
82078
  offer: {
81496
82079
  kind: "number",
@@ -81504,13 +82087,13 @@ var ACTUATOR_CAP_VOCABULARY = {
81504
82087
  ],
81505
82088
  state: [{
81506
82089
  kind: "numeric",
81507
- field: "percentage",
82090
+ field: "level",
81508
82091
  quantity: t("the brightness", "la luminosità"),
81509
82092
  label: t("Brightness", "Luminosità"),
81510
82093
  unit: "%",
81511
82094
  icon: "sun"
81512
82095
  }],
81513
- events: { onBrightnessChanged: { notOperatorFacing: "the `percentage` state trigger reads the same change" } }
82096
+ events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
81514
82097
  },
81515
82098
  color: {
81516
82099
  scope: "device",
@@ -81524,7 +82107,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81524
82107
  icon: "sun",
81525
82108
  args: { color: {
81526
82109
  mode: "mired",
81527
- mireds: 370
82110
+ colorTemperature: 370
81528
82111
  } }
81529
82112
  },
81530
82113
  {
@@ -81535,7 +82118,7 @@ var ACTUATOR_CAP_VOCABULARY = {
81535
82118
  icon: "snowflake",
81536
82119
  args: { color: {
81537
82120
  mode: "mired",
81538
- mireds: 200
82121
+ colorTemperature: 200
81539
82122
  } }
81540
82123
  },
81541
82124
  {
@@ -82149,7 +82732,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82149
82732
  label: t("Fan off", "Ventilatore spento"),
82150
82733
  phrase: t("Stop {device}", "Ferma {device}"),
82151
82734
  icon: "power-off",
82152
- args: { percentage: 0 }
82735
+ args: { speed: 0 }
82153
82736
  },
82154
82737
  {
82155
82738
  id: "fan.full",
@@ -82157,16 +82740,16 @@ var ACTUATOR_CAP_VOCABULARY = {
82157
82740
  label: t("Fan full", "Ventilatore al massimo"),
82158
82741
  phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
82159
82742
  icon: "fan",
82160
- args: { percentage: 100 }
82743
+ args: { speed: 100 }
82161
82744
  },
82162
82745
  {
82163
82746
  id: "fan.speed",
82164
82747
  method: "setPercentage",
82165
82748
  label: t("Set speed", "Imposta la velocità"),
82166
- phrase: t("Set the speed of {device} to {percentage}", "Imposta la velocità di {device} al {percentage}"),
82749
+ phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
82167
82750
  icon: "fan",
82168
82751
  openArgs: [{
82169
- key: "percentage",
82752
+ key: "speed",
82170
82753
  label: t("Speed", "Velocità"),
82171
82754
  offer: {
82172
82755
  kind: "number",
@@ -82228,7 +82811,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82228
82811
  ],
82229
82812
  state: [{
82230
82813
  kind: "numeric",
82231
- field: "percentage",
82814
+ field: "speed",
82232
82815
  quantity: t("the speed", "la velocità"),
82233
82816
  label: t("Speed", "Velocità"),
82234
82817
  unit: "%",
@@ -83026,7 +83609,7 @@ var APPLIANCE_CAP_VOCABULARY = {
83026
83609
  },
83027
83610
  {
83028
83611
  kind: "numeric",
83029
- field: "progressPercent",
83612
+ field: "progress",
83030
83613
  quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
83031
83614
  label: t("Progress", "Avanzamento"),
83032
83615
  unit: "%",
@@ -83384,10 +83967,10 @@ var APPLIANCE_CAP_VOCABULARY = {
83384
83967
  id: "media.seek",
83385
83968
  method: "seek",
83386
83969
  label: t("Seek", "Vai al punto"),
83387
- phrase: t("Move {device} to {positionMs}", "Porta {device} al punto {positionMs}"),
83970
+ phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
83388
83971
  icon: "fast-forward",
83389
83972
  openArgs: [{
83390
- key: "positionMs",
83973
+ key: "position",
83391
83974
  label: t("Position", "Posizione"),
83392
83975
  offer: {
83393
83976
  kind: "number",
@@ -84436,7 +85019,7 @@ var SENSOR_CAP_VOCABULARY = {
84436
85019
  state: [
84437
85020
  {
84438
85021
  kind: "numeric",
84439
- field: "co2Ppm",
85022
+ field: "co2",
84440
85023
  quantity: t("the CO₂", "la CO₂"),
84441
85024
  label: t("CO₂", "CO₂"),
84442
85025
  unit: "ppm",
@@ -84444,7 +85027,7 @@ var SENSOR_CAP_VOCABULARY = {
84444
85027
  },
84445
85028
  {
84446
85029
  kind: "numeric",
84447
- field: "vocPpb",
85030
+ field: "voc",
84448
85031
  quantity: t("the VOC level", "il livello di COV"),
84449
85032
  label: t("VOC", "COV"),
84450
85033
  unit: "ppb",
@@ -84480,7 +85063,7 @@ var SENSOR_CAP_VOCABULARY = {
84480
85063
  noun: t("Light level", "Luminosità ambiente"),
84481
85064
  state: [{
84482
85065
  kind: "numeric",
84483
- field: "lux",
85066
+ field: "illuminance",
84484
85067
  quantity: t("the light level", "la luminosità"),
84485
85068
  label: t("Illuminance", "Illuminamento"),
84486
85069
  unit: "lx",
@@ -84683,7 +85266,7 @@ var SENSOR_CAP_VOCABULARY = {
84683
85266
  noun: t("Humidity", "Umidità"),
84684
85267
  state: [{
84685
85268
  kind: "numeric",
84686
- field: "percent",
85269
+ field: "humidity",
84687
85270
  quantity: t("the humidity", "l’umidità"),
84688
85271
  label: t("Humidity", "Umidità"),
84689
85272
  unit: "%",
@@ -84731,7 +85314,7 @@ var SENSOR_CAP_VOCABULARY = {
84731
85314
  state: [
84732
85315
  {
84733
85316
  kind: "numeric",
84734
- field: "watts",
85317
+ field: "power",
84735
85318
  quantity: t("the power draw", "il consumo"),
84736
85319
  label: t("Power", "Potenza"),
84737
85320
  unit: "W",
@@ -84739,7 +85322,7 @@ var SENSOR_CAP_VOCABULARY = {
84739
85322
  },
84740
85323
  {
84741
85324
  kind: "numeric",
84742
- field: "kwhTotal",
85325
+ field: "energy",
84743
85326
  quantity: t("the energy used", "l’energia consumata"),
84744
85327
  label: t("Energy", "Energia"),
84745
85328
  unit: "kWh",
@@ -84747,7 +85330,7 @@ var SENSOR_CAP_VOCABULARY = {
84747
85330
  },
84748
85331
  {
84749
85332
  kind: "numeric",
84750
- field: "volts",
85333
+ field: "voltage",
84751
85334
  quantity: t("the voltage", "la tensione"),
84752
85335
  label: t("Voltage", "Tensione"),
84753
85336
  unit: "V",
@@ -84755,7 +85338,7 @@ var SENSOR_CAP_VOCABULARY = {
84755
85338
  },
84756
85339
  {
84757
85340
  kind: "numeric",
84758
- field: "amps",
85341
+ field: "current",
84759
85342
  quantity: t("the current", "la corrente"),
84760
85343
  label: t("Current", "Corrente"),
84761
85344
  unit: "A",
@@ -84772,13 +85355,6 @@ var SENSOR_CAP_VOCABULARY = {
84772
85355
  label: t("Where", "Posizione"),
84773
85356
  phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
84774
85357
  icon: "map-pin"
84775
- }, {
84776
- kind: "numeric",
84777
- field: "batteryPercent",
84778
- quantity: t("the phone battery", "la batteria del telefono"),
84779
- label: t("Battery", "Batteria"),
84780
- unit: "%",
84781
- icon: "battery"
84782
85358
  }]
84783
85359
  },
84784
85360
  "pressure-sensor": {
@@ -84786,7 +85362,7 @@ var SENSOR_CAP_VOCABULARY = {
84786
85362
  noun: t("Pressure", "Pressione"),
84787
85363
  state: [{
84788
85364
  kind: "numeric",
84789
- field: "hpa",
85365
+ field: "pressure",
84790
85366
  quantity: t("the pressure", "la pressione"),
84791
85367
  label: t("Pressure", "Pressione"),
84792
85368
  unit: "hPa",
@@ -84822,7 +85398,7 @@ var SENSOR_CAP_VOCABULARY = {
84822
85398
  noun: t("Temperature", "Temperatura"),
84823
85399
  state: [{
84824
85400
  kind: "numeric",
84825
- field: "celsius",
85401
+ field: "temperature",
84826
85402
  quantity: t("the temperature", "la temperatura"),
84827
85403
  label: t("Temperature", "Temperatura"),
84828
85404
  unit: "°C",
@@ -84893,7 +85469,7 @@ var SENSOR_CAP_VOCABULARY = {
84893
85469
  state: [
84894
85470
  {
84895
85471
  kind: "numeric",
84896
- field: "percentage",
85472
+ field: "level",
84897
85473
  quantity: t("the battery level", "il livello della batteria"),
84898
85474
  label: t("Battery", "Batteria"),
84899
85475
  unit: "%",
@@ -84935,7 +85511,7 @@ var SENSOR_CAP_VOCABULARY = {
84935
85511
  icon: "battery-charging"
84936
85512
  }
84937
85513
  ],
84938
- events: { onStatusChanged: { notOperatorFacing: "the percentage / sleeping / charging state triggers each read one field of this change" } },
85514
+ events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
84939
85515
  extraEvents: [{
84940
85516
  category: EventCategory.DeviceAwake,
84941
85517
  label: t("Woke up", "Risvegliato"),
@@ -84953,7 +85529,7 @@ var SENSOR_CAP_VOCABULARY = {
84953
85529
  noun: t("Network link", "Collegamento di rete"),
84954
85530
  state: [{
84955
85531
  kind: "numeric",
84956
- field: "signalPercent",
85532
+ field: "signal",
84957
85533
  quantity: t("the signal strength", "l’intensità del segnale"),
84958
85534
  label: t("Signal", "Segnale"),
84959
85535
  unit: "%",
@@ -85046,6 +85622,8 @@ var ML = "model and training machinery — no device action";
85046
85622
  var INTERNAL = "internal plumbing between addons — never operator-facing";
85047
85623
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
85048
85624
  var SYSTEM_CAP_VOCABULARY = {
85625
+ "action-source": system(INTERNAL),
85626
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
85049
85627
  "addon-pages": system(UI_PLUMBING),
85050
85628
  "addon-pages-source": system(UI_PLUMBING),
85051
85629
  "addon-routes": system(UI_PLUMBING),
@@ -85242,6 +85820,31 @@ Object.freeze({
85242
85820
  form: "single",
85243
85821
  optional: false
85244
85822
  }],
85823
+ "actions.get": [{
85824
+ name: "deviceId",
85825
+ form: "single",
85826
+ optional: true
85827
+ }],
85828
+ "actions.getLinks": [{
85829
+ name: "deviceId",
85830
+ form: "single",
85831
+ optional: false
85832
+ }],
85833
+ "actions.listForCamera": [{
85834
+ name: "deviceId",
85835
+ form: "single",
85836
+ optional: false
85837
+ }],
85838
+ "actions.run": [{
85839
+ name: "deviceId",
85840
+ form: "single",
85841
+ optional: true
85842
+ }],
85843
+ "actions.setLinks": [{
85844
+ name: "deviceId",
85845
+ form: "single",
85846
+ optional: false
85847
+ }],
85245
85848
  "addonSettings.getDeviceSettings": [{
85246
85849
  name: "deviceId",
85247
85850
  form: "single",
@@ -89323,6 +89926,7 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
89323
89926
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
89324
89927
  DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
89325
89928
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
89929
+ String.raw`(?<=^|["'(\s>,\[{])`;
89326
89930
  var MB = 1024 * 1024;
89327
89931
  1024 * MB, 3072 * MB;
89328
89932
  //#endregion
@@ -90086,7 +90690,6 @@ var COLD_PRESENCE = {
90086
90690
  state: "not_home",
90087
90691
  location: null,
90088
90692
  gps: null,
90089
- batteryPercent: null,
90090
90693
  lastChangedAt: 0
90091
90694
  };
90092
90695
  /**
@@ -90217,7 +90820,7 @@ var UnifiFeatureDevice = class extends BaseDevice {
90217
90820
  switch (capForMetric(this.metric)) {
90218
90821
  case "temperature-sensor":
90219
90822
  this.runtimeState.setCapState("temperature-sensor", {
90220
- celsius: infra.cpuTempC ?? 0,
90823
+ temperature: infra.cpuTempC ?? 0,
90221
90824
  lastFetchedAt: now
90222
90825
  });
90223
90826
  return;
@@ -90254,7 +90857,6 @@ var UnifiFeatureDevice = class extends BaseDevice {
90254
90857
  state: client.connected ? "home" : "not_home",
90255
90858
  location: client.ip,
90256
90859
  gps: null,
90257
- batteryPercent: null,
90258
90860
  lastChangedAt: Date.now()
90259
90861
  };
90260
90862
  this.runtimeState.setCapState("presence", status);
@@ -90294,7 +90896,7 @@ var UnifiFeatureDevice = class extends BaseDevice {
90294
90896
  switch (capForMetric(this.metric)) {
90295
90897
  case "temperature-sensor": {
90296
90898
  const COLD = {
90297
- celsius: 0,
90899
+ temperature: 0,
90298
90900
  lastFetchedAt: 0
90299
90901
  };
90300
90902
  this.ctx.registerNativeCap(temperatureSensorCapability, { getStatus: async () => this.runtimeState.getCapState("temperature-sensor") ?? COLD });