@camstack/addon-model-studio 1.1.322 → 1.1.324

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 (19) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{MotionZonesSettings-DIoKHy61.mjs → MotionZonesSettings-BFSzpDlc.mjs} +2 -2
  3. package/dist/{PrivacyMaskSettings-SkRUYyni.mjs → PrivacyMaskSettings-bzbS1q77.mjs} +4 -4
  4. package/dist/{SceneMonitorEditor-CB_GHDb6.mjs → SceneMonitorEditor-5jtlWR_n.mjs} +3 -3
  5. package/dist/_stub.js +1298 -1264
  6. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-onE-V79z.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-DMLYtwXQ.mjs} +3 -3
  7. package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D8h8qfbd.mjs +26 -0
  8. package/dist/{hostInit-DvZygcN-.mjs → hostInit-BQhIepOe.mjs} +2 -2
  9. package/dist/model-studio.addon.js +803 -64
  10. package/dist/model-studio.addon.mjs +803 -64
  11. package/dist/{player-overlays-_zERIZfO.mjs → player-overlays-DwnklNvS.mjs} +1 -1
  12. package/dist/remoteEntry.js +1 -1
  13. package/dist/{responsive-CFFPG77U.mjs → responsive-DFhiWOST.mjs} +1 -1
  14. package/dist/{scene-monitor-copy-DEJ23ZZg.mjs → scene-monitor-copy-DVaiZyk_.mjs} +1 -1
  15. package/dist/{square-Prn9rHwz.mjs → square-CL1GZCDy.mjs} +1 -1
  16. package/dist/{trash-2-Ddf3KFsK.mjs → trash-2-Bs4Zqar1.mjs} +1 -1
  17. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-DDvX_rph.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-B3z4_zyV.mjs} +1 -1
  18. package/package.json +1 -1
  19. package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Dt_IlI_h.mjs +0 -26
@@ -5850,7 +5850,7 @@ var ZodIssueCode = {
5850
5850
  var ZodFirstPartyTypeKind;
5851
5851
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5852
5852
  //#endregion
5853
- //#region ../types/dist/sleep-6eG9C8Xl.mjs
5853
+ //#region ../types/dist/sleep-63gM_1JR.mjs
5854
5854
  /**
5855
5855
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5856
5856
  * window to float samples (D455).
@@ -9211,6 +9211,7 @@ var CAP_NAMES = Object.freeze([
9211
9211
  "recording-signal",
9212
9212
  "records",
9213
9213
  "remote",
9214
+ "remote-layout-components",
9214
9215
  "running-state",
9215
9216
  "scene-monitor",
9216
9217
  "script-runner",
@@ -16068,6 +16069,478 @@ object({
16068
16069
  path: ["expr"]
16069
16070
  });
16070
16071
  });
16072
+ /**
16073
+ * Identity envelope for a device's upstream-system metadata.
16074
+ *
16075
+ * Two jobs:
16076
+ *
16077
+ * 1. **Dispatch key.** `id` is the natural identifier the SOURCE system
16078
+ * uses to address this device (HA `entity_id`, Reolink MAC, ONVIF
16079
+ * WS-Discovery endpoint UUID, …). Provider code keeps a
16080
+ * `Map<sourceInfo.id, IDevice>` to route inbound push events in O(1)
16081
+ * without round-tripping through `stableId` / `id` mappings.
16082
+ *
16083
+ * 2. **Stability bridge.** `uniqueId` is the *immutable* upstream id when
16084
+ * one exists (HA `entity.unique_id`, Reolink MAC again — same as `id`
16085
+ * for vendors where MAC is the natural key). For systems where the
16086
+ * live dispatch key can be renamed (HA `entity_id` can be edited),
16087
+ * providers detect renames by matching `uniqueId` and patch `id`
16088
+ * in-place so dispatch keeps working.
16089
+ *
16090
+ * **Display unit and precision are NOT stored here.** They are a read-time
16091
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
16092
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
16093
+ * neither is repeated on each sample. The `device_class` tag is an
16094
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
16095
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
16096
+ *
16097
+ * The `raw` escape hatch carries provider-specific attributes that don't
16098
+ * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
16099
+ * details, …). Providers control what lands here.
16100
+ */
16101
+ var SourceInfoSchema = object({
16102
+ /** Live dispatch key — mutable when the source system allows rename. */
16103
+ id: string(),
16104
+ /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
16105
+ system: string(),
16106
+ /** Immutable upstream identifier when available (HA `unique_id`, … ).
16107
+ * Used to detect rename when `id` changes. */
16108
+ uniqueId: string().optional(),
16109
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
16110
+ quantity: ReadingQuantitySchema.optional(),
16111
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
16112
+ dateTimeShape: _enum([
16113
+ "date",
16114
+ "time",
16115
+ "datetime"
16116
+ ]).optional(),
16117
+ /** Provider-specific extras that don't fit the structured slots.
16118
+ * Whitelist what goes here per provider — do NOT dump entire HA
16119
+ * attribute blobs (would explode the meta JSON column). */
16120
+ raw: record(string(), unknown()).optional()
16121
+ });
16122
+ /**
16123
+ * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
16124
+ * unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
16125
+ * `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
16126
+ * decimals). K derives from `°C = K − 273.15`.
16127
+ */
16128
+ var UNIT_TABLE = {
16129
+ "°C": {
16130
+ dimension: "temperature",
16131
+ factor: 1,
16132
+ offset: 0
16133
+ },
16134
+ "°F": {
16135
+ dimension: "temperature",
16136
+ factor: 5 / 9,
16137
+ offset: -160 / 9
16138
+ },
16139
+ K: {
16140
+ dimension: "temperature",
16141
+ factor: 1,
16142
+ offset: -273.15
16143
+ },
16144
+ hPa: {
16145
+ dimension: "pressure",
16146
+ factor: 1,
16147
+ offset: 0
16148
+ },
16149
+ kPa: {
16150
+ dimension: "pressure",
16151
+ factor: 10,
16152
+ offset: 0
16153
+ },
16154
+ Pa: {
16155
+ dimension: "pressure",
16156
+ factor: .01,
16157
+ offset: 0
16158
+ },
16159
+ mbar: {
16160
+ dimension: "pressure",
16161
+ factor: 1,
16162
+ offset: 0
16163
+ },
16164
+ bar: {
16165
+ dimension: "pressure",
16166
+ factor: 1e3,
16167
+ offset: 0
16168
+ },
16169
+ inHg: {
16170
+ dimension: "pressure",
16171
+ factor: 33.8639,
16172
+ offset: 0
16173
+ },
16174
+ mmHg: {
16175
+ dimension: "pressure",
16176
+ factor: 1.33322,
16177
+ offset: 0
16178
+ },
16179
+ psi: {
16180
+ dimension: "pressure",
16181
+ factor: 68.9476,
16182
+ offset: 0
16183
+ },
16184
+ "m/s": {
16185
+ dimension: "speed",
16186
+ factor: 1,
16187
+ offset: 0
16188
+ },
16189
+ "km/h": {
16190
+ dimension: "speed",
16191
+ factor: 1 / 3.6,
16192
+ offset: 0
16193
+ },
16194
+ mph: {
16195
+ dimension: "speed",
16196
+ factor: .44704,
16197
+ offset: 0
16198
+ },
16199
+ kn: {
16200
+ dimension: "speed",
16201
+ factor: .514444,
16202
+ offset: 0
16203
+ },
16204
+ "mm/h": {
16205
+ dimension: "precipitation-rate",
16206
+ factor: 1,
16207
+ offset: 0
16208
+ },
16209
+ "in/h": {
16210
+ dimension: "precipitation-rate",
16211
+ factor: 25.4,
16212
+ offset: 0
16213
+ },
16214
+ m: {
16215
+ dimension: "length",
16216
+ factor: 1,
16217
+ offset: 0
16218
+ },
16219
+ mm: {
16220
+ dimension: "length",
16221
+ factor: .001,
16222
+ offset: 0
16223
+ },
16224
+ cm: {
16225
+ dimension: "length",
16226
+ factor: .01,
16227
+ offset: 0
16228
+ },
16229
+ km: {
16230
+ dimension: "length",
16231
+ factor: 1e3,
16232
+ offset: 0
16233
+ },
16234
+ in: {
16235
+ dimension: "length",
16236
+ factor: .0254,
16237
+ offset: 0
16238
+ },
16239
+ ft: {
16240
+ dimension: "length",
16241
+ factor: .3048,
16242
+ offset: 0
16243
+ },
16244
+ mi: {
16245
+ dimension: "length",
16246
+ factor: 1609.344,
16247
+ offset: 0
16248
+ },
16249
+ "m²": {
16250
+ dimension: "area",
16251
+ factor: 1,
16252
+ offset: 0
16253
+ },
16254
+ lx: {
16255
+ dimension: "illuminance",
16256
+ factor: 1,
16257
+ offset: 0
16258
+ },
16259
+ "W/m²": {
16260
+ dimension: "irradiance",
16261
+ factor: 1,
16262
+ offset: 0
16263
+ },
16264
+ W: {
16265
+ dimension: "power",
16266
+ factor: 1,
16267
+ offset: 0
16268
+ },
16269
+ kW: {
16270
+ dimension: "power",
16271
+ factor: 1e3,
16272
+ offset: 0
16273
+ },
16274
+ VA: {
16275
+ dimension: "apparent-power",
16276
+ factor: 1,
16277
+ offset: 0
16278
+ },
16279
+ Wh: {
16280
+ dimension: "energy",
16281
+ factor: 1,
16282
+ offset: 0
16283
+ },
16284
+ kWh: {
16285
+ dimension: "energy",
16286
+ factor: 1e3,
16287
+ offset: 0
16288
+ },
16289
+ MWh: {
16290
+ dimension: "energy",
16291
+ factor: 1e6,
16292
+ offset: 0
16293
+ },
16294
+ V: {
16295
+ dimension: "voltage",
16296
+ factor: 1,
16297
+ offset: 0
16298
+ },
16299
+ mV: {
16300
+ dimension: "voltage",
16301
+ factor: .001,
16302
+ offset: 0
16303
+ },
16304
+ A: {
16305
+ dimension: "current",
16306
+ factor: 1,
16307
+ offset: 0
16308
+ },
16309
+ mA: {
16310
+ dimension: "current",
16311
+ factor: .001,
16312
+ offset: 0
16313
+ },
16314
+ ppm: {
16315
+ dimension: "concentration-volume",
16316
+ factor: 1,
16317
+ offset: 0
16318
+ },
16319
+ ppb: {
16320
+ dimension: "concentration-volume",
16321
+ factor: .001,
16322
+ offset: 0
16323
+ },
16324
+ "µg/m³": {
16325
+ dimension: "concentration-mass",
16326
+ factor: 1,
16327
+ offset: 0
16328
+ },
16329
+ "mg/m³": {
16330
+ dimension: "concentration-mass",
16331
+ factor: 1e3,
16332
+ offset: 0
16333
+ },
16334
+ kg: {
16335
+ dimension: "mass",
16336
+ factor: 1,
16337
+ offset: 0
16338
+ },
16339
+ g: {
16340
+ dimension: "mass",
16341
+ factor: .001,
16342
+ offset: 0
16343
+ },
16344
+ lb: {
16345
+ dimension: "mass",
16346
+ factor: .453592,
16347
+ offset: 0
16348
+ },
16349
+ oz: {
16350
+ dimension: "mass",
16351
+ factor: .0283495,
16352
+ offset: 0
16353
+ },
16354
+ "%": {
16355
+ dimension: "percentage",
16356
+ factor: 1,
16357
+ offset: 0
16358
+ },
16359
+ L: {
16360
+ dimension: "volume",
16361
+ factor: 1,
16362
+ offset: 0
16363
+ },
16364
+ mL: {
16365
+ dimension: "volume",
16366
+ factor: .001,
16367
+ offset: 0
16368
+ },
16369
+ gal: {
16370
+ dimension: "volume",
16371
+ factor: 3.785411784,
16372
+ offset: 0
16373
+ },
16374
+ s: {
16375
+ dimension: "duration",
16376
+ factor: 1,
16377
+ offset: 0
16378
+ },
16379
+ ms: {
16380
+ dimension: "duration",
16381
+ factor: .001,
16382
+ offset: 0
16383
+ },
16384
+ min: {
16385
+ dimension: "duration",
16386
+ factor: 60,
16387
+ offset: 0
16388
+ },
16389
+ h: {
16390
+ dimension: "duration",
16391
+ factor: 3600,
16392
+ offset: 0
16393
+ },
16394
+ d: {
16395
+ dimension: "duration",
16396
+ factor: 86400,
16397
+ offset: 0
16398
+ },
16399
+ B: {
16400
+ dimension: "information",
16401
+ factor: 1,
16402
+ offset: 0
16403
+ },
16404
+ KiB: {
16405
+ dimension: "information",
16406
+ factor: 1024,
16407
+ offset: 0
16408
+ },
16409
+ MiB: {
16410
+ dimension: "information",
16411
+ factor: 1024 ** 2,
16412
+ offset: 0
16413
+ },
16414
+ GiB: {
16415
+ dimension: "information",
16416
+ factor: 1024 ** 3,
16417
+ offset: 0
16418
+ },
16419
+ TiB: {
16420
+ dimension: "information",
16421
+ factor: 1024 ** 4,
16422
+ offset: 0
16423
+ },
16424
+ kB: {
16425
+ dimension: "information",
16426
+ factor: 1e3,
16427
+ offset: 0
16428
+ },
16429
+ MB: {
16430
+ dimension: "information",
16431
+ factor: 1e6,
16432
+ offset: 0
16433
+ },
16434
+ GB: {
16435
+ dimension: "information",
16436
+ factor: 1e9,
16437
+ offset: 0
16438
+ },
16439
+ "°": {
16440
+ dimension: "angle",
16441
+ factor: 1,
16442
+ offset: 0
16443
+ },
16444
+ dBFS: {
16445
+ dimension: "sound-level",
16446
+ factor: 1,
16447
+ offset: 0
16448
+ },
16449
+ dBm: {
16450
+ dimension: "signal-level",
16451
+ factor: 1,
16452
+ offset: 0
16453
+ },
16454
+ mired: {
16455
+ dimension: "colour-temperature",
16456
+ factor: 1,
16457
+ offset: 0
16458
+ },
16459
+ kbps: {
16460
+ dimension: "bitrate",
16461
+ factor: 1,
16462
+ offset: 0
16463
+ },
16464
+ Mbps: {
16465
+ dimension: "bitrate",
16466
+ factor: 1e3,
16467
+ offset: 0
16468
+ },
16469
+ fps: {
16470
+ dimension: "rate",
16471
+ factor: 1,
16472
+ offset: 0
16473
+ }
16474
+ };
16475
+ /** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
16476
+ function specFor(unit) {
16477
+ return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
16478
+ }
16479
+ /** Value in `spec`'s unit → its dimension's canonical unit. */
16480
+ function toCanonical(value, spec) {
16481
+ return value * spec.factor + spec.offset;
16482
+ }
16483
+ /** Canonical-unit value → the unit described by `spec`. */
16484
+ function fromCanonical(canonical, spec) {
16485
+ return (canonical - spec.offset) / spec.factor;
16486
+ }
16487
+ /**
16488
+ * Graceful variant for the render path: returns the converted value, or
16489
+ * `undefined` when the conversion is refused (unknown unit / cross-dimension).
16490
+ * Never throws — a render caller falls back to the raw value + source unit.
16491
+ */
16492
+ function tryConvertUnit(value, from, to) {
16493
+ if (from === to) return value;
16494
+ const fromSpec = specFor(from);
16495
+ if (!fromSpec) return void 0;
16496
+ const toSpec = specFor(to);
16497
+ if (!toSpec) return void 0;
16498
+ if (fromSpec.dimension !== toSpec.dimension) return void 0;
16499
+ return fromCanonical(toCanonical(value, fromSpec), toSpec);
16500
+ }
16501
+ /**
16502
+ * A number a person types for a physical quantity — the input side of the
16503
+ * value + unit contract (D972).
16504
+ *
16505
+ * Readings are stored in the base unit of their quantity (D944); a cap method
16506
+ * param is stored in the unit its input schema DECLARES (`paramMeta`). The
16507
+ * wire pair {@link QuantityValue} says "this number is expressed in this
16508
+ * unit"; {@link convertQuantityInput} brings it to the stored unit and refuses,
16509
+ * with a typed {@link QuantityUnitError}, a unit that cannot be that quantity.
16510
+ */
16511
+ /** A unit that cannot be applied to the number it was given with. */
16512
+ var QuantityUnitError = class extends Error {
16513
+ code;
16514
+ constructor(code, message) {
16515
+ super(`[${code}] ${message}`);
16516
+ this.name = "QuantityUnitError";
16517
+ this.code = code;
16518
+ }
16519
+ };
16520
+ /** The wire pair: `value` is expressed in `unit`. */
16521
+ var QuantityValueSchema = object({
16522
+ value: number(),
16523
+ unit: string().min(1)
16524
+ });
16525
+ /**
16526
+ * Zod `.meta()` bag for a cap METHOD param that is a physical quantity — the
16527
+ * input-side twin of `readingMeta`. The value is carried in `unit` (a gram
16528
+ * portion stays in grams); the unit must belong to the quantity, checked once
16529
+ * when the cap is declared.
16530
+ */
16531
+ function paramMeta(declared) {
16532
+ const base = baseUnitOf(declared.quantity);
16533
+ if (base === void 0) throw new QuantityUnitError("unknown-unit", `quantity "${declared.quantity}" has no unit`);
16534
+ if (tryConvertUnit(1, declared.unit, base) === void 0) throw new QuantityUnitError("cross-dimension", `"${declared.unit}" is not a unit of ${declared.quantity}`);
16535
+ return {
16536
+ quantity: declared.quantity,
16537
+ unit: declared.unit
16538
+ };
16539
+ }
16540
+ object({
16541
+ quantity: ReadingQuantitySchema,
16542
+ unit: string().min(1)
16543
+ });
16071
16544
  /** How a leaf compares a device field to a value. Derived from the field's
16072
16545
  * `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
16073
16546
  var AutomationConditionOperatorSchema = _enum([
@@ -16090,8 +16563,10 @@ var AutomationConditionLeafSchema = object({
16090
16563
  string(),
16091
16564
  number(),
16092
16565
  boolean(),
16093
- array(union([string(), number()]))
16094
- ])
16566
+ array(union([string(), number()])),
16567
+ QuantityValueSchema
16568
+ ]),
16569
+ unit: string().min(1).optional()
16095
16570
  });
16096
16571
  var AutomationConditionStationaryPresentSchema = object({
16097
16572
  kind: literal("stationary-present"),
@@ -16244,7 +16719,13 @@ var AutomationCapStepSchema = object({
16244
16719
  /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
16245
16720
  * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
16246
16721
  * the expression leaf. */
16247
- args: record(string(), unknown()).optional()
16722
+ args: record(string(), unknown()).optional(),
16723
+ /**
16724
+ * The unit the person picked per arg (display only). `args` always hold the
16725
+ * value in the unit the method's input DECLARES (`paramMeta`); on a write an
16726
+ * arg may be a {@link QuantityValue}, which the save boundary converts.
16727
+ */
16728
+ argUnits: record(string(), string().min(1)).optional()
16248
16729
  });
16249
16730
  /**
16250
16731
  * The steps an internal Action may contain: cap and wait, and nothing that can
@@ -16624,6 +17105,16 @@ object({
16624
17105
  lastRunAt: number().nullable(),
16625
17106
  lastError: string().nullable()
16626
17107
  });
17108
+ /** A bare number accepted as a base unit — the save succeeded, say so. */
17109
+ var UnitWarningSchema = object({
17110
+ /** Where in the saved record, e.g. `conditions.children.0` or `steps.2.args.grams`. */
17111
+ path: string(),
17112
+ deviceId: number().int().optional(),
17113
+ field: string(),
17114
+ value: number(),
17115
+ assumedUnit: string(),
17116
+ message: string()
17117
+ });
16627
17118
  /**
16628
17119
  * notification-output — canonical, capability-gated notification delivery.
16629
17120
  *
@@ -21996,56 +22487,6 @@ var DeviceAdminLinkSchema = object({
21996
22487
  });
21997
22488
  method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema.nullable(), { auth: "admin" });
21998
22489
  /**
21999
- * Identity envelope for a device's upstream-system metadata.
22000
- *
22001
- * Two jobs:
22002
- *
22003
- * 1. **Dispatch key.** `id` is the natural identifier the SOURCE system
22004
- * uses to address this device (HA `entity_id`, Reolink MAC, ONVIF
22005
- * WS-Discovery endpoint UUID, …). Provider code keeps a
22006
- * `Map<sourceInfo.id, IDevice>` to route inbound push events in O(1)
22007
- * without round-tripping through `stableId` / `id` mappings.
22008
- *
22009
- * 2. **Stability bridge.** `uniqueId` is the *immutable* upstream id when
22010
- * one exists (HA `entity.unique_id`, Reolink MAC again — same as `id`
22011
- * for vendors where MAC is the natural key). For systems where the
22012
- * live dispatch key can be renamed (HA `entity_id` can be edited),
22013
- * providers detect renames by matching `uniqueId` and patch `id`
22014
- * in-place so dispatch keeps working.
22015
- *
22016
- * **Display unit and precision are NOT stored here.** They are a read-time
22017
- * preference (D944). A polymorphic reading stores its `quantity` here once,
22018
- * at adopt, and a date/time value stores `dateTimeShape` the same way —
22019
- * neither is repeated on each sample. The `device_class` tag is an
22020
- * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
22021
- * to derive the CamStack `DeviceRole`; it is never stored after that point.
22022
- *
22023
- * The `raw` escape hatch carries provider-specific attributes that don't
22024
- * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
22025
- * details, …). Providers control what lands here.
22026
- */
22027
- var SourceInfoSchema = object({
22028
- /** Live dispatch key — mutable when the source system allows rename. */
22029
- id: string(),
22030
- /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
22031
- system: string(),
22032
- /** Immutable upstream identifier when available (HA `unique_id`, … ).
22033
- * Used to detect rename when `id` changes. */
22034
- uniqueId: string().optional(),
22035
- /** Quantity of a polymorphic reading, written once at adopt (D944). */
22036
- quantity: ReadingQuantitySchema.optional(),
22037
- /** Date / time / datetime shape of a string reading, written once at adopt. */
22038
- dateTimeShape: _enum([
22039
- "date",
22040
- "time",
22041
- "datetime"
22042
- ]).optional(),
22043
- /** Provider-specific extras that don't fit the structured slots.
22044
- * Whitelist what goes here per provider — do NOT dump entire HA
22045
- * attribute blobs (would explode the meta JSON column). */
22046
- raw: record(string(), unknown()).optional()
22047
- });
22048
- /**
22049
22490
  * device-discovery — device-scoped capability for parents that host /
22050
22491
  * enumerate child devices (Reolink Hub / NVR, ONVIF gateway, future
22051
22492
  * integrations). The parent advertises a list of discoverable children,
@@ -58058,13 +58499,19 @@ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
58058
58499
  }), method(ActionIdInput, _void(), {
58059
58500
  kind: "mutation",
58060
58501
  auth: "admin"
58061
- }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
58502
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({
58503
+ action: ActionEntrySchema,
58504
+ warnings: array(UnitWarningSchema).optional()
58505
+ }), {
58062
58506
  kind: "mutation",
58063
58507
  auth: "admin"
58064
58508
  }), method(object({
58065
58509
  actionId: string().min(1),
58066
58510
  action: ActionInputSchema.partial()
58067
- }), object({ action: ActionEntrySchema }), {
58511
+ }), object({
58512
+ action: ActionEntrySchema,
58513
+ warnings: array(UnitWarningSchema).optional()
58514
+ }), {
58068
58515
  kind: "mutation",
58069
58516
  auth: "admin"
58070
58517
  }), method(ActionIdInput, _void(), {
@@ -58370,6 +58817,30 @@ object({
58370
58817
  at: number()
58371
58818
  }).nullable()
58372
58819
  });
58820
+ var RemoteButtonShapeSchema = _enum([
58821
+ "rect",
58822
+ "rounded",
58823
+ "round",
58824
+ "pill"
58825
+ ]);
58826
+ var RemoteButtonToneSchema = _enum([
58827
+ "neutral",
58828
+ "accent",
58829
+ "danger",
58830
+ "red",
58831
+ "green",
58832
+ "yellow",
58833
+ "blue"
58834
+ ]);
58835
+ var RemoteGroupFrameSchema = _enum([
58836
+ "none",
58837
+ "panel",
58838
+ "divider",
58839
+ "dpad",
58840
+ "rocker-v",
58841
+ "rocker-h",
58842
+ "keypad"
58843
+ ]);
58373
58844
  var RemoteLayoutButtonSchema = object({
58374
58845
  id: string().min(1).max(40),
58375
58846
  /** 0-based grid position. */
@@ -58379,12 +58850,94 @@ var RemoteLayoutButtonSchema = object({
58379
58850
  colSpan: number().int().min(1).max(24),
58380
58851
  icon: string().min(1).max(60).optional(),
58381
58852
  label: string().min(1).max(40).optional(),
58382
- role: RemoteRoleSchema
58853
+ role: RemoteRoleSchema,
58854
+ /** Absent = the plain rendering every layout had before shapes existed. */
58855
+ shape: RemoteButtonShapeSchema.optional(),
58856
+ tone: RemoteButtonToneSchema.optional(),
58857
+ /** The group this button belongs to; it must lie inside that group's rectangle. */
58858
+ groupId: string().min(1).max(40).optional()
58859
+ });
58860
+ /**
58861
+ * A rectangle of the grid that moves as ONE thing and is drawn with a frame: a
58862
+ * titled panel, a section divider, a D-pad ring. `componentId` names the palette
58863
+ * entry it came from (informational: the layout is self-contained).
58864
+ */
58865
+ var RemoteLayoutGroupSchema = object({
58866
+ id: string().min(1).max(40),
58867
+ componentId: string().min(1).max(80),
58868
+ frame: RemoteGroupFrameSchema,
58869
+ row: number().int().min(0).max(24),
58870
+ col: number().int().min(0).max(24),
58871
+ rowSpan: number().int().min(1).max(24),
58872
+ colSpan: number().int().min(1).max(24),
58873
+ title: string().min(1).max(40).optional()
58383
58874
  });
58875
+ function rectsOverlap(a, b) {
58876
+ 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;
58877
+ }
58878
+ function rectContains(outer, inner) {
58879
+ 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;
58880
+ }
58881
+ /**
58882
+ * The group rules of a layout: inside the grid, no two groups overlap, a
58883
+ * member lies inside its group, a divider holds nothing, and a button that is
58884
+ * not a member never sits inside someone else's rectangle (a group moves as
58885
+ * one thing, so a stray key under it would be left behind).
58886
+ */
58887
+ function refineGroups(grid, ctx) {
58888
+ const groups = grid.groups ?? [];
58889
+ const byId = /* @__PURE__ */ new Map();
58890
+ const fail = (message, path) => {
58891
+ ctx.addIssue({
58892
+ code: "custom",
58893
+ message,
58894
+ path
58895
+ });
58896
+ };
58897
+ groups.forEach((group, index) => {
58898
+ if (byId.has(group.id)) fail(`group id "${group.id}" is used twice`, [
58899
+ "groups",
58900
+ index,
58901
+ "id"
58902
+ ]);
58903
+ byId.set(group.id, group);
58904
+ if (group.row + group.rowSpan > grid.rows || group.col + group.colSpan > grid.columns) fail(`group "${group.id}" leaves the grid`, ["groups", index]);
58905
+ for (let other = 0; other < index; other += 1) {
58906
+ const earlier = groups[other];
58907
+ if (earlier !== void 0 && rectsOverlap(earlier, group)) {
58908
+ fail(`groups "${earlier.id}" and "${group.id}" overlap`, ["groups", index]);
58909
+ return;
58910
+ }
58911
+ }
58912
+ });
58913
+ grid.buttons.forEach((button, index) => {
58914
+ if (button.groupId !== void 0) {
58915
+ const group = byId.get(button.groupId);
58916
+ if (group === void 0) {
58917
+ fail(`button "${button.id}" names the unknown group "${button.groupId}"`, [
58918
+ "buttons",
58919
+ index,
58920
+ "groupId"
58921
+ ]);
58922
+ return;
58923
+ }
58924
+ if (group.frame === "divider") fail(`a divider group ("${group.id}") holds no buttons`, [
58925
+ "buttons",
58926
+ index,
58927
+ "groupId"
58928
+ ]);
58929
+ if (!rectContains(group, button)) fail(`button "${button.id}" lies outside its group "${group.id}"`, ["buttons", index]);
58930
+ return;
58931
+ }
58932
+ const foreign = groups.find((group) => rectsOverlap(group, button));
58933
+ if (foreign !== void 0) fail(`button "${button.id}" lies inside group "${foreign.id}" but is not a member`, ["buttons", index]);
58934
+ });
58935
+ }
58384
58936
  object({
58385
58937
  columns: number().int().min(1).max(24),
58386
58938
  rows: number().int().min(1).max(24),
58387
- buttons: array(RemoteLayoutButtonSchema).max(200)
58939
+ buttons: array(RemoteLayoutButtonSchema).max(200),
58940
+ groups: array(RemoteLayoutGroupSchema).max(50).optional()
58388
58941
  }).superRefine((grid, ctx) => {
58389
58942
  const ids = /* @__PURE__ */ new Set();
58390
58943
  const taken = /* @__PURE__ */ new Map();
@@ -58421,6 +58974,7 @@ object({
58421
58974
  taken.set(cell, button.id);
58422
58975
  }
58423
58976
  });
58977
+ refineGroups(grid, ctx);
58424
58978
  });
58425
58979
  var RemoteBoundCommandSchema = object({
58426
58980
  role: RemoteRoleSchema,
@@ -58552,6 +59106,91 @@ method(object({ kind: TemplateKindSchema.optional() }), array(TemplateRecordSche
58552
59106
  kind: "mutation",
58553
59107
  auth: "admin"
58554
59108
  });
59109
+ var RemoteComponentCategorySchema = _enum([
59110
+ "power",
59111
+ "navigation",
59112
+ "volume",
59113
+ "numbers",
59114
+ "media",
59115
+ "colour",
59116
+ "apps",
59117
+ "layout"
59118
+ ]);
59119
+ /** `<namespace>:<name>`, both lowercase slugs — `core:dpad`, `<addonId>:<name>`. */
59120
+ var COMPONENT_ID_RE = /^[a-z0-9][a-z0-9_.-]*:[a-z0-9][a-z0-9_-]*$/;
59121
+ var RemoteComponentButtonSchema = object({
59122
+ /** Joined to the instance id when the component is inserted. */
59123
+ idSuffix: string().regex(/^[a-z0-9][a-z0-9_-]{0,19}$/, { message: "a short lowercase slug" }),
59124
+ row: number().int().min(0).max(24),
59125
+ col: number().int().min(0).max(24),
59126
+ rowSpan: number().int().min(1).max(24),
59127
+ colSpan: number().int().min(1).max(24),
59128
+ role: RemoteRoleSchema,
59129
+ icon: string().min(1).max(60).optional(),
59130
+ label: string().min(1).max(40).optional(),
59131
+ shape: RemoteButtonShapeSchema.optional(),
59132
+ tone: RemoteButtonToneSchema.optional()
59133
+ });
59134
+ var RemoteLayoutComponentSchema = object({
59135
+ id: string().max(80).regex(COMPONENT_ID_RE, { message: "<namespace>:<name>, lowercase" }),
59136
+ name: string().min(1).max(60),
59137
+ nameI18n: object({
59138
+ en: string().min(1).max(60),
59139
+ it: string().min(1).max(60)
59140
+ }).optional(),
59141
+ /** A display icon key, resolved by the UI like a button icon. */
59142
+ icon: string().min(1).max(60),
59143
+ category: RemoteComponentCategorySchema,
59144
+ size: object({
59145
+ rows: number().int().min(1).max(24),
59146
+ cols: number().int().min(1).max(24)
59147
+ }),
59148
+ /** The frame drawn around the whole component, or null for loose buttons. */
59149
+ group: object({
59150
+ frame: RemoteGroupFrameSchema,
59151
+ title: string().min(1).max(40).optional()
59152
+ }).nullable(),
59153
+ buttons: array(RemoteComponentButtonSchema).max(40)
59154
+ }).superRefine((component, ctx) => {
59155
+ const suffixes = /* @__PURE__ */ new Set();
59156
+ component.buttons.forEach((button, index) => {
59157
+ if (suffixes.has(button.idSuffix)) ctx.addIssue({
59158
+ code: "custom",
59159
+ message: `button "${button.idSuffix}" is listed twice`,
59160
+ path: [
59161
+ "buttons",
59162
+ index,
59163
+ "idSuffix"
59164
+ ]
59165
+ });
59166
+ suffixes.add(button.idSuffix);
59167
+ if (button.row + button.rowSpan > component.size.rows || button.col + button.colSpan > component.size.cols) {
59168
+ ctx.addIssue({
59169
+ code: "custom",
59170
+ message: `button "${button.idSuffix}" leaves the component`,
59171
+ path: ["buttons", index]
59172
+ });
59173
+ return;
59174
+ }
59175
+ for (let other = 0; other < index; other += 1) {
59176
+ const earlier = component.buttons[other];
59177
+ if (earlier !== void 0 && rectsOverlap(earlier, button)) {
59178
+ ctx.addIssue({
59179
+ code: "custom",
59180
+ message: `buttons "${earlier.idSuffix}" and "${button.idSuffix}" overlap`,
59181
+ path: ["buttons", index]
59182
+ });
59183
+ return;
59184
+ }
59185
+ }
59186
+ });
59187
+ if (component.group?.frame === "divider" && component.buttons.length > 0) ctx.addIssue({
59188
+ code: "custom",
59189
+ message: "a divider component holds no buttons",
59190
+ path: ["buttons"]
59191
+ });
59192
+ });
59193
+ method(object({}), array(RemoteLayoutComponentSchema), { auth: "protected" });
58555
59194
  DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _void(), {
58556
59195
  kind: "mutation",
58557
59196
  auth: "protected"
@@ -62711,6 +63350,14 @@ var PetFeederDeviceStatusSchema = _enum([
62711
63350
  "on_batteries"
62712
63351
  ]);
62713
63352
  var gramsPortion = number().int().min(4).max(200);
63353
+ /** A portion REQUESTED of the feeder is a mass the method takes in GRAMS (the
63354
+ * hardware's own unit). The bounds are grams; a caller entering another unit
63355
+ * converts to grams first (`paramMeta`, D972). The STATUS fields above stay
63356
+ * plain grams — they are not readings in the kilogram base. */
63357
+ var gramsParam = gramsPortion.meta(paramMeta({
63358
+ quantity: "mass",
63359
+ unit: "g"
63360
+ }));
62714
63361
  object({
62715
63362
  /** Food in the TANK (hopper), as the feeder's own sensor reports it
62716
63363
  * (`state.food`). A coarse level code, not grams — `0` is empty. The
@@ -62785,9 +63432,9 @@ object({
62785
63432
  });
62786
63433
  DeviceType.PetFeeder, method(object({
62787
63434
  deviceId: number().int().nonnegative(),
62788
- grams: gramsPortion.optional(),
62789
- hopper1: gramsPortion.optional(),
62790
- hopper2: gramsPortion.optional()
63435
+ grams: gramsParam.optional(),
63436
+ hopper1: gramsParam.optional(),
63437
+ hopper2: gramsParam.optional()
62791
63438
  }), _void(), {
62792
63439
  kind: "mutation",
62793
63440
  auth: "protected"
@@ -67479,6 +68126,87 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
67479
68126
  kind: "mutation",
67480
68127
  auth: "admin"
67481
68128
  });
68129
+ var btn = (idSuffix, row, col, init) => ({
68130
+ idSuffix,
68131
+ row,
68132
+ col,
68133
+ rowSpan: 1,
68134
+ colSpan: 1,
68135
+ ...init
68136
+ });
68137
+ function rocker(id, name, it, frame, title, buttons, size) {
68138
+ return {
68139
+ id,
68140
+ name,
68141
+ nameI18n: {
68142
+ en: name,
68143
+ it
68144
+ },
68145
+ icon: frame === "rocker-v" ? "chevrons-up-down" : "chevrons-left-right",
68146
+ category: id.includes("channel") || id.includes("volume") ? "volume" : "navigation",
68147
+ size,
68148
+ group: title === null ? { frame } : {
68149
+ frame,
68150
+ title
68151
+ },
68152
+ buttons
68153
+ };
68154
+ }
68155
+ btn("power", 0, 0, {
68156
+ role: "power",
68157
+ shape: "round",
68158
+ tone: "danger"
68159
+ }), btn("up", 0, 1, { role: "up" }), btn("left", 1, 0, { role: "left" }), btn("ok", 1, 1, {
68160
+ role: "ok",
68161
+ shape: "round",
68162
+ tone: "accent"
68163
+ }), 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, {
68164
+ role: "volume_up",
68165
+ shape: "pill"
68166
+ }), btn("down", 1, 0, {
68167
+ role: "volume_down",
68168
+ shape: "pill"
68169
+ })], {
68170
+ rows: 2,
68171
+ cols: 1
68172
+ }), rocker("core:rocker-channel-v", "Channel rocker", "Bilanciere canali", "rocker-v", "CH", [btn("up", 0, 0, {
68173
+ role: "channel_up",
68174
+ shape: "pill"
68175
+ }), btn("down", 1, 0, {
68176
+ role: "channel_down",
68177
+ shape: "pill"
68178
+ })], {
68179
+ rows: 2,
68180
+ cols: 1
68181
+ }), rocker("core:rocker-h", "Minus / plus rocker", "Bilanciere meno / più", "rocker-h", null, [btn("minus", 0, 0, {
68182
+ role: "custom:minus",
68183
+ label: "−",
68184
+ shape: "pill"
68185
+ }), btn("plus", 0, 1, {
68186
+ role: "custom:plus",
68187
+ label: "+",
68188
+ shape: "pill"
68189
+ })], {
68190
+ rows: 1,
68191
+ cols: 2
68192
+ }), [...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, {
68193
+ role: "custom:color_red",
68194
+ tone: "red"
68195
+ }), btn("green", 0, 1, {
68196
+ role: "custom:color_green",
68197
+ tone: "green"
68198
+ }), btn("yellow", 0, 2, {
68199
+ role: "custom:color_yellow",
68200
+ tone: "yellow"
68201
+ }), btn("blue", 0, 3, {
68202
+ role: "custom:color_blue",
68203
+ tone: "blue"
68204
+ }), 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" }), [
68205
+ 1,
68206
+ 2,
68207
+ 3,
68208
+ 4
68209
+ ].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" });
67482
68210
  discriminatedUnion("kind", [object({
67483
68211
  kind: literal("raw"),
67484
68212
  timings: array(number().int()),
@@ -75441,6 +76169,13 @@ Object.freeze({
75441
76169
  access: "create",
75442
76170
  role: "admin"
75443
76171
  },
76172
+ "remoteLayoutComponents.listComponents": {
76173
+ capName: "remote-layout-components",
76174
+ capScope: "system",
76175
+ addonId: null,
76176
+ access: "view",
76177
+ role: "admin"
76178
+ },
75444
76179
  "runningState.getStatus": {
75445
76180
  capName: "running-state",
75446
76181
  capScope: "device",
@@ -79484,7 +80219,8 @@ var APPLIANCE_CAP_VOCABULARY = {
79484
80219
  min: 4,
79485
80220
  max: 200,
79486
80221
  step: 1,
79487
- unit: "g"
80222
+ unit: "g",
80223
+ quantity: "mass"
79488
80224
  },
79489
80225
  optional: true
79490
80226
  }]
@@ -79503,7 +80239,8 @@ var APPLIANCE_CAP_VOCABULARY = {
79503
80239
  min: 4,
79504
80240
  max: 200,
79505
80241
  step: 1,
79506
- unit: "g"
80242
+ unit: "g",
80243
+ quantity: "mass"
79507
80244
  },
79508
80245
  optional: true
79509
80246
  }, {
@@ -79514,7 +80251,8 @@ var APPLIANCE_CAP_VOCABULARY = {
79514
80251
  min: 4,
79515
80252
  max: 200,
79516
80253
  step: 1,
79517
- unit: "g"
80254
+ unit: "g",
80255
+ quantity: "mass"
79518
80256
  },
79519
80257
  optional: true
79520
80258
  }]
@@ -81614,6 +82352,7 @@ var SYSTEM_CAP_VOCABULARY = {
81614
82352
  "recording-export": system("recording exports — requested from the timeline, not an automation step"),
81615
82353
  records: system(INTERNAL),
81616
82354
  remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
82355
+ "remote-layout-components": system("the palette of the remote template editor — read-only data, never actuated"),
81617
82356
  "server-management": system(CLUSTER),
81618
82357
  "settings-store": system(INTERNAL),
81619
82358
  "smtp-provider": system(INTERNAL),