@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
@@ -5588,7 +5588,7 @@ var ZodIssueCode = {
5588
5588
  var ZodFirstPartyTypeKind;
5589
5589
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5590
5590
  //#endregion
5591
- //#region ../types/dist/sleep-6eG9C8Xl.mjs
5591
+ //#region ../types/dist/sleep-63gM_1JR.mjs
5592
5592
  /**
5593
5593
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5594
5594
  * window to float samples (D455).
@@ -9116,6 +9116,7 @@ var CAP_NAMES = Object.freeze([
9116
9116
  "recording-signal",
9117
9117
  "records",
9118
9118
  "remote",
9119
+ "remote-layout-components",
9119
9120
  "running-state",
9120
9121
  "scene-monitor",
9121
9122
  "script-runner",
@@ -15852,6 +15853,478 @@ object({
15852
15853
  path: ["expr"]
15853
15854
  });
15854
15855
  });
15856
+ /**
15857
+ * Identity envelope for a device's upstream-system metadata.
15858
+ *
15859
+ * Two jobs:
15860
+ *
15861
+ * 1. **Dispatch key.** `id` is the natural identifier the SOURCE system
15862
+ * uses to address this device (HA `entity_id`, Reolink MAC, ONVIF
15863
+ * WS-Discovery endpoint UUID, …). Provider code keeps a
15864
+ * `Map<sourceInfo.id, IDevice>` to route inbound push events in O(1)
15865
+ * without round-tripping through `stableId` / `id` mappings.
15866
+ *
15867
+ * 2. **Stability bridge.** `uniqueId` is the *immutable* upstream id when
15868
+ * one exists (HA `entity.unique_id`, Reolink MAC again — same as `id`
15869
+ * for vendors where MAC is the natural key). For systems where the
15870
+ * live dispatch key can be renamed (HA `entity_id` can be edited),
15871
+ * providers detect renames by matching `uniqueId` and patch `id`
15872
+ * in-place so dispatch keeps working.
15873
+ *
15874
+ * **Display unit and precision are NOT stored here.** They are a read-time
15875
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
15876
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
15877
+ * neither is repeated on each sample. The `device_class` tag is an
15878
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
15879
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
15880
+ *
15881
+ * The `raw` escape hatch carries provider-specific attributes that don't
15882
+ * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
15883
+ * details, …). Providers control what lands here.
15884
+ */
15885
+ var SourceInfoSchema = object({
15886
+ /** Live dispatch key — mutable when the source system allows rename. */
15887
+ id: string(),
15888
+ /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
15889
+ system: string(),
15890
+ /** Immutable upstream identifier when available (HA `unique_id`, … ).
15891
+ * Used to detect rename when `id` changes. */
15892
+ uniqueId: string().optional(),
15893
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
15894
+ quantity: ReadingQuantitySchema.optional(),
15895
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
15896
+ dateTimeShape: _enum([
15897
+ "date",
15898
+ "time",
15899
+ "datetime"
15900
+ ]).optional(),
15901
+ /** Provider-specific extras that don't fit the structured slots.
15902
+ * Whitelist what goes here per provider — do NOT dump entire HA
15903
+ * attribute blobs (would explode the meta JSON column). */
15904
+ raw: record(string(), unknown()).optional()
15905
+ });
15906
+ /**
15907
+ * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
15908
+ * unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
15909
+ * `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
15910
+ * decimals). K derives from `°C = K − 273.15`.
15911
+ */
15912
+ var UNIT_TABLE = {
15913
+ "°C": {
15914
+ dimension: "temperature",
15915
+ factor: 1,
15916
+ offset: 0
15917
+ },
15918
+ "°F": {
15919
+ dimension: "temperature",
15920
+ factor: 5 / 9,
15921
+ offset: -160 / 9
15922
+ },
15923
+ K: {
15924
+ dimension: "temperature",
15925
+ factor: 1,
15926
+ offset: -273.15
15927
+ },
15928
+ hPa: {
15929
+ dimension: "pressure",
15930
+ factor: 1,
15931
+ offset: 0
15932
+ },
15933
+ kPa: {
15934
+ dimension: "pressure",
15935
+ factor: 10,
15936
+ offset: 0
15937
+ },
15938
+ Pa: {
15939
+ dimension: "pressure",
15940
+ factor: .01,
15941
+ offset: 0
15942
+ },
15943
+ mbar: {
15944
+ dimension: "pressure",
15945
+ factor: 1,
15946
+ offset: 0
15947
+ },
15948
+ bar: {
15949
+ dimension: "pressure",
15950
+ factor: 1e3,
15951
+ offset: 0
15952
+ },
15953
+ inHg: {
15954
+ dimension: "pressure",
15955
+ factor: 33.8639,
15956
+ offset: 0
15957
+ },
15958
+ mmHg: {
15959
+ dimension: "pressure",
15960
+ factor: 1.33322,
15961
+ offset: 0
15962
+ },
15963
+ psi: {
15964
+ dimension: "pressure",
15965
+ factor: 68.9476,
15966
+ offset: 0
15967
+ },
15968
+ "m/s": {
15969
+ dimension: "speed",
15970
+ factor: 1,
15971
+ offset: 0
15972
+ },
15973
+ "km/h": {
15974
+ dimension: "speed",
15975
+ factor: 1 / 3.6,
15976
+ offset: 0
15977
+ },
15978
+ mph: {
15979
+ dimension: "speed",
15980
+ factor: .44704,
15981
+ offset: 0
15982
+ },
15983
+ kn: {
15984
+ dimension: "speed",
15985
+ factor: .514444,
15986
+ offset: 0
15987
+ },
15988
+ "mm/h": {
15989
+ dimension: "precipitation-rate",
15990
+ factor: 1,
15991
+ offset: 0
15992
+ },
15993
+ "in/h": {
15994
+ dimension: "precipitation-rate",
15995
+ factor: 25.4,
15996
+ offset: 0
15997
+ },
15998
+ m: {
15999
+ dimension: "length",
16000
+ factor: 1,
16001
+ offset: 0
16002
+ },
16003
+ mm: {
16004
+ dimension: "length",
16005
+ factor: .001,
16006
+ offset: 0
16007
+ },
16008
+ cm: {
16009
+ dimension: "length",
16010
+ factor: .01,
16011
+ offset: 0
16012
+ },
16013
+ km: {
16014
+ dimension: "length",
16015
+ factor: 1e3,
16016
+ offset: 0
16017
+ },
16018
+ in: {
16019
+ dimension: "length",
16020
+ factor: .0254,
16021
+ offset: 0
16022
+ },
16023
+ ft: {
16024
+ dimension: "length",
16025
+ factor: .3048,
16026
+ offset: 0
16027
+ },
16028
+ mi: {
16029
+ dimension: "length",
16030
+ factor: 1609.344,
16031
+ offset: 0
16032
+ },
16033
+ "m²": {
16034
+ dimension: "area",
16035
+ factor: 1,
16036
+ offset: 0
16037
+ },
16038
+ lx: {
16039
+ dimension: "illuminance",
16040
+ factor: 1,
16041
+ offset: 0
16042
+ },
16043
+ "W/m²": {
16044
+ dimension: "irradiance",
16045
+ factor: 1,
16046
+ offset: 0
16047
+ },
16048
+ W: {
16049
+ dimension: "power",
16050
+ factor: 1,
16051
+ offset: 0
16052
+ },
16053
+ kW: {
16054
+ dimension: "power",
16055
+ factor: 1e3,
16056
+ offset: 0
16057
+ },
16058
+ VA: {
16059
+ dimension: "apparent-power",
16060
+ factor: 1,
16061
+ offset: 0
16062
+ },
16063
+ Wh: {
16064
+ dimension: "energy",
16065
+ factor: 1,
16066
+ offset: 0
16067
+ },
16068
+ kWh: {
16069
+ dimension: "energy",
16070
+ factor: 1e3,
16071
+ offset: 0
16072
+ },
16073
+ MWh: {
16074
+ dimension: "energy",
16075
+ factor: 1e6,
16076
+ offset: 0
16077
+ },
16078
+ V: {
16079
+ dimension: "voltage",
16080
+ factor: 1,
16081
+ offset: 0
16082
+ },
16083
+ mV: {
16084
+ dimension: "voltage",
16085
+ factor: .001,
16086
+ offset: 0
16087
+ },
16088
+ A: {
16089
+ dimension: "current",
16090
+ factor: 1,
16091
+ offset: 0
16092
+ },
16093
+ mA: {
16094
+ dimension: "current",
16095
+ factor: .001,
16096
+ offset: 0
16097
+ },
16098
+ ppm: {
16099
+ dimension: "concentration-volume",
16100
+ factor: 1,
16101
+ offset: 0
16102
+ },
16103
+ ppb: {
16104
+ dimension: "concentration-volume",
16105
+ factor: .001,
16106
+ offset: 0
16107
+ },
16108
+ "µg/m³": {
16109
+ dimension: "concentration-mass",
16110
+ factor: 1,
16111
+ offset: 0
16112
+ },
16113
+ "mg/m³": {
16114
+ dimension: "concentration-mass",
16115
+ factor: 1e3,
16116
+ offset: 0
16117
+ },
16118
+ kg: {
16119
+ dimension: "mass",
16120
+ factor: 1,
16121
+ offset: 0
16122
+ },
16123
+ g: {
16124
+ dimension: "mass",
16125
+ factor: .001,
16126
+ offset: 0
16127
+ },
16128
+ lb: {
16129
+ dimension: "mass",
16130
+ factor: .453592,
16131
+ offset: 0
16132
+ },
16133
+ oz: {
16134
+ dimension: "mass",
16135
+ factor: .0283495,
16136
+ offset: 0
16137
+ },
16138
+ "%": {
16139
+ dimension: "percentage",
16140
+ factor: 1,
16141
+ offset: 0
16142
+ },
16143
+ L: {
16144
+ dimension: "volume",
16145
+ factor: 1,
16146
+ offset: 0
16147
+ },
16148
+ mL: {
16149
+ dimension: "volume",
16150
+ factor: .001,
16151
+ offset: 0
16152
+ },
16153
+ gal: {
16154
+ dimension: "volume",
16155
+ factor: 3.785411784,
16156
+ offset: 0
16157
+ },
16158
+ s: {
16159
+ dimension: "duration",
16160
+ factor: 1,
16161
+ offset: 0
16162
+ },
16163
+ ms: {
16164
+ dimension: "duration",
16165
+ factor: .001,
16166
+ offset: 0
16167
+ },
16168
+ min: {
16169
+ dimension: "duration",
16170
+ factor: 60,
16171
+ offset: 0
16172
+ },
16173
+ h: {
16174
+ dimension: "duration",
16175
+ factor: 3600,
16176
+ offset: 0
16177
+ },
16178
+ d: {
16179
+ dimension: "duration",
16180
+ factor: 86400,
16181
+ offset: 0
16182
+ },
16183
+ B: {
16184
+ dimension: "information",
16185
+ factor: 1,
16186
+ offset: 0
16187
+ },
16188
+ KiB: {
16189
+ dimension: "information",
16190
+ factor: 1024,
16191
+ offset: 0
16192
+ },
16193
+ MiB: {
16194
+ dimension: "information",
16195
+ factor: 1024 ** 2,
16196
+ offset: 0
16197
+ },
16198
+ GiB: {
16199
+ dimension: "information",
16200
+ factor: 1024 ** 3,
16201
+ offset: 0
16202
+ },
16203
+ TiB: {
16204
+ dimension: "information",
16205
+ factor: 1024 ** 4,
16206
+ offset: 0
16207
+ },
16208
+ kB: {
16209
+ dimension: "information",
16210
+ factor: 1e3,
16211
+ offset: 0
16212
+ },
16213
+ MB: {
16214
+ dimension: "information",
16215
+ factor: 1e6,
16216
+ offset: 0
16217
+ },
16218
+ GB: {
16219
+ dimension: "information",
16220
+ factor: 1e9,
16221
+ offset: 0
16222
+ },
16223
+ "°": {
16224
+ dimension: "angle",
16225
+ factor: 1,
16226
+ offset: 0
16227
+ },
16228
+ dBFS: {
16229
+ dimension: "sound-level",
16230
+ factor: 1,
16231
+ offset: 0
16232
+ },
16233
+ dBm: {
16234
+ dimension: "signal-level",
16235
+ factor: 1,
16236
+ offset: 0
16237
+ },
16238
+ mired: {
16239
+ dimension: "colour-temperature",
16240
+ factor: 1,
16241
+ offset: 0
16242
+ },
16243
+ kbps: {
16244
+ dimension: "bitrate",
16245
+ factor: 1,
16246
+ offset: 0
16247
+ },
16248
+ Mbps: {
16249
+ dimension: "bitrate",
16250
+ factor: 1e3,
16251
+ offset: 0
16252
+ },
16253
+ fps: {
16254
+ dimension: "rate",
16255
+ factor: 1,
16256
+ offset: 0
16257
+ }
16258
+ };
16259
+ /** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
16260
+ function specFor(unit) {
16261
+ return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
16262
+ }
16263
+ /** Value in `spec`'s unit → its dimension's canonical unit. */
16264
+ function toCanonical(value, spec) {
16265
+ return value * spec.factor + spec.offset;
16266
+ }
16267
+ /** Canonical-unit value → the unit described by `spec`. */
16268
+ function fromCanonical(canonical, spec) {
16269
+ return (canonical - spec.offset) / spec.factor;
16270
+ }
16271
+ /**
16272
+ * Graceful variant for the render path: returns the converted value, or
16273
+ * `undefined` when the conversion is refused (unknown unit / cross-dimension).
16274
+ * Never throws — a render caller falls back to the raw value + source unit.
16275
+ */
16276
+ function tryConvertUnit(value, from, to) {
16277
+ if (from === to) return value;
16278
+ const fromSpec = specFor(from);
16279
+ if (!fromSpec) return void 0;
16280
+ const toSpec = specFor(to);
16281
+ if (!toSpec) return void 0;
16282
+ if (fromSpec.dimension !== toSpec.dimension) return void 0;
16283
+ return fromCanonical(toCanonical(value, fromSpec), toSpec);
16284
+ }
16285
+ /**
16286
+ * A number a person types for a physical quantity — the input side of the
16287
+ * value + unit contract (D972).
16288
+ *
16289
+ * Readings are stored in the base unit of their quantity (D944); a cap method
16290
+ * param is stored in the unit its input schema DECLARES (`paramMeta`). The
16291
+ * wire pair {@link QuantityValue} says "this number is expressed in this
16292
+ * unit"; {@link convertQuantityInput} brings it to the stored unit and refuses,
16293
+ * with a typed {@link QuantityUnitError}, a unit that cannot be that quantity.
16294
+ */
16295
+ /** A unit that cannot be applied to the number it was given with. */
16296
+ var QuantityUnitError = class extends Error {
16297
+ code;
16298
+ constructor(code, message) {
16299
+ super(`[${code}] ${message}`);
16300
+ this.name = "QuantityUnitError";
16301
+ this.code = code;
16302
+ }
16303
+ };
16304
+ /** The wire pair: `value` is expressed in `unit`. */
16305
+ var QuantityValueSchema = object({
16306
+ value: number(),
16307
+ unit: string().min(1)
16308
+ });
16309
+ /**
16310
+ * Zod `.meta()` bag for a cap METHOD param that is a physical quantity — the
16311
+ * input-side twin of `readingMeta`. The value is carried in `unit` (a gram
16312
+ * portion stays in grams); the unit must belong to the quantity, checked once
16313
+ * when the cap is declared.
16314
+ */
16315
+ function paramMeta(declared) {
16316
+ const base = baseUnitOf(declared.quantity);
16317
+ if (base === void 0) throw new QuantityUnitError("unknown-unit", `quantity "${declared.quantity}" has no unit`);
16318
+ if (tryConvertUnit(1, declared.unit, base) === void 0) throw new QuantityUnitError("cross-dimension", `"${declared.unit}" is not a unit of ${declared.quantity}`);
16319
+ return {
16320
+ quantity: declared.quantity,
16321
+ unit: declared.unit
16322
+ };
16323
+ }
16324
+ object({
16325
+ quantity: ReadingQuantitySchema,
16326
+ unit: string().min(1)
16327
+ });
15855
16328
  /** How a leaf compares a device field to a value. Derived from the field's
15856
16329
  * `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
15857
16330
  var AutomationConditionOperatorSchema = _enum([
@@ -15874,8 +16347,10 @@ var AutomationConditionLeafSchema = object({
15874
16347
  string(),
15875
16348
  number(),
15876
16349
  boolean(),
15877
- array(union([string(), number()]))
15878
- ])
16350
+ array(union([string(), number()])),
16351
+ QuantityValueSchema
16352
+ ]),
16353
+ unit: string().min(1).optional()
15879
16354
  });
15880
16355
  var AutomationConditionStationaryPresentSchema = object({
15881
16356
  kind: literal("stationary-present"),
@@ -16028,7 +16503,13 @@ var AutomationCapStepSchema = object({
16028
16503
  /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
16029
16504
  * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
16030
16505
  * the expression leaf. */
16031
- args: record(string(), unknown()).optional()
16506
+ args: record(string(), unknown()).optional(),
16507
+ /**
16508
+ * The unit the person picked per arg (display only). `args` always hold the
16509
+ * value in the unit the method's input DECLARES (`paramMeta`); on a write an
16510
+ * arg may be a {@link QuantityValue}, which the save boundary converts.
16511
+ */
16512
+ argUnits: record(string(), string().min(1)).optional()
16032
16513
  });
16033
16514
  /**
16034
16515
  * The steps an internal Action may contain: cap and wait, and nothing that can
@@ -16408,6 +16889,16 @@ object({
16408
16889
  lastRunAt: number().nullable(),
16409
16890
  lastError: string().nullable()
16410
16891
  });
16892
+ /** A bare number accepted as a base unit — the save succeeded, say so. */
16893
+ var UnitWarningSchema = object({
16894
+ /** Where in the saved record, e.g. `conditions.children.0` or `steps.2.args.grams`. */
16895
+ path: string(),
16896
+ deviceId: number().int().optional(),
16897
+ field: string(),
16898
+ value: number(),
16899
+ assumedUnit: string(),
16900
+ message: string()
16901
+ });
16411
16902
  /**
16412
16903
  * notification-output — canonical, capability-gated notification delivery.
16413
16904
  *
@@ -21752,56 +22243,6 @@ var DeviceAdminLinkSchema = object({
21752
22243
  });
21753
22244
  method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema.nullable(), { auth: "admin" });
21754
22245
  /**
21755
- * Identity envelope for a device's upstream-system metadata.
21756
- *
21757
- * Two jobs:
21758
- *
21759
- * 1. **Dispatch key.** `id` is the natural identifier the SOURCE system
21760
- * uses to address this device (HA `entity_id`, Reolink MAC, ONVIF
21761
- * WS-Discovery endpoint UUID, …). Provider code keeps a
21762
- * `Map<sourceInfo.id, IDevice>` to route inbound push events in O(1)
21763
- * without round-tripping through `stableId` / `id` mappings.
21764
- *
21765
- * 2. **Stability bridge.** `uniqueId` is the *immutable* upstream id when
21766
- * one exists (HA `entity.unique_id`, Reolink MAC again — same as `id`
21767
- * for vendors where MAC is the natural key). For systems where the
21768
- * live dispatch key can be renamed (HA `entity_id` can be edited),
21769
- * providers detect renames by matching `uniqueId` and patch `id`
21770
- * in-place so dispatch keeps working.
21771
- *
21772
- * **Display unit and precision are NOT stored here.** They are a read-time
21773
- * preference (D944). A polymorphic reading stores its `quantity` here once,
21774
- * at adopt, and a date/time value stores `dateTimeShape` the same way —
21775
- * neither is repeated on each sample. The `device_class` tag is an
21776
- * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
21777
- * to derive the CamStack `DeviceRole`; it is never stored after that point.
21778
- *
21779
- * The `raw` escape hatch carries provider-specific attributes that don't
21780
- * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
21781
- * details, …). Providers control what lands here.
21782
- */
21783
- var SourceInfoSchema = object({
21784
- /** Live dispatch key — mutable when the source system allows rename. */
21785
- id: string(),
21786
- /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
21787
- system: string(),
21788
- /** Immutable upstream identifier when available (HA `unique_id`, … ).
21789
- * Used to detect rename when `id` changes. */
21790
- uniqueId: string().optional(),
21791
- /** Quantity of a polymorphic reading, written once at adopt (D944). */
21792
- quantity: ReadingQuantitySchema.optional(),
21793
- /** Date / time / datetime shape of a string reading, written once at adopt. */
21794
- dateTimeShape: _enum([
21795
- "date",
21796
- "time",
21797
- "datetime"
21798
- ]).optional(),
21799
- /** Provider-specific extras that don't fit the structured slots.
21800
- * Whitelist what goes here per provider — do NOT dump entire HA
21801
- * attribute blobs (would explode the meta JSON column). */
21802
- raw: record(string(), unknown()).optional()
21803
- });
21804
- /**
21805
22246
  * device-discovery — device-scoped capability for parents that host /
21806
22247
  * enumerate child devices (Reolink Hub / NVR, ONVIF gateway, future
21807
22248
  * integrations). The parent advertises a list of discoverable children,
@@ -57808,13 +58249,19 @@ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
57808
58249
  }), method(ActionIdInput, _void(), {
57809
58250
  kind: "mutation",
57810
58251
  auth: "admin"
57811
- }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
58252
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({
58253
+ action: ActionEntrySchema,
58254
+ warnings: array(UnitWarningSchema).optional()
58255
+ }), {
57812
58256
  kind: "mutation",
57813
58257
  auth: "admin"
57814
58258
  }), method(object({
57815
58259
  actionId: string().min(1),
57816
58260
  action: ActionInputSchema.partial()
57817
- }), object({ action: ActionEntrySchema }), {
58261
+ }), object({
58262
+ action: ActionEntrySchema,
58263
+ warnings: array(UnitWarningSchema).optional()
58264
+ }), {
57818
58265
  kind: "mutation",
57819
58266
  auth: "admin"
57820
58267
  }), method(ActionIdInput, _void(), {
@@ -58120,6 +58567,30 @@ object({
58120
58567
  at: number()
58121
58568
  }).nullable()
58122
58569
  });
58570
+ var RemoteButtonShapeSchema = _enum([
58571
+ "rect",
58572
+ "rounded",
58573
+ "round",
58574
+ "pill"
58575
+ ]);
58576
+ var RemoteButtonToneSchema = _enum([
58577
+ "neutral",
58578
+ "accent",
58579
+ "danger",
58580
+ "red",
58581
+ "green",
58582
+ "yellow",
58583
+ "blue"
58584
+ ]);
58585
+ var RemoteGroupFrameSchema = _enum([
58586
+ "none",
58587
+ "panel",
58588
+ "divider",
58589
+ "dpad",
58590
+ "rocker-v",
58591
+ "rocker-h",
58592
+ "keypad"
58593
+ ]);
58123
58594
  var RemoteLayoutButtonSchema = object({
58124
58595
  id: string().min(1).max(40),
58125
58596
  /** 0-based grid position. */
@@ -58129,12 +58600,94 @@ var RemoteLayoutButtonSchema = object({
58129
58600
  colSpan: number().int().min(1).max(24),
58130
58601
  icon: string().min(1).max(60).optional(),
58131
58602
  label: string().min(1).max(40).optional(),
58132
- role: RemoteRoleSchema
58603
+ role: RemoteRoleSchema,
58604
+ /** Absent = the plain rendering every layout had before shapes existed. */
58605
+ shape: RemoteButtonShapeSchema.optional(),
58606
+ tone: RemoteButtonToneSchema.optional(),
58607
+ /** The group this button belongs to; it must lie inside that group's rectangle. */
58608
+ groupId: string().min(1).max(40).optional()
58609
+ });
58610
+ /**
58611
+ * A rectangle of the grid that moves as ONE thing and is drawn with a frame: a
58612
+ * titled panel, a section divider, a D-pad ring. `componentId` names the palette
58613
+ * entry it came from (informational: the layout is self-contained).
58614
+ */
58615
+ var RemoteLayoutGroupSchema = object({
58616
+ id: string().min(1).max(40),
58617
+ componentId: string().min(1).max(80),
58618
+ frame: RemoteGroupFrameSchema,
58619
+ row: number().int().min(0).max(24),
58620
+ col: number().int().min(0).max(24),
58621
+ rowSpan: number().int().min(1).max(24),
58622
+ colSpan: number().int().min(1).max(24),
58623
+ title: string().min(1).max(40).optional()
58133
58624
  });
58625
+ function rectsOverlap(a, b) {
58626
+ 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;
58627
+ }
58628
+ function rectContains(outer, inner) {
58629
+ 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;
58630
+ }
58631
+ /**
58632
+ * The group rules of a layout: inside the grid, no two groups overlap, a
58633
+ * member lies inside its group, a divider holds nothing, and a button that is
58634
+ * not a member never sits inside someone else's rectangle (a group moves as
58635
+ * one thing, so a stray key under it would be left behind).
58636
+ */
58637
+ function refineGroups(grid, ctx) {
58638
+ const groups = grid.groups ?? [];
58639
+ const byId = /* @__PURE__ */ new Map();
58640
+ const fail = (message, path) => {
58641
+ ctx.addIssue({
58642
+ code: "custom",
58643
+ message,
58644
+ path
58645
+ });
58646
+ };
58647
+ groups.forEach((group, index) => {
58648
+ if (byId.has(group.id)) fail(`group id "${group.id}" is used twice`, [
58649
+ "groups",
58650
+ index,
58651
+ "id"
58652
+ ]);
58653
+ byId.set(group.id, group);
58654
+ if (group.row + group.rowSpan > grid.rows || group.col + group.colSpan > grid.columns) fail(`group "${group.id}" leaves the grid`, ["groups", index]);
58655
+ for (let other = 0; other < index; other += 1) {
58656
+ const earlier = groups[other];
58657
+ if (earlier !== void 0 && rectsOverlap(earlier, group)) {
58658
+ fail(`groups "${earlier.id}" and "${group.id}" overlap`, ["groups", index]);
58659
+ return;
58660
+ }
58661
+ }
58662
+ });
58663
+ grid.buttons.forEach((button, index) => {
58664
+ if (button.groupId !== void 0) {
58665
+ const group = byId.get(button.groupId);
58666
+ if (group === void 0) {
58667
+ fail(`button "${button.id}" names the unknown group "${button.groupId}"`, [
58668
+ "buttons",
58669
+ index,
58670
+ "groupId"
58671
+ ]);
58672
+ return;
58673
+ }
58674
+ if (group.frame === "divider") fail(`a divider group ("${group.id}") holds no buttons`, [
58675
+ "buttons",
58676
+ index,
58677
+ "groupId"
58678
+ ]);
58679
+ if (!rectContains(group, button)) fail(`button "${button.id}" lies outside its group "${group.id}"`, ["buttons", index]);
58680
+ return;
58681
+ }
58682
+ const foreign = groups.find((group) => rectsOverlap(group, button));
58683
+ if (foreign !== void 0) fail(`button "${button.id}" lies inside group "${foreign.id}" but is not a member`, ["buttons", index]);
58684
+ });
58685
+ }
58134
58686
  object({
58135
58687
  columns: number().int().min(1).max(24),
58136
58688
  rows: number().int().min(1).max(24),
58137
- buttons: array(RemoteLayoutButtonSchema).max(200)
58689
+ buttons: array(RemoteLayoutButtonSchema).max(200),
58690
+ groups: array(RemoteLayoutGroupSchema).max(50).optional()
58138
58691
  }).superRefine((grid, ctx) => {
58139
58692
  const ids = /* @__PURE__ */ new Set();
58140
58693
  const taken = /* @__PURE__ */ new Map();
@@ -58171,6 +58724,7 @@ object({
58171
58724
  taken.set(cell, button.id);
58172
58725
  }
58173
58726
  });
58727
+ refineGroups(grid, ctx);
58174
58728
  });
58175
58729
  var RemoteBoundCommandSchema = object({
58176
58730
  role: RemoteRoleSchema,
@@ -58302,6 +58856,91 @@ method(object({ kind: TemplateKindSchema.optional() }), array(TemplateRecordSche
58302
58856
  kind: "mutation",
58303
58857
  auth: "admin"
58304
58858
  });
58859
+ var RemoteComponentCategorySchema = _enum([
58860
+ "power",
58861
+ "navigation",
58862
+ "volume",
58863
+ "numbers",
58864
+ "media",
58865
+ "colour",
58866
+ "apps",
58867
+ "layout"
58868
+ ]);
58869
+ /** `<namespace>:<name>`, both lowercase slugs — `core:dpad`, `<addonId>:<name>`. */
58870
+ var COMPONENT_ID_RE = /^[a-z0-9][a-z0-9_.-]*:[a-z0-9][a-z0-9_-]*$/;
58871
+ var RemoteComponentButtonSchema = object({
58872
+ /** Joined to the instance id when the component is inserted. */
58873
+ idSuffix: string().regex(/^[a-z0-9][a-z0-9_-]{0,19}$/, { message: "a short lowercase slug" }),
58874
+ row: number().int().min(0).max(24),
58875
+ col: number().int().min(0).max(24),
58876
+ rowSpan: number().int().min(1).max(24),
58877
+ colSpan: number().int().min(1).max(24),
58878
+ role: RemoteRoleSchema,
58879
+ icon: string().min(1).max(60).optional(),
58880
+ label: string().min(1).max(40).optional(),
58881
+ shape: RemoteButtonShapeSchema.optional(),
58882
+ tone: RemoteButtonToneSchema.optional()
58883
+ });
58884
+ var RemoteLayoutComponentSchema = object({
58885
+ id: string().max(80).regex(COMPONENT_ID_RE, { message: "<namespace>:<name>, lowercase" }),
58886
+ name: string().min(1).max(60),
58887
+ nameI18n: object({
58888
+ en: string().min(1).max(60),
58889
+ it: string().min(1).max(60)
58890
+ }).optional(),
58891
+ /** A display icon key, resolved by the UI like a button icon. */
58892
+ icon: string().min(1).max(60),
58893
+ category: RemoteComponentCategorySchema,
58894
+ size: object({
58895
+ rows: number().int().min(1).max(24),
58896
+ cols: number().int().min(1).max(24)
58897
+ }),
58898
+ /** The frame drawn around the whole component, or null for loose buttons. */
58899
+ group: object({
58900
+ frame: RemoteGroupFrameSchema,
58901
+ title: string().min(1).max(40).optional()
58902
+ }).nullable(),
58903
+ buttons: array(RemoteComponentButtonSchema).max(40)
58904
+ }).superRefine((component, ctx) => {
58905
+ const suffixes = /* @__PURE__ */ new Set();
58906
+ component.buttons.forEach((button, index) => {
58907
+ if (suffixes.has(button.idSuffix)) ctx.addIssue({
58908
+ code: "custom",
58909
+ message: `button "${button.idSuffix}" is listed twice`,
58910
+ path: [
58911
+ "buttons",
58912
+ index,
58913
+ "idSuffix"
58914
+ ]
58915
+ });
58916
+ suffixes.add(button.idSuffix);
58917
+ if (button.row + button.rowSpan > component.size.rows || button.col + button.colSpan > component.size.cols) {
58918
+ ctx.addIssue({
58919
+ code: "custom",
58920
+ message: `button "${button.idSuffix}" leaves the component`,
58921
+ path: ["buttons", index]
58922
+ });
58923
+ return;
58924
+ }
58925
+ for (let other = 0; other < index; other += 1) {
58926
+ const earlier = component.buttons[other];
58927
+ if (earlier !== void 0 && rectsOverlap(earlier, button)) {
58928
+ ctx.addIssue({
58929
+ code: "custom",
58930
+ message: `buttons "${earlier.idSuffix}" and "${button.idSuffix}" overlap`,
58931
+ path: ["buttons", index]
58932
+ });
58933
+ return;
58934
+ }
58935
+ }
58936
+ });
58937
+ if (component.group?.frame === "divider" && component.buttons.length > 0) ctx.addIssue({
58938
+ code: "custom",
58939
+ message: "a divider component holds no buttons",
58940
+ path: ["buttons"]
58941
+ });
58942
+ });
58943
+ method(object({}), array(RemoteLayoutComponentSchema), { auth: "protected" });
58305
58944
  DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _void(), {
58306
58945
  kind: "mutation",
58307
58946
  auth: "protected"
@@ -62461,6 +63100,14 @@ var PetFeederDeviceStatusSchema = _enum([
62461
63100
  "on_batteries"
62462
63101
  ]);
62463
63102
  var gramsPortion = number().int().min(4).max(200);
63103
+ /** A portion REQUESTED of the feeder is a mass the method takes in GRAMS (the
63104
+ * hardware's own unit). The bounds are grams; a caller entering another unit
63105
+ * converts to grams first (`paramMeta`, D972). The STATUS fields above stay
63106
+ * plain grams — they are not readings in the kilogram base. */
63107
+ var gramsParam = gramsPortion.meta(paramMeta({
63108
+ quantity: "mass",
63109
+ unit: "g"
63110
+ }));
62464
63111
  object({
62465
63112
  /** Food in the TANK (hopper), as the feeder's own sensor reports it
62466
63113
  * (`state.food`). A coarse level code, not grams — `0` is empty. The
@@ -62535,9 +63182,9 @@ object({
62535
63182
  });
62536
63183
  DeviceType.PetFeeder, method(object({
62537
63184
  deviceId: number().int().nonnegative(),
62538
- grams: gramsPortion.optional(),
62539
- hopper1: gramsPortion.optional(),
62540
- hopper2: gramsPortion.optional()
63185
+ grams: gramsParam.optional(),
63186
+ hopper1: gramsParam.optional(),
63187
+ hopper2: gramsParam.optional()
62541
63188
  }), _void(), {
62542
63189
  kind: "mutation",
62543
63190
  auth: "protected"
@@ -67331,6 +67978,87 @@ var trainingCapability = {
67331
67978
  })
67332
67979
  }
67333
67980
  };
67981
+ var btn = (idSuffix, row, col, init) => ({
67982
+ idSuffix,
67983
+ row,
67984
+ col,
67985
+ rowSpan: 1,
67986
+ colSpan: 1,
67987
+ ...init
67988
+ });
67989
+ function rocker(id, name, it, frame, title, buttons, size) {
67990
+ return {
67991
+ id,
67992
+ name,
67993
+ nameI18n: {
67994
+ en: name,
67995
+ it
67996
+ },
67997
+ icon: frame === "rocker-v" ? "chevrons-up-down" : "chevrons-left-right",
67998
+ category: id.includes("channel") || id.includes("volume") ? "volume" : "navigation",
67999
+ size,
68000
+ group: title === null ? { frame } : {
68001
+ frame,
68002
+ title
68003
+ },
68004
+ buttons
68005
+ };
68006
+ }
68007
+ btn("power", 0, 0, {
68008
+ role: "power",
68009
+ shape: "round",
68010
+ tone: "danger"
68011
+ }), btn("up", 0, 1, { role: "up" }), btn("left", 1, 0, { role: "left" }), btn("ok", 1, 1, {
68012
+ role: "ok",
68013
+ shape: "round",
68014
+ tone: "accent"
68015
+ }), 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, {
68016
+ role: "volume_up",
68017
+ shape: "pill"
68018
+ }), btn("down", 1, 0, {
68019
+ role: "volume_down",
68020
+ shape: "pill"
68021
+ })], {
68022
+ rows: 2,
68023
+ cols: 1
68024
+ }), rocker("core:rocker-channel-v", "Channel rocker", "Bilanciere canali", "rocker-v", "CH", [btn("up", 0, 0, {
68025
+ role: "channel_up",
68026
+ shape: "pill"
68027
+ }), btn("down", 1, 0, {
68028
+ role: "channel_down",
68029
+ shape: "pill"
68030
+ })], {
68031
+ rows: 2,
68032
+ cols: 1
68033
+ }), rocker("core:rocker-h", "Minus / plus rocker", "Bilanciere meno / più", "rocker-h", null, [btn("minus", 0, 0, {
68034
+ role: "custom:minus",
68035
+ label: "−",
68036
+ shape: "pill"
68037
+ }), btn("plus", 0, 1, {
68038
+ role: "custom:plus",
68039
+ label: "+",
68040
+ shape: "pill"
68041
+ })], {
68042
+ rows: 1,
68043
+ cols: 2
68044
+ }), [...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, {
68045
+ role: "custom:color_red",
68046
+ tone: "red"
68047
+ }), btn("green", 0, 1, {
68048
+ role: "custom:color_green",
68049
+ tone: "green"
68050
+ }), btn("yellow", 0, 2, {
68051
+ role: "custom:color_yellow",
68052
+ tone: "yellow"
68053
+ }), btn("blue", 0, 3, {
68054
+ role: "custom:color_blue",
68055
+ tone: "blue"
68056
+ }), 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" }), [
68057
+ 1,
68058
+ 2,
68059
+ 3,
68060
+ 4
68061
+ ].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" });
67334
68062
  discriminatedUnion("kind", [object({
67335
68063
  kind: literal("raw"),
67336
68064
  timings: array(number().int()),
@@ -75195,6 +75923,13 @@ Object.freeze({
75195
75923
  access: "create",
75196
75924
  role: "admin"
75197
75925
  },
75926
+ "remoteLayoutComponents.listComponents": {
75927
+ capName: "remote-layout-components",
75928
+ capScope: "system",
75929
+ addonId: null,
75930
+ access: "view",
75931
+ role: "admin"
75932
+ },
75198
75933
  "runningState.getStatus": {
75199
75934
  capName: "running-state",
75200
75935
  capScope: "device",
@@ -79238,7 +79973,8 @@ var APPLIANCE_CAP_VOCABULARY = {
79238
79973
  min: 4,
79239
79974
  max: 200,
79240
79975
  step: 1,
79241
- unit: "g"
79976
+ unit: "g",
79977
+ quantity: "mass"
79242
79978
  },
79243
79979
  optional: true
79244
79980
  }]
@@ -79257,7 +79993,8 @@ var APPLIANCE_CAP_VOCABULARY = {
79257
79993
  min: 4,
79258
79994
  max: 200,
79259
79995
  step: 1,
79260
- unit: "g"
79996
+ unit: "g",
79997
+ quantity: "mass"
79261
79998
  },
79262
79999
  optional: true
79263
80000
  }, {
@@ -79268,7 +80005,8 @@ var APPLIANCE_CAP_VOCABULARY = {
79268
80005
  min: 4,
79269
80006
  max: 200,
79270
80007
  step: 1,
79271
- unit: "g"
80008
+ unit: "g",
80009
+ quantity: "mass"
79272
80010
  },
79273
80011
  optional: true
79274
80012
  }]
@@ -81368,6 +82106,7 @@ var SYSTEM_CAP_VOCABULARY = {
81368
82106
  "recording-export": system("recording exports — requested from the timeline, not an automation step"),
81369
82107
  records: system(INTERNAL),
81370
82108
  remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
82109
+ "remote-layout-components": system("the palette of the remote template editor — read-only data, never actuated"),
81371
82110
  "server-management": system(CLUSTER),
81372
82111
  "settings-store": system(INTERNAL),
81373
82112
  "smtp-provider": system(INTERNAL),