@camstack/addon-training 0.1.108 → 0.1.110

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.
Files changed (22) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{MotionZonesSettings-9m904uxm.mjs → MotionZonesSettings-D4s4e-dn.mjs} +2 -2
  3. package/dist/{PrivacyMaskSettings-DDk1oMby.mjs → PrivacyMaskSettings-BtdwrmHQ.mjs} +2 -2
  4. package/dist/{SceneMonitorEditor-Dsq9Dzqm.mjs → SceneMonitorEditor-DOX6WQaZ.mjs} +2 -2
  5. package/dist/_stub.js +1399 -1365
  6. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-Bz804aB6.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-C3SgLMLF.mjs} +3 -3
  7. package/dist/_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CmVZHQ-k.mjs +26 -0
  8. package/dist/{dist-B8XLeyRL.js → dist-BSSYC9T8.js} +803 -64
  9. package/dist/{dist-BycZdx08.mjs → dist-De0e9-YH.mjs} +803 -64
  10. package/dist/{hostInit-CcF5kCoS.mjs → hostInit-DjLtjT-_.mjs} +2 -2
  11. package/dist/index.js +1 -1
  12. package/dist/orchestrator/training.addon.js +1 -1
  13. package/dist/orchestrator/training.addon.mjs +1 -1
  14. package/dist/{player-overlays-eJNV-UU-.mjs → player-overlays-C_B-yc3g.mjs} +1 -1
  15. package/dist/providers/modal/training-modal.addon.js +1 -1
  16. package/dist/providers/modal/training-modal.addon.mjs +1 -1
  17. package/dist/remoteEntry.js +1 -1
  18. package/dist/{responsive-DlCVUok5.mjs → responsive-YANRyril.mjs} +1 -1
  19. package/dist/{scene-monitor-copy-D3BXN-0E.mjs → scene-monitor-copy-B0w92y1z.mjs} +1 -1
  20. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-9YL8IM8w.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-CosY3aLX.mjs} +1 -1
  21. package/package.json +1 -1
  22. package/dist/_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CjEDvlSM.mjs +0 -26
@@ -5621,7 +5621,7 @@ var ZodIssueCode = {
5621
5621
  var ZodFirstPartyTypeKind;
5622
5622
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5623
5623
  //#endregion
5624
- //#region ../types/dist/sleep-6eG9C8Xl.mjs
5624
+ //#region ../types/dist/sleep-63gM_1JR.mjs
5625
5625
  /**
5626
5626
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5627
5627
  * window to float samples (D455).
@@ -9149,6 +9149,7 @@ var CAP_NAMES = Object.freeze([
9149
9149
  "recording-signal",
9150
9150
  "records",
9151
9151
  "remote",
9152
+ "remote-layout-components",
9152
9153
  "running-state",
9153
9154
  "scene-monitor",
9154
9155
  "script-runner",
@@ -15885,6 +15886,478 @@ object({
15885
15886
  path: ["expr"]
15886
15887
  });
15887
15888
  });
15889
+ /**
15890
+ * Identity envelope for a device's upstream-system metadata.
15891
+ *
15892
+ * Two jobs:
15893
+ *
15894
+ * 1. **Dispatch key.** `id` is the natural identifier the SOURCE system
15895
+ * uses to address this device (HA `entity_id`, Reolink MAC, ONVIF
15896
+ * WS-Discovery endpoint UUID, …). Provider code keeps a
15897
+ * `Map<sourceInfo.id, IDevice>` to route inbound push events in O(1)
15898
+ * without round-tripping through `stableId` / `id` mappings.
15899
+ *
15900
+ * 2. **Stability bridge.** `uniqueId` is the *immutable* upstream id when
15901
+ * one exists (HA `entity.unique_id`, Reolink MAC again — same as `id`
15902
+ * for vendors where MAC is the natural key). For systems where the
15903
+ * live dispatch key can be renamed (HA `entity_id` can be edited),
15904
+ * providers detect renames by matching `uniqueId` and patch `id`
15905
+ * in-place so dispatch keeps working.
15906
+ *
15907
+ * **Display unit and precision are NOT stored here.** They are a read-time
15908
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
15909
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
15910
+ * neither is repeated on each sample. The `device_class` tag is an
15911
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
15912
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
15913
+ *
15914
+ * The `raw` escape hatch carries provider-specific attributes that don't
15915
+ * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
15916
+ * details, …). Providers control what lands here.
15917
+ */
15918
+ var SourceInfoSchema = object({
15919
+ /** Live dispatch key — mutable when the source system allows rename. */
15920
+ id: string(),
15921
+ /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
15922
+ system: string(),
15923
+ /** Immutable upstream identifier when available (HA `unique_id`, … ).
15924
+ * Used to detect rename when `id` changes. */
15925
+ uniqueId: string().optional(),
15926
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
15927
+ quantity: ReadingQuantitySchema.optional(),
15928
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
15929
+ dateTimeShape: _enum([
15930
+ "date",
15931
+ "time",
15932
+ "datetime"
15933
+ ]).optional(),
15934
+ /** Provider-specific extras that don't fit the structured slots.
15935
+ * Whitelist what goes here per provider — do NOT dump entire HA
15936
+ * attribute blobs (would explode the meta JSON column). */
15937
+ raw: record(string(), unknown()).optional()
15938
+ });
15939
+ /**
15940
+ * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
15941
+ * unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
15942
+ * `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
15943
+ * decimals). K derives from `°C = K − 273.15`.
15944
+ */
15945
+ var UNIT_TABLE = {
15946
+ "°C": {
15947
+ dimension: "temperature",
15948
+ factor: 1,
15949
+ offset: 0
15950
+ },
15951
+ "°F": {
15952
+ dimension: "temperature",
15953
+ factor: 5 / 9,
15954
+ offset: -160 / 9
15955
+ },
15956
+ K: {
15957
+ dimension: "temperature",
15958
+ factor: 1,
15959
+ offset: -273.15
15960
+ },
15961
+ hPa: {
15962
+ dimension: "pressure",
15963
+ factor: 1,
15964
+ offset: 0
15965
+ },
15966
+ kPa: {
15967
+ dimension: "pressure",
15968
+ factor: 10,
15969
+ offset: 0
15970
+ },
15971
+ Pa: {
15972
+ dimension: "pressure",
15973
+ factor: .01,
15974
+ offset: 0
15975
+ },
15976
+ mbar: {
15977
+ dimension: "pressure",
15978
+ factor: 1,
15979
+ offset: 0
15980
+ },
15981
+ bar: {
15982
+ dimension: "pressure",
15983
+ factor: 1e3,
15984
+ offset: 0
15985
+ },
15986
+ inHg: {
15987
+ dimension: "pressure",
15988
+ factor: 33.8639,
15989
+ offset: 0
15990
+ },
15991
+ mmHg: {
15992
+ dimension: "pressure",
15993
+ factor: 1.33322,
15994
+ offset: 0
15995
+ },
15996
+ psi: {
15997
+ dimension: "pressure",
15998
+ factor: 68.9476,
15999
+ offset: 0
16000
+ },
16001
+ "m/s": {
16002
+ dimension: "speed",
16003
+ factor: 1,
16004
+ offset: 0
16005
+ },
16006
+ "km/h": {
16007
+ dimension: "speed",
16008
+ factor: 1 / 3.6,
16009
+ offset: 0
16010
+ },
16011
+ mph: {
16012
+ dimension: "speed",
16013
+ factor: .44704,
16014
+ offset: 0
16015
+ },
16016
+ kn: {
16017
+ dimension: "speed",
16018
+ factor: .514444,
16019
+ offset: 0
16020
+ },
16021
+ "mm/h": {
16022
+ dimension: "precipitation-rate",
16023
+ factor: 1,
16024
+ offset: 0
16025
+ },
16026
+ "in/h": {
16027
+ dimension: "precipitation-rate",
16028
+ factor: 25.4,
16029
+ offset: 0
16030
+ },
16031
+ m: {
16032
+ dimension: "length",
16033
+ factor: 1,
16034
+ offset: 0
16035
+ },
16036
+ mm: {
16037
+ dimension: "length",
16038
+ factor: .001,
16039
+ offset: 0
16040
+ },
16041
+ cm: {
16042
+ dimension: "length",
16043
+ factor: .01,
16044
+ offset: 0
16045
+ },
16046
+ km: {
16047
+ dimension: "length",
16048
+ factor: 1e3,
16049
+ offset: 0
16050
+ },
16051
+ in: {
16052
+ dimension: "length",
16053
+ factor: .0254,
16054
+ offset: 0
16055
+ },
16056
+ ft: {
16057
+ dimension: "length",
16058
+ factor: .3048,
16059
+ offset: 0
16060
+ },
16061
+ mi: {
16062
+ dimension: "length",
16063
+ factor: 1609.344,
16064
+ offset: 0
16065
+ },
16066
+ "m²": {
16067
+ dimension: "area",
16068
+ factor: 1,
16069
+ offset: 0
16070
+ },
16071
+ lx: {
16072
+ dimension: "illuminance",
16073
+ factor: 1,
16074
+ offset: 0
16075
+ },
16076
+ "W/m²": {
16077
+ dimension: "irradiance",
16078
+ factor: 1,
16079
+ offset: 0
16080
+ },
16081
+ W: {
16082
+ dimension: "power",
16083
+ factor: 1,
16084
+ offset: 0
16085
+ },
16086
+ kW: {
16087
+ dimension: "power",
16088
+ factor: 1e3,
16089
+ offset: 0
16090
+ },
16091
+ VA: {
16092
+ dimension: "apparent-power",
16093
+ factor: 1,
16094
+ offset: 0
16095
+ },
16096
+ Wh: {
16097
+ dimension: "energy",
16098
+ factor: 1,
16099
+ offset: 0
16100
+ },
16101
+ kWh: {
16102
+ dimension: "energy",
16103
+ factor: 1e3,
16104
+ offset: 0
16105
+ },
16106
+ MWh: {
16107
+ dimension: "energy",
16108
+ factor: 1e6,
16109
+ offset: 0
16110
+ },
16111
+ V: {
16112
+ dimension: "voltage",
16113
+ factor: 1,
16114
+ offset: 0
16115
+ },
16116
+ mV: {
16117
+ dimension: "voltage",
16118
+ factor: .001,
16119
+ offset: 0
16120
+ },
16121
+ A: {
16122
+ dimension: "current",
16123
+ factor: 1,
16124
+ offset: 0
16125
+ },
16126
+ mA: {
16127
+ dimension: "current",
16128
+ factor: .001,
16129
+ offset: 0
16130
+ },
16131
+ ppm: {
16132
+ dimension: "concentration-volume",
16133
+ factor: 1,
16134
+ offset: 0
16135
+ },
16136
+ ppb: {
16137
+ dimension: "concentration-volume",
16138
+ factor: .001,
16139
+ offset: 0
16140
+ },
16141
+ "µg/m³": {
16142
+ dimension: "concentration-mass",
16143
+ factor: 1,
16144
+ offset: 0
16145
+ },
16146
+ "mg/m³": {
16147
+ dimension: "concentration-mass",
16148
+ factor: 1e3,
16149
+ offset: 0
16150
+ },
16151
+ kg: {
16152
+ dimension: "mass",
16153
+ factor: 1,
16154
+ offset: 0
16155
+ },
16156
+ g: {
16157
+ dimension: "mass",
16158
+ factor: .001,
16159
+ offset: 0
16160
+ },
16161
+ lb: {
16162
+ dimension: "mass",
16163
+ factor: .453592,
16164
+ offset: 0
16165
+ },
16166
+ oz: {
16167
+ dimension: "mass",
16168
+ factor: .0283495,
16169
+ offset: 0
16170
+ },
16171
+ "%": {
16172
+ dimension: "percentage",
16173
+ factor: 1,
16174
+ offset: 0
16175
+ },
16176
+ L: {
16177
+ dimension: "volume",
16178
+ factor: 1,
16179
+ offset: 0
16180
+ },
16181
+ mL: {
16182
+ dimension: "volume",
16183
+ factor: .001,
16184
+ offset: 0
16185
+ },
16186
+ gal: {
16187
+ dimension: "volume",
16188
+ factor: 3.785411784,
16189
+ offset: 0
16190
+ },
16191
+ s: {
16192
+ dimension: "duration",
16193
+ factor: 1,
16194
+ offset: 0
16195
+ },
16196
+ ms: {
16197
+ dimension: "duration",
16198
+ factor: .001,
16199
+ offset: 0
16200
+ },
16201
+ min: {
16202
+ dimension: "duration",
16203
+ factor: 60,
16204
+ offset: 0
16205
+ },
16206
+ h: {
16207
+ dimension: "duration",
16208
+ factor: 3600,
16209
+ offset: 0
16210
+ },
16211
+ d: {
16212
+ dimension: "duration",
16213
+ factor: 86400,
16214
+ offset: 0
16215
+ },
16216
+ B: {
16217
+ dimension: "information",
16218
+ factor: 1,
16219
+ offset: 0
16220
+ },
16221
+ KiB: {
16222
+ dimension: "information",
16223
+ factor: 1024,
16224
+ offset: 0
16225
+ },
16226
+ MiB: {
16227
+ dimension: "information",
16228
+ factor: 1024 ** 2,
16229
+ offset: 0
16230
+ },
16231
+ GiB: {
16232
+ dimension: "information",
16233
+ factor: 1024 ** 3,
16234
+ offset: 0
16235
+ },
16236
+ TiB: {
16237
+ dimension: "information",
16238
+ factor: 1024 ** 4,
16239
+ offset: 0
16240
+ },
16241
+ kB: {
16242
+ dimension: "information",
16243
+ factor: 1e3,
16244
+ offset: 0
16245
+ },
16246
+ MB: {
16247
+ dimension: "information",
16248
+ factor: 1e6,
16249
+ offset: 0
16250
+ },
16251
+ GB: {
16252
+ dimension: "information",
16253
+ factor: 1e9,
16254
+ offset: 0
16255
+ },
16256
+ "°": {
16257
+ dimension: "angle",
16258
+ factor: 1,
16259
+ offset: 0
16260
+ },
16261
+ dBFS: {
16262
+ dimension: "sound-level",
16263
+ factor: 1,
16264
+ offset: 0
16265
+ },
16266
+ dBm: {
16267
+ dimension: "signal-level",
16268
+ factor: 1,
16269
+ offset: 0
16270
+ },
16271
+ mired: {
16272
+ dimension: "colour-temperature",
16273
+ factor: 1,
16274
+ offset: 0
16275
+ },
16276
+ kbps: {
16277
+ dimension: "bitrate",
16278
+ factor: 1,
16279
+ offset: 0
16280
+ },
16281
+ Mbps: {
16282
+ dimension: "bitrate",
16283
+ factor: 1e3,
16284
+ offset: 0
16285
+ },
16286
+ fps: {
16287
+ dimension: "rate",
16288
+ factor: 1,
16289
+ offset: 0
16290
+ }
16291
+ };
16292
+ /** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
16293
+ function specFor(unit) {
16294
+ return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
16295
+ }
16296
+ /** Value in `spec`'s unit → its dimension's canonical unit. */
16297
+ function toCanonical(value, spec) {
16298
+ return value * spec.factor + spec.offset;
16299
+ }
16300
+ /** Canonical-unit value → the unit described by `spec`. */
16301
+ function fromCanonical(canonical, spec) {
16302
+ return (canonical - spec.offset) / spec.factor;
16303
+ }
16304
+ /**
16305
+ * Graceful variant for the render path: returns the converted value, or
16306
+ * `undefined` when the conversion is refused (unknown unit / cross-dimension).
16307
+ * Never throws — a render caller falls back to the raw value + source unit.
16308
+ */
16309
+ function tryConvertUnit(value, from, to) {
16310
+ if (from === to) return value;
16311
+ const fromSpec = specFor(from);
16312
+ if (!fromSpec) return void 0;
16313
+ const toSpec = specFor(to);
16314
+ if (!toSpec) return void 0;
16315
+ if (fromSpec.dimension !== toSpec.dimension) return void 0;
16316
+ return fromCanonical(toCanonical(value, fromSpec), toSpec);
16317
+ }
16318
+ /**
16319
+ * A number a person types for a physical quantity — the input side of the
16320
+ * value + unit contract (D972).
16321
+ *
16322
+ * Readings are stored in the base unit of their quantity (D944); a cap method
16323
+ * param is stored in the unit its input schema DECLARES (`paramMeta`). The
16324
+ * wire pair {@link QuantityValue} says "this number is expressed in this
16325
+ * unit"; {@link convertQuantityInput} brings it to the stored unit and refuses,
16326
+ * with a typed {@link QuantityUnitError}, a unit that cannot be that quantity.
16327
+ */
16328
+ /** A unit that cannot be applied to the number it was given with. */
16329
+ var QuantityUnitError = class extends Error {
16330
+ code;
16331
+ constructor(code, message) {
16332
+ super(`[${code}] ${message}`);
16333
+ this.name = "QuantityUnitError";
16334
+ this.code = code;
16335
+ }
16336
+ };
16337
+ /** The wire pair: `value` is expressed in `unit`. */
16338
+ var QuantityValueSchema = object({
16339
+ value: number(),
16340
+ unit: string().min(1)
16341
+ });
16342
+ /**
16343
+ * Zod `.meta()` bag for a cap METHOD param that is a physical quantity — the
16344
+ * input-side twin of `readingMeta`. The value is carried in `unit` (a gram
16345
+ * portion stays in grams); the unit must belong to the quantity, checked once
16346
+ * when the cap is declared.
16347
+ */
16348
+ function paramMeta(declared) {
16349
+ const base = baseUnitOf(declared.quantity);
16350
+ if (base === void 0) throw new QuantityUnitError("unknown-unit", `quantity "${declared.quantity}" has no unit`);
16351
+ if (tryConvertUnit(1, declared.unit, base) === void 0) throw new QuantityUnitError("cross-dimension", `"${declared.unit}" is not a unit of ${declared.quantity}`);
16352
+ return {
16353
+ quantity: declared.quantity,
16354
+ unit: declared.unit
16355
+ };
16356
+ }
16357
+ object({
16358
+ quantity: ReadingQuantitySchema,
16359
+ unit: string().min(1)
16360
+ });
15888
16361
  /** How a leaf compares a device field to a value. Derived from the field's
15889
16362
  * `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
15890
16363
  var AutomationConditionOperatorSchema = _enum([
@@ -15907,8 +16380,10 @@ var AutomationConditionLeafSchema = object({
15907
16380
  string(),
15908
16381
  number(),
15909
16382
  boolean(),
15910
- array(union([string(), number()]))
15911
- ])
16383
+ array(union([string(), number()])),
16384
+ QuantityValueSchema
16385
+ ]),
16386
+ unit: string().min(1).optional()
15912
16387
  });
15913
16388
  var AutomationConditionStationaryPresentSchema = object({
15914
16389
  kind: literal("stationary-present"),
@@ -16061,7 +16536,13 @@ var AutomationCapStepSchema = object({
16061
16536
  /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
16062
16537
  * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
16063
16538
  * the expression leaf. */
16064
- args: record(string(), unknown()).optional()
16539
+ args: record(string(), unknown()).optional(),
16540
+ /**
16541
+ * The unit the person picked per arg (display only). `args` always hold the
16542
+ * value in the unit the method's input DECLARES (`paramMeta`); on a write an
16543
+ * arg may be a {@link QuantityValue}, which the save boundary converts.
16544
+ */
16545
+ argUnits: record(string(), string().min(1)).optional()
16065
16546
  });
16066
16547
  /**
16067
16548
  * The steps an internal Action may contain: cap and wait, and nothing that can
@@ -16441,6 +16922,16 @@ object({
16441
16922
  lastRunAt: number().nullable(),
16442
16923
  lastError: string().nullable()
16443
16924
  });
16925
+ /** A bare number accepted as a base unit — the save succeeded, say so. */
16926
+ var UnitWarningSchema = object({
16927
+ /** Where in the saved record, e.g. `conditions.children.0` or `steps.2.args.grams`. */
16928
+ path: string(),
16929
+ deviceId: number().int().optional(),
16930
+ field: string(),
16931
+ value: number(),
16932
+ assumedUnit: string(),
16933
+ message: string()
16934
+ });
16444
16935
  /**
16445
16936
  * notification-output — canonical, capability-gated notification delivery.
16446
16937
  *
@@ -21785,56 +22276,6 @@ var DeviceAdminLinkSchema = object({
21785
22276
  });
21786
22277
  method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema.nullable(), { auth: "admin" });
21787
22278
  /**
21788
- * Identity envelope for a device's upstream-system metadata.
21789
- *
21790
- * Two jobs:
21791
- *
21792
- * 1. **Dispatch key.** `id` is the natural identifier the SOURCE system
21793
- * uses to address this device (HA `entity_id`, Reolink MAC, ONVIF
21794
- * WS-Discovery endpoint UUID, …). Provider code keeps a
21795
- * `Map<sourceInfo.id, IDevice>` to route inbound push events in O(1)
21796
- * without round-tripping through `stableId` / `id` mappings.
21797
- *
21798
- * 2. **Stability bridge.** `uniqueId` is the *immutable* upstream id when
21799
- * one exists (HA `entity.unique_id`, Reolink MAC again — same as `id`
21800
- * for vendors where MAC is the natural key). For systems where the
21801
- * live dispatch key can be renamed (HA `entity_id` can be edited),
21802
- * providers detect renames by matching `uniqueId` and patch `id`
21803
- * in-place so dispatch keeps working.
21804
- *
21805
- * **Display unit and precision are NOT stored here.** They are a read-time
21806
- * preference (D944). A polymorphic reading stores its `quantity` here once,
21807
- * at adopt, and a date/time value stores `dateTimeShape` the same way —
21808
- * neither is repeated on each sample. The `device_class` tag is an
21809
- * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
21810
- * to derive the CamStack `DeviceRole`; it is never stored after that point.
21811
- *
21812
- * The `raw` escape hatch carries provider-specific attributes that don't
21813
- * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
21814
- * details, …). Providers control what lands here.
21815
- */
21816
- var SourceInfoSchema = object({
21817
- /** Live dispatch key — mutable when the source system allows rename. */
21818
- id: string(),
21819
- /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
21820
- system: string(),
21821
- /** Immutable upstream identifier when available (HA `unique_id`, … ).
21822
- * Used to detect rename when `id` changes. */
21823
- uniqueId: string().optional(),
21824
- /** Quantity of a polymorphic reading, written once at adopt (D944). */
21825
- quantity: ReadingQuantitySchema.optional(),
21826
- /** Date / time / datetime shape of a string reading, written once at adopt. */
21827
- dateTimeShape: _enum([
21828
- "date",
21829
- "time",
21830
- "datetime"
21831
- ]).optional(),
21832
- /** Provider-specific extras that don't fit the structured slots.
21833
- * Whitelist what goes here per provider — do NOT dump entire HA
21834
- * attribute blobs (would explode the meta JSON column). */
21835
- raw: record(string(), unknown()).optional()
21836
- });
21837
- /**
21838
22279
  * device-discovery — device-scoped capability for parents that host /
21839
22280
  * enumerate child devices (Reolink Hub / NVR, ONVIF gateway, future
21840
22281
  * integrations). The parent advertises a list of discoverable children,
@@ -57841,13 +58282,19 @@ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
57841
58282
  }), method(ActionIdInput, _void(), {
57842
58283
  kind: "mutation",
57843
58284
  auth: "admin"
57844
- }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
58285
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({
58286
+ action: ActionEntrySchema,
58287
+ warnings: array(UnitWarningSchema).optional()
58288
+ }), {
57845
58289
  kind: "mutation",
57846
58290
  auth: "admin"
57847
58291
  }), method(object({
57848
58292
  actionId: string().min(1),
57849
58293
  action: ActionInputSchema.partial()
57850
- }), object({ action: ActionEntrySchema }), {
58294
+ }), object({
58295
+ action: ActionEntrySchema,
58296
+ warnings: array(UnitWarningSchema).optional()
58297
+ }), {
57851
58298
  kind: "mutation",
57852
58299
  auth: "admin"
57853
58300
  }), method(ActionIdInput, _void(), {
@@ -58153,6 +58600,30 @@ object({
58153
58600
  at: number()
58154
58601
  }).nullable()
58155
58602
  });
58603
+ var RemoteButtonShapeSchema = _enum([
58604
+ "rect",
58605
+ "rounded",
58606
+ "round",
58607
+ "pill"
58608
+ ]);
58609
+ var RemoteButtonToneSchema = _enum([
58610
+ "neutral",
58611
+ "accent",
58612
+ "danger",
58613
+ "red",
58614
+ "green",
58615
+ "yellow",
58616
+ "blue"
58617
+ ]);
58618
+ var RemoteGroupFrameSchema = _enum([
58619
+ "none",
58620
+ "panel",
58621
+ "divider",
58622
+ "dpad",
58623
+ "rocker-v",
58624
+ "rocker-h",
58625
+ "keypad"
58626
+ ]);
58156
58627
  var RemoteLayoutButtonSchema = object({
58157
58628
  id: string().min(1).max(40),
58158
58629
  /** 0-based grid position. */
@@ -58162,12 +58633,94 @@ var RemoteLayoutButtonSchema = object({
58162
58633
  colSpan: number().int().min(1).max(24),
58163
58634
  icon: string().min(1).max(60).optional(),
58164
58635
  label: string().min(1).max(40).optional(),
58165
- role: RemoteRoleSchema
58636
+ role: RemoteRoleSchema,
58637
+ /** Absent = the plain rendering every layout had before shapes existed. */
58638
+ shape: RemoteButtonShapeSchema.optional(),
58639
+ tone: RemoteButtonToneSchema.optional(),
58640
+ /** The group this button belongs to; it must lie inside that group's rectangle. */
58641
+ groupId: string().min(1).max(40).optional()
58642
+ });
58643
+ /**
58644
+ * A rectangle of the grid that moves as ONE thing and is drawn with a frame: a
58645
+ * titled panel, a section divider, a D-pad ring. `componentId` names the palette
58646
+ * entry it came from (informational: the layout is self-contained).
58647
+ */
58648
+ var RemoteLayoutGroupSchema = object({
58649
+ id: string().min(1).max(40),
58650
+ componentId: string().min(1).max(80),
58651
+ frame: RemoteGroupFrameSchema,
58652
+ row: number().int().min(0).max(24),
58653
+ col: number().int().min(0).max(24),
58654
+ rowSpan: number().int().min(1).max(24),
58655
+ colSpan: number().int().min(1).max(24),
58656
+ title: string().min(1).max(40).optional()
58166
58657
  });
58658
+ function rectsOverlap(a, b) {
58659
+ return a.row < b.row + b.rowSpan && b.row < a.row + a.rowSpan && a.col < b.col + b.colSpan && b.col < a.col + a.colSpan;
58660
+ }
58661
+ function rectContains(outer, inner) {
58662
+ return inner.row >= outer.row && inner.col >= outer.col && inner.row + inner.rowSpan <= outer.row + outer.rowSpan && inner.col + inner.colSpan <= outer.col + outer.colSpan;
58663
+ }
58664
+ /**
58665
+ * The group rules of a layout: inside the grid, no two groups overlap, a
58666
+ * member lies inside its group, a divider holds nothing, and a button that is
58667
+ * not a member never sits inside someone else's rectangle (a group moves as
58668
+ * one thing, so a stray key under it would be left behind).
58669
+ */
58670
+ function refineGroups(grid, ctx) {
58671
+ const groups = grid.groups ?? [];
58672
+ const byId = /* @__PURE__ */ new Map();
58673
+ const fail = (message, path) => {
58674
+ ctx.addIssue({
58675
+ code: "custom",
58676
+ message,
58677
+ path
58678
+ });
58679
+ };
58680
+ groups.forEach((group, index) => {
58681
+ if (byId.has(group.id)) fail(`group id "${group.id}" is used twice`, [
58682
+ "groups",
58683
+ index,
58684
+ "id"
58685
+ ]);
58686
+ byId.set(group.id, group);
58687
+ if (group.row + group.rowSpan > grid.rows || group.col + group.colSpan > grid.columns) fail(`group "${group.id}" leaves the grid`, ["groups", index]);
58688
+ for (let other = 0; other < index; other += 1) {
58689
+ const earlier = groups[other];
58690
+ if (earlier !== void 0 && rectsOverlap(earlier, group)) {
58691
+ fail(`groups "${earlier.id}" and "${group.id}" overlap`, ["groups", index]);
58692
+ return;
58693
+ }
58694
+ }
58695
+ });
58696
+ grid.buttons.forEach((button, index) => {
58697
+ if (button.groupId !== void 0) {
58698
+ const group = byId.get(button.groupId);
58699
+ if (group === void 0) {
58700
+ fail(`button "${button.id}" names the unknown group "${button.groupId}"`, [
58701
+ "buttons",
58702
+ index,
58703
+ "groupId"
58704
+ ]);
58705
+ return;
58706
+ }
58707
+ if (group.frame === "divider") fail(`a divider group ("${group.id}") holds no buttons`, [
58708
+ "buttons",
58709
+ index,
58710
+ "groupId"
58711
+ ]);
58712
+ if (!rectContains(group, button)) fail(`button "${button.id}" lies outside its group "${group.id}"`, ["buttons", index]);
58713
+ return;
58714
+ }
58715
+ const foreign = groups.find((group) => rectsOverlap(group, button));
58716
+ if (foreign !== void 0) fail(`button "${button.id}" lies inside group "${foreign.id}" but is not a member`, ["buttons", index]);
58717
+ });
58718
+ }
58167
58719
  object({
58168
58720
  columns: number().int().min(1).max(24),
58169
58721
  rows: number().int().min(1).max(24),
58170
- buttons: array(RemoteLayoutButtonSchema).max(200)
58722
+ buttons: array(RemoteLayoutButtonSchema).max(200),
58723
+ groups: array(RemoteLayoutGroupSchema).max(50).optional()
58171
58724
  }).superRefine((grid, ctx) => {
58172
58725
  const ids = /* @__PURE__ */ new Set();
58173
58726
  const taken = /* @__PURE__ */ new Map();
@@ -58204,6 +58757,7 @@ object({
58204
58757
  taken.set(cell, button.id);
58205
58758
  }
58206
58759
  });
58760
+ refineGroups(grid, ctx);
58207
58761
  });
58208
58762
  var RemoteBoundCommandSchema = object({
58209
58763
  role: RemoteRoleSchema,
@@ -58335,6 +58889,91 @@ method(object({ kind: TemplateKindSchema.optional() }), array(TemplateRecordSche
58335
58889
  kind: "mutation",
58336
58890
  auth: "admin"
58337
58891
  });
58892
+ var RemoteComponentCategorySchema = _enum([
58893
+ "power",
58894
+ "navigation",
58895
+ "volume",
58896
+ "numbers",
58897
+ "media",
58898
+ "colour",
58899
+ "apps",
58900
+ "layout"
58901
+ ]);
58902
+ /** `<namespace>:<name>`, both lowercase slugs — `core:dpad`, `<addonId>:<name>`. */
58903
+ var COMPONENT_ID_RE = /^[a-z0-9][a-z0-9_.-]*:[a-z0-9][a-z0-9_-]*$/;
58904
+ var RemoteComponentButtonSchema = object({
58905
+ /** Joined to the instance id when the component is inserted. */
58906
+ idSuffix: string().regex(/^[a-z0-9][a-z0-9_-]{0,19}$/, { message: "a short lowercase slug" }),
58907
+ row: number().int().min(0).max(24),
58908
+ col: number().int().min(0).max(24),
58909
+ rowSpan: number().int().min(1).max(24),
58910
+ colSpan: number().int().min(1).max(24),
58911
+ role: RemoteRoleSchema,
58912
+ icon: string().min(1).max(60).optional(),
58913
+ label: string().min(1).max(40).optional(),
58914
+ shape: RemoteButtonShapeSchema.optional(),
58915
+ tone: RemoteButtonToneSchema.optional()
58916
+ });
58917
+ var RemoteLayoutComponentSchema = object({
58918
+ id: string().max(80).regex(COMPONENT_ID_RE, { message: "<namespace>:<name>, lowercase" }),
58919
+ name: string().min(1).max(60),
58920
+ nameI18n: object({
58921
+ en: string().min(1).max(60),
58922
+ it: string().min(1).max(60)
58923
+ }).optional(),
58924
+ /** A display icon key, resolved by the UI like a button icon. */
58925
+ icon: string().min(1).max(60),
58926
+ category: RemoteComponentCategorySchema,
58927
+ size: object({
58928
+ rows: number().int().min(1).max(24),
58929
+ cols: number().int().min(1).max(24)
58930
+ }),
58931
+ /** The frame drawn around the whole component, or null for loose buttons. */
58932
+ group: object({
58933
+ frame: RemoteGroupFrameSchema,
58934
+ title: string().min(1).max(40).optional()
58935
+ }).nullable(),
58936
+ buttons: array(RemoteComponentButtonSchema).max(40)
58937
+ }).superRefine((component, ctx) => {
58938
+ const suffixes = /* @__PURE__ */ new Set();
58939
+ component.buttons.forEach((button, index) => {
58940
+ if (suffixes.has(button.idSuffix)) ctx.addIssue({
58941
+ code: "custom",
58942
+ message: `button "${button.idSuffix}" is listed twice`,
58943
+ path: [
58944
+ "buttons",
58945
+ index,
58946
+ "idSuffix"
58947
+ ]
58948
+ });
58949
+ suffixes.add(button.idSuffix);
58950
+ if (button.row + button.rowSpan > component.size.rows || button.col + button.colSpan > component.size.cols) {
58951
+ ctx.addIssue({
58952
+ code: "custom",
58953
+ message: `button "${button.idSuffix}" leaves the component`,
58954
+ path: ["buttons", index]
58955
+ });
58956
+ return;
58957
+ }
58958
+ for (let other = 0; other < index; other += 1) {
58959
+ const earlier = component.buttons[other];
58960
+ if (earlier !== void 0 && rectsOverlap(earlier, button)) {
58961
+ ctx.addIssue({
58962
+ code: "custom",
58963
+ message: `buttons "${earlier.idSuffix}" and "${button.idSuffix}" overlap`,
58964
+ path: ["buttons", index]
58965
+ });
58966
+ return;
58967
+ }
58968
+ }
58969
+ });
58970
+ if (component.group?.frame === "divider" && component.buttons.length > 0) ctx.addIssue({
58971
+ code: "custom",
58972
+ message: "a divider component holds no buttons",
58973
+ path: ["buttons"]
58974
+ });
58975
+ });
58976
+ method(object({}), array(RemoteLayoutComponentSchema), { auth: "protected" });
58338
58977
  DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _void(), {
58339
58978
  kind: "mutation",
58340
58979
  auth: "protected"
@@ -62494,6 +63133,14 @@ var PetFeederDeviceStatusSchema = _enum([
62494
63133
  "on_batteries"
62495
63134
  ]);
62496
63135
  var gramsPortion = number().int().min(4).max(200);
63136
+ /** A portion REQUESTED of the feeder is a mass the method takes in GRAMS (the
63137
+ * hardware's own unit). The bounds are grams; a caller entering another unit
63138
+ * converts to grams first (`paramMeta`, D972). The STATUS fields above stay
63139
+ * plain grams — they are not readings in the kilogram base. */
63140
+ var gramsParam = gramsPortion.meta(paramMeta({
63141
+ quantity: "mass",
63142
+ unit: "g"
63143
+ }));
62497
63144
  object({
62498
63145
  /** Food in the TANK (hopper), as the feeder's own sensor reports it
62499
63146
  * (`state.food`). A coarse level code, not grams — `0` is empty. The
@@ -62568,9 +63215,9 @@ object({
62568
63215
  });
62569
63216
  DeviceType.PetFeeder, method(object({
62570
63217
  deviceId: number().int().nonnegative(),
62571
- grams: gramsPortion.optional(),
62572
- hopper1: gramsPortion.optional(),
62573
- hopper2: gramsPortion.optional()
63218
+ grams: gramsParam.optional(),
63219
+ hopper1: gramsParam.optional(),
63220
+ hopper2: gramsParam.optional()
62574
63221
  }), _void(), {
62575
63222
  kind: "mutation",
62576
63223
  auth: "protected"
@@ -67364,6 +68011,87 @@ var trainingCapability = {
67364
68011
  })
67365
68012
  }
67366
68013
  };
68014
+ var btn = (idSuffix, row, col, init) => ({
68015
+ idSuffix,
68016
+ row,
68017
+ col,
68018
+ rowSpan: 1,
68019
+ colSpan: 1,
68020
+ ...init
68021
+ });
68022
+ function rocker(id, name, it, frame, title, buttons, size) {
68023
+ return {
68024
+ id,
68025
+ name,
68026
+ nameI18n: {
68027
+ en: name,
68028
+ it
68029
+ },
68030
+ icon: frame === "rocker-v" ? "chevrons-up-down" : "chevrons-left-right",
68031
+ category: id.includes("channel") || id.includes("volume") ? "volume" : "navigation",
68032
+ size,
68033
+ group: title === null ? { frame } : {
68034
+ frame,
68035
+ title
68036
+ },
68037
+ buttons
68038
+ };
68039
+ }
68040
+ btn("power", 0, 0, {
68041
+ role: "power",
68042
+ shape: "round",
68043
+ tone: "danger"
68044
+ }), btn("up", 0, 1, { role: "up" }), btn("left", 1, 0, { role: "left" }), btn("ok", 1, 1, {
68045
+ role: "ok",
68046
+ shape: "round",
68047
+ tone: "accent"
68048
+ }), btn("right", 1, 2, { role: "right" }), btn("down", 2, 1, { role: "down" }), rocker("core:rocker-volume-v", "Volume rocker", "Bilanciere volume", "rocker-v", "VOL", [btn("up", 0, 0, {
68049
+ role: "volume_up",
68050
+ shape: "pill"
68051
+ }), btn("down", 1, 0, {
68052
+ role: "volume_down",
68053
+ shape: "pill"
68054
+ })], {
68055
+ rows: 2,
68056
+ cols: 1
68057
+ }), rocker("core:rocker-channel-v", "Channel rocker", "Bilanciere canali", "rocker-v", "CH", [btn("up", 0, 0, {
68058
+ role: "channel_up",
68059
+ shape: "pill"
68060
+ }), btn("down", 1, 0, {
68061
+ role: "channel_down",
68062
+ shape: "pill"
68063
+ })], {
68064
+ rows: 2,
68065
+ cols: 1
68066
+ }), rocker("core:rocker-h", "Minus / plus rocker", "Bilanciere meno / più", "rocker-h", null, [btn("minus", 0, 0, {
68067
+ role: "custom:minus",
68068
+ label: "−",
68069
+ shape: "pill"
68070
+ }), btn("plus", 0, 1, {
68071
+ role: "custom:plus",
68072
+ label: "+",
68073
+ shape: "pill"
68074
+ })], {
68075
+ rows: 1,
68076
+ cols: 2
68077
+ }), [...Array.from({ length: 9 }, (_, i) => btn(`d${i + 1}`, Math.floor(i / 3), i % 3, { role: `digit_${i + 1}` })), btn("d0", 3, 1, { role: "digit_0" })], btn("red", 0, 0, {
68078
+ role: "custom:color_red",
68079
+ tone: "red"
68080
+ }), btn("green", 0, 1, {
68081
+ role: "custom:color_green",
68082
+ tone: "green"
68083
+ }), btn("yellow", 0, 2, {
68084
+ role: "custom:color_yellow",
68085
+ tone: "yellow"
68086
+ }), btn("blue", 0, 3, {
68087
+ role: "custom:color_blue",
68088
+ tone: "blue"
68089
+ }), btn("rewind", 0, 0, { role: "rewind" }), btn("play", 0, 1, { role: "play" }), btn("pause", 0, 2, { role: "pause" }), btn("forward", 0, 3, { role: "fast_forward" }), btn("stop", 0, 4, { role: "stop" }), [
68090
+ 1,
68091
+ 2,
68092
+ 3,
68093
+ 4
68094
+ ].map((n) => btn(`app${n}`, 0, n - 1, { role: `custom:app_slot_${n}` })), btn("back", 0, 0, { role: "back" }), btn("home", 0, 1, { role: "home" }), btn("menu", 0, 2, { role: "menu" });
67367
68095
  discriminatedUnion("kind", [object({
67368
68096
  kind: literal("raw"),
67369
68097
  timings: array(number().int()),
@@ -75228,6 +75956,13 @@ Object.freeze({
75228
75956
  access: "create",
75229
75957
  role: "admin"
75230
75958
  },
75959
+ "remoteLayoutComponents.listComponents": {
75960
+ capName: "remote-layout-components",
75961
+ capScope: "system",
75962
+ addonId: null,
75963
+ access: "view",
75964
+ role: "admin"
75965
+ },
75231
75966
  "runningState.getStatus": {
75232
75967
  capName: "running-state",
75233
75968
  capScope: "device",
@@ -79271,7 +80006,8 @@ var APPLIANCE_CAP_VOCABULARY = {
79271
80006
  min: 4,
79272
80007
  max: 200,
79273
80008
  step: 1,
79274
- unit: "g"
80009
+ unit: "g",
80010
+ quantity: "mass"
79275
80011
  },
79276
80012
  optional: true
79277
80013
  }]
@@ -79290,7 +80026,8 @@ var APPLIANCE_CAP_VOCABULARY = {
79290
80026
  min: 4,
79291
80027
  max: 200,
79292
80028
  step: 1,
79293
- unit: "g"
80029
+ unit: "g",
80030
+ quantity: "mass"
79294
80031
  },
79295
80032
  optional: true
79296
80033
  }, {
@@ -79301,7 +80038,8 @@ var APPLIANCE_CAP_VOCABULARY = {
79301
80038
  min: 4,
79302
80039
  max: 200,
79303
80040
  step: 1,
79304
- unit: "g"
80041
+ unit: "g",
80042
+ quantity: "mass"
79305
80043
  },
79306
80044
  optional: true
79307
80045
  }]
@@ -81401,6 +82139,7 @@ var SYSTEM_CAP_VOCABULARY = {
81401
82139
  "recording-export": system("recording exports — requested from the timeline, not an automation step"),
81402
82140
  records: system(INTERNAL),
81403
82141
  remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
82142
+ "remote-layout-components": system("the palette of the remote template editor — read-only data, never actuated"),
81404
82143
  "server-management": system(CLUSTER),
81405
82144
  "settings-store": system(INTERNAL),
81406
82145
  "smtp-provider": system(INTERNAL),