@camstack/addon-provider-homematic 1.2.160 → 1.2.162

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
@@ -8,7 +8,7 @@ let http = require("http");
8
8
  let crypto$1 = require("crypto");
9
9
  let fs_promises = require("fs/promises");
10
10
  let path = require("path");
11
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
11
+ //#region ../types/dist/event-category-DmZDPDf_.mjs
12
12
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
13
13
  EventCategory["SystemBoot"] = "system.boot";
14
14
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -635,7 +635,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
635
635
  * (a track was promoted to a stationary-registry entry) or departs (the
636
636
  * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
637
637
  * the dedicated "Stationary" section — never the live event feed. Payload:
638
- * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
638
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
639
639
  */
640
640
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
641
641
  /**
@@ -773,6 +773,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
773
773
  */
774
774
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
775
775
  /**
776
+ * An action run started or ended. Emitted by the `actions` surface.
777
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
778
+ */
779
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
780
+ /**
776
781
  * Progress update from a running model conversion job.
777
782
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
778
783
  * Emitted by `addon-model-studio` on the converting node.
@@ -5584,7 +5589,7 @@ var ZodIssueCode = {
5584
5589
  var ZodFirstPartyTypeKind;
5585
5590
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5586
5591
  //#endregion
5587
- //#region ../types/dist/sleep--jXS9hWo.mjs
5592
+ //#region ../types/dist/sleep-C_u6cEAJ.mjs
5588
5593
  /**
5589
5594
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5590
5595
  * window to float samples (D455).
@@ -8892,6 +8897,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
8892
8897
  /** Every capability name, so addon code can check a name without the schema graph. */
8893
8898
  var CAP_NAMES = Object.freeze([
8894
8899
  "accessories",
8900
+ "action-source",
8901
+ "actions",
8895
8902
  "addon-pages",
8896
8903
  "addon-pages-source",
8897
8904
  "addon-routes",
@@ -8975,6 +8982,7 @@ var CAP_NAMES = Object.freeze([
8975
8982
  "image-settings",
8976
8983
  "integrations",
8977
8984
  "intercom",
8985
+ "ir-transmitter",
8978
8986
  "keychain",
8979
8987
  "keychain-admin",
8980
8988
  "lawn-mower-control",
@@ -9003,6 +9011,7 @@ var CAP_NAMES = Object.freeze([
9003
9011
  "network-link",
9004
9012
  "network-quality",
9005
9013
  "node-certificates",
9014
+ "node-removal-hook",
9006
9015
  "nodes",
9007
9016
  "notification-output",
9008
9017
  "notification-rules",
@@ -9033,6 +9042,7 @@ var CAP_NAMES = Object.freeze([
9033
9042
  "recording-onboard",
9034
9043
  "recording-signal",
9035
9044
  "records",
9045
+ "remote",
9036
9046
  "running-state",
9037
9047
  "scene-monitor",
9038
9048
  "script-runner",
@@ -9056,6 +9066,7 @@ var CAP_NAMES = Object.freeze([
9056
9066
  "system",
9057
9067
  "tamper",
9058
9068
  "temperature-sensor",
9069
+ "template",
9059
9070
  "terminal-session",
9060
9071
  "toast",
9061
9072
  "training",
@@ -9080,11 +9091,11 @@ var CAP_NAMES = Object.freeze([
9080
9091
  Object.freeze({
9081
9092
  "air-quality-sensor": [
9082
9093
  {
9083
- "path": "co2Ppm",
9094
+ "path": "co2",
9084
9095
  "valueType": "number"
9085
9096
  },
9086
9097
  {
9087
- "path": "vocPpb",
9098
+ "path": "voc",
9088
9099
  "valueType": "number"
9089
9100
  },
9090
9101
  {
@@ -9134,7 +9145,7 @@ Object.freeze({
9134
9145
  }
9135
9146
  ],
9136
9147
  "ambient-light-sensor": [{
9137
- "path": "lux",
9148
+ "path": "illuminance",
9138
9149
  "valueType": "number"
9139
9150
  }],
9140
9151
  "audio-metrics": [
@@ -9195,7 +9206,7 @@ Object.freeze({
9195
9206
  ],
9196
9207
  "battery": [
9197
9208
  {
9198
- "path": "percentage",
9209
+ "path": "level",
9199
9210
  "valueType": "number"
9200
9211
  },
9201
9212
  {
@@ -9217,7 +9228,7 @@ Object.freeze({
9217
9228
  "valueType": "number"
9218
9229
  },
9219
9230
  {
9220
- "path": "volts",
9231
+ "path": "voltage",
9221
9232
  "valueType": "number"
9222
9233
  },
9223
9234
  {
@@ -9238,7 +9249,7 @@ Object.freeze({
9238
9249
  "valueType": "boolean"
9239
9250
  }],
9240
9251
  "brightness": [{
9241
- "path": "percentage",
9252
+ "path": "level",
9242
9253
  "valueType": "number"
9243
9254
  }],
9244
9255
  "broker": [{
@@ -9439,7 +9450,7 @@ Object.freeze({
9439
9450
  "valueType": "number"
9440
9451
  },
9441
9452
  {
9442
- "path": "mireds",
9453
+ "path": "colorTemperature",
9443
9454
  "valueType": "number"
9444
9455
  }
9445
9456
  ],
@@ -9618,15 +9629,15 @@ Object.freeze({
9618
9629
  "valueType": "string"
9619
9630
  },
9620
9631
  {
9621
- "path": "remainingSeconds",
9632
+ "path": "remaining",
9622
9633
  "valueType": "number"
9623
9634
  },
9624
9635
  {
9625
- "path": "delayedStartSeconds",
9636
+ "path": "delayedStart",
9626
9637
  "valueType": "number"
9627
9638
  },
9628
9639
  {
9629
- "path": "progressPercent",
9640
+ "path": "progress",
9630
9641
  "valueType": "number"
9631
9642
  },
9632
9643
  {
@@ -9740,7 +9751,7 @@ Object.freeze({
9740
9751
  ],
9741
9752
  "fan-control": [
9742
9753
  {
9743
- "path": "percentage",
9754
+ "path": "speed",
9744
9755
  "valueType": "number"
9745
9756
  },
9746
9757
  {
@@ -9840,7 +9851,7 @@ Object.freeze({
9840
9851
  }
9841
9852
  ],
9842
9853
  "humidity-sensor": [{
9843
- "path": "percent",
9854
+ "path": "humidity",
9844
9855
  "valueType": "number"
9845
9856
  }],
9846
9857
  "image": [{
@@ -9950,7 +9961,7 @@ Object.freeze({
9950
9961
  "valueType": "number"
9951
9962
  },
9952
9963
  {
9953
- "path": "progressPercent",
9964
+ "path": "progress",
9954
9965
  "valueType": "number"
9955
9966
  },
9956
9967
  {
@@ -9996,6 +10007,11 @@ Object.freeze({
9996
10007
  "standby"
9997
10008
  ]
9998
10009
  },
10010
+ {
10011
+ "path": "stateSource",
10012
+ "valueType": "enum",
10013
+ "enumValues": ["reported", "assumed"]
10014
+ },
9999
10015
  {
10000
10016
  "path": "volumeLevel",
10001
10017
  "valueType": "number"
@@ -10029,11 +10045,11 @@ Object.freeze({
10029
10045
  "valueType": "string"
10030
10046
  },
10031
10047
  {
10032
- "path": "positionMs",
10048
+ "path": "position",
10033
10049
  "valueType": "number"
10034
10050
  },
10035
10051
  {
10036
- "path": "durationMs",
10052
+ "path": "duration",
10037
10053
  "valueType": "number"
10038
10054
  },
10039
10055
  {
@@ -10135,11 +10151,11 @@ Object.freeze({
10135
10151
  ]
10136
10152
  },
10137
10153
  {
10138
- "path": "signalPercent",
10154
+ "path": "signal",
10139
10155
  "valueType": "number"
10140
10156
  },
10141
10157
  {
10142
- "path": "rssiDbm",
10158
+ "path": "rssi",
10143
10159
  "valueType": "number"
10144
10160
  },
10145
10161
  {
@@ -10194,6 +10210,18 @@ Object.freeze({
10194
10210
  "path": "foodLevel",
10195
10211
  "valueType": "number"
10196
10212
  },
10213
+ {
10214
+ "path": "bowlFood",
10215
+ "valueType": "number"
10216
+ },
10217
+ {
10218
+ "path": "eating",
10219
+ "valueType": "boolean"
10220
+ },
10221
+ {
10222
+ "path": "supportsMarkRefilled",
10223
+ "valueType": "boolean"
10224
+ },
10197
10225
  {
10198
10226
  "path": "food1",
10199
10227
  "valueType": "number"
@@ -10210,6 +10238,11 @@ Object.freeze({
10210
10238
  "path": "batteryPower",
10211
10239
  "valueType": "number"
10212
10240
  },
10241
+ {
10242
+ "path": "powerSource",
10243
+ "valueType": "enum",
10244
+ "enumValues": ["mains", "battery"]
10245
+ },
10213
10246
  {
10214
10247
  "path": "desiccantLeftDays",
10215
10248
  "valueType": "number"
@@ -10254,27 +10287,39 @@ Object.freeze({
10254
10287
  {
10255
10288
  "path": "volume",
10256
10289
  "valueType": "number"
10290
+ },
10291
+ {
10292
+ "path": "lastFeedGrams",
10293
+ "valueType": "number"
10294
+ },
10295
+ {
10296
+ "path": "lastFeedHopper1",
10297
+ "valueType": "number"
10298
+ },
10299
+ {
10300
+ "path": "lastFeedHopper2",
10301
+ "valueType": "number"
10257
10302
  }
10258
10303
  ],
10259
10304
  "power-meter": [
10260
10305
  {
10261
- "path": "watts",
10306
+ "path": "power",
10262
10307
  "valueType": "number"
10263
10308
  },
10264
10309
  {
10265
- "path": "kwhTotal",
10310
+ "path": "energy",
10266
10311
  "valueType": "number"
10267
10312
  },
10268
10313
  {
10269
- "path": "volts",
10314
+ "path": "voltage",
10270
10315
  "valueType": "number"
10271
10316
  },
10272
10317
  {
10273
- "path": "amps",
10318
+ "path": "current",
10274
10319
  "valueType": "number"
10275
10320
  },
10276
10321
  {
10277
- "path": "va",
10322
+ "path": "apparentPower",
10278
10323
  "valueType": "number"
10279
10324
  },
10280
10325
  {
@@ -10302,14 +10347,10 @@ Object.freeze({
10302
10347
  {
10303
10348
  "path": "gps.accuracyMeters",
10304
10349
  "valueType": "number"
10305
- },
10306
- {
10307
- "path": "batteryPercent",
10308
- "valueType": "number"
10309
10350
  }
10310
10351
  ],
10311
10352
  "pressure-sensor": [{
10312
- "path": "hpa",
10353
+ "path": "pressure",
10313
10354
  "valueType": "number"
10314
10355
  }],
10315
10356
  "privacy-mask": [{
@@ -10623,7 +10664,7 @@ Object.freeze({
10623
10664
  "valueType": "boolean"
10624
10665
  }],
10625
10666
  "temperature-sensor": [{
10626
- "path": "celsius",
10667
+ "path": "temperature",
10627
10668
  "valueType": "number"
10628
10669
  }],
10629
10670
  "update": [
@@ -10671,7 +10712,7 @@ Object.freeze({
10671
10712
  "valueType": "string"
10672
10713
  },
10673
10714
  {
10674
- "path": "progressPercent",
10715
+ "path": "progress",
10675
10716
  "valueType": "number"
10676
10717
  },
10677
10718
  {
@@ -10797,7 +10838,7 @@ object({
10797
10838
  params: record(string(), union([string(), number()])).optional()
10798
10839
  });
10799
10840
  //#endregion
10800
- //#region ../types/dist/err-msg-DIorGYeC.mjs
10841
+ //#region ../types/dist/err-msg-BCdlR2Li.mjs
10801
10842
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10802
10843
  DeviceType["Camera"] = "camera";
10803
10844
  DeviceType["Hub"] = "hub";
@@ -11143,6 +11184,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
11143
11184
  /** Fallback when the notifier service name doesn't match a known
11144
11185
  * pattern. */
11145
11186
  DeviceRole["GenericNotifier"] = "generic-notifier";
11187
+ /** Emits a pulse train (infrared or RF) on request — the `ir-transmitter`
11188
+ * cap. A `DeviceType.Generic` device. */
11189
+ DeviceRole["IrTransmitter"] = "ir-transmitter";
11146
11190
  return DeviceRole;
11147
11191
  }({});
11148
11192
  /**
@@ -11156,7 +11200,7 @@ function errMsg(err) {
11156
11200
  return String(err);
11157
11201
  }
11158
11202
  //#endregion
11159
- //#region ../types/dist/composition-Bb8iwHEJ.mjs
11203
+ //#region ../types/dist/composition-bIBW5_13.mjs
11160
11204
  /**
11161
11205
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11162
11206
  *
@@ -15676,6 +15720,14 @@ var AutomationConditionLeafSchema = object({
15676
15720
  array(union([string(), number()]))
15677
15721
  ])
15678
15722
  });
15723
+ var AutomationConditionStationaryPresentSchema = object({
15724
+ kind: literal("stationary-present"),
15725
+ deviceId: number().int().nonnegative(),
15726
+ /** Class names; a macro class matches every raw class that maps to it. */
15727
+ classes: array(string().min(1)).min(1).optional(),
15728
+ /** The parked object must be in at least one of these zones. */
15729
+ zones: array(string().min(1)).min(1).optional()
15730
+ });
15679
15731
  /**
15680
15732
  * The expression leaf, declared as a plain object rather than an intersection
15681
15733
  * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
@@ -15709,7 +15761,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
15709
15761
  child: AutomationConditionSchema
15710
15762
  }),
15711
15763
  AutomationConditionLeafSchema,
15712
- AutomationConditionExpressionSchema
15764
+ AutomationConditionExpressionSchema,
15765
+ AutomationConditionStationaryPresentSchema
15713
15766
  ]));
15714
15767
  /**
15715
15768
  * What starts a run.
@@ -15750,7 +15803,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15750
15803
  kind: literal("schedule"),
15751
15804
  cron: string().min(1).max(120)
15752
15805
  }),
15753
- object({ kind: literal("manual") })
15806
+ object({ kind: literal("manual") }),
15807
+ object({
15808
+ kind: literal("detection"),
15809
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
15810
+ * matches every raw class that maps to it; any other name matches
15811
+ * exactly. */
15812
+ classes: array(string().min(1)).min(1),
15813
+ deviceIds: array(number().int().nonnegative()).min(1),
15814
+ /** The track must have visited at least one of these zones. */
15815
+ zones: array(string().min(1)).min(1).optional(),
15816
+ /** Best-so-far confidence the track must reach. */
15817
+ minScore: number().min(0).max(1).optional()
15818
+ }),
15819
+ object({
15820
+ kind: literal("stationary"),
15821
+ deviceIds: array(number().int().nonnegative()).min(1),
15822
+ /** Class names; a macro class matches like the detection trigger. */
15823
+ classes: array(string().min(1)).min(1).optional(),
15824
+ /** The parked object must be in at least one of these zones. */
15825
+ zones: array(string().min(1)).min(1).optional(),
15826
+ /** `appeared`: it became parked. `departed`: the registry retired it.
15827
+ * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
15828
+ phase: _enum(["appeared", "departed"])
15829
+ }),
15830
+ object({
15831
+ kind: literal("interval"),
15832
+ minutes: number().int().min(1).max(1440)
15833
+ })
15754
15834
  ]);
15755
15835
  /**
15756
15836
  * One action step.
@@ -15770,28 +15850,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15770
15850
  * is called. The runner still receives a number and is untouched; the
15771
15851
  * resolution is the recipe's job, not the runner's.
15772
15852
  */
15853
+ var AutomationWaitStepSchema = object({
15854
+ kind: literal("wait"),
15855
+ seconds: number().min(0).max(300)
15856
+ });
15857
+ var AutomationCapStepSchema = object({
15858
+ kind: literal("cap"),
15859
+ /**
15860
+ * One device, several same-family devices (locks / climate family / …),
15861
+ * or `{ $var }` for the device that triggered the run. An array is
15862
+ * fan-out at codegen — each id gets the same cap.method call.
15863
+ */
15864
+ deviceId: union([
15865
+ number().int(),
15866
+ array(number().int()).min(1),
15867
+ object({ $var: string().min(1) })
15868
+ ]),
15869
+ cap: string().min(1),
15870
+ method: string().min(1),
15871
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
15872
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
15873
+ * the expression leaf. */
15874
+ args: record(string(), unknown()).optional()
15875
+ });
15876
+ /**
15877
+ * The steps an internal Action may contain: cap and wait, and nothing that can
15878
+ * call back into an Action — so an Action can never reach itself (no cycles).
15879
+ */
15880
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
15773
15881
  var AutomationActionSchema = discriminatedUnion("kind", [
15882
+ AutomationWaitStepSchema,
15883
+ AutomationCapStepSchema,
15774
15884
  object({
15775
- kind: literal("wait"),
15776
- seconds: number().min(0).max(300)
15777
- }),
15778
- object({
15779
- kind: literal("cap"),
15780
- /**
15781
- * One device, several same-family devices (locks / climate family / …),
15782
- * or `{ $var }` for the device that triggered the run. An array is
15783
- * fan-out at codegen — each id gets the same cap.method call.
15784
- */
15785
- deviceId: union([
15786
- number().int(),
15787
- array(number().int()).min(1),
15788
- object({ $var: string().min(1) })
15789
- ]),
15790
- cap: string().min(1),
15791
- method: string().min(1),
15792
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
15793
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
15794
- args: record(string(), unknown()).optional()
15885
+ kind: literal("action"),
15886
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
15887
+ actionId: string().min(1),
15888
+ /** Values for the action's declared variables; the others take their default. */
15889
+ variables: record(string(), union([
15890
+ string(),
15891
+ number(),
15892
+ boolean()
15893
+ ])).optional()
15795
15894
  }),
15796
15895
  object({
15797
15896
  kind: literal("code"),
@@ -15800,10 +15899,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
15800
15899
  code: string().min(1).max(2e4)
15801
15900
  })
15802
15901
  ]);
15902
+ /**
15903
+ * How a trigger that arrives while the automation is busy (or cooling down) is
15904
+ * handled. One of the automation's execution modes, never a property of its
15905
+ * actions. Absent means `single`.
15906
+ *
15907
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
15908
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
15909
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
15910
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
15911
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
15912
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
15913
+ * wait, never the single/queued limits.
15914
+ */
15915
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
15916
+ object({ mode: literal("single") }),
15917
+ object({ mode: literal("restart") }),
15918
+ object({
15919
+ mode: literal("queued"),
15920
+ max: number().int().min(1).max(10)
15921
+ }),
15922
+ object({
15923
+ mode: literal("parallel"),
15924
+ max: number().int().min(1).max(10)
15925
+ }),
15926
+ object({
15927
+ mode: literal("cooldown"),
15928
+ minutes: number().int().min(1).max(1440)
15929
+ })
15930
+ ]);
15803
15931
  var AutomationRecipeSchema = object({
15804
15932
  triggers: array(AutomationTriggerSchema),
15805
15933
  conditions: AutomationConditionSchema.optional(),
15806
- actions: array(AutomationActionSchema)
15934
+ actions: array(AutomationActionSchema),
15935
+ runMode: AutomationRunModeSchema.optional(),
15936
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
15937
+ keepTraces: number().int().min(1).max(100).optional()
15938
+ });
15939
+ new Set([
15940
+ {
15941
+ id: "person",
15942
+ name: "Person"
15943
+ },
15944
+ {
15945
+ id: "vehicle",
15946
+ name: "Vehicle"
15947
+ },
15948
+ {
15949
+ id: "animal",
15950
+ name: "Animal"
15951
+ },
15952
+ {
15953
+ id: "package",
15954
+ name: "Package"
15955
+ }
15956
+ ].map((l) => l.id));
15957
+ var COCO_TO_MACRO = {
15958
+ mapping: {
15959
+ person: "person",
15960
+ bicycle: "vehicle",
15961
+ car: "vehicle",
15962
+ motorcycle: "vehicle",
15963
+ airplane: "vehicle",
15964
+ bus: "vehicle",
15965
+ train: "vehicle",
15966
+ truck: "vehicle",
15967
+ boat: "vehicle",
15968
+ bird: "animal",
15969
+ cat: "animal",
15970
+ dog: "animal",
15971
+ horse: "animal",
15972
+ sheep: "animal",
15973
+ cow: "animal",
15974
+ elephant: "animal",
15975
+ bear: "animal",
15976
+ zebra: "animal",
15977
+ giraffe: "animal",
15978
+ suitcase: "package",
15979
+ backpack: "package",
15980
+ handbag: "package"
15981
+ },
15982
+ preserveOriginal: false
15983
+ };
15984
+ new Set(Object.values(COCO_TO_MACRO.mapping));
15985
+ /**
15986
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
15987
+ * so the editor can paint every block green or red with its reason.
15988
+ *
15989
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
15990
+ * every condition node (containers and leaves, including the ones a
15991
+ * short-circuit never reached), the run-mode verdict and every action step —
15992
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
15993
+ * for an Action run). Skipped
15994
+ * evaluations are traced too, and identical consecutive skips collapse into a
15995
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
15996
+ *
15997
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
15998
+ * vocabulary so an Action run records the same step shape.
15999
+ */
16000
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
16001
+ var TraceValueSchema = union([
16002
+ string(),
16003
+ number(),
16004
+ boolean(),
16005
+ _null(),
16006
+ array(union([string(), number()]))
16007
+ ]);
16008
+ var AutomationRunOutcomeSchema = _enum([
16009
+ "ran",
16010
+ "skipped-trigger",
16011
+ "skipped-conditions",
16012
+ "skipped-cooldown",
16013
+ "skipped-overlap",
16014
+ "failed",
16015
+ "aborted"
16016
+ ]);
16017
+ /** `not-evaluated` = a short-circuit never reached the node. */
16018
+ var TraceNodeOutcomeSchema = _enum([
16019
+ "pass",
16020
+ "fail",
16021
+ "not-evaluated"
16022
+ ]);
16023
+ var AutomationTriggerTraceSchema = object({
16024
+ kind: string().min(1),
16025
+ outcome: _enum(["pass", "fail"]),
16026
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
16027
+ reason: string(),
16028
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
16029
+ vars: record(string(), TraceValueSchema)
16030
+ });
16031
+ var AutomationConditionTraceNodeSchema = object({
16032
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
16033
+ path: string().min(1),
16034
+ kind: _enum([
16035
+ "all",
16036
+ "any",
16037
+ "not",
16038
+ "condition",
16039
+ "expression",
16040
+ "stationary-present"
16041
+ ]),
16042
+ outcome: TraceNodeOutcomeSchema,
16043
+ reason: string(),
16044
+ deviceId: number().int().optional(),
16045
+ cap: string().optional(),
16046
+ fieldPath: string().optional(),
16047
+ operator: string().optional(),
16048
+ expected: TraceValueSchema.optional(),
16049
+ /** What the mirror held; `null` when it held nothing (unknown). */
16050
+ observed: TraceValueSchema.optional()
16051
+ });
16052
+ var AutomationRunVerdictSchema = object({
16053
+ mode: AutomationRunModeSchema,
16054
+ decision: _enum([
16055
+ "run",
16056
+ "queued",
16057
+ "restart",
16058
+ "drop",
16059
+ "cooldown",
16060
+ "bypass"
16061
+ ]),
16062
+ reason: string()
16063
+ });
16064
+ /** One executed (or not-run) step — shared by automations and Action runs. */
16065
+ var AutomationStepTraceSchema = object({
16066
+ /** `actions.<i>` */
16067
+ path: string().min(1),
16068
+ kind: string().min(1),
16069
+ cap: string().optional(),
16070
+ method: string().optional(),
16071
+ deviceId: number().int().optional(),
16072
+ startedAt: number().optional(),
16073
+ endedAt: number().optional(),
16074
+ outcome: _enum([
16075
+ "ok",
16076
+ "error",
16077
+ "not-run"
16078
+ ]),
16079
+ message: string().optional()
16080
+ });
16081
+ var AutomationRunTraceSchema = object({
16082
+ runId: string().min(1),
16083
+ startedAt: number(),
16084
+ endedAt: number(),
16085
+ trigger: AutomationTriggerTraceSchema,
16086
+ conditions: array(AutomationConditionTraceNodeSchema),
16087
+ verdict: AutomationRunVerdictSchema.optional(),
16088
+ steps: array(AutomationStepTraceSchema),
16089
+ outcome: AutomationRunOutcomeSchema,
16090
+ /** How many identical consecutive skips this trace stands for (>= 1). */
16091
+ repeat: number().int().min(1),
16092
+ /** When the latest of those repeats happened. */
16093
+ lastAt: number()
16094
+ });
16095
+ /**
16096
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
16097
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
16098
+ */
16099
+ var AutomationStateDocSchema = object({
16100
+ version: literal(1),
16101
+ lastRunStartedAt: number().nullable(),
16102
+ traces: array(AutomationRunTraceSchema)
16103
+ });
16104
+ /**
16105
+ * Actions — named, runnable things an operator (or an automation) can start,
16106
+ * declared as LIST ENTRIES, never as devices.
16107
+ *
16108
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
16109
+ * hub's `actions` record kind) or declared by an addon through the
16110
+ * `action-source` collection (a Home Assistant script, a scene …). The one
16111
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
16112
+ * catalog of its own (D3) — every `list` asks the providers live.
16113
+ *
16114
+ * This file is the data half: the entry schema, the variables an entry may
16115
+ * declare, and the pure resolution of the values a run was given. It holds no
16116
+ * runtime.
16117
+ */
16118
+ /** What a variable's value is. `select` takes one of `options`. */
16119
+ var ActionVariableTypeSchema = _enum([
16120
+ "string",
16121
+ "number",
16122
+ "boolean",
16123
+ "select"
16124
+ ]);
16125
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
16126
+ var ActionVariableValueSchema = union([
16127
+ string(),
16128
+ number(),
16129
+ boolean()
16130
+ ]);
16131
+ /**
16132
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
16133
+ * value) or `{{vars.name}}` (inside a string) in their args.
16134
+ */
16135
+ var ActionVariableSchema = object({
16136
+ name: string().regex(ACTION_VARIABLE_NAME_RE),
16137
+ label: string().min(1).max(120).optional(),
16138
+ type: ActionVariableTypeSchema,
16139
+ required: boolean().optional(),
16140
+ default: ActionVariableValueSchema.optional(),
16141
+ /** The allowed values of a `select`. */
16142
+ options: array(string().min(1)).min(1).optional()
16143
+ }).superRefine((v, ctx) => {
16144
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
16145
+ code: "custom",
16146
+ message: "a select variable needs options",
16147
+ path: ["options"]
16148
+ });
16149
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
16150
+ code: "custom",
16151
+ message: variableValueError(v, v.default) ?? "invalid default",
16152
+ path: ["default"]
16153
+ });
16154
+ });
16155
+ /** Why `value` is not a legal value for the variable, or null when it is. */
16156
+ function variableValueError(variable, value) {
16157
+ switch (variable.type) {
16158
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
16159
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
16160
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
16161
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
16162
+ }
16163
+ }
16164
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
16165
+ function isInternalActionId(actionId) {
16166
+ return actionId.startsWith("internal:") && actionId.length > 9;
16167
+ }
16168
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
16169
+ function addonIdOfActionId(actionId) {
16170
+ if (isInternalActionId(actionId)) return null;
16171
+ const at = actionId.indexOf(":");
16172
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
16173
+ }
16174
+ var ActionStatusSchema = object({
16175
+ isRunning: boolean(),
16176
+ /** Ms epoch the latest run STARTED; null when it never ran. */
16177
+ lastRunAt: number().nullable(),
16178
+ /** The latest run's failure; null on success or when it never ran. */
16179
+ lastError: string().nullable()
16180
+ });
16181
+ var ActionEntrySchema = object({
16182
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
16183
+ id: string().min(3),
16184
+ name: string().min(1).max(120),
16185
+ /** The addon that declared it; `core-blocks` for an internal one. */
16186
+ addonId: string().min(1),
16187
+ source: ActionSourceKindSchema,
16188
+ /** An addon-declared entry is edited where it was declared. */
16189
+ readOnly: boolean(),
16190
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
16191
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
16192
+ variables: array(ActionVariableSchema).max(20).optional(),
16193
+ status: ActionStatusSchema
16194
+ }).superRefine((entry, ctx) => {
16195
+ if (entry.source === "internal") {
16196
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
16197
+ code: "custom",
16198
+ message: "an internal action id is internal:<uuid>",
16199
+ path: ["id"]
16200
+ });
16201
+ return;
16202
+ }
16203
+ if (entry.steps !== void 0) ctx.addIssue({
16204
+ code: "custom",
16205
+ message: "only an internal action carries steps",
16206
+ path: ["steps"]
16207
+ });
16208
+ if (!entry.readOnly) ctx.addIssue({
16209
+ code: "custom",
16210
+ message: "an addon action is read-only here",
16211
+ path: ["readOnly"]
16212
+ });
16213
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
16214
+ code: "custom",
16215
+ message: "an addon action id is <addonId>:<localId>",
16216
+ path: ["id"]
16217
+ });
16218
+ });
16219
+ /** What an operator writes for an internal action. */
16220
+ var ActionInputSchema = object({
16221
+ name: string().min(1).max(120),
16222
+ steps: array(ActionStepSchema).min(1).max(50),
16223
+ variables: array(ActionVariableSchema).max(20).optional()
16224
+ });
16225
+ /** Who started a run. Absent = the operator pressed it. */
16226
+ var ActionRunOriginSchema = object({
16227
+ /** The automation block whose step started it. */
16228
+ blockId: string().min(1).optional() });
16229
+ /** `started` = the caller did not ask to wait. */
16230
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
16231
+ var ActionRunResultSchema = object({
16232
+ runId: string().min(1),
16233
+ outcome: ActionRunOutcomeSchema,
16234
+ /** Why it did not run, or what failed. */
16235
+ message: string().optional()
16236
+ });
16237
+ _enum([
16238
+ "not-found",
16239
+ "read-only",
16240
+ "invalid-variables",
16241
+ "invalid-steps",
16242
+ "not-linked",
16243
+ "busy"
16244
+ ]);
16245
+ object({
16246
+ actionId: string(),
16247
+ runId: string(),
16248
+ isRunning: boolean(),
16249
+ /** Set when the run ended. */
16250
+ outcome: AutomationRunOutcomeSchema.optional(),
16251
+ lastRunAt: number().nullable(),
16252
+ lastError: string().nullable()
15807
16253
  });
15808
16254
  /**
15809
16255
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -16164,9 +16610,21 @@ var AlertSeveritySchema = _enum([
16164
16610
  "error",
16165
16611
  "critical"
16166
16612
  ]);
16613
+ /**
16614
+ * An action that RUNS something instead of going somewhere: the admin UI calls
16615
+ * the addon's custom action (`addons.custom`) with exactly these fields. The
16616
+ * producer owns the meaning; the UI knows nothing about the addon or the kind.
16617
+ */
16618
+ var AlertActionInvokeSchema = object({
16619
+ addonId: string().min(1),
16620
+ action: string().min(1),
16621
+ input: record(string(), unknown()).optional()
16622
+ });
16623
+ /** An action has `href` (a link) OR `invoke` (a button that runs a custom action). */
16167
16624
  var AlertActionSchema = object({
16168
16625
  label: string(),
16169
16626
  href: string().optional(),
16627
+ invoke: AlertActionInvokeSchema.optional(),
16170
16628
  /**
16171
16629
  * How `href` is followed. Absent / `app`: an admin-ui route (client-side).
16172
16630
  * `server`: a route the HUB serves (an addon HTTP route such as an OAuth
@@ -18539,7 +18997,7 @@ var CompositionEventPlanSchema = object({
18539
18997
  stateField: string(),
18540
18998
  fieldKey: string()
18541
18999
  });
18542
- /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
19000
+ /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
18543
19001
  var CompositionProblemSchema = object({
18544
19002
  code: _enum([
18545
19003
  "reserved-variant",
@@ -18821,12 +19279,13 @@ var CHANGED = {
18821
19279
  expr: "now"
18822
19280
  };
18823
19281
  function numeric(input) {
19282
+ const { alsoFrom, ...base } = input;
18824
19283
  return {
18825
- ...input,
19284
+ ...base,
18826
19285
  family: "numeric",
18827
19286
  deviceType: DeviceType.Sensor,
18828
19287
  memberDeviceType: DeviceType.Sensor,
18829
- memberDeviceTypes: [DeviceType.Sensor],
19288
+ memberDeviceTypes: [DeviceType.Sensor, ...alsoFrom?.deviceTypes ?? []],
18830
19289
  activeWhen: void 0,
18831
19290
  commandCaps: [],
18832
19291
  baseUnit: baseUnitOf(input.quantity),
@@ -18834,9 +19293,10 @@ function numeric(input) {
18834
19293
  accept: [{
18835
19294
  cap: input.outputCap,
18836
19295
  fieldPath: input.outputField
18837
- }]
19296
+ }, ...alsoFrom === void 0 ? [] : [alsoFrom.accept]]
18838
19297
  };
18839
19298
  }
19299
+ var CLIMATE_DEVICES = [DeviceType.Thermostat, DeviceType.Climate];
18840
19300
  function binary(input) {
18841
19301
  return {
18842
19302
  ...input,
@@ -18858,29 +19318,43 @@ var GROUP_TEMPLATES = [
18858
19318
  summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
18859
19319
  role: DeviceRole.TemperatureSensor,
18860
19320
  outputCap: "temperature-sensor",
18861
- outputField: "celsius",
19321
+ outputField: "temperature",
18862
19322
  quantity: "temperature",
18863
19323
  defaultAggregate: "mean",
18864
19324
  empty: "null",
18865
- companions: [NOW$2]
19325
+ companions: [NOW$2],
19326
+ alsoFrom: {
19327
+ deviceTypes: CLIMATE_DEVICES,
19328
+ accept: {
19329
+ cap: "climate-control",
19330
+ fieldPath: "currentTemp"
19331
+ }
19332
+ }
18866
19333
  }),
18867
19334
  numeric({
18868
19335
  id: "humidity",
18869
19336
  summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
18870
19337
  role: DeviceRole.HumiditySensor,
18871
19338
  outputCap: "humidity-sensor",
18872
- outputField: "percent",
19339
+ outputField: "humidity",
18873
19340
  quantity: "humidity",
18874
19341
  defaultAggregate: "mean",
18875
19342
  empty: "null",
18876
- companions: [NOW$2]
19343
+ companions: [NOW$2],
19344
+ alsoFrom: {
19345
+ deviceTypes: CLIMATE_DEVICES,
19346
+ accept: {
19347
+ cap: "climate-control",
19348
+ fieldPath: "currentHumidity"
19349
+ }
19350
+ }
18877
19351
  }),
18878
19352
  numeric({
18879
19353
  id: "pressure",
18880
19354
  summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
18881
19355
  role: DeviceRole.PressureSensor,
18882
19356
  outputCap: "pressure-sensor",
18883
- outputField: "hpa",
19357
+ outputField: "pressure",
18884
19358
  quantity: "pressure",
18885
19359
  defaultAggregate: "mean",
18886
19360
  empty: "null",
@@ -18891,7 +19365,7 @@ var GROUP_TEMPLATES = [
18891
19365
  summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
18892
19366
  role: DeviceRole.AmbientLightSensor,
18893
19367
  outputCap: "ambient-light-sensor",
18894
- outputField: "lux",
19368
+ outputField: "illuminance",
18895
19369
  quantity: "illuminance",
18896
19370
  defaultAggregate: "mean",
18897
19371
  empty: "null",
@@ -18902,7 +19376,7 @@ var GROUP_TEMPLATES = [
18902
19376
  summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
18903
19377
  role: DeviceRole.PowerSensor,
18904
19378
  outputCap: "power-meter",
18905
- outputField: "watts",
19379
+ outputField: "power",
18906
19380
  quantity: "power",
18907
19381
  defaultAggregate: "sum",
18908
19382
  empty: "omit",
@@ -18913,7 +19387,7 @@ var GROUP_TEMPLATES = [
18913
19387
  summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
18914
19388
  role: DeviceRole.EnergySensor,
18915
19389
  outputCap: "power-meter",
18916
- outputField: "kwhTotal",
19390
+ outputField: "energy",
18917
19391
  quantity: "energy",
18918
19392
  defaultAggregate: "sum",
18919
19393
  empty: "omit",
@@ -18924,7 +19398,7 @@ var GROUP_TEMPLATES = [
18924
19398
  summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
18925
19399
  role: DeviceRole.BatterySensor,
18926
19400
  outputCap: "battery",
18927
- outputField: "percentage",
19401
+ outputField: "level",
18928
19402
  quantity: "percentage",
18929
19403
  defaultAggregate: "mean",
18930
19404
  empty: "null",
@@ -19502,7 +19976,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
19502
19976
  kind: "mutation",
19503
19977
  auth: "admin",
19504
19978
  access: "view"
19505
- });
19979
+ }), method(object({
19980
+ blockId: string().min(1),
19981
+ trace: AutomationRunTraceSchema.optional(),
19982
+ runStartedAt: number().optional(),
19983
+ keepTraces: number().int().min(1).max(100).optional()
19984
+ }), _void(), {
19985
+ kind: "mutation",
19986
+ auth: "admin"
19987
+ }), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
19988
+ traces: array(AutomationRunTraceSchema),
19989
+ lastError: string().nullable()
19990
+ }), { auth: "admin" });
19506
19991
  /**
19507
19992
  * `custom-model-registry` — collection cap exposing operator-registered
19508
19993
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -42829,6 +43314,14 @@ method(object({
42829
43314
  kind: "mutation",
42830
43315
  auth: "admin"
42831
43316
  });
43317
+ method(object({
43318
+ nodeId: string().min(1),
43319
+ /** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
43320
+ removedAt: number()
43321
+ }), object({ counts: record(string(), number().int().nonnegative()) }), {
43322
+ kind: "mutation",
43323
+ auth: "admin"
43324
+ });
42832
43325
  /**
42833
43326
  * `device-removal-hook` — the reliable way an addon learns a device was DELETED,
42834
43327
  * so it can delete what it keeps for that device (D865).
@@ -43476,6 +43969,17 @@ var LlmProfileSchema = object({
43476
43969
  * field in its editor only when its descriptor lists it in `profileFields`. */
43477
43970
  topK: number().int().positive().optional(),
43478
43971
  maxTokens: number().int().positive().optional(),
43972
+ /** How hard the model "thinks" before it answers. Omitted = the model's own
43973
+ * default. `off` matters for structured output: a thinking model can spend
43974
+ * the whole completion on reasoning and return an empty `content`. Carried
43975
+ * only by the wires that have it — a kind offers the field in its editor
43976
+ * only when its descriptor lists it in `profileFields`. */
43977
+ reasoningEffort: _enum([
43978
+ "off",
43979
+ "low",
43980
+ "medium",
43981
+ "high"
43982
+ ]).optional(),
43479
43983
  /** Prompt context window. Advisory for cloud kinds (they enforce their own). */
43480
43984
  contextLength: number().int().positive().optional(),
43481
43985
  /** Default system prompt. A caller's `system` REPLACES it (never appends —
@@ -43514,6 +44018,7 @@ var LlmProfileFieldSchema = _enum([
43514
44018
  "topP",
43515
44019
  "topK",
43516
44020
  "maxTokens",
44021
+ "reasoningEffort",
43517
44022
  "contextLength",
43518
44023
  "systemPrompt",
43519
44024
  "timeoutMs",
@@ -44764,6 +45269,43 @@ var NotificationActionSchema = object({
44764
45269
  */
44765
45270
  mode: _enum(["navigate", "background"]).optional()
44766
45271
  });
45272
+ /** A delivery parameter value: what a notifier declared it takes. */
45273
+ var DeliveryParamValueSchema = union([
45274
+ string(),
45275
+ number(),
45276
+ boolean()
45277
+ ]);
45278
+ /**
45279
+ * One extra delivery parameter a notifier KIND declares (D938). A rule that
45280
+ * addresses a target of that kind may set it; a kind that declares nothing is
45281
+ * never handed one. The admin UI derives its control from this descriptor —
45282
+ * no editor knows a service by name.
45283
+ *
45284
+ * Optional fields are `.optional()`, never `.default()` (a Zod default does not
45285
+ * run on the addon cap path).
45286
+ */
45287
+ var DeliveryParamSchema = object({
45288
+ key: string().min(1),
45289
+ label: string(),
45290
+ /** i18n key of the label, resolved by the declaring addon's namespace. */
45291
+ labelKey: string().optional(),
45292
+ type: _enum([
45293
+ "enum",
45294
+ "number",
45295
+ "boolean",
45296
+ "string"
45297
+ ]),
45298
+ /** `enum` only: the closed set of accepted values. */
45299
+ options: array(object({
45300
+ value: string(),
45301
+ label: string()
45302
+ })).optional(),
45303
+ /** `number` only. */
45304
+ min: number().optional(),
45305
+ max: number().optional(),
45306
+ /** What the notifier does when the rule sets nothing; shown as the placeholder. */
45307
+ default: DeliveryParamValueSchema.optional()
45308
+ });
44767
45309
  /**
44768
45310
  * The canonical notification. `body` is the only hard field (Apprise model).
44769
45311
  * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
@@ -44786,7 +45328,13 @@ var NotificationSchema = object({
44786
45328
  tag: string().optional(),
44787
45329
  deviceId: number().optional(),
44788
45330
  eventId: string().optional(),
44789
- metadata: record(string(), unknown()).optional()
45331
+ metadata: record(string(), unknown()).optional(),
45332
+ /**
45333
+ * Values for the parameters the target's kind DECLARES in
45334
+ * {@link TargetKindSchema} `deliveryParams`. Already validated against the
45335
+ * declaration by the sender; a notifier reads only the keys it declared.
45336
+ */
45337
+ delivery: record(string(), DeliveryParamValueSchema).optional()
44790
45338
  });
44791
45339
  /** One declared native severity/priority level for a kind. */
44792
45340
  var TargetKindLevelSchema = object({
@@ -44892,6 +45440,11 @@ var TargetKindSchema = object({
44892
45440
  */
44893
45441
  destinationKeys: array(string()).optional(),
44894
45442
  supportsDiscovery: boolean(),
45443
+ /**
45444
+ * Extra delivery parameters a rule may set for a target of this kind (D938).
45445
+ * Absent = none; the rule editor then offers nothing.
45446
+ */
45447
+ deliveryParams: array(DeliveryParamSchema).optional(),
44895
45448
  caps: TargetKindCapsSchema
44896
45449
  });
44897
45450
  /**
@@ -44991,51 +45544,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
44991
45544
  kind: "mutation",
44992
45545
  auth: "admin"
44993
45546
  });
44994
- new Set([
44995
- {
44996
- id: "person",
44997
- name: "Person"
44998
- },
44999
- {
45000
- id: "vehicle",
45001
- name: "Vehicle"
45002
- },
45003
- {
45004
- id: "animal",
45005
- name: "Animal"
45006
- },
45007
- {
45008
- id: "package",
45009
- name: "Package"
45010
- }
45011
- ].map((l) => l.id));
45012
- var COCO_TO_MACRO = {
45013
- mapping: {
45014
- person: "person",
45015
- bicycle: "vehicle",
45016
- car: "vehicle",
45017
- motorcycle: "vehicle",
45018
- airplane: "vehicle",
45019
- bus: "vehicle",
45020
- train: "vehicle",
45021
- truck: "vehicle",
45022
- boat: "vehicle",
45023
- bird: "animal",
45024
- cat: "animal",
45025
- dog: "animal",
45026
- horse: "animal",
45027
- sheep: "animal",
45028
- cow: "animal",
45029
- elephant: "animal",
45030
- bear: "animal",
45031
- zebra: "animal",
45032
- giraffe: "animal",
45033
- suitcase: "package",
45034
- backpack: "package",
45035
- handbag: "package"
45036
- },
45037
- preserveOriginal: false
45038
- };
45039
45547
  var AUDIO_MACRO_LABELS = [
45040
45548
  {
45041
45549
  id: "speech",
@@ -57228,7 +57736,9 @@ var ManifestShapeSchema = object({
57228
57736
  icon: string().optional()
57229
57737
  }).optional(),
57230
57738
  protected: boolean().optional(),
57231
- removable: boolean().optional()
57739
+ removable: boolean().optional(),
57740
+ /** Declared Integrations-page block (mirror of `AddonDeclaration.integrationSection`). */
57741
+ integrationSection: _enum(["system", "providers"]).optional()
57232
57742
  });
57233
57743
  var AddonProcessSchema = object({
57234
57744
  pid: number().optional(),
@@ -57602,9 +58112,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
57602
58112
  */
57603
58113
  var AirQualitySensorStatusSchema = object({
57604
58114
  /** Carbon dioxide concentration in ppm. */
57605
- co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
58115
+ co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
57606
58116
  /** Total volatile organic compounds in ppb. */
57607
- vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
58117
+ voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
57608
58118
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
57609
58119
  pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
57610
58120
  /** Particulate matter ≤ 10 μm in µg/m³. */
@@ -57637,12 +58147,12 @@ var airQualitySensorCapability = {
57637
58147
  * History (D850): recorded series.
57638
58148
  */
57639
58149
  history: { fields: {
57640
- co2Ppm: {
58150
+ co2: {
57641
58151
  kind: "gauge",
57642
58152
  unit: "ppm",
57643
58153
  deadband: 5
57644
58154
  },
57645
- vocPpb: {
58155
+ voc: {
57646
58156
  kind: "gauge",
57647
58157
  unit: "ppb",
57648
58158
  deadband: 5
@@ -57673,7 +58183,7 @@ var airQualitySensorCapability = {
57673
58183
  */
57674
58184
  var AmbientLightSensorStatusSchema = object({
57675
58185
  /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
57676
- lux: number().min(0).nullable().meta(readingMeta("illuminance", {
58186
+ illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
57677
58187
  precision: 0,
57678
58188
  label: "Illuminance"
57679
58189
  })),
@@ -57702,7 +58212,7 @@ var ambientLightSensorCapability = {
57702
58212
  /**
57703
58213
  * History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
57704
58214
  */
57705
- history: { fields: { lux: {
58215
+ history: { fields: { illuminance: {
57706
58216
  kind: "gauge",
57707
58217
  unit: "lx",
57708
58218
  deadbandPct: 10,
@@ -57841,6 +58351,84 @@ var audioMetricsCapability = {
57841
58351
  */
57842
58352
  history: "none"
57843
58353
  };
58354
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58355
+ actionId: string().min(1),
58356
+ variables: record(string(), ActionVariableValueSchema).optional()
58357
+ }), _void(), {
58358
+ kind: "mutation",
58359
+ auth: "admin"
58360
+ }), method(object({ actionId: string().min(1) }), _void(), {
58361
+ kind: "mutation",
58362
+ auth: "admin"
58363
+ });
58364
+ /**
58365
+ * actions — the ONE surface for runnable actions, for the UI and for
58366
+ * automations. Provided by the core-blocks builtin on the hub.
58367
+ *
58368
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
58369
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
58370
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
58371
+ * the run status from the hub's `runs` ledger; there is no catalog here to
58372
+ * drift (D3).
58373
+ *
58374
+ * **Permissions follow the camera, as linked devices do.** An action is linked
58375
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
58376
+ * non-admin may `get` / `run` an action only through a camera they may see that
58377
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
58378
+ * gate sees it) and the provider refuses an action that camera does not link.
58379
+ * An admin, and an in-process caller (an automation block), may run any action.
58380
+ * Every refusal is typed (`[not-linked] …`) and logged.
58381
+ *
58382
+ * A second `run` of an action that is already running is REFUSED — traced and
58383
+ * logged — never queued.
58384
+ */
58385
+ var ActionIdInput = object({ actionId: string().min(1) });
58386
+ var ActionLinksSchema = object({
58387
+ deviceId: number().int().nonnegative(),
58388
+ actionIds: array(string().min(1)).max(100)
58389
+ });
58390
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58391
+ actionId: string().min(1),
58392
+ deviceId: number().int().nonnegative().optional()
58393
+ }), object({ action: ActionEntrySchema.nullable() }), {
58394
+ role: "viewer",
58395
+ auth: "protected",
58396
+ kind: "query",
58397
+ caller: "required"
58398
+ }), method(object({
58399
+ actionId: string().min(1),
58400
+ variables: record(string(), ActionVariableValueSchema).optional(),
58401
+ wait: boolean().optional(),
58402
+ origin: ActionRunOriginSchema.optional(),
58403
+ deviceId: number().int().nonnegative().optional()
58404
+ }), ActionRunResultSchema, {
58405
+ role: "viewer",
58406
+ auth: "protected",
58407
+ kind: "mutation",
58408
+ caller: "required"
58409
+ }), method(ActionIdInput, _void(), {
58410
+ kind: "mutation",
58411
+ auth: "admin"
58412
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
58413
+ kind: "mutation",
58414
+ auth: "admin"
58415
+ }), method(object({
58416
+ actionId: string().min(1),
58417
+ action: ActionInputSchema.partial()
58418
+ }), object({ action: ActionEntrySchema }), {
58419
+ kind: "mutation",
58420
+ auth: "admin"
58421
+ }), method(ActionIdInput, _void(), {
58422
+ kind: "mutation",
58423
+ auth: "admin"
58424
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
58425
+ role: "viewer",
58426
+ auth: "protected",
58427
+ kind: "query"
58428
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
58429
+ kind: "mutation",
58430
+ auth: "admin"
58431
+ });
57844
58432
  /**
57845
58433
  * Automation-control cap. Models HA `automation.*` entities on
57846
58434
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -57937,7 +58525,7 @@ var BatteryStatusSchema = object({
57937
58525
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
57938
58526
  * declines to band a non-finite reading, which is the correct shape.
57939
58527
  */
57940
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
58528
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
57941
58529
  /**
57942
58530
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
57943
58531
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -57947,7 +58535,7 @@ var BatteryStatusSchema = object({
57947
58535
  * `'unknown'` is the absence of an answer: the provider has not read the
57948
58536
  * power fields yet, or the firmware reported neither. Before it existed,
57949
58537
  * two unreadable fields produced `'none'`, and a camera nobody had reached
57950
- * was drawn "on battery alone" — the same D315 defect `percentage` closed
58538
+ * was drawn "on battery alone" — the same D315 defect `level` closed
57951
58539
  * by going nullable. Consumers SKIP it (no charger state published, no
57952
58540
  * glyph, no HomeKit ChargingState) rather than coerce it either way.
57953
58541
  */
@@ -57971,7 +58559,7 @@ var BatteryStatusSchema = object({
57971
58559
  * that only report charge. A second reading on this cap, not a second
57972
58560
  * `numeric-sensor` on the same device.
57973
58561
  */
57974
- volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
58562
+ voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
57975
58563
  /**
57976
58564
  * Ms epoch of the last time the device PROVED it was reachable — a
57977
58565
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -58005,7 +58593,7 @@ var BatteryStatusSchema = object({
58005
58593
  /**
58006
58594
  * True when the source is a BINARY low-battery indicator (HA
58007
58595
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
58008
- * charge level — `percentage` is then a coarse stand-in (100 = normal,
58596
+ * charge level — `level` is then a coarse stand-in (100 = normal,
58009
58597
  * sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
58010
58598
  * misleading exact percentage. Absent/false → genuine 0–100 % reading.
58011
58599
  */
@@ -58025,15 +58613,15 @@ var BATTERY_DEVICE_TYPES = [
58025
58613
  var batteryCustomize = {
58026
58614
  version: 1,
58027
58615
  targets: [{
58028
- id: "percentage",
58616
+ id: "level",
58029
58617
  cardinality: "scalar",
58030
58618
  vocabulary: {
58031
58619
  cap: "battery",
58032
- stateField: "percentage"
58620
+ stateField: "level"
58033
58621
  },
58034
58622
  deviceTypes: BATTERY_DEVICE_TYPES,
58035
58623
  apply: "add",
58036
- path: "percentage",
58624
+ path: "level",
58037
58625
  sources: [{
58038
58626
  kind: "field",
58039
58627
  valueKinds: ["number", "string"],
@@ -58127,7 +58715,7 @@ onStatusChanged: { data: object({
58127
58715
  schema: BatteryStatusSchema,
58128
58716
  kind: "push",
58129
58717
  empty: {
58130
- percentage: null,
58718
+ level: null,
58131
58719
  charging: "unknown",
58132
58720
  sleeping: false,
58133
58721
  lastUpdated: 0
@@ -58154,11 +58742,11 @@ onStatusChanged: { data: object({
58154
58742
  * History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
58155
58743
  */
58156
58744
  history: { fields: {
58157
- percentage: {
58745
+ level: {
58158
58746
  kind: "gauge",
58159
58747
  unit: "%"
58160
58748
  },
58161
- volts: {
58749
+ voltage: {
58162
58750
  kind: "gauge",
58163
58751
  unit: "V"
58164
58752
  }
@@ -58218,14 +58806,14 @@ var binaryCapability = {
58218
58806
  * controls to this single-method surface.
58219
58807
  *
58220
58808
  * The cap is intentionally minimal: a single `setBrightness({deviceId,
58221
- * percentage})` mutation plus the auto-injected `getStatus`. Drivers
58809
+ * level})` mutation plus the auto-injected `getStatus`. Drivers
58222
58810
  * that expose richer controls (color temperature, scenes, schedules)
58223
58811
  * should surface those via the device's `getSettingsUISchema()`
58224
58812
  * instead of bloating this cap.
58225
58813
  */
58226
58814
  var BrightnessStatusSchema = object({
58227
58815
  /** Current level as 0..100 inclusive. Firmware-reported. */
58228
- percentage: number().min(0).max(100).meta(readingMeta("percentage")),
58816
+ level: number().min(0).max(100).meta(readingMeta("percentage")),
58229
58817
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
58230
58818
  lastChangedAt: number()
58231
58819
  });
@@ -58237,7 +58825,7 @@ var brightnessCapability = {
58237
58825
  deviceTypes: [DeviceType.Light],
58238
58826
  methods: { setBrightness: method(object({
58239
58827
  deviceId: number().int().nonnegative(),
58240
- percentage: number().min(0).max(100)
58828
+ level: number().min(0).max(100)
58241
58829
  }), _void(), {
58242
58830
  kind: "mutation",
58243
58831
  auth: "protected"
@@ -58250,7 +58838,7 @@ var brightnessCapability = {
58250
58838
  */
58251
58839
  onBrightnessChanged: { data: object({
58252
58840
  deviceId: number(),
58253
- percentage: number().min(0).max(100),
58841
+ level: number().min(0).max(100),
58254
58842
  lastChangedAt: number()
58255
58843
  }) } },
58256
58844
  status: {
@@ -58273,17 +58861,379 @@ onBrightnessChanged: { data: object({
58273
58861
  /**
58274
58862
  * History (D850): recorded series. Actuator tier: level keeps every change.
58275
58863
  */
58276
- history: { fields: { percentage: {
58864
+ history: { fields: { level: {
58277
58865
  kind: "gauge",
58278
58866
  unit: "%",
58279
58867
  minIntervalMs: 0
58280
58868
  } } }
58281
58869
  };
58870
+ /**
58871
+ * The role vocabulary of a remote (D963): what a button MEANS, independent of
58872
+ * how it is wired. A layout template places buttons by role; a device binds a
58873
+ * role to an action; an exporter asks for a role by name. Roles are shared so
58874
+ * one layout fits every appliance and one voice command fits every binding.
58875
+ *
58876
+ * A role outside this list is `custom:<key>` — the operator's own button (a
58877
+ * SmartIR source, a channel preset). The key is a lowercase slug.
58878
+ */
58879
+ var REMOTE_KNOWN_ROLES = [
58880
+ "power",
58881
+ "power_on",
58882
+ "power_off",
58883
+ "volume_up",
58884
+ "volume_down",
58885
+ "mute",
58886
+ "channel_up",
58887
+ "channel_down",
58888
+ "up",
58889
+ "down",
58890
+ "left",
58891
+ "right",
58892
+ "ok",
58893
+ "back",
58894
+ "home",
58895
+ "menu",
58896
+ "info",
58897
+ "guide",
58898
+ "exit",
58899
+ "input",
58900
+ "play",
58901
+ "pause",
58902
+ "play_pause",
58903
+ "stop",
58904
+ "next",
58905
+ "previous",
58906
+ "fast_forward",
58907
+ "rewind",
58908
+ "digit_0",
58909
+ "digit_1",
58910
+ "digit_2",
58911
+ "digit_3",
58912
+ "digit_4",
58913
+ "digit_5",
58914
+ "digit_6",
58915
+ "digit_7",
58916
+ "digit_8",
58917
+ "digit_9"
58918
+ ];
58919
+ var CUSTOM_KEY_RE = /^[a-z0-9][a-z0-9_]{0,47}$/;
58920
+ var KNOWN_ROLES = new Set(REMOTE_KNOWN_ROLES);
58921
+ function isRemoteKnownRole(value) {
58922
+ return KNOWN_ROLES.has(value);
58923
+ }
58924
+ function isRemoteCustomKey(key) {
58925
+ return CUSTOM_KEY_RE.test(key);
58926
+ }
58927
+ /** The key of a `custom:<key>` role, or null for any other string. */
58928
+ function customKeyOfRole(role) {
58929
+ if (!role.startsWith("custom:")) return null;
58930
+ const key = role.slice(7);
58931
+ return isRemoteCustomKey(key) ? key : null;
58932
+ }
58933
+ function isRemoteRole(value) {
58934
+ return isRemoteKnownRole(value) || customKeyOfRole(value) !== null;
58935
+ }
58936
+ var RemoteRoleSchema = string().refine(isRemoteRole, { message: "a role is one of the known roles or custom:<key> (lowercase letters, digits, _)" });
58937
+ var RemoteBindingSchema = object({ action: AutomationCapStepSchema.extend({ deviceId: number().int().nonnegative() }) });
58938
+ var RemoteCustomButtonSchema = object({
58939
+ /** The slug after `custom:` in the role. */
58940
+ key: string().refine(isRemoteCustomKey, { message: "a lowercase slug" }),
58941
+ /** What the operator calls it, and what a voice command may name. */
58942
+ name: string().min(1).max(60)
58943
+ });
58944
+ var RemotePowerModeSchema = _enum(["toggle", "discrete"]);
58945
+ var RemoteConfigSchema = object({
58946
+ layoutTemplateId: string().min(1).nullable(),
58947
+ /**
58948
+ * The transmitter device new bindings and an import send through. A binding
58949
+ * names its own device, so one command may override it.
58950
+ */
58951
+ defaultTransmitterId: number().int().nonnegative().nullable().default(null),
58952
+ /** Keyed by role. A role with no entry falls back to the native cap. */
58953
+ bindings: record(string(), RemoteBindingSchema),
58954
+ customButtons: array(RemoteCustomButtonSchema).max(100),
58955
+ power: object({ mode: RemotePowerModeSchema })
58956
+ }).superRefine((config, ctx) => {
58957
+ const roles = Object.keys(config.bindings);
58958
+ if (roles.length > 200) ctx.addIssue({
58959
+ code: "custom",
58960
+ message: "too many bindings",
58961
+ path: ["bindings"]
58962
+ });
58963
+ for (const role of roles) if (!isRemoteRole(role)) ctx.addIssue({
58964
+ code: "custom",
58965
+ message: `"${role}" is not a role`,
58966
+ path: ["bindings", role]
58967
+ });
58968
+ const keys = /* @__PURE__ */ new Set();
58969
+ config.customButtons.forEach((button, index) => {
58970
+ if (keys.has(button.key)) ctx.addIssue({
58971
+ code: "custom",
58972
+ message: `custom button "${button.key}" is listed twice`,
58973
+ path: [
58974
+ "customButtons",
58975
+ index,
58976
+ "key"
58977
+ ]
58978
+ });
58979
+ keys.add(button.key);
58980
+ });
58981
+ for (const role of roles) {
58982
+ const key = customKeyOfRole(role);
58983
+ if (key !== null && !keys.has(key)) ctx.addIssue({
58984
+ code: "custom",
58985
+ message: `${role} is bound but has no entry in customButtons`,
58986
+ path: ["bindings", role]
58987
+ });
58988
+ }
58989
+ });
58990
+ object({
58991
+ power: object({
58992
+ state: _enum(["on", "off"]),
58993
+ at: number()
58994
+ }).nullable(),
58995
+ muted: object({
58996
+ value: boolean(),
58997
+ at: number()
58998
+ }).nullable()
58999
+ });
59000
+ var RemoteLayoutButtonSchema = object({
59001
+ id: string().min(1).max(40),
59002
+ /** 0-based grid position. */
59003
+ row: number().int().min(0).max(24),
59004
+ col: number().int().min(0).max(24),
59005
+ rowSpan: number().int().min(1).max(24),
59006
+ colSpan: number().int().min(1).max(24),
59007
+ icon: string().min(1).max(60).optional(),
59008
+ label: string().min(1).max(40).optional(),
59009
+ role: RemoteRoleSchema
59010
+ });
59011
+ object({
59012
+ columns: number().int().min(1).max(24),
59013
+ rows: number().int().min(1).max(24),
59014
+ buttons: array(RemoteLayoutButtonSchema).max(200)
59015
+ }).superRefine((grid, ctx) => {
59016
+ const ids = /* @__PURE__ */ new Set();
59017
+ const taken = /* @__PURE__ */ new Map();
59018
+ grid.buttons.forEach((button, index) => {
59019
+ if (ids.has(button.id)) ctx.addIssue({
59020
+ code: "custom",
59021
+ message: `button id "${button.id}" is used twice`,
59022
+ path: [
59023
+ "buttons",
59024
+ index,
59025
+ "id"
59026
+ ]
59027
+ });
59028
+ ids.add(button.id);
59029
+ if (button.row + button.rowSpan > grid.rows || button.col + button.colSpan > grid.columns) {
59030
+ ctx.addIssue({
59031
+ code: "custom",
59032
+ message: `button "${button.id}" leaves the grid`,
59033
+ path: ["buttons", index]
59034
+ });
59035
+ return;
59036
+ }
59037
+ for (let r = button.row; r < button.row + button.rowSpan; r += 1) for (let c = button.col; c < button.col + button.colSpan; c += 1) {
59038
+ const cell = `${r}:${c}`;
59039
+ const other = taken.get(cell);
59040
+ if (other !== void 0) {
59041
+ ctx.addIssue({
59042
+ code: "custom",
59043
+ message: `buttons "${other}" and "${button.id}" overlap`,
59044
+ path: ["buttons", index]
59045
+ });
59046
+ return;
59047
+ }
59048
+ taken.set(cell, button.id);
59049
+ }
59050
+ });
59051
+ });
59052
+ var RemoteBoundCommandSchema = object({
59053
+ role: RemoteRoleSchema,
59054
+ /** The operator's name for a custom button; absent for a known role. */
59055
+ label: string().optional(),
59056
+ custom: boolean()
59057
+ });
59058
+ var RemoteCommandListSchema = object({
59059
+ layoutTemplateId: string().nullable(),
59060
+ power: object({ mode: RemotePowerModeSchema }),
59061
+ /** Only the roles that are BOUND — the ones a press can reach. */
59062
+ commands: array(RemoteBoundCommandSchema)
59063
+ });
59064
+ var RemoteRefusalReasonSchema = _enum([
59065
+ "no-remote",
59066
+ "unbound-role",
59067
+ "action-failed"
59068
+ ]);
59069
+ var RemoteSendResultSchema = discriminatedUnion("status", [
59070
+ object({
59071
+ status: literal("sent"),
59072
+ /** How many times the action ran. */
59073
+ sends: number().int().min(1)
59074
+ }),
59075
+ object({
59076
+ status: literal("skipped"),
59077
+ reason: literal("already-in-state")
59078
+ }),
59079
+ object({
59080
+ status: literal("refused"),
59081
+ reason: RemoteRefusalReasonSchema,
59082
+ detail: string().optional()
59083
+ })
59084
+ ]);
59085
+ var RemoteMediaCommandSchema = _enum([
59086
+ "play",
59087
+ "pause",
59088
+ "stop",
59089
+ "next",
59090
+ "previous",
59091
+ "setMute",
59092
+ "selectSource"
59093
+ ]);
59094
+ /** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
59095
+ var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
59096
+ var RemoteSetConfigResultSchema = discriminatedUnion("status", [object({ status: literal("saved") }), object({
59097
+ status: literal("refused"),
59098
+ reason: _enum(["unknown-template", "not-a-media-player"]),
59099
+ detail: string()
59100
+ })]);
59101
+ var deviceInput = object({ deviceId: number().int().nonnegative() });
59102
+ DeviceType.MediaPlayer;
59103
+ method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
59104
+ kind: "mutation",
59105
+ auth: "admin"
59106
+ }), method(deviceInput, RemoteCommandListSchema, { auth: "protected" }), method(deviceInput.extend({
59107
+ role: RemoteRoleSchema,
59108
+ /** Sends in a row (volume steps). */
59109
+ repeat: number().int().min(1).max(50).default(1)
59110
+ }), RemoteSendResultSchema, {
59111
+ kind: "mutation",
59112
+ auth: "protected"
59113
+ }), method(deviceInput.extend({
59114
+ command: RemoteMediaCommandSchema,
59115
+ muted: boolean().optional(),
59116
+ source: string().optional(),
59117
+ repeat: number().int().min(1).max(50).default(1)
59118
+ }), RemoteMediaCommandResultSchema, {
59119
+ kind: "mutation",
59120
+ auth: "protected"
59121
+ });
59122
+ var TemplateKindSchema = _enum(["remote"]);
59123
+ /**
59124
+ * template — the generic store of reusable, named operator data (D963). A
59125
+ * record is `{ id, kind, name, payload }`; the kinds (today only `remote`, the
59126
+ * button grid of a remote) and the schema each payload must satisfy are
59127
+ * declared by the system in `template-kinds.ts`. Nothing here is specific to a
59128
+ * kind: the engine validates a payload against its kind's schema and, on a
59129
+ * delete, asks the kind's owner whether anything still points at the record.
59130
+ *
59131
+ * Every refusal is a typed result — a UI says "unknown kind" or names the
59132
+ * devices that still use a template, without parsing text.
59133
+ */
59134
+ var TemplateRecordSchema = object({
59135
+ id: string().min(1),
59136
+ kind: TemplateKindSchema,
59137
+ name: string().min(1).max(120),
59138
+ /** Shaped by the kind; `checkTemplatePayload(kind, payload)` reads it. */
59139
+ payload: unknown(),
59140
+ createdAt: number(),
59141
+ updatedAt: number()
59142
+ });
59143
+ var TemplateWriteResultSchema = discriminatedUnion("status", [object({
59144
+ status: literal("saved"),
59145
+ template: TemplateRecordSchema
59146
+ }), object({
59147
+ status: literal("refused"),
59148
+ reason: _enum(["invalid-payload", "not-found"]),
59149
+ detail: string()
59150
+ })]);
59151
+ /** A device still pointing at a template. */
59152
+ var TemplateUserSchema = object({
59153
+ deviceId: number().int().nonnegative(),
59154
+ name: string()
59155
+ });
59156
+ var TemplateDeleteResultSchema = discriminatedUnion("status", [
59157
+ object({ status: literal("deleted") }),
59158
+ object({ status: literal("not-found") }),
59159
+ object({
59160
+ status: literal("in-use"),
59161
+ devices: array(TemplateUserSchema).min(1)
59162
+ })
59163
+ ]);
59164
+ method(object({ kind: TemplateKindSchema.optional() }), array(TemplateRecordSchema), { auth: "protected" }), method(object({ id: string().min(1) }), TemplateRecordSchema.nullable(), { auth: "protected" }), method(object({
59165
+ kind: TemplateKindSchema,
59166
+ name: string().min(1).max(120),
59167
+ payload: unknown()
59168
+ }), TemplateWriteResultSchema, {
59169
+ kind: "mutation",
59170
+ auth: "admin"
59171
+ }), method(object({
59172
+ id: string().min(1),
59173
+ name: string().min(1).max(120).optional(),
59174
+ payload: unknown().optional()
59175
+ }), TemplateWriteResultSchema, {
59176
+ kind: "mutation",
59177
+ auth: "admin"
59178
+ }), method(object({ id: string().min(1) }), TemplateDeleteResultSchema, {
59179
+ kind: "mutation",
59180
+ auth: "admin"
59181
+ });
58282
59182
  DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _void(), {
58283
59183
  kind: "mutation",
58284
59184
  auth: "protected"
58285
59185
  });
58286
59186
  /**
59187
+ * A pulse train in signed microseconds: mark (+) first, then space (-), strictly
59188
+ * alternating, no zeros, 2..1024 entries.
59189
+ */
59190
+ var IrTimingsSchema = array(number().int()).min(2).max(1024).superRefine((timings, ctx) => {
59191
+ timings.forEach((value, index) => {
59192
+ const expectMark = index % 2 === 0;
59193
+ if (value === 0 || value > 0 !== expectMark) ctx.addIssue({
59194
+ code: "custom",
59195
+ message: `timing ${index} must be a non-zero ${expectMark ? "mark (positive)" : "space (negative)"}`,
59196
+ path: [index]
59197
+ });
59198
+ });
59199
+ });
59200
+ /** Per-command transmit options. Every key is optional; a transmitter applies what it can. */
59201
+ var TransmitSettingsSchema = object({
59202
+ carrierHz: number().int().positive().optional(),
59203
+ dutyCyclePercent: number().int().min(1).max(100).optional(),
59204
+ repeat: number().int().min(0).max(20).optional(),
59205
+ repeatDelayMs: number().int().min(0).optional()
59206
+ });
59207
+ /** The setting keys a transmitter may declare as settable (`getInfo.settable`). */
59208
+ var TransmitSettingKeySchema = _enum([
59209
+ "carrierHz",
59210
+ "dutyCyclePercent",
59211
+ "repeat",
59212
+ "repeatDelayMs"
59213
+ ]);
59214
+ var IrMediumSchema = _enum(["ir", "rf"]);
59215
+ var IrSendResultSchema = object({
59216
+ ok: literal(true),
59217
+ /** Settings the transmitter could not apply; the train was still sent. */
59218
+ ignoredSettings: array(string())
59219
+ });
59220
+ var IrTransmitterInfoSchema = object({
59221
+ media: array(IrMediumSchema),
59222
+ settable: array(TransmitSettingKeySchema),
59223
+ maxTimings: number().int().positive()
59224
+ });
59225
+ DeviceType.Generic, method(object({
59226
+ deviceId: number().int().nonnegative(),
59227
+ timings: IrTimingsSchema,
59228
+ settings: TransmitSettingsSchema.optional()
59229
+ }), IrSendResultSchema, {
59230
+ kind: "mutation",
59231
+ auth: "protected"
59232
+ }), method(object({ deviceId: number().int().nonnegative() }), IrTransmitterInfoSchema, {
59233
+ kind: "query",
59234
+ auth: "protected"
59235
+ });
59236
+ /**
58287
59237
  * `calendar-source` — collection cap exposing per-provider calendars and
58288
59238
  * their events. Every addon that contributes calendars (Home Assistant,
58289
59239
  * a future Google Calendar addon, …) registers a provider here. The hub
@@ -58870,11 +59820,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
58870
59820
  }),
58871
59821
  object({
58872
59822
  mode: literal("mired"),
58873
- mireds: number().int().min(50).max(1e3)
59823
+ colorTemperature: number().int().min(50).max(1e3)
58874
59824
  })
58875
59825
  ]);
58876
59826
  var ColorStatusSchema = object({
58877
- /** Active color mode — which of `rgb` / `hsv` / `mireds` reflects
59827
+ /** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
58878
59828
  * the bulb's current state. */
58879
59829
  mode: _enum([
58880
59830
  "rgb",
@@ -58886,7 +59836,7 @@ var ColorStatusSchema = object({
58886
59836
  /** Populated when `mode === 'hsv'`. */
58887
59837
  hsv: HsvTripletSchema.optional(),
58888
59838
  /** Populated when `mode === 'mired'`. */
58889
- mireds: number().int().optional().meta(readingMeta("colour-temperature")),
59839
+ colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
58890
59840
  /** Ms epoch of the last operator-driven change. */
58891
59841
  lastChangedAt: number()
58892
59842
  });
@@ -58918,7 +59868,7 @@ onColorChanged: { data: object({
58918
59868
  ]),
58919
59869
  rgb: RgbTripletSchema.optional(),
58920
59870
  hsv: HsvTripletSchema.optional(),
58921
- mireds: number().int().optional(),
59871
+ colorTemperature: number().int().optional(),
58922
59872
  lastChangedAt: number()
58923
59873
  }) } },
58924
59874
  status: {
@@ -59795,11 +60745,11 @@ var DishwasherStatusSchema = object({
59795
60745
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
59796
60746
  availablePrograms: array(string()),
59797
60747
  /** Seconds remaining on the active cycle, or null when not running / not known. */
59798
- remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60748
+ remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59799
60749
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
59800
- delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
60750
+ delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
59801
60751
  /** Cycle completion 0..100, or null when not applicable. */
59802
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60752
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59803
60753
  /** True when the door is reported open; null when the door state is not known. */
59804
60754
  doorOpen: boolean().nullable(),
59805
60755
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -60018,14 +60968,14 @@ var dishwasherCapability = {
60018
60968
  runtimeState: DishwasherStatusSchema,
60019
60969
  durability: "session",
60020
60970
  history: { fields: {
60021
- progressPercent: {
60971
+ progress: {
60022
60972
  kind: "gauge",
60023
60973
  unit: "%",
60024
60974
  deadband: 2
60025
60975
  },
60026
60976
  state: { kind: "enum" },
60027
60977
  operationState: { kind: "enum" },
60028
- remainingSeconds: {
60978
+ remaining: {
60029
60979
  kind: "gauge",
60030
60980
  unit: "s",
60031
60981
  minIntervalMs: 6e4
@@ -60616,7 +61566,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
60616
61566
  var FanControlStatusSchema = object({
60617
61567
  /** Active speed as 0..100 inclusive. Null when the device has no
60618
61568
  * speed surface (single-speed fan). */
60619
- percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61569
+ speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60620
61570
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
60621
61571
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
60622
61572
  percentageStep: number().positive().optional().meta(readingMeta("percentage")),
@@ -60643,7 +61593,7 @@ var fanControlCapability = {
60643
61593
  methods: {
60644
61594
  setPercentage: method(object({
60645
61595
  deviceId: number().int().nonnegative(),
60646
- percentage: number().min(0).max(100)
61596
+ speed: number().min(0).max(100)
60647
61597
  }), _void(), {
60648
61598
  kind: "mutation",
60649
61599
  auth: "protected"
@@ -60676,7 +61626,7 @@ var fanControlCapability = {
60676
61626
  },
60677
61627
  /**
60678
61628
  * Runtime-state slice — mirrored by the kernel. UI fan speed
60679
- * sliders read `percentage` for live updates.
61629
+ * sliders read `speed` for live updates.
60680
61630
  */
60681
61631
  runtimeState: FanControlStatusSchema,
60682
61632
  /**
@@ -60690,7 +61640,7 @@ var fanControlCapability = {
60690
61640
  * History (D850): recorded series. Actuator tier: speed keeps every change.
60691
61641
  */
60692
61642
  history: { fields: {
60693
- percentage: {
61643
+ speed: {
60694
61644
  kind: "gauge",
60695
61645
  unit: "%",
60696
61646
  minIntervalMs: 0
@@ -61135,7 +62085,7 @@ var humidifierCapability = {
61135
62085
  */
61136
62086
  var HumiditySensorStatusSchema = object({
61137
62087
  /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
61138
- percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
62088
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
61139
62089
  precision: 0,
61140
62090
  label: "Humidity"
61141
62091
  })),
@@ -61164,7 +62114,7 @@ var humiditySensorCapability = {
61164
62114
  /**
61165
62115
  * History (D850): recorded series.
61166
62116
  */
61167
- history: { fields: { percent: {
62117
+ history: { fields: { humidity: {
61168
62118
  kind: "gauge",
61169
62119
  unit: "%",
61170
62120
  deadband: .5
@@ -61804,7 +62754,7 @@ var LawnMowerControlStatusSchema = object({
61804
62754
  batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61805
62755
  /** 0..100 mowing-completion percentage of the current task, or null when no
61806
62756
  * task is active / progress is unavailable. */
61807
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62757
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61808
62758
  /** Current device/event code (dynamic — mostly status, sometimes an error),
61809
62759
  * or null when unknown. */
61810
62760
  currentCode: number().nullable().meta(readingMeta("none")),
@@ -62304,7 +63254,7 @@ var lockControlCapability = {
62304
63254
  volatileStateFields: ["lastChangedAt"]
62305
63255
  };
62306
63256
  /**
62307
- * A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
63257
+ * A monotonic total between resets (D944). Energy uses `power-meter.energy`
62308
63258
  * so dashboards keep one series; every other instantaneous reading lands here.
62309
63259
  * `value` is the time integral in the source quantity's accumulated base
62310
63260
  * (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
@@ -62380,9 +63330,35 @@ var MediaInfoSchema = object({
62380
63330
  /** Optional cover-art / thumbnail URL. */
62381
63331
  imageUrl: string().optional()
62382
63332
  });
63333
+ /**
63334
+ * Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
63335
+ * the device said so. `assumed`: the driver has no feedback (infrared) and
63336
+ * tracks what it last SENT; it moves only after a send was accepted, and a
63337
+ * human at the real remote makes it wrong. A UI shows an assumed value as a
63338
+ * guess, never as a reading.
63339
+ */
63340
+ var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
63341
+ /** The fields of a media-player status a driver can only guess. */
63342
+ var MediaPlayerAssumedFieldSchema = _enum([
63343
+ "state",
63344
+ "volumeLevel",
63345
+ "isMuted",
63346
+ "source"
63347
+ ]);
62383
63348
  var MediaPlayerStatusSchema = object({
62384
63349
  /** Playback lifecycle state. */
62385
63350
  state: MediaPlayerStateSchema,
63351
+ /**
63352
+ * Whether the state fields are readings or the driver's own bookkeeping.
63353
+ * A slice written before this field existed parses as `reported`.
63354
+ */
63355
+ stateSource: MediaPlayerStateSourceSchema.default("reported"),
63356
+ /**
63357
+ * Which fields are the driver's own guess. `stateSource` is `assumed` when
63358
+ * this is not empty; a field not listed here is a reading even then (power
63359
+ * from a running-state source, mute guessed from the last send).
63360
+ */
63361
+ assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
62386
63362
  /** Volume as 0..100 inclusive. Null when the device has no volume
62387
63363
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
62388
63364
  volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -62398,10 +63374,10 @@ var MediaPlayerStatusSchema = object({
62398
63374
  currentMedia: MediaInfoSchema.nullable(),
62399
63375
  /** Current playback position in ms. Null when not seekable or
62400
63376
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
62401
- positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
63377
+ position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62402
63378
  /** Total duration of the current media in ms. Null when unknown
62403
63379
  * (live stream) or nothing is playing. */
62404
- durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
63380
+ duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
62405
63381
  /** Shuffle toggle. Null when no shuffle surface.
62406
63382
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
62407
63383
  shuffle: boolean().nullable(),
@@ -62440,7 +63416,7 @@ var mediaPlayerCapability = {
62440
63416
  }),
62441
63417
  seek: method(object({
62442
63418
  deviceId: number().int().nonnegative(),
62443
- positionMs: number().int().nonnegative()
63419
+ position: number().int().nonnegative()
62444
63420
  }), _void(), {
62445
63421
  kind: "mutation",
62446
63422
  auth: "protected"
@@ -62512,7 +63488,7 @@ var mediaPlayerCapability = {
62512
63488
  /**
62513
63489
  * History (D850): volume (a level an operator or an automation sets; the
62514
63490
  * default 10 s floor folds a slider drag into its end value) and mute.
62515
- * `positionMs` is not recorded: it moves continuously while playing.
63491
+ * `position` is not recorded: it moves continuously while playing.
62516
63492
  */
62517
63493
  history: { fields: {
62518
63494
  volumeLevel: {
@@ -63474,9 +64450,9 @@ var NetworkLinkStatusSchema = object({
63474
64450
  * one whose reading has not landed must not be drawn at 0 %. Consumers
63475
64451
  * SKIP a null rather than coerce it.
63476
64452
  */
63477
- signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64453
+ signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63478
64454
  /** Raw received signal strength in dBm, when the firmware reports one. */
63479
- rssiDbm: number().optional().meta(readingMeta("signal-level")),
64455
+ rssi: number().optional().meta(readingMeta("signal-level")),
63480
64456
  /** Network name of a wireless link, when the firmware reports it. */
63481
64457
  ssid: string().optional(),
63482
64458
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
@@ -63512,7 +64488,7 @@ onStatusChanged: { data: object({
63512
64488
  kind: "push",
63513
64489
  empty: {
63514
64490
  type: "unknown",
63515
- signalPercent: null,
64491
+ signal: null,
63516
64492
  lastUpdated: 0
63517
64493
  }
63518
64494
  },
@@ -63535,13 +64511,13 @@ onStatusChanged: { data: object({
63535
64511
  * History (D850): recorded series.
63536
64512
  */
63537
64513
  history: { fields: {
63538
- signalPercent: {
64514
+ signal: {
63539
64515
  kind: "gauge",
63540
64516
  unit: "%",
63541
64517
  deadband: 3,
63542
64518
  minIntervalMs: 6e4
63543
64519
  },
63544
- rssiDbm: {
64520
+ rssi: {
63545
64521
  kind: "gauge",
63546
64522
  unit: "dBm",
63547
64523
  deadband: 2,
@@ -64286,10 +65262,20 @@ var PetFeederDeviceStatusSchema = _enum([
64286
65262
  ]);
64287
65263
  var gramsPortion = number().int().min(4).max(200);
64288
65264
  var PetFeederStatusSchema = object({
64289
- /** Food currently in the bowl (grams). Null when the device has not
64290
- * reported a reading yet. On dual-hopper models this is the combined
64291
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
65265
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
65266
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
65267
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
65268
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
65269
+ * the device has not reported yet. */
64292
65270
  foodLevel: number().nullable().meta(readingMeta("none")),
65271
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
65272
+ * feeder's own units. Null when the model reports none. */
65273
+ bowlFood: number().nullable().meta(readingMeta("none")),
65274
+ /** True while the pet is eating (`state.eating`). */
65275
+ eating: boolean(),
65276
+ /** Whether `markFoodReplenished` does anything on this model. When false
65277
+ * the client must not offer it. */
65278
+ supportsMarkRefilled: boolean(),
64293
65279
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
64294
65280
  * single-hopper models. */
64295
65281
  food1: number().nullable().meta(readingMeta("none")),
@@ -64297,12 +65283,17 @@ var PetFeederStatusSchema = object({
64297
65283
  * single-hopper models. */
64298
65284
  food2: number().nullable().meta(readingMeta("none")),
64299
65285
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
64300
- * (`device_class: problem`, on = low). True when the bowl is empty /
64301
- * below the feeder's low threshold. */
65286
+ * (`device_class: problem`, on = low). True when the tank reports empty,
65287
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
64302
65288
  lowFood: boolean(),
64303
- /** Battery charge 0..100 (%). Null on mains-powered models or when the
64304
- * device has no battery reading. */
65289
+ /** Battery charge 0..100 (%). On a feeder running on `mains` this is the
65290
+ * BACKUP battery's charge, not the power source. Null when the device has
65291
+ * no battery reading. */
64305
65292
  batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
65293
+ /** What the feeder is powered from right now: `mains` (plugged in, the
65294
+ * battery is only a backup) or `battery`. Null when the device does not
65295
+ * report it. */
65296
+ powerSource: _enum(["mains", "battery"]).nullable(),
64306
65297
  /**
64307
65298
  * Desiccant life remaining, stored in seconds (duration base, D944).
64308
65299
  * Providers convert from the days the feeder reports. Null when the
@@ -64330,6 +65321,14 @@ var PetFeederStatusSchema = object({
64330
65321
  feedSound: boolean(),
64331
65322
  /** Speaker / prompt volume level (device-scaled integer). */
64332
65323
  volume: number().meta(readingMeta("none")),
65324
+ /** Portion (grams) of the last manual feed sent from a single-hopper
65325
+ * stepper — the default the next manual feed starts from. Persisted per
65326
+ * device by the provider; null until a manual feed has been sent. */
65327
+ lastFeedGrams: gramsPortion.nullable(),
65328
+ /** Hopper-1 portion of the last manual feed (dual-hopper); null until sent. */
65329
+ lastFeedHopper1: gramsPortion.nullable(),
65330
+ /** Hopper-2 portion of the last manual feed (dual-hopper); null until sent. */
65331
+ lastFeedHopper2: gramsPortion.nullable(),
64333
65332
  /** Ms epoch when the slice was last refreshed from the cloud. */
64334
65333
  lastFetchedAt: number()
64335
65334
  });
@@ -64366,7 +65365,9 @@ var petFeederCapability = {
64366
65365
  kind: "mutation",
64367
65366
  auth: "protected"
64368
65367
  }),
64369
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
65368
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
65369
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
65370
+ * change any sensor reading. */
64370
65371
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
64371
65372
  kind: "mutation",
64372
65373
  auth: "protected"
@@ -64439,6 +65440,7 @@ var petFeederCapability = {
64439
65440
  */
64440
65441
  history: { fields: {
64441
65442
  foodLevel: { kind: "gauge" },
65443
+ bowlFood: { kind: "gauge" },
64442
65444
  food1: { kind: "gauge" },
64443
65445
  food2: { kind: "gauge" },
64444
65446
  batteryPower: {
@@ -64848,16 +65850,16 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
64848
65850
  * in real-world devices.
64849
65851
  */
64850
65852
  var PowerMeterStatusSchema = object({
64851
- /** Instantaneous power draw in watts. */
64852
- watts: number().optional().meta(readingMeta("power", { label: "Power" })),
65853
+ /** Instantaneous power draw (watts). */
65854
+ power: number().optional().meta(readingMeta("power", { label: "Power" })),
64853
65855
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
64854
- kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
64855
- /** Voltage in volts. */
64856
- volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
65856
+ energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
65857
+ /** Voltage (volts). */
65858
+ voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
64857
65859
  /** Current in amperes. */
64858
- amps: number().optional().meta(readingMeta("current", { label: "Current" })),
65860
+ current: number().optional().meta(readingMeta("current", { label: "Current" })),
64859
65861
  /** Apparent power in volt-amperes. */
64860
- va: number().optional().meta(readingMeta("apparent-power")),
65862
+ apparentPower: number().optional().meta(readingMeta("apparent-power")),
64861
65863
  /** Power factor (0..1) if reported. */
64862
65864
  powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
64863
65865
  /** Ms epoch when the slice was last updated. */
@@ -64915,14 +65917,14 @@ function powerTarget(id, unit) {
64915
65917
  advancedExpression: true
64916
65918
  };
64917
65919
  }
64918
- /** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
65920
+ /** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
64919
65921
  var powerMeterCustomize = {
64920
65922
  version: 1,
64921
65923
  targets: [
64922
- powerTarget("watts", "W"),
64923
- powerTarget("kwhTotal", "kWh"),
64924
- powerTarget("volts", "V"),
64925
- powerTarget("amps", "A")
65924
+ powerTarget("power", "W"),
65925
+ powerTarget("energy", "kWh"),
65926
+ powerTarget("voltage", "V"),
65927
+ powerTarget("current", "A")
64926
65928
  ]
64927
65929
  };
64928
65930
  var powerMeterCapability = {
@@ -64938,41 +65940,41 @@ var powerMeterCapability = {
64938
65940
  },
64939
65941
  runtimeState: PowerMeterStatusSchema,
64940
65942
  /**
64941
- * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
65943
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
64942
65944
  *
64943
65945
  * See `RuntimeStateDurability`. Enforced by
64944
65946
  * `scripts/check-runtime-state-durability.ts`.
64945
65947
  */
64946
65948
  durability: "restored",
64947
65949
  /**
64948
- * History (D850): recorded series. Gauges are floored at 30 s; `kwhTotal` is the one monotonic meter (counter).
65950
+ * History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
64949
65951
  */
64950
65952
  history: { fields: {
64951
- watts: {
65953
+ power: {
64952
65954
  kind: "gauge",
64953
65955
  unit: "W",
64954
65956
  deadband: 1,
64955
65957
  minIntervalMs: 3e4
64956
65958
  },
64957
- kwhTotal: {
65959
+ energy: {
64958
65960
  kind: "counter",
64959
65961
  unit: "kWh",
64960
65962
  deadband: .01,
64961
65963
  minIntervalMs: 6e4
64962
65964
  },
64963
- volts: {
65965
+ voltage: {
64964
65966
  kind: "gauge",
64965
65967
  unit: "V",
64966
65968
  deadband: 1,
64967
65969
  minIntervalMs: 3e4
64968
65970
  },
64969
- amps: {
65971
+ current: {
64970
65972
  kind: "gauge",
64971
65973
  unit: "A",
64972
65974
  deadband: .05,
64973
65975
  minIntervalMs: 3e4
64974
65976
  },
64975
- va: {
65977
+ apparentPower: {
64976
65978
  kind: "gauge",
64977
65979
  unit: "VA",
64978
65980
  deadband: 1,
@@ -65022,9 +66024,6 @@ var PresenceStatusSchema = object({
65022
66024
  location: string().nullable(),
65023
66025
  /** GPS coordinates when available. Null otherwise. */
65024
66026
  gps: GpsLocationSchema.nullable(),
65025
- /** Optional battery level of the tracking device (0..100). Null
65026
- * when not reported. */
65027
- batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
65028
66027
  /** Ms epoch when the slice was last updated. */
65029
66028
  lastChangedAt: number()
65030
66029
  });
@@ -65053,14 +66052,7 @@ var presenceCapability = {
65053
66052
  * `scripts/check-runtime-state-durability.ts`.
65054
66053
  */
65055
66054
  durability: "restored",
65056
- /**
65057
- * History (D850): recorded series.
65058
- */
65059
- history: { fields: { batteryPercent: {
65060
- kind: "gauge",
65061
- unit: "%",
65062
- deadband: 1
65063
- } } },
66055
+ history: "none",
65064
66056
  /** Clock fields: written, but excluded from the compare that decides
65065
66057
  * whether persisting is worth a SQLite commit. */
65066
66058
  volatileStateFields: ["lastChangedAt"]
@@ -65075,7 +66067,7 @@ var presenceCapability = {
65075
66067
  */
65076
66068
  var PressureSensorStatusSchema = object({
65077
66069
  /** Current pressure in hPa. `null` when the sample was refused (D393). */
65078
- hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
66070
+ pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
65079
66071
  /** Ms epoch when the slice was last updated. */
65080
66072
  lastFetchedAt: number()
65081
66073
  });
@@ -65101,7 +66093,7 @@ var pressureSensorCapability = {
65101
66093
  /**
65102
66094
  * History (D850): recorded series.
65103
66095
  */
65104
- history: { fields: { hpa: {
66096
+ history: { fields: { pressure: {
65105
66097
  kind: "gauge",
65106
66098
  unit: "hPa",
65107
66099
  deadband: .1
@@ -68800,7 +69792,7 @@ var tamperCapability = {
68800
69792
  */
68801
69793
  var TemperatureSensorStatusSchema = object({
68802
69794
  /** Current temperature in Celsius. `null` when the sample was refused (D393). */
68803
- celsius: number().nullable().meta(readingMeta("temperature", {
69795
+ temperature: number().nullable().meta(readingMeta("temperature", {
68804
69796
  precision: 1,
68805
69797
  label: "Temperature"
68806
69798
  })),
@@ -68829,7 +69821,7 @@ var temperatureSensorCapability = {
68829
69821
  /**
68830
69822
  * History (D850): recorded series.
68831
69823
  */
68832
- history: { fields: { celsius: {
69824
+ history: { fields: { temperature: {
68833
69825
  kind: "gauge",
68834
69826
  unit: "°C",
68835
69827
  deadband: .1
@@ -69126,7 +70118,7 @@ var VacuumControlStatusSchema = object({
69126
70118
  /** Speed tokens the hardware accepts — drives the UI selector. */
69127
70119
  availableFanSpeeds: array(string()),
69128
70120
  /** 0..100 cleaning-completion percentage of the current task, or null. */
69129
- progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
70121
+ progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
69130
70122
  /** Current error code (0 / null = no error). */
69131
70123
  errorCode: number().nullable().meta(readingMeta("none")),
69132
70124
  /** Human label for {@link errorCode}, or null when none / undecodable. */
@@ -70374,6 +71366,14 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
70374
71366
  kind: "mutation",
70375
71367
  auth: "admin"
70376
71368
  });
71369
+ discriminatedUnion("kind", [object({
71370
+ kind: literal("raw"),
71371
+ timings: array(number().int()),
71372
+ settings: TransmitSettingsSchema.optional()
71373
+ }), object({
71374
+ kind: literal("pronto"),
71375
+ code: string()
71376
+ })]);
70377
71377
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
70378
71378
  /**
70379
71379
  * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
@@ -70759,7 +71759,7 @@ function tryConvertUnit(value, from, to) {
70759
71759
  return fromCanonical(toCanonical(value, fromSpec), toSpec);
70760
71760
  }
70761
71761
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
70762
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, 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.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
71762
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, 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.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
70763
71763
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
70764
71764
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
70765
71765
  new Set(CAP_NAMES);
@@ -71163,7 +72163,7 @@ var BaseDevice = class {
71163
72163
  * `device-local-state.ts`. Drivers access via:
71164
72164
  *
71165
72165
  * `this.state.battery.sleeping = true` // patches the battery slice
71166
- * `const pct = this.state.battery.percentage` // reads the battery slice
72166
+ * `const pct = this.state.battery.level` // reads the battery slice
71167
72167
  * `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
71168
72168
  *
71169
72169
  * Adding a new cap with `runtimeState:` automatically extends this
@@ -71203,7 +72203,7 @@ var BaseDevice = class {
71203
72203
  * shape; drivers don't redeclare it, they just write through.
71204
72204
  *
71205
72205
  * Read: `this.runtimeState.getCapState('battery')` →
71206
- * `{percentage, charging, sleeping, lastUpdated}` for any
72206
+ * `{level, charging, sleeping, lastUpdated}` for any
71207
72207
  * provider that registers `batteryCapability`.
71208
72208
  * Write: `this.runtimeState.setCapState('battery', { … })`.
71209
72209
  *
@@ -72377,6 +73377,104 @@ Object.freeze({
72377
73377
  access: "create",
72378
73378
  role: "admin"
72379
73379
  },
73380
+ "actions.create": {
73381
+ capName: "actions",
73382
+ capScope: "system",
73383
+ addonId: null,
73384
+ access: "create",
73385
+ role: "admin"
73386
+ },
73387
+ "actions.delete": {
73388
+ capName: "actions",
73389
+ capScope: "system",
73390
+ addonId: null,
73391
+ access: "delete",
73392
+ role: "admin"
73393
+ },
73394
+ "actions.get": {
73395
+ capName: "actions",
73396
+ capScope: "system",
73397
+ addonId: null,
73398
+ access: "view",
73399
+ role: "viewer"
73400
+ },
73401
+ "actions.getLinks": {
73402
+ capName: "actions",
73403
+ capScope: "system",
73404
+ addonId: null,
73405
+ access: "view",
73406
+ role: "admin"
73407
+ },
73408
+ "actions.getTraces": {
73409
+ capName: "actions",
73410
+ capScope: "system",
73411
+ addonId: null,
73412
+ access: "view",
73413
+ role: "admin"
73414
+ },
73415
+ "actions.list": {
73416
+ capName: "actions",
73417
+ capScope: "system",
73418
+ addonId: null,
73419
+ access: "view",
73420
+ role: "admin"
73421
+ },
73422
+ "actions.listForCamera": {
73423
+ capName: "actions",
73424
+ capScope: "system",
73425
+ addonId: null,
73426
+ access: "view",
73427
+ role: "viewer"
73428
+ },
73429
+ "actions.run": {
73430
+ capName: "actions",
73431
+ capScope: "system",
73432
+ addonId: null,
73433
+ access: "create",
73434
+ role: "viewer"
73435
+ },
73436
+ "actions.setLinks": {
73437
+ capName: "actions",
73438
+ capScope: "system",
73439
+ addonId: null,
73440
+ access: "create",
73441
+ role: "admin"
73442
+ },
73443
+ "actions.stop": {
73444
+ capName: "actions",
73445
+ capScope: "system",
73446
+ addonId: null,
73447
+ access: "create",
73448
+ role: "admin"
73449
+ },
73450
+ "actions.update": {
73451
+ capName: "actions",
73452
+ capScope: "system",
73453
+ addonId: null,
73454
+ access: "create",
73455
+ role: "admin"
73456
+ },
73457
+ "actionSource.listActions": {
73458
+ capName: "action-source",
73459
+ capScope: "system",
73460
+ addonId: null,
73461
+ access: "view",
73462
+ role: "admin"
73463
+ },
73464
+ "actionSource.runAction": {
73465
+ capName: "action-source",
73466
+ capScope: "system",
73467
+ addonId: null,
73468
+ access: "create",
73469
+ role: "admin"
73470
+ },
73471
+ "actionSource.stopAction": {
73472
+ capName: "action-source",
73473
+ capScope: "system",
73474
+ addonId: null,
73475
+ access: "create",
73476
+ role: "admin"
73477
+ },
72380
73478
  "addonPages.listPages": {
72381
73479
  capName: "addon-pages",
72382
73480
  capScope: "system",
@@ -73658,6 +74756,13 @@ Object.freeze({
73658
74756
  access: "view",
73659
74757
  role: "admin"
73660
74758
  },
74759
+ "coreBlocks.getAutomationRunState": {
74760
+ capName: "core-blocks",
74761
+ capScope: "system",
74762
+ addonId: null,
74763
+ access: "view",
74764
+ role: "admin"
74765
+ },
73661
74766
  "coreBlocks.getBlockSettings": {
73662
74767
  capName: "core-blocks",
73663
74768
  capScope: "system",
@@ -73679,6 +74784,13 @@ Object.freeze({
73679
74784
  access: "view",
73680
74785
  role: "admin"
73681
74786
  },
74787
+ "coreBlocks.getTraces": {
74788
+ capName: "core-blocks",
74789
+ capScope: "system",
74790
+ addonId: null,
74791
+ access: "view",
74792
+ role: "admin"
74793
+ },
73682
74794
  "coreBlocks.getTypeDefs": {
73683
74795
  capName: "core-blocks",
73684
74796
  capScope: "system",
@@ -73714,6 +74826,13 @@ Object.freeze({
73714
74826
  access: "create",
73715
74827
  role: "admin"
73716
74828
  },
74829
+ "coreBlocks.reportAutomationRun": {
74830
+ capName: "core-blocks",
74831
+ capScope: "system",
74832
+ addonId: null,
74833
+ access: "create",
74834
+ role: "admin"
74835
+ },
73717
74836
  "coreBlocks.restart": {
73718
74837
  capName: "core-blocks",
73719
74838
  capScope: "system",
@@ -75793,6 +76912,20 @@ Object.freeze({
75793
76912
  access: "create",
75794
76913
  role: "viewer"
75795
76914
  },
76915
+ "irTransmitter.getInfo": {
76916
+ capName: "ir-transmitter",
76917
+ capScope: "device",
76918
+ addonId: null,
76919
+ access: "view",
76920
+ role: "viewer"
76921
+ },
76922
+ "irTransmitter.send": {
76923
+ capName: "ir-transmitter",
76924
+ capScope: "device",
76925
+ addonId: null,
76926
+ access: "create",
76927
+ role: "viewer"
76928
+ },
75796
76929
  "keychain.delete": {
75797
76930
  capName: "keychain",
75798
76931
  capScope: "system",
@@ -77025,6 +78158,13 @@ Object.freeze({
77025
78158
  access: "create",
77026
78159
  role: "admin"
77027
78160
  },
78161
+ "nodeRemovalHook.onNodeRemoved": {
78162
+ capName: "node-removal-hook",
78163
+ capScope: "system",
78164
+ addonId: null,
78165
+ access: "create",
78166
+ role: "admin"
78167
+ },
77028
78168
  "nodes.clusterAddonStatus": {
77029
78169
  capName: "nodes",
77030
78170
  capScope: "system",
@@ -79916,6 +81056,41 @@ Object.freeze({
79916
81056
  access: "create",
79917
81057
  role: "admin"
79918
81058
  },
81059
+ "remote.getConfig": {
81060
+ capName: "remote",
81061
+ capScope: "system",
81062
+ addonId: null,
81063
+ access: "view",
81064
+ role: "admin"
81065
+ },
81066
+ "remote.listCommands": {
81067
+ capName: "remote",
81068
+ capScope: "system",
81069
+ addonId: null,
81070
+ access: "view",
81071
+ role: "admin"
81072
+ },
81073
+ "remote.sendCommand": {
81074
+ capName: "remote",
81075
+ capScope: "system",
81076
+ addonId: null,
81077
+ access: "create",
81078
+ role: "admin"
81079
+ },
81080
+ "remote.sendMediaCommand": {
81081
+ capName: "remote",
81082
+ capScope: "system",
81083
+ addonId: null,
81084
+ access: "create",
81085
+ role: "admin"
81086
+ },
81087
+ "remote.setConfig": {
81088
+ capName: "remote",
81089
+ capScope: "system",
81090
+ addonId: null,
81091
+ access: "create",
81092
+ role: "admin"
81093
+ },
79919
81094
  "runningState.getStatus": {
79920
81095
  capName: "running-state",
79921
81096
  capScope: "device",
@@ -81106,6 +82281,41 @@ Object.freeze({
81106
82281
  access: "view",
81107
82282
  role: "viewer"
81108
82283
  },
82284
+ "template.create": {
82285
+ capName: "template",
82286
+ capScope: "system",
82287
+ addonId: null,
82288
+ access: "create",
82289
+ role: "admin"
82290
+ },
82291
+ "template.delete": {
82292
+ capName: "template",
82293
+ capScope: "system",
82294
+ addonId: null,
82295
+ access: "delete",
82296
+ role: "admin"
82297
+ },
82298
+ "template.get": {
82299
+ capName: "template",
82300
+ capScope: "system",
82301
+ addonId: null,
82302
+ access: "view",
82303
+ role: "admin"
82304
+ },
82305
+ "template.list": {
82306
+ capName: "template",
82307
+ capScope: "system",
82308
+ addonId: null,
82309
+ access: "view",
82310
+ role: "admin"
82311
+ },
82312
+ "template.update": {
82313
+ capName: "template",
82314
+ capScope: "system",
82315
+ addonId: null,
82316
+ access: "create",
82317
+ role: "admin"
82318
+ },
81109
82319
  "terminalSession.adoptLegacyMonitor": {
81110
82320
  capName: "terminal-session",
81111
82321
  capScope: "system",
@@ -82287,7 +83497,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82287
83497
  label: t("Full brightness", "Luminosità massima"),
82288
83498
  phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
82289
83499
  icon: "sun",
82290
- args: { percentage: 100 }
83500
+ args: { level: 100 }
82291
83501
  },
82292
83502
  {
82293
83503
  id: "brightness.half",
@@ -82295,7 +83505,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82295
83505
  label: t("Half brightness", "Luminosità a metà"),
82296
83506
  phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
82297
83507
  icon: "sun-dim",
82298
- args: { percentage: 50 }
83508
+ args: { level: 50 }
82299
83509
  },
82300
83510
  {
82301
83511
  id: "brightness.off",
@@ -82303,16 +83513,16 @@ var ACTUATOR_CAP_VOCABULARY = {
82303
83513
  label: t("Brightness off", "Luminosità a zero"),
82304
83514
  phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
82305
83515
  icon: "lightbulb-off",
82306
- args: { percentage: 0 }
83516
+ args: { level: 0 }
82307
83517
  },
82308
83518
  {
82309
83519
  id: "brightness.set",
82310
83520
  method: "setBrightness",
82311
83521
  label: t("Set brightness", "Imposta la luminosità"),
82312
- phrase: t("Set the brightness of {device} to {percentage}", "Imposta la luminosità di {device} a {percentage}"),
83522
+ phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
82313
83523
  icon: "sun-medium",
82314
83524
  openArgs: [{
82315
- key: "percentage",
83525
+ key: "level",
82316
83526
  label: PERCENT,
82317
83527
  offer: {
82318
83528
  kind: "number",
@@ -82326,13 +83536,13 @@ var ACTUATOR_CAP_VOCABULARY = {
82326
83536
  ],
82327
83537
  state: [{
82328
83538
  kind: "numeric",
82329
- field: "percentage",
83539
+ field: "level",
82330
83540
  quantity: t("the brightness", "la luminosità"),
82331
83541
  label: t("Brightness", "Luminosità"),
82332
83542
  unit: "%",
82333
83543
  icon: "sun"
82334
83544
  }],
82335
- events: { onBrightnessChanged: { notOperatorFacing: "the `percentage` state trigger reads the same change" } }
83545
+ events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
82336
83546
  },
82337
83547
  color: {
82338
83548
  scope: "device",
@@ -82346,7 +83556,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82346
83556
  icon: "sun",
82347
83557
  args: { color: {
82348
83558
  mode: "mired",
82349
- mireds: 370
83559
+ colorTemperature: 370
82350
83560
  } }
82351
83561
  },
82352
83562
  {
@@ -82357,7 +83567,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82357
83567
  icon: "snowflake",
82358
83568
  args: { color: {
82359
83569
  mode: "mired",
82360
- mireds: 200
83570
+ colorTemperature: 200
82361
83571
  } }
82362
83572
  },
82363
83573
  {
@@ -82971,7 +84181,7 @@ var ACTUATOR_CAP_VOCABULARY = {
82971
84181
  label: t("Fan off", "Ventilatore spento"),
82972
84182
  phrase: t("Stop {device}", "Ferma {device}"),
82973
84183
  icon: "power-off",
82974
- args: { percentage: 0 }
84184
+ args: { speed: 0 }
82975
84185
  },
82976
84186
  {
82977
84187
  id: "fan.full",
@@ -82979,16 +84189,16 @@ var ACTUATOR_CAP_VOCABULARY = {
82979
84189
  label: t("Fan full", "Ventilatore al massimo"),
82980
84190
  phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
82981
84191
  icon: "fan",
82982
- args: { percentage: 100 }
84192
+ args: { speed: 100 }
82983
84193
  },
82984
84194
  {
82985
84195
  id: "fan.speed",
82986
84196
  method: "setPercentage",
82987
84197
  label: t("Set speed", "Imposta la velocità"),
82988
- phrase: t("Set the speed of {device} to {percentage}", "Imposta la velocità di {device} al {percentage}"),
84198
+ phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
82989
84199
  icon: "fan",
82990
84200
  openArgs: [{
82991
- key: "percentage",
84201
+ key: "speed",
82992
84202
  label: t("Speed", "Velocità"),
82993
84203
  offer: {
82994
84204
  kind: "number",
@@ -83050,7 +84260,7 @@ var ACTUATOR_CAP_VOCABULARY = {
83050
84260
  ],
83051
84261
  state: [{
83052
84262
  kind: "numeric",
83053
- field: "percentage",
84263
+ field: "speed",
83054
84264
  quantity: t("the speed", "la velocità"),
83055
84265
  label: t("Speed", "Velocità"),
83056
84266
  unit: "%",
@@ -83416,6 +84626,11 @@ var ACTUATOR_CAP_VOCABULARY = {
83416
84626
  icon: "circle-dot"
83417
84627
  }]
83418
84628
  },
84629
+ "ir-transmitter": {
84630
+ scope: "device",
84631
+ noun: t("IR / RF transmitter", "Trasmettitore IR / RF"),
84632
+ notOperatorFacing: { send: "a raw pulse train is not something an operator composes in an automation; the remote that owns the command table sends it" }
84633
+ },
83419
84634
  control: {
83420
84635
  scope: "device",
83421
84636
  noun: t("Control", "Controllo"),
@@ -83848,7 +85063,7 @@ var APPLIANCE_CAP_VOCABULARY = {
83848
85063
  },
83849
85064
  {
83850
85065
  kind: "numeric",
83851
- field: "progressPercent",
85066
+ field: "progress",
83852
85067
  quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
83853
85068
  label: t("Progress", "Avanzamento"),
83854
85069
  unit: "%",
@@ -84206,10 +85421,10 @@ var APPLIANCE_CAP_VOCABULARY = {
84206
85421
  id: "media.seek",
84207
85422
  method: "seek",
84208
85423
  label: t("Seek", "Vai al punto"),
84209
- phrase: t("Move {device} to {positionMs}", "Porta {device} al punto {positionMs}"),
85424
+ phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
84210
85425
  icon: "fast-forward",
84211
85426
  openArgs: [{
84212
- key: "positionMs",
85427
+ key: "position",
84213
85428
  label: t("Position", "Posizione"),
84214
85429
  offer: {
84215
85430
  kind: "number",
@@ -85258,7 +86473,7 @@ var SENSOR_CAP_VOCABULARY = {
85258
86473
  state: [
85259
86474
  {
85260
86475
  kind: "numeric",
85261
- field: "co2Ppm",
86476
+ field: "co2",
85262
86477
  quantity: t("the CO₂", "la CO₂"),
85263
86478
  label: t("CO₂", "CO₂"),
85264
86479
  unit: "ppm",
@@ -85266,7 +86481,7 @@ var SENSOR_CAP_VOCABULARY = {
85266
86481
  },
85267
86482
  {
85268
86483
  kind: "numeric",
85269
- field: "vocPpb",
86484
+ field: "voc",
85270
86485
  quantity: t("the VOC level", "il livello di COV"),
85271
86486
  label: t("VOC", "COV"),
85272
86487
  unit: "ppb",
@@ -85302,7 +86517,7 @@ var SENSOR_CAP_VOCABULARY = {
85302
86517
  noun: t("Light level", "Luminosità ambiente"),
85303
86518
  state: [{
85304
86519
  kind: "numeric",
85305
- field: "lux",
86520
+ field: "illuminance",
85306
86521
  quantity: t("the light level", "la luminosità"),
85307
86522
  label: t("Illuminance", "Illuminamento"),
85308
86523
  unit: "lx",
@@ -85505,7 +86720,7 @@ var SENSOR_CAP_VOCABULARY = {
85505
86720
  noun: t("Humidity", "Umidità"),
85506
86721
  state: [{
85507
86722
  kind: "numeric",
85508
- field: "percent",
86723
+ field: "humidity",
85509
86724
  quantity: t("the humidity", "l’umidità"),
85510
86725
  label: t("Humidity", "Umidità"),
85511
86726
  unit: "%",
@@ -85553,7 +86768,7 @@ var SENSOR_CAP_VOCABULARY = {
85553
86768
  state: [
85554
86769
  {
85555
86770
  kind: "numeric",
85556
- field: "watts",
86771
+ field: "power",
85557
86772
  quantity: t("the power draw", "il consumo"),
85558
86773
  label: t("Power", "Potenza"),
85559
86774
  unit: "W",
@@ -85561,7 +86776,7 @@ var SENSOR_CAP_VOCABULARY = {
85561
86776
  },
85562
86777
  {
85563
86778
  kind: "numeric",
85564
- field: "kwhTotal",
86779
+ field: "energy",
85565
86780
  quantity: t("the energy used", "l’energia consumata"),
85566
86781
  label: t("Energy", "Energia"),
85567
86782
  unit: "kWh",
@@ -85569,7 +86784,7 @@ var SENSOR_CAP_VOCABULARY = {
85569
86784
  },
85570
86785
  {
85571
86786
  kind: "numeric",
85572
- field: "volts",
86787
+ field: "voltage",
85573
86788
  quantity: t("the voltage", "la tensione"),
85574
86789
  label: t("Voltage", "Tensione"),
85575
86790
  unit: "V",
@@ -85577,7 +86792,7 @@ var SENSOR_CAP_VOCABULARY = {
85577
86792
  },
85578
86793
  {
85579
86794
  kind: "numeric",
85580
- field: "amps",
86795
+ field: "current",
85581
86796
  quantity: t("the current", "la corrente"),
85582
86797
  label: t("Current", "Corrente"),
85583
86798
  unit: "A",
@@ -85594,13 +86809,6 @@ var SENSOR_CAP_VOCABULARY = {
85594
86809
  label: t("Where", "Posizione"),
85595
86810
  phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
85596
86811
  icon: "map-pin"
85597
- }, {
85598
- kind: "numeric",
85599
- field: "batteryPercent",
85600
- quantity: t("the phone battery", "la batteria del telefono"),
85601
- label: t("Battery", "Batteria"),
85602
- unit: "%",
85603
- icon: "battery"
85604
86812
  }]
85605
86813
  },
85606
86814
  "pressure-sensor": {
@@ -85608,7 +86816,7 @@ var SENSOR_CAP_VOCABULARY = {
85608
86816
  noun: t("Pressure", "Pressione"),
85609
86817
  state: [{
85610
86818
  kind: "numeric",
85611
- field: "hpa",
86819
+ field: "pressure",
85612
86820
  quantity: t("the pressure", "la pressione"),
85613
86821
  label: t("Pressure", "Pressione"),
85614
86822
  unit: "hPa",
@@ -85644,7 +86852,7 @@ var SENSOR_CAP_VOCABULARY = {
85644
86852
  noun: t("Temperature", "Temperatura"),
85645
86853
  state: [{
85646
86854
  kind: "numeric",
85647
- field: "celsius",
86855
+ field: "temperature",
85648
86856
  quantity: t("the temperature", "la temperatura"),
85649
86857
  label: t("Temperature", "Temperatura"),
85650
86858
  unit: "°C",
@@ -85715,7 +86923,7 @@ var SENSOR_CAP_VOCABULARY = {
85715
86923
  state: [
85716
86924
  {
85717
86925
  kind: "numeric",
85718
- field: "percentage",
86926
+ field: "level",
85719
86927
  quantity: t("the battery level", "il livello della batteria"),
85720
86928
  label: t("Battery", "Batteria"),
85721
86929
  unit: "%",
@@ -85757,7 +86965,7 @@ var SENSOR_CAP_VOCABULARY = {
85757
86965
  icon: "battery-charging"
85758
86966
  }
85759
86967
  ],
85760
- events: { onStatusChanged: { notOperatorFacing: "the percentage / sleeping / charging state triggers each read one field of this change" } },
86968
+ events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
85761
86969
  extraEvents: [{
85762
86970
  category: EventCategory.DeviceAwake,
85763
86971
  label: t("Woke up", "Risvegliato"),
@@ -85775,7 +86983,7 @@ var SENSOR_CAP_VOCABULARY = {
85775
86983
  noun: t("Network link", "Collegamento di rete"),
85776
86984
  state: [{
85777
86985
  kind: "numeric",
85778
- field: "signalPercent",
86986
+ field: "signal",
85779
86987
  quantity: t("the signal strength", "l’intensità del segnale"),
85780
86988
  label: t("Signal", "Segnale"),
85781
86989
  unit: "%",
@@ -85868,6 +87076,8 @@ var ML = "model and training machinery — no device action";
85868
87076
  var INTERNAL = "internal plumbing between addons — never operator-facing";
85869
87077
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
85870
87078
  var SYSTEM_CAP_VOCABULARY = {
87079
+ "action-source": system(INTERNAL),
87080
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
85871
87081
  "addon-pages": system(UI_PLUMBING),
85872
87082
  "addon-pages-source": system(UI_PLUMBING),
85873
87083
  "addon-routes": system(UI_PLUMBING),
@@ -85898,6 +87108,7 @@ var SYSTEM_CAP_VOCABULARY = {
85898
87108
  "device-manager": system("the device registry itself — rename, move and remove are administration"),
85899
87109
  "device-provider": system(INTERNAL),
85900
87110
  "device-removal-hook": system(INTERNAL),
87111
+ "node-removal-hook": system(INTERNAL),
85901
87112
  "device-state": system("the runtime-state mirror every trigger already reads through"),
85902
87113
  "display-palette": system("the cluster display palette — one colour table for every client, not a device action"),
85903
87114
  "embedding-encoder": system(ML),
@@ -85937,6 +87148,7 @@ var SYSTEM_CAP_VOCABULARY = {
85937
87148
  "recording-archive": system(STORAGE),
85938
87149
  "recording-export": system("recording exports — requested from the timeline, not an automation step"),
85939
87150
  records: system(INTERNAL),
87151
+ remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
85940
87152
  "server-management": system(CLUSTER),
85941
87153
  "settings-store": system(INTERNAL),
85942
87154
  "smtp-provider": system(INTERNAL),
@@ -85949,6 +87161,7 @@ var SYSTEM_CAP_VOCABULARY = {
85949
87161
  "storage-provider": system(STORAGE),
85950
87162
  "stream-broker": system(PIPELINE),
85951
87163
  system: system(CLUSTER),
87164
+ template: system("operator-authored templates — edited in their own editor, never actuated"),
85952
87165
  "terminal-session": system(CLUSTER),
85953
87166
  toast: system(UI_PLUMBING),
85954
87167
  training: system(ML),
@@ -86064,6 +87277,31 @@ Object.freeze({
86064
87277
  form: "single",
86065
87278
  optional: false
86066
87279
  }],
87280
+ "actions.get": [{
87281
+ name: "deviceId",
87282
+ form: "single",
87283
+ optional: true
87284
+ }],
87285
+ "actions.getLinks": [{
87286
+ name: "deviceId",
87287
+ form: "single",
87288
+ optional: false
87289
+ }],
87290
+ "actions.listForCamera": [{
87291
+ name: "deviceId",
87292
+ form: "single",
87293
+ optional: false
87294
+ }],
87295
+ "actions.run": [{
87296
+ name: "deviceId",
87297
+ form: "single",
87298
+ optional: true
87299
+ }],
87300
+ "actions.setLinks": [{
87301
+ name: "deviceId",
87302
+ form: "single",
87303
+ optional: false
87304
+ }],
86067
87305
  "addonSettings.getDeviceSettings": [{
86068
87306
  name: "deviceId",
86069
87307
  form: "single",
@@ -87115,6 +88353,16 @@ Object.freeze({
87115
88353
  form: "single",
87116
88354
  optional: false
87117
88355
  }],
88356
+ "irTransmitter.getInfo": [{
88357
+ name: "deviceId",
88358
+ form: "single",
88359
+ optional: false
88360
+ }],
88361
+ "irTransmitter.send": [{
88362
+ name: "deviceId",
88363
+ form: "single",
88364
+ optional: false
88365
+ }],
87118
88366
  "keychain.deleteDeviceScope": [{
87119
88367
  name: "deviceId",
87120
88368
  form: "single",
@@ -88340,6 +89588,31 @@ Object.freeze({
88340
89588
  form: "single",
88341
89589
  optional: true
88342
89590
  }],
89591
+ "remote.getConfig": [{
89592
+ name: "deviceId",
89593
+ form: "single",
89594
+ optional: false
89595
+ }],
89596
+ "remote.listCommands": [{
89597
+ name: "deviceId",
89598
+ form: "single",
89599
+ optional: false
89600
+ }],
89601
+ "remote.sendCommand": [{
89602
+ name: "deviceId",
89603
+ form: "single",
89604
+ optional: false
89605
+ }],
89606
+ "remote.sendMediaCommand": [{
89607
+ name: "deviceId",
89608
+ form: "single",
89609
+ optional: false
89610
+ }],
89611
+ "remote.setConfig": [{
89612
+ name: "deviceId",
89613
+ form: "single",
89614
+ optional: false
89615
+ }],
88343
89616
  "runningState.getStatus": [{
88344
89617
  name: "deviceId",
88345
89618
  form: "single",
@@ -90287,8 +91560,17 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
90287
91560
  }
90288
91561
  return best;
90289
91562
  }
91563
+ String.raw`(?<=^|["'(\s>,\[{])`;
90290
91564
  var MB = 1024 * 1024;
90291
91565
  1024 * MB, 3072 * MB;
91566
+ object({
91567
+ manufacturer: string().optional(),
91568
+ supportedModels: array(string()).optional(),
91569
+ supportedController: string().optional(),
91570
+ commandsEncoding: string(),
91571
+ delay: number().nonnegative().optional(),
91572
+ commands: record(string(), unknown())
91573
+ });
90292
91574
  //#endregion
90293
91575
  //#region src/callback-host.ts
90294
91576
  /**
@@ -115052,6 +116334,14 @@ var HmChildDevice = class extends BaseDevice {
115052
116334
  this.valueChangedUnsub = facade.on("valueChanged", (event) => {
115053
116335
  if (event.device !== this.address) return;
115054
116336
  if (!this.ownsEventChannel(event)) return;
116337
+ this.ctx.logger.debug("Homematic value change accepted", {
116338
+ tags: { deviceId: this.ctx.id },
116339
+ meta: {
116340
+ address: this.address,
116341
+ channel: event.channel,
116342
+ parameter: event.parameter
116343
+ }
116344
+ });
115055
116345
  handle(event, "live");
115056
116346
  });
115057
116347
  this.seedTriggerUnsubs = [facade.on("ready", () => this.seedCurrentValues()), facade.on("connection", () => this.seedCurrentValues())];
@@ -115126,13 +116416,33 @@ var HmChildDevice = class extends BaseDevice {
115126
116416
  if (status.includes(event.channel)) return true;
115127
116417
  return this.resolveHmDevice()?.channels.some((c) => status.includes(c.address) && (c.dataPoints ?? []).some((d) => d.parameter === event.parameter)) !== true;
115128
116418
  }
116419
+ /** A value change the filter refused — named, per reason, on the record. */
116420
+ logValueDropped(event, reason) {
116421
+ this.ctx.logger.debug("Homematic value change dropped", {
116422
+ tags: { deviceId: this.ctx.id },
116423
+ meta: {
116424
+ address: this.address,
116425
+ ownedChannel: this.channel,
116426
+ channel: event.channel,
116427
+ parameter: event.parameter,
116428
+ reason
116429
+ }
116430
+ });
116431
+ }
115129
116432
  ownsEventChannel(event) {
115130
116433
  const owned = this.channel;
115131
116434
  if (owned === void 0) return true;
115132
116435
  const channel = event.channel;
115133
116436
  if (typeof channel === "string" && channel.length > 0) {
115134
- if (!this.ownedChannels().includes(channel)) return false;
115135
- return this.isPreferredSource(event);
116437
+ if (!this.ownedChannels().includes(channel)) {
116438
+ this.logValueDropped(event, "foreign-channel");
116439
+ return false;
116440
+ }
116441
+ if (!this.isPreferredSource(event)) {
116442
+ this.logValueDropped(event, "not-status-source");
116443
+ return false;
116444
+ }
116445
+ return true;
115136
116446
  }
115137
116447
  this.ctx.logger.warn("Homematic value event without a channel — dropped", {
115138
116448
  tags: { deviceId: this.ctx.id },
@@ -115639,15 +116949,15 @@ var HmActuatorDevice = class extends HmChildDevice {
115639
116949
  * keep that class a thin actuator-wiring layer.
115640
116950
  */
115641
116951
  var COLD_TEMPERATURE = {
115642
- celsius: 0,
116952
+ temperature: 0,
115643
116953
  lastFetchedAt: 0
115644
116954
  };
115645
116955
  var COLD_HUMIDITY = {
115646
- percent: 0,
116956
+ humidity: 0,
115647
116957
  lastFetchedAt: 0
115648
116958
  };
115649
116959
  var COLD_AMBIENT_LIGHT = {
115650
- lux: 0,
116960
+ illuminance: 0,
115651
116961
  lastFetchedAt: 0
115652
116962
  };
115653
116963
  var COLD_POWER = { lastFetchedAt: 0 };
@@ -115739,15 +117049,15 @@ var HmSensorDevice = class extends HmChildDevice {
115739
117049
  decodeSensorPush(parameter, value, now) {
115740
117050
  if (typeof value !== "number") return;
115741
117051
  if (this.caps.has("temperature-sensor") && parameter === "ACTUAL_TEMPERATURE") this.runtimeState.setCapState("temperature-sensor", {
115742
- celsius: value,
117052
+ temperature: value,
115743
117053
  lastFetchedAt: now
115744
117054
  });
115745
117055
  if (this.caps.has("humidity-sensor") && parameter === "HUMIDITY") this.runtimeState.setCapState("humidity-sensor", {
115746
- percent: value,
117056
+ humidity: value,
115747
117057
  lastFetchedAt: now
115748
117058
  });
115749
117059
  if (this.caps.has("ambient-light-sensor") && parameter === "ILLUMINATION") this.runtimeState.setCapState("ambient-light-sensor", {
115750
- lux: Math.max(0, value),
117060
+ illuminance: Math.max(0, value),
115751
117061
  lastFetchedAt: now
115752
117062
  });
115753
117063
  this.decodePowerPush(parameter, value, now);
@@ -115760,28 +117070,28 @@ var HmSensorDevice = class extends HmChildDevice {
115760
117070
  case "POWER":
115761
117071
  this.runtimeState.setCapState("power-meter", {
115762
117072
  ...prev,
115763
- watts: value,
117073
+ power: value,
115764
117074
  lastFetchedAt: now
115765
117075
  });
115766
117076
  return;
115767
117077
  case "ENERGY_COUNTER":
115768
117078
  this.runtimeState.setCapState("power-meter", {
115769
117079
  ...prev,
115770
- kwhTotal: value / 1e3,
117080
+ energy: value / 1e3,
115771
117081
  lastFetchedAt: now
115772
117082
  });
115773
117083
  return;
115774
117084
  case "VOLTAGE":
115775
117085
  this.runtimeState.setCapState("power-meter", {
115776
117086
  ...prev,
115777
- volts: value,
117087
+ voltage: value,
115778
117088
  lastFetchedAt: now
115779
117089
  });
115780
117090
  return;
115781
117091
  case "CURRENT":
115782
117092
  this.runtimeState.setCapState("power-meter", {
115783
117093
  ...prev,
115784
- amps: value / 1e3,
117094
+ current: value / 1e3,
115785
117095
  lastFetchedAt: now
115786
117096
  });
115787
117097
  return;
@@ -115879,7 +117189,7 @@ var COLD_BINARY = {
115879
117189
  lastChangedAt: 0
115880
117190
  };
115881
117191
  var COLD_TEMP = {
115882
- celsius: 0,
117192
+ temperature: 0,
115883
117193
  lastFetchedAt: 0
115884
117194
  };
115885
117195
  /**
@@ -115943,7 +117253,7 @@ var HmDiagnosticDevice = class extends HmChildDevice {
115943
117253
  if (stored === null) return;
115944
117254
  if (this.capName === "temperature-sensor") {
115945
117255
  this.runtimeState.setCapState("temperature-sensor", {
115946
- celsius: stored,
117256
+ temperature: stored,
115947
117257
  lastFetchedAt: now
115948
117258
  });
115949
117259
  return;