@camstack/addon-provider-bosch 0.1.44 → 0.1.46

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.
@@ -802,7 +802,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
802
802
  return EventCategory;
803
803
  }({});
804
804
  //#endregion
805
- //#region ../types/dist/device-type-CbDhVwFP.mjs
805
+ //#region ../types/dist/device-type-DTTZKB2I.mjs
806
806
  /**
807
807
  import { errMsg } from '@camstack/types'
808
808
  * Extract a human-readable message from an unknown error value.
@@ -928,6 +928,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
928
928
  * (`reboot`, `device-admin-link`). Products are NOT its children — they are
929
929
  * adopted beside it — and its connection settings stay on the integration. */
930
930
  DeviceType["Gateway"] = "gateway";
931
+ /** A heat pump (any vendor): the compressor unit with its circuits (heating, hot
932
+ * water, cooling, solar), its system modes and its power. One main read-only
933
+ * `heat-pump-status` cap carries all of it; `heat-pump-control` sets it (D1026). */
934
+ DeviceType["HeatPump"] = "heat-pump";
931
935
  return DeviceType;
932
936
  }({});
933
937
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -6012,7 +6016,7 @@ var ZodIssueCode = {
6012
6016
  var ZodFirstPartyTypeKind;
6013
6017
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6014
6018
  //#endregion
6015
- //#region ../types/dist/sleep-CS5Xp1iQ.mjs
6019
+ //#region ../types/dist/sleep-DnVqHJc4.mjs
6016
6020
  /**
6017
6021
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6018
6022
  * window to float samples (D455).
@@ -8759,16 +8763,6 @@ var BaseAddon = class {
8759
8763
  return null;
8760
8764
  }
8761
8765
  /**
8762
- * The form schema of ONE integration instance, or `null` to use the scoped form.
8763
- *
8764
- * Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
8765
- * needs its connection fields, the default integration the addon's own sections. It is the
8766
- * SHAPE of the form only — the values live on the instance record.
8767
- */
8768
- async integrationFormSchema(_instance) {
8769
- return null;
8770
- }
8771
- /**
8772
8766
  * D986 scoped settings: the form shape of the CLUSTER-wide settings one capability this addon
8773
8767
  * provides owns, or `null` when it has none. `cap` is a capability name the addon's manifest
8774
8768
  * declares. Stored as `addon-settings` rows `<addonId>.<field>`. The keys of this schema, of
@@ -8796,8 +8790,7 @@ var BaseAddon = class {
8796
8790
  }
8797
8791
  /**
8798
8792
  * The host's read of the scoped declarations: the schema one capability declares for `scope`,
8799
- * or `null`. Beside {@link getIntegrationFormSchema}, the same shape: the hook is protected, the
8800
- * host asks this method.
8793
+ * or `null`. The hook is protected, the host asks this method.
8801
8794
  */
8802
8795
  async getScopedSettingsSchema(scope, cap) {
8803
8796
  switch (scope) {
@@ -8965,22 +8958,6 @@ var BaseAddon = class {
8965
8958
  return Object.fromEntries([...collectHydratedFieldValues(form)].filter(([, value]) => value !== void 0));
8966
8959
  }
8967
8960
  /**
8968
- * What the create wizard and the integration edit page render for one instance: the addon's
8969
- * per-instance schema when it declares one, else its global schema (what they always showed).
8970
- */
8971
- async getIntegrationFormSchema(instance, nodeId) {
8972
- const scoped = await this.integrationFormSchema(instance);
8973
- if (scoped === null) return this.scopedForm(nodeId);
8974
- return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
8975
- }
8976
- /** The form of the scoped schemas this addon declared, hydrated from its store. */
8977
- async scopedForm(nodeId) {
8978
- await this.loadScopedSchemas();
8979
- const schema = this.scopedFormSchema();
8980
- if (!schema) return { sections: [] };
8981
- return hydrateSchema(schema, await this.readRawProjectedStore(nodeId), { inlineSecretsAsViews: true });
8982
- }
8983
- /**
8984
8961
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
8985
8962
  * every `perNode: true` field carries THAT node's scoped value on its bare
8986
8963
  * key (absent scoped key ⇒ key absent, so the schema `default` wins — no
@@ -9167,12 +9144,6 @@ var BaseAddon = class {
9167
9144
  this._nodeKeysByCap = /* @__PURE__ */ new Map();
9168
9145
  this._addonSecretTree = null;
9169
9146
  }
9170
- /** The form of an addon's scoped schemas: their sections, each shown once. */
9171
- scopedFormSchema(cap) {
9172
- const byId = /* @__PURE__ */ new Map();
9173
- for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
9174
- return byId.size === 0 ? null : { sections: [...byId.values()] };
9175
- }
9176
9147
  /** Every loaded scoped schema of `cap`, or of every loaded capability. */
9177
9148
  loadedScopedSchemas(cap) {
9178
9149
  if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
@@ -9762,6 +9733,10 @@ function normalizeAddonInitResult(result) {
9762
9733
  if (Array.isArray(result)) return { providers: result };
9763
9734
  return result;
9764
9735
  }
9736
+ object({
9737
+ code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
9738
+ params: record(string(), union([string(), number$1()])).optional()
9739
+ });
9765
9740
  object({
9766
9741
  field: string().min(1),
9767
9742
  on: literal("advance")
@@ -9838,12 +9813,6 @@ function systemMethod(input, output, options) {
9838
9813
  systemOnly: true
9839
9814
  };
9840
9815
  }
9841
- /**
9842
- * A method a SOURCE of a collection cap may legitimately not serve — OPTIONAL
9843
- * on `InferProvider`. The `providerOptional: true` literal is what
9844
- * `InferProvider` keys on; see {@link CapabilityMethodSchema.providerOptional}
9845
- * for when this is the honest answer and when it is a soft stub.
9846
- */
9847
9816
  function optionalMethod(input, output, options) {
9848
9817
  return {
9849
9818
  ...method(input, output, options),
@@ -10653,7 +10622,7 @@ function t(en, it) {
10653
10622
  * Things an operator switches, opens, sets or arms: lights, plugs, sirens,
10654
10623
  * covers, valves, locks, climate, fans, alarm panels.
10655
10624
  */
10656
- var ON = t("On", "Acceso");
10625
+ var ON$1 = t("On", "Acceso");
10657
10626
  var PERCENT = t("Percentage", "Percentuale");
10658
10627
  var OPEN_CLOSE_STATES = [
10659
10628
  {
@@ -10713,7 +10682,7 @@ var ACTUATOR_CAP_VOCABULARY = {
10713
10682
  state: [{
10714
10683
  kind: "boolean",
10715
10684
  field: "on",
10716
- label: ON,
10685
+ label: ON$1,
10717
10686
  whenTrue: t("When {device} turns on", "Quando {device} si accende"),
10718
10687
  whenFalse: t("When {device} turns off", "Quando {device} si spegne"),
10719
10688
  icon: "power"
@@ -11879,7 +11848,7 @@ var ACTUATOR_CAP_VOCABULARY = {
11879
11848
  state: [{
11880
11849
  kind: "boolean",
11881
11850
  field: "on",
11882
- label: ON,
11851
+ label: ON$1,
11883
11852
  whenTrue: t("When {device} turns on", "Quando {device} si accende"),
11884
11853
  whenFalse: t("When {device} turns off", "Quando {device} si spegne"),
11885
11854
  icon: "power"
@@ -14291,6 +14260,133 @@ var CAMERA_CAP_VOCABULARY = {
14291
14260
  }
14292
14261
  };
14293
14262
  /**
14263
+ * The heat pump (D1026): its system modes are phrased for automations, each with the
14264
+ * values the gateway really accepts; the per-circuit parameters need a circuit and are
14265
+ * set from the device page.
14266
+ */
14267
+ var OFF = {
14268
+ value: "off",
14269
+ label: t("Off", "Off")
14270
+ };
14271
+ var AUTO = {
14272
+ value: "auto",
14273
+ label: t("Auto", "Auto")
14274
+ };
14275
+ var ON = {
14276
+ value: "on",
14277
+ label: t("On", "On")
14278
+ };
14279
+ var MANUAL = {
14280
+ value: "manual",
14281
+ label: t("Manual", "Manuale")
14282
+ };
14283
+ var HOLIDAY_PROGRAMMES = [OFF, ...[
14284
+ 1,
14285
+ 2,
14286
+ 3,
14287
+ 4,
14288
+ 5,
14289
+ 6,
14290
+ 7,
14291
+ 8
14292
+ ].map((n) => ({
14293
+ value: `hm${n}`,
14294
+ label: t(`Programme ${n}`, `Programma ${n}`)
14295
+ }))];
14296
+ var PER_CIRCUIT = "Needs a circuit: set from the device page, not offered to automations";
14297
+ var HEAT_PUMP_CAP_VOCABULARY = {
14298
+ "heat-pump-status": {
14299
+ scope: "device",
14300
+ noun: t("Heat pump", "Pompa di calore")
14301
+ },
14302
+ "heat-pump-control": {
14303
+ scope: "device",
14304
+ noun: t("Heat pump settings", "Impostazioni pompa di calore"),
14305
+ actions: [
14306
+ {
14307
+ id: "heatPump.silent",
14308
+ method: "setSilentMode",
14309
+ label: t("Set silent mode", "Imposta la modalità silenziosa"),
14310
+ phrase: t("Set silent mode of {device} to {mode}", "Imposta la modalità silenziosa di {device} su {mode}"),
14311
+ icon: "volume-x",
14312
+ openArgs: [{
14313
+ key: "mode",
14314
+ label: t("Silent mode", "Modalità silenziosa"),
14315
+ offer: {
14316
+ kind: "enum",
14317
+ values: [
14318
+ OFF,
14319
+ AUTO,
14320
+ ON
14321
+ ]
14322
+ }
14323
+ }]
14324
+ },
14325
+ {
14326
+ id: "heatPump.away",
14327
+ method: "setAwayMode",
14328
+ label: t("Set away mode", "Imposta la modalità assente"),
14329
+ phrase: t("Set away mode of {device} to {mode}", "Imposta la modalità assente di {device} su {mode}"),
14330
+ icon: "plane",
14331
+ openArgs: [{
14332
+ key: "mode",
14333
+ label: t("Away mode", "Modalità assente"),
14334
+ offer: {
14335
+ kind: "enum",
14336
+ values: [OFF, ON]
14337
+ }
14338
+ }]
14339
+ },
14340
+ {
14341
+ id: "heatPump.additionalHeater",
14342
+ method: "setAdditionalHeaterMode",
14343
+ label: t("Set additional heater", "Imposta il riscaldatore aggiuntivo"),
14344
+ phrase: t("Set the additional heater of {device} to {mode}", "Imposta il riscaldatore aggiuntivo di {device} su {mode}"),
14345
+ icon: "flame",
14346
+ openArgs: [{
14347
+ key: "mode",
14348
+ label: t("Additional heater", "Riscaldatore aggiuntivo"),
14349
+ offer: {
14350
+ kind: "enum",
14351
+ values: [
14352
+ OFF,
14353
+ MANUAL,
14354
+ AUTO
14355
+ ]
14356
+ }
14357
+ }]
14358
+ },
14359
+ {
14360
+ id: "heatPump.holiday",
14361
+ method: "setHolidayMode",
14362
+ label: t("Set holiday mode", "Imposta la modalità vacanza"),
14363
+ phrase: t("Set holiday mode of {device} to {mode}", "Imposta la modalità vacanza di {device} su {mode}"),
14364
+ icon: "tree-palm",
14365
+ openArgs: [{
14366
+ key: "mode",
14367
+ label: t("Holiday programme", "Programma vacanza"),
14368
+ offer: {
14369
+ kind: "enum",
14370
+ values: HOLIDAY_PROGRAMMES
14371
+ }
14372
+ }]
14373
+ }
14374
+ ],
14375
+ notOperatorFacing: {
14376
+ setCircuitOperationMode: PER_CIRCUIT,
14377
+ setCircuitHeatCoolMode: PER_CIRCUIT,
14378
+ setCircuitSummerWinter: PER_CIRCUIT,
14379
+ setCircuitComfortSetpoint: PER_CIRCUIT,
14380
+ setCircuitEcoSetpoint: PER_CIRCUIT,
14381
+ setCircuitMaxFlowTemp: PER_CIRCUIT,
14382
+ setDhwOperationMode: PER_CIRCUIT,
14383
+ setDhwTemperatureLevel: PER_CIRCUIT,
14384
+ setDhwChargeDuration: PER_CIRCUIT,
14385
+ startExtraHotWater: PER_CIRCUIT
14386
+ }
14387
+ }
14388
+ };
14389
+ /**
14294
14390
  * Things an operator WATCHES: contacts, motion, leaks, smoke, measurements,
14295
14391
  * presence, reachability, batteries, doorbells and event sources.
14296
14392
  */
@@ -15012,6 +15108,7 @@ var CAP_VOCABULARY_BY_NAME = {
15012
15108
  ...ACTUATOR_CAP_VOCABULARY,
15013
15109
  ...APPLIANCE_CAP_VOCABULARY,
15014
15110
  ...CAMERA_CAP_VOCABULARY,
15111
+ ...HEAT_PUMP_CAP_VOCABULARY,
15015
15112
  ...SENSOR_CAP_VOCABULARY,
15016
15113
  ...SYSTEM_CAP_VOCABULARY
15017
15114
  };
@@ -15259,6 +15356,8 @@ var CAP_NAMES = Object.freeze([
15259
15356
  "group-active",
15260
15357
  "group-commands",
15261
15358
  "group-status",
15359
+ "heat-pump-control",
15360
+ "heat-pump-status",
15262
15361
  "heating-valve",
15263
15362
  "hero-layout",
15264
15363
  "humidifier",
@@ -16124,6 +16223,48 @@ Object.freeze({
16124
16223
  "valueType": "number"
16125
16224
  }
16126
16225
  ],
16226
+ "heat-pump-status": [
16227
+ {
16228
+ "path": "power.thermalW",
16229
+ "valueType": "number"
16230
+ },
16231
+ {
16232
+ "path": "power.electricalW",
16233
+ "valueType": "number"
16234
+ },
16235
+ {
16236
+ "path": "power.compressorPct",
16237
+ "valueType": "number"
16238
+ },
16239
+ {
16240
+ "path": "temperatures.supply",
16241
+ "valueType": "number"
16242
+ },
16243
+ {
16244
+ "path": "temperatures.return",
16245
+ "valueType": "number"
16246
+ },
16247
+ {
16248
+ "path": "temperatures.outdoor",
16249
+ "valueType": "number"
16250
+ },
16251
+ {
16252
+ "path": "energy.thermalKwh",
16253
+ "valueType": "number"
16254
+ },
16255
+ {
16256
+ "path": "energy.electricalKwh",
16257
+ "valueType": "number"
16258
+ },
16259
+ {
16260
+ "path": "waterPressureBar",
16261
+ "valueType": "number"
16262
+ },
16263
+ {
16264
+ "path": "starts",
16265
+ "valueType": "number"
16266
+ }
16267
+ ],
16127
16268
  "heating-valve": [{
16128
16269
  "path": "position",
16129
16270
  "valueType": "number"
@@ -17264,12 +17405,8 @@ Object.freeze({
17264
17405
  "uploadDataset": 72e5
17265
17406
  }
17266
17407
  });
17267
- object({
17268
- code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
17269
- params: record(string(), union([string(), number$1()])).optional()
17270
- });
17271
17408
  //#endregion
17272
- //#region ../types/dist/composition-CXCX78IR.mjs
17409
+ //#region ../types/dist/composition-C6TZGqUb.mjs
17273
17410
  /**
17274
17411
  * The ffmpeg build every CamStack node downloads, per platform and arch.
17275
17412
  *
@@ -18983,25 +19120,6 @@ var RequiredAddonUpdateSchema = object({
18983
19120
  installed: string().nullable()
18984
19121
  });
18985
19122
  /**
18986
- * Which integration instance a settings form is for. An addon that serves several instances
18987
- * (`instanceMode: multiple`) can show a different form per instance — an NVR integration
18988
- * needs its connection, the default integration needs the addon's own sections.
18989
- *
18990
- * - `integrationId` is `null` in the create wizard: nothing exists yet, the operator is
18991
- * setting up a NEW instance.
18992
- * - `mode` is the wizard mode of the integration type (`getAvailableTypes`).
18993
- * - `candidateStableId` is the discovery candidate a wizard was seeded from, if any.
18994
- */
18995
- var IntegrationInstanceRefSchema = object({
18996
- integrationId: string().nullable(),
18997
- mode: _enum([
18998
- "standalone",
18999
- "account",
19000
- "broker"
19001
- ]),
19002
- candidateStableId: string().optional()
19003
- });
19004
- /**
19005
19123
  * notification-output — canonical, capability-gated notification delivery.
19006
19124
  *
19007
19125
  * Apprise-derived model (see
@@ -44435,21 +44553,44 @@ var AutomationCapStepBaseSchema = object({
44435
44553
  */
44436
44554
  argUnits: record(string(), string().min(1)).optional()
44437
44555
  });
44438
- var AutomationCapStepSchema = AutomationCapStepBaseSchema.refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
44556
+ /**
44557
+ * The steps an internal Action may contain: cap and wait, and nothing that can
44558
+ * call back into an Action — so an Action can never reach itself (no cycles).
44559
+ * This is the LEAF shape: no condition of its own.
44560
+ */
44561
+ var LeafActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepBaseSchema.refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
44439
44562
  message: "a cap step needs a target",
44440
44563
  path: ["target"]
44441
44564
  }).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
44442
44565
  message: "a domain target needs the vocabulary action it applies to (actionId)",
44443
44566
  path: ["actionId"]
44444
- });
44567
+ })]);
44445
44568
  /**
44446
- * The steps an internal Action may contain: cap and wait, and nothing that can
44447
- * call back into an Action — so an Action can never reach itself (no cycles).
44569
+ * The optional guard of a step (#472): `if` is the ONE `when` tree (D980), read
44570
+ * on the state mirror when the step is reached; `else` runs when it is false.
44571
+ * An `else` holds leaf steps only — the nesting is bounded at ONE level, so a
44572
+ * branch can never grow a tree the trace has to draw.
44448
44573
  */
44449
- var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
44574
+ var stepGuardShape = {
44575
+ if: AutomationWhenSchema.optional(),
44576
+ else: array(LeafActionStepSchema).max(10).optional()
44577
+ };
44578
+ var elseNeedsIf = {
44579
+ message: "an else list needs the condition it is the other side of (if)",
44580
+ path: ["else"]
44581
+ };
44582
+ var GuardedWaitStepSchema = AutomationWaitStepSchema.extend(stepGuardShape).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
44583
+ var GuardedCapStepSchema = AutomationCapStepBaseSchema.extend(stepGuardShape).refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
44584
+ message: "a cap step needs a target",
44585
+ path: ["target"]
44586
+ }).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
44587
+ message: "a domain target needs the vocabulary action it applies to (actionId)",
44588
+ path: ["actionId"]
44589
+ }).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
44590
+ var ActionStepSchema = discriminatedUnion("kind", [GuardedWaitStepSchema, GuardedCapStepSchema]);
44450
44591
  var AutomationActionSchema = discriminatedUnion("kind", [
44451
- AutomationWaitStepSchema,
44452
- AutomationCapStepSchema,
44592
+ GuardedWaitStepSchema,
44593
+ GuardedCapStepSchema,
44453
44594
  object({
44454
44595
  kind: literal("action"),
44455
44596
  /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
@@ -44829,7 +44970,7 @@ var StepDeviceTraceSchema = object({
44829
44970
  outcome: _enum(["ok", "error"]),
44830
44971
  message: string().optional()
44831
44972
  });
44832
- var AutomationStepTraceSchema = object({
44973
+ var StepTraceBaseSchema = object({
44833
44974
  /** `actions.<i>` */
44834
44975
  path: string().min(1),
44835
44976
  kind: string().min(1),
@@ -44848,15 +44989,35 @@ var AutomationStepTraceSchema = object({
44848
44989
  * is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
44849
44990
  */
44850
44991
  args: record(string(), TraceValueSchema).optional(),
44851
- /** `empty`: the target resolved to no device, so nothing was done — never silently `ok`. */
44992
+ /**
44993
+ * The device values a templated arg printed as `—`, one `ref: reason` each
44994
+ * (D1021): why a sentence says "—" where a number was expected. Bounded.
44995
+ */
44996
+ templates: array(string()).max(20).optional(),
44997
+ /**
44998
+ * What the step's own `if` read when the step was reached (#472): `true` the
44999
+ * step ran, `false` its `else` ran (or nothing did), `unknown` a state leaf
45000
+ * could not be read, so NEITHER side ran. Absent on an unguarded step.
45001
+ */
45002
+ condition: _enum([
45003
+ "true",
45004
+ "false",
45005
+ "unknown"
45006
+ ]).optional(),
45007
+ /** Why the condition read what it read, in the evaluator's own words. */
45008
+ conditionReason: string().optional(),
45009
+ /** `empty`: the target resolved to no device, so nothing was done — never silently `ok`. `skipped`: its `if` was false (or unknown) and nothing ran in its place. */
44852
45010
  outcome: _enum([
44853
45011
  "ok",
44854
45012
  "error",
44855
45013
  "not-run",
44856
- "empty"
45014
+ "empty",
45015
+ "skipped"
44857
45016
  ]),
44858
45017
  message: string().optional()
44859
45018
  });
45019
+ /** A step trace; the `else` steps that ran are one level of the same shape (the nesting is bounded at one). */
45020
+ var AutomationStepTraceSchema = StepTraceBaseSchema.extend({ else: array(StepTraceBaseSchema).optional() });
44860
45021
  var AutomationRunTraceSchema = object({
44861
45022
  runId: string().min(1),
44862
45023
  startedAt: number$1(),
@@ -45032,6 +45193,7 @@ _enum([
45032
45193
  "invalid-variables",
45033
45194
  "invalid-steps",
45034
45195
  "not-linked",
45196
+ "template-scope",
45035
45197
  "busy"
45036
45198
  ]);
45037
45199
  object({
@@ -45356,10 +45518,6 @@ method(object({
45356
45518
  scope: ScopedSettingsScopeSchema,
45357
45519
  nodeId: string().optional()
45358
45520
  }), record(string(), unknown()).nullable()), method(object({
45359
- addonId: string(),
45360
- nodeId: string().optional(),
45361
- instance: IntegrationInstanceRefSchema
45362
- }), SettingsSchemaWithValuesSchema.nullable()), method(object({
45363
45521
  addonId: string(),
45364
45522
  nodeId: string().optional()
45365
45523
  }), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
@@ -50599,7 +50757,7 @@ var mediaPlayerCapability = {
50599
50757
  isMuted: { kind: "boolean" }
50600
50758
  } }
50601
50759
  };
50602
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.MediaPlayer;
50760
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.HeatPump, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.MediaPlayer;
50603
50761
  var NUMBER = {
50604
50762
  kind: "fixed",
50605
50763
  result: ["number"]
@@ -52618,6 +52776,43 @@ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(Expor
52618
52776
  auth: "admin"
52619
52777
  });
52620
52778
  var DeviceAliasSchema = string().max(64).regex(/^[a-z][a-z0-9_]*$/);
52779
+ var DeviceTemplateSchema = string().min(1).max(4096);
52780
+ var DeviceTemplateRefusalSchema = object({
52781
+ reason: _enum([
52782
+ "too-long",
52783
+ "syntax",
52784
+ "forbidden-syntax",
52785
+ "forbidden-member",
52786
+ "unknown-filter",
52787
+ "ambiguous-field",
52788
+ "busy",
52789
+ "budget-exceeded",
52790
+ "timeout",
52791
+ "output-too-large",
52792
+ "render-failed",
52793
+ "out-of-scope",
52794
+ "unavailable"
52795
+ ]),
52796
+ message: string()
52797
+ });
52798
+ var DeviceTemplateTraceEntrySchema = object({
52799
+ ref: string(),
52800
+ reason: _enum([
52801
+ "unknown-alias",
52802
+ "unknown-field",
52803
+ "no-field",
52804
+ "unavailable"
52805
+ ])
52806
+ });
52807
+ var DeviceTemplateRenderSchema = discriminatedUnion("ok", [object({
52808
+ ok: literal(true),
52809
+ text: string(),
52810
+ trace: array(DeviceTemplateTraceEntrySchema)
52811
+ }), object({
52812
+ ok: literal(false),
52813
+ refusal: DeviceTemplateRefusalSchema,
52814
+ trace: array(DeviceTemplateTraceEntrySchema)
52815
+ })]);
52621
52816
  /**
52622
52817
  * A SITE ZONE — a named part of the property that several locations belong to.
52623
52818
  *
@@ -53449,6 +53644,22 @@ method(object({
53449
53644
  kind: "mutation",
53450
53645
  auth: "admin"
53451
53646
  }), method(object({
53647
+ templates: array(DeviceTemplateSchema).max(64),
53648
+ allowedDeviceIds: array(number$1().int()).optional()
53649
+ }), object({ results: array(DeviceTemplateRenderSchema) }), { auth: "admin" }), method(object({
53650
+ templates: array(string()).max(64),
53651
+ allowedDeviceIds: array(number$1().int()).optional()
53652
+ }), object({ results: array(discriminatedUnion("ok", [object({
53653
+ ok: literal(true),
53654
+ aliases: array(object({
53655
+ alias: string(),
53656
+ deviceId: number$1().int().nullable(),
53657
+ allowed: boolean()
53658
+ }))
53659
+ }), object({
53660
+ ok: literal(false),
53661
+ refusal: DeviceTemplateRefusalSchema
53662
+ })])) }), { auth: "admin" }), method(object({
53452
53663
  deviceId: number$1(),
53453
53664
  location: string().nullable()
53454
53665
  }), _void(), {
@@ -56639,6 +56850,332 @@ var groupStatusCapability = {
56639
56850
  durability: "session",
56640
56851
  history: "none"
56641
56852
  };
56853
+ /**
56854
+ * Heat pump settable surface (D1026): one typed method per parameter, never a
56855
+ * generic `set(key, value)`. The values a mode takes and the limits of a number
56856
+ * are the ones `heat-pump-status` carries (`allowed`, `min`, `max`, `step`),
56857
+ * read from the gateway; a provider refuses a value outside them. The state is
56858
+ * `heat-pump-status` — this cap has none.
56859
+ */
56860
+ var deviceId = number$1().int().nonnegative();
56861
+ var circuitId = string().min(1);
56862
+ var mode = string().min(1);
56863
+ var temp = number$1().finite();
56864
+ var heatPumpControlCapability = {
56865
+ name: "heat-pump-control",
56866
+ scope: "device",
56867
+ deviceNative: true,
56868
+ mode: "singleton",
56869
+ deviceTypes: [DeviceType.HeatPump],
56870
+ methods: {
56871
+ /** off / auto / on. */
56872
+ setSilentMode: method(object({
56873
+ deviceId,
56874
+ mode
56875
+ }), _void(), {
56876
+ kind: "mutation",
56877
+ auth: "protected"
56878
+ }),
56879
+ /** off / on. */
56880
+ setAwayMode: method(object({
56881
+ deviceId,
56882
+ mode
56883
+ }), _void(), {
56884
+ kind: "mutation",
56885
+ auth: "protected"
56886
+ }),
56887
+ /** off / manual / auto. */
56888
+ setAdditionalHeaterMode: method(object({
56889
+ deviceId,
56890
+ mode
56891
+ }), _void(), {
56892
+ kind: "mutation",
56893
+ auth: "protected"
56894
+ }),
56895
+ /** off / hm1 .. hm8 (the programmes the gateway lists). */
56896
+ setHolidayMode: method(object({
56897
+ deviceId,
56898
+ mode
56899
+ }), _void(), {
56900
+ kind: "mutation",
56901
+ auth: "protected"
56902
+ }),
56903
+ setCircuitOperationMode: method(object({
56904
+ deviceId,
56905
+ circuitId,
56906
+ mode
56907
+ }), _void(), {
56908
+ kind: "mutation",
56909
+ auth: "protected"
56910
+ }),
56911
+ setCircuitHeatCoolMode: method(object({
56912
+ deviceId,
56913
+ circuitId,
56914
+ mode
56915
+ }), _void(), {
56916
+ kind: "mutation",
56917
+ auth: "protected"
56918
+ }),
56919
+ setCircuitSummerWinter: method(object({
56920
+ deviceId,
56921
+ circuitId,
56922
+ mode
56923
+ }), _void(), {
56924
+ kind: "mutation",
56925
+ auth: "protected"
56926
+ }),
56927
+ setCircuitComfortSetpoint: method(object({
56928
+ deviceId,
56929
+ circuitId,
56930
+ temp
56931
+ }), _void(), {
56932
+ kind: "mutation",
56933
+ auth: "protected"
56934
+ }),
56935
+ setCircuitEcoSetpoint: method(object({
56936
+ deviceId,
56937
+ circuitId,
56938
+ temp
56939
+ }), _void(), {
56940
+ kind: "mutation",
56941
+ auth: "protected"
56942
+ }),
56943
+ setCircuitMaxFlowTemp: method(object({
56944
+ deviceId,
56945
+ circuitId,
56946
+ temp
56947
+ }), _void(), {
56948
+ kind: "mutation",
56949
+ auth: "protected"
56950
+ }),
56951
+ setDhwOperationMode: method(object({
56952
+ deviceId,
56953
+ circuitId,
56954
+ mode
56955
+ }), _void(), {
56956
+ kind: "mutation",
56957
+ auth: "protected"
56958
+ }),
56959
+ setDhwTemperatureLevel: method(object({
56960
+ deviceId,
56961
+ circuitId,
56962
+ level: mode
56963
+ }), _void(), {
56964
+ kind: "mutation",
56965
+ auth: "protected"
56966
+ }),
56967
+ setDhwChargeDuration: method(object({
56968
+ deviceId,
56969
+ circuitId,
56970
+ minutes: number$1().int().positive()
56971
+ }), _void(), {
56972
+ kind: "mutation",
56973
+ auth: "protected"
56974
+ }),
56975
+ /** Start an extra-hot-water charge now: an action, not a stored mode. */
56976
+ startExtraHotWater: method(object({
56977
+ deviceId,
56978
+ circuitId
56979
+ }), _void(), {
56980
+ kind: "mutation",
56981
+ auth: "protected"
56982
+ })
56983
+ }
56984
+ };
56985
+ var HeatPumpCircuitKindSchema = _enum([
56986
+ "heating",
56987
+ "dhw",
56988
+ "cooling",
56989
+ "solar"
56990
+ ]);
56991
+ /** A setting that takes one of a list of values the gateway named. */
56992
+ var HeatPumpOptionSettingSchema = object({
56993
+ /** The active value. Null when the gateway did not answer. */
56994
+ value: string().nullable(),
56995
+ /** Values the gateway accepts, in its order. Empty = read-only here. */
56996
+ allowed: array(string())
56997
+ });
56998
+ /** A numeric setting with the limits the gateway named. */
56999
+ var HeatPumpRangeSettingSchema = object({
57000
+ value: number$1().nullable(),
57001
+ min: number$1().nullable(),
57002
+ max: number$1().nullable(),
57003
+ step: number$1().nullable()
57004
+ });
57005
+ var HeatPumpCircuitSchema = object({
57006
+ /** Gateway id of the circuit (`hc1`, `dhw1`): the key of the series. */
57007
+ id: string().min(1),
57008
+ kind: HeatPumpCircuitKindSchema,
57009
+ label: string(),
57010
+ icon: IconKeySchema,
57011
+ /** Measured temperature of the circuit (room, tank, collector). */
57012
+ currentTemp: number$1().nullable().meta(readingMeta("temperature")),
57013
+ /** The setpoint in force now. */
57014
+ targetTemp: number$1().nullable().meta(readingMeta("temperature")),
57015
+ /** The operation mode in force (mirror of `operationMode.value`, for the series). */
57016
+ mode: string().nullable(),
57017
+ /** Whether the circuit is calling for heat right now. Null = unknown. */
57018
+ active: boolean().nullable(),
57019
+ operationMode: HeatPumpOptionSettingSchema.nullable(),
57020
+ /** Heating circuits: heat / cool / heatCool. */
57021
+ heatCoolMode: HeatPumpOptionSettingSchema.nullable(),
57022
+ /** Heating circuits: summer / winter changeover. */
57023
+ summerWinter: HeatPumpOptionSettingSchema.nullable(),
57024
+ comfortSetpoint: HeatPumpRangeSettingSchema.nullable(),
57025
+ ecoSetpoint: HeatPumpRangeSettingSchema.nullable(),
57026
+ maxFlowTemp: HeatPumpRangeSettingSchema.nullable(),
57027
+ /** Hot water: the temperature level (off / low / high / eco). */
57028
+ temperatureLevel: HeatPumpOptionSettingSchema.nullable(),
57029
+ /** Hot water: how long a charge lasts, minutes. */
57030
+ chargeDuration: HeatPumpRangeSettingSchema.nullable(),
57031
+ /** Hot water: whether an extra-hot-water charge is running. Null = no such surface. */
57032
+ extraHotWater: boolean().nullable()
57033
+ });
57034
+ /** The independent system modes, each with its real values. A null mode = the gateway has none. */
57035
+ var HeatPumpSystemSchema = object({
57036
+ silent: HeatPumpOptionSettingSchema.nullable(),
57037
+ away: HeatPumpOptionSettingSchema.nullable(),
57038
+ additionalHeater: HeatPumpOptionSettingSchema.nullable(),
57039
+ holiday: HeatPumpOptionSettingSchema.nullable()
57040
+ });
57041
+ var HeatPumpPowerSchema = object({
57042
+ thermalW: number$1().nullable().meta(readingMeta("power")),
57043
+ electricalW: number$1().nullable().meta(readingMeta("power")),
57044
+ /** Compressor modulation, percent. */
57045
+ compressorPct: number$1().nullable().meta(readingMeta("percentage"))
57046
+ });
57047
+ var HeatPumpTemperaturesSchema = object({
57048
+ supply: number$1().nullable().meta(readingMeta("temperature")),
57049
+ return: number$1().nullable().meta(readingMeta("temperature")),
57050
+ outdoor: number$1().nullable().meta(readingMeta("temperature"))
57051
+ });
57052
+ /** Lifetime meters, kWh: the energy of a period is computed at read time from their difference (D944). */
57053
+ var HeatPumpEnergySchema = object({
57054
+ thermalKwh: number$1().nullable().meta(readingMeta("energy")),
57055
+ electricalKwh: number$1().nullable().meta(readingMeta("energy"))
57056
+ });
57057
+ var HeatPumpStatusSchema = object({
57058
+ circuits: array(HeatPumpCircuitSchema),
57059
+ system: HeatPumpSystemSchema,
57060
+ power: HeatPumpPowerSchema,
57061
+ temperatures: HeatPumpTemperaturesSchema,
57062
+ energy: HeatPumpEnergySchema,
57063
+ /** Water pressure of the heating loop, bar. */
57064
+ waterPressureBar: number$1().nullable(),
57065
+ /** Compressor starts since commissioning. */
57066
+ starts: number$1().nullable(),
57067
+ /** Ms epoch when the slice was last read from the gateway. */
57068
+ lastFetchedAt: number$1()
57069
+ });
57070
+ /**
57071
+ * The instantaneous COP: thermal over electrical power. Null unless BOTH powers
57072
+ * are known and the unit draws power — a missing reading is never a 0 or a
57073
+ * division by it (D393). Computed on every read; there is no stored value.
57074
+ */
57075
+ function heatPumpInstantCop(power) {
57076
+ const { thermalW, electricalW } = power;
57077
+ if (thermalW === null || electricalW === null || electricalW <= 0) return null;
57078
+ return thermalW / electricalW;
57079
+ }
57080
+ var heatPumpStatusCapability = {
57081
+ name: "heat-pump-status",
57082
+ scope: "device",
57083
+ deviceNative: true,
57084
+ mode: "singleton",
57085
+ deviceTypes: [DeviceType.HeatPump],
57086
+ methods: {},
57087
+ status: {
57088
+ schema: HeatPumpStatusSchema,
57089
+ kind: "push"
57090
+ },
57091
+ runtimeState: HeatPumpStatusSchema,
57092
+ /**
57093
+ * Runtime-state durability: **session** — a heat pump's gateway answers on the
57094
+ * next poll, so a restart that clears the slice loses nothing a reader needs
57095
+ * (the series are recorded). See `RuntimeStateDurability`.
57096
+ */
57097
+ durability: "session",
57098
+ /**
57099
+ * History (D850, D917): `circuits[]` is a keyed array — one series per circuit and
57100
+ * field, named by the circuit's label. The mode is a state (D896); the energy
57101
+ * totals are lifetime counters, the kWh of a period is computed at read time (D944).
57102
+ * The COP is never recorded: it derives from the two powers.
57103
+ */
57104
+ history: {
57105
+ fields: {
57106
+ "power.thermalW": {
57107
+ kind: "gauge",
57108
+ unit: "W",
57109
+ deadband: 50,
57110
+ displayName: "Thermal power"
57111
+ },
57112
+ "power.electricalW": {
57113
+ kind: "gauge",
57114
+ unit: "W",
57115
+ deadband: 50,
57116
+ displayName: "Electrical power"
57117
+ },
57118
+ "power.compressorPct": {
57119
+ kind: "gauge",
57120
+ unit: "%",
57121
+ deadband: 2,
57122
+ displayName: "Compressor"
57123
+ },
57124
+ "temperatures.supply": {
57125
+ kind: "gauge",
57126
+ unit: "°C",
57127
+ deadband: .5,
57128
+ displayName: "Supply"
57129
+ },
57130
+ "temperatures.return": {
57131
+ kind: "gauge",
57132
+ unit: "°C",
57133
+ deadband: .5,
57134
+ displayName: "Return"
57135
+ },
57136
+ "temperatures.outdoor": {
57137
+ kind: "gauge",
57138
+ unit: "°C",
57139
+ deadband: .5,
57140
+ displayName: "Outdoor"
57141
+ },
57142
+ "energy.thermalKwh": {
57143
+ kind: "counter",
57144
+ unit: "kWh",
57145
+ displayName: "Thermal energy"
57146
+ },
57147
+ "energy.electricalKwh": {
57148
+ kind: "counter",
57149
+ unit: "kWh",
57150
+ displayName: "Electrical energy"
57151
+ }
57152
+ },
57153
+ items: {
57154
+ path: "circuits",
57155
+ key: "id",
57156
+ label: "label",
57157
+ fields: {
57158
+ currentTemp: {
57159
+ kind: "gauge",
57160
+ unit: "°C",
57161
+ deadband: .5,
57162
+ displayName: "Temperature"
57163
+ },
57164
+ targetTemp: {
57165
+ kind: "gauge",
57166
+ unit: "°C",
57167
+ minIntervalMs: 0,
57168
+ displayName: "Setpoint"
57169
+ },
57170
+ mode: {
57171
+ kind: "enum",
57172
+ displayName: "Mode"
57173
+ }
57174
+ }
57175
+ }
57176
+ },
57177
+ volatileStateFields: ["lastFetchedAt"]
57178
+ };
56642
57179
  var HeatingValveStatusSchema = object({
56643
57180
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
56644
57181
  position: number$1().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -56853,6 +57390,7 @@ var HeroSceneSchema = object({
56853
57390
  "ac",
56854
57391
  "thermostat",
56855
57392
  "heater",
57393
+ "heat-pump",
56856
57394
  "fan",
56857
57395
  "washer",
56858
57396
  "dryer",
@@ -56895,7 +57433,7 @@ var HeroSceneSchema = object({
56895
57433
  /** 0..1: volume, cover position, cycle progress. */
56896
57434
  level: number$1().min(0).max(1).nullable()
56897
57435
  });
56898
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway;
57436
+ DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway;
56899
57437
  DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
56900
57438
  /** A string in both languages, as the cap vocabulary writes it (D920): no `t()` in the model. */
56901
57439
  var HeroTextSchema = object({
@@ -56903,6 +57441,7 @@ var HeroTextSchema = object({
56903
57441
  it: string()
56904
57442
  });
56905
57443
  var HeroRowIdSchema = _enum([
57444
+ "progress",
56906
57445
  "run-for",
56907
57446
  "cycle",
56908
57447
  "program",
@@ -56917,6 +57456,7 @@ var HeroRowIdSchema = _enum([
56917
57456
  "alarm",
56918
57457
  "fan",
56919
57458
  "vacuum",
57459
+ "vacuum-suction",
56920
57460
  "mower",
56921
57461
  "feeder",
56922
57462
  "fountain",
@@ -56935,7 +57475,10 @@ var HeroRowIdSchema = _enum([
56935
57475
  "climate-fan",
56936
57476
  "setpoint",
56937
57477
  "channel",
56938
- "control"
57478
+ "control",
57479
+ "circuit",
57480
+ "heat-pump-power",
57481
+ "system-mode"
56939
57482
  ]);
56940
57483
  var HeroArgValueSchema = lazy(() => union([
56941
57484
  string(),
@@ -57000,16 +57543,32 @@ var HeroControlSchema = discriminatedUnion("kind", [
57000
57543
  label: HeroTextSchema,
57001
57544
  current: string().nullable(),
57002
57545
  options: array(string()),
57546
+ /** A readable label per option value; an option without one is drawn as its raw value. */
57547
+ optionLabels: record(string(), HeroTextSchema).optional(),
57003
57548
  call: HeroCallSchema,
57004
57549
  valueArg: string()
57005
57550
  })
57006
57551
  ]);
57552
+ /** Declared by its interface: a `z.infer` of one more icon union pushes the cap's inferred type past what the compiler can print. */
57553
+ var HeroPillSchema = object({
57554
+ icon: IconKeySchema.nullable(),
57555
+ text: HeroTextSchema,
57556
+ tone: HeroToneSchema
57557
+ });
57007
57558
  var HeroRowSchema = object({
57008
57559
  id: HeroRowIdSchema,
57560
+ /** The row's own name when the kind's label would not tell rows of one kind apart (a circuit's label). */
57561
+ label: HeroTextSchema.optional(),
57562
+ /** A group header drawn above the row when it differs from the previous row's ("System"). */
57563
+ group: HeroTextSchema.optional(),
57564
+ /** Values drawn as pills across the row (a heat pump's powers). */
57565
+ pills: array(HeroPillSchema).optional(),
57009
57566
  icon: IconKeySchema,
57010
57567
  /** The row's status, in words. `null` = nothing to say. */
57011
57568
  status: HeroTextSchema.nullable(),
57012
- controls: array(HeroControlSchema)
57569
+ controls: array(HeroControlSchema),
57570
+ /** A bar under the row's label, 0..1; `null` / absent = no bar. */
57571
+ progress: number$1().min(0).max(1).nullable().optional()
57013
57572
  });
57014
57573
  var HeroDetailSchema = object({
57015
57574
  label: HeroTextSchema,
@@ -57017,7 +57576,11 @@ var HeroDetailSchema = object({
57017
57576
  unit: string().nullable()
57018
57577
  });
57019
57578
  var HeroBadgeSchema = object({
57020
- id: _enum(["battery", "network-link"]),
57579
+ id: _enum([
57580
+ "battery",
57581
+ "network-link",
57582
+ "integration"
57583
+ ]),
57021
57584
  icon: IconKeySchema,
57022
57585
  tone: HeroToneSchema,
57023
57586
  /** Percent for a battery, link quality for a link; `null` = not known. */
@@ -57065,7 +57628,13 @@ var HeroTimedRunSlotSchema = object({
57065
57628
  activeUntil: number$1().nullable(),
57066
57629
  quickRun: TimedRunQuickRunSchema,
57067
57630
  run: HeroActionSchema,
57068
- cancel: HeroActionSchema
57631
+ cancel: HeroActionSchema,
57632
+ /**
57633
+ * Whole units left (the limit's unit, ceil). `null` while nothing is armed.
57634
+ * A number, not words: the row formats it, and a text here would not fit the
57635
+ * hero-layout declaration.
57636
+ */
57637
+ remaining: number$1().nullable()
57069
57638
  });
57070
57639
  var HeroPowerSupplySlotSchema = object({
57071
57640
  /** The plug's state; `null` = unknown, never off. */
@@ -57117,7 +57686,12 @@ var HeroFactSchema = object({
57117
57686
  var HeroSideTileSchema = object({
57118
57687
  label: HeroTextSchema,
57119
57688
  value: HeroTextSchema,
57120
- tone: HeroToneSchema.optional()
57689
+ tone: HeroToneSchema.optional(),
57690
+ /**
57691
+ * Epoch ms when the tile reads a clock time (an appliance's end): a client prints it in
57692
+ * the viewer's own time zone; `value` is the fallback for one that cannot.
57693
+ */
57694
+ at: number$1().optional()
57121
57695
  });
57122
57696
  var HeroSideTilesSchema = object({
57123
57697
  left: array(HeroSideTileSchema).max(2),
@@ -57153,8 +57727,12 @@ var HeroModelSchema = object({
57153
57727
  sideTiles: HeroSideTilesSchema,
57154
57728
  /** The status line under the main icon. */
57155
57729
  title: HeroTextSchema,
57730
+ /** The scene's top line when it says more than `title`; absent = draw the title. */
57731
+ caption: HeroTextSchema.optional(),
57156
57732
  badges: array(HeroBadgeSchema),
57157
57733
  remote: HeroRemoteSlotSchema.nullable(),
57734
+ /** The foot button that opens the device's history: "History", "Cycles", "Meals", "Access". */
57735
+ historyLabel: HeroTextSchema,
57158
57736
  consumables: HeroConsumablesSlotSchema.nullable(),
57159
57737
  consumption: HeroConsumptionSlotSchema.nullable(),
57160
57738
  ownPower: HeroOwnPowerSlotSchema.nullable(),
@@ -57175,18 +57753,31 @@ var HeroModelSchema = object({
57175
57753
  * 4. a trailing fact (power, else battery, else consumables).
57176
57754
  *
57177
57755
  * Selection rules, all taken from the model:
57178
- * - ONE control is ever shown: the device's own power; else the FIRST control of
57756
+ * - the primary control is the device's own power; else the FIRST control of
57179
57757
  * the FIRST row that has any (a later row's control is never promoted). When
57180
57758
  * the model lists that part in `gated`, the row has no inline control: the
57181
57759
  * projection never falls through to another control the caller could use.
57760
+ * - a timed run, when the model carries the slot, sits beside that primary:
57761
+ * Run while idle, Stop while armed. Gating the slot hides the control.
57182
57762
  * - the status line is the head (what plays, else the state) plus at most
57183
- * {@link ROW_STATUS_FACTS_MAX} facts, the trailing fact excluded;
57763
+ * {@link ROW_STATUS_FACTS_MAX} facts, the trailing fact excluded, then the
57764
+ * time left on an armed timed run;
57184
57765
  * - the trailing fact is the first of power, battery, consumables.
57185
57766
  *
57186
57767
  * `projectDeviceRow` is pure and computes nothing: it selects from a
57187
57768
  * {@link HeroModel}. A row that needed a value the hero lacks would mean the
57188
57769
  * model is missing it, so the fix goes in the model.
57189
57770
  */
57771
+ /**
57772
+ * The row's timed-run control. `id` is `timed-run.run` while idle and
57773
+ * `timed-run.cancel` while armed; `duration` and `unit` are the one-tap run
57774
+ * a Run press starts.
57775
+ */
57776
+ var HeroRowTimedRunSchema = object({
57777
+ id: _enum(["timed-run.run", "timed-run.cancel"]),
57778
+ duration: number$1(),
57779
+ unit: _enum(["min", "s"])
57780
+ });
57190
57781
  var HeroRowModelSchema = object({
57191
57782
  deviceId: number$1().int(),
57192
57783
  name: string(),
@@ -57199,6 +57790,11 @@ var HeroRowModelSchema = object({
57199
57790
  statusLine: array(HeroTextSchema),
57200
57791
  /** `null` = the type has no inline control, or the caller may not act on it. */
57201
57792
  primary: HeroControlSchema.nullable(),
57793
+ /**
57794
+ * Beside `primary` when the device offers a timed run: Run while idle, Stop
57795
+ * while armed. `null` when there is no slot, or the caller may not act.
57796
+ */
57797
+ timedRun: HeroRowTimedRunSchema.nullable(),
57202
57798
  trailing: HeroFactSchema.nullable()
57203
57799
  });
57204
57800
  /**
@@ -57612,6 +58208,12 @@ var IntegrationWithStateSchema = object({
57612
58208
  name: string(),
57613
58209
  /** A projection of the instance body's switch (`setInstanceEnabled` is the only writer). */
57614
58210
  enabled: boolean(),
58211
+ /**
58212
+ * True when the addon that holds the switch did not answer (down, failed, or keeps no record for
58213
+ * this row): `enabled` is then the stored registry flag, a stale value, not the state. A reader
58214
+ * must not draw it as on or off (D315).
58215
+ */
58216
+ switchUnknown: literal(true).optional(),
57615
58217
  info: record(string(), unknown()).readonly(),
57616
58218
  /** The discovery candidate this integration was set up from (D984). */
57617
58219
  originStableId: string().optional(),
@@ -57625,6 +58227,12 @@ var IntegrationLiteSchema = object({
57625
58227
  name: string(),
57626
58228
  /** A projection of the instance body's switch (`setInstanceEnabled` is the only writer). */
57627
58229
  enabled: boolean(),
58230
+ /**
58231
+ * True when the addon that holds the switch did not answer (down, failed, or keeps no record for
58232
+ * this row): `enabled` is then the stored registry flag, a stale value, not the state. A reader
58233
+ * must not draw it as on or off (D315).
58234
+ */
58235
+ switchUnknown: literal(true).optional(),
57628
58236
  info: record(string(), unknown()).readonly(),
57629
58237
  /** The discovery candidate this integration was set up from (D984). */
57630
58238
  originStableId: string().optional(),
@@ -57723,9 +58331,9 @@ var UpdateIntegrationInputSchema = object({
57723
58331
  id: string(),
57724
58332
  name: string().optional(),
57725
58333
  /**
57726
- * Accepted for a FIXED builtin only (`info.fixed`: alarm panel, camera grid, core blocks,
57727
- * terminal), which keeps no instance record: the registry flag is its one store. Any other
57728
- * integration is switched through `integration-instances.setInstanceEnabled`.
58334
+ * Honoured ONLY for a fixed builtin (`info.fixed`), whose switch is the hub's own registry flag
58335
+ * and works while its addon is down. Any other row is refused (a typed `BAD_REQUEST`): its
58336
+ * switch is `integration-instances.setInstanceEnabled`.
57729
58337
  */
57730
58338
  enabled: boolean().optional(),
57731
58339
  info: record(string(), unknown()).optional()
@@ -57759,6 +58367,12 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
57759
58367
  * the form as a `SecretFieldView`, never text (D789). A patch value omitted, `''`
57760
58368
  * or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
57761
58369
  * clears it.
58370
+ *
58371
+ * Who may call (epic #440): every method but `describe` and `instanceFormSchema` is open to any
58372
+ * session (`caller: 'scoped'`) and default-denies. An admin passes everywhere. A non-admin passes
58373
+ * only on a call pinned to one addon whose `describe().userScoped` is true, and that provider
58374
+ * filters every row on the `caller` the router injected (a notification target is somebody's
58375
+ * phone: a user sees and changes only their own). An addon that says nothing keeps the admin door.
57762
58376
  */
57763
58377
  var ConfigSchemaPassthrough$2 = custom();
57764
58378
  var IntegrationInstanceIdSchema = string().min(1);
@@ -57872,7 +58486,15 @@ var IntegrationInstanceDescriptionSchema = object({
57872
58486
  */
57873
58487
  mintsId: boolean().optional(),
57874
58488
  /** The links to other instances the provider's kinds declare; absent means none. */
57875
- links: array(IntegrationInstanceLinkSchema).optional()
58489
+ links: array(IntegrationInstanceLinkSchema).optional(),
58490
+ /**
58491
+ * The rows belong to individual users (a notification target is somebody's phone). The router
58492
+ * then opens `listInstances`, `getInstance`, `createInstance`, `updateInstance`,
58493
+ * `setInstanceEnabled`, `deleteInstance` and `testInstance` to a NON-admin session on a call
58494
+ * pinned to this addon, and hands the provider the `caller` so it keeps a user to their own rows.
58495
+ * Absent means admin only, as it always was.
58496
+ */
58497
+ userScoped: boolean().optional()
57876
58498
  });
57877
58499
  var IntegrationInstanceStatusSchema = object({
57878
58500
  state: _enum([
@@ -57930,8 +58552,14 @@ var integrationInstancesCapability = {
57930
58552
  /** The discovery candidate the wizard was seeded from (`stableId`), if any. */
57931
58553
  candidateStableId: string().min(1).optional()
57932
58554
  }), ConfigSchemaPassthrough$2, { auth: "admin" }),
57933
- listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
57934
- getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
58555
+ listInstances: method(looseObject({}), array(IntegrationInstanceSchema).readonly(), {
58556
+ auth: "protected",
58557
+ caller: "scoped"
58558
+ }),
58559
+ getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), {
58560
+ auth: "protected",
58561
+ caller: "scoped"
58562
+ }),
57935
58563
  /**
57936
58564
  * Insert the typed record. With `id` it is stored under that id (a duplicate id is refused);
57937
58565
  * without one the PROVIDER mints it and answers it. `kind` picks one of `describe().kinds`.
@@ -57964,11 +58592,13 @@ var integrationInstancesCapability = {
57964
58592
  seed: object({ info: record(string(), unknown()) }).optional()
57965
58593
  }), object({ id: string() }), {
57966
58594
  kind: "mutation",
57967
- auth: "admin"
58595
+ auth: "protected",
58596
+ caller: "scoped"
57968
58597
  }),
57969
58598
  updateInstance: method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), {
57970
58599
  kind: "mutation",
57971
- auth: "admin"
58600
+ auth: "protected",
58601
+ caller: "scoped"
57972
58602
  }),
57973
58603
  /**
57974
58604
  * Switch ONE instance on or off, for every role; a missing record is created from the addon's
@@ -57980,7 +58610,8 @@ var integrationInstancesCapability = {
57980
58610
  enabled: boolean()
57981
58611
  }), object({ ok: literal(true) }), {
57982
58612
  kind: "mutation",
57983
- auth: "admin"
58613
+ auth: "protected",
58614
+ caller: "scoped"
57984
58615
  }),
57985
58616
  /**
57986
58617
  * Delete the record and its keychain refs together. For an instance that brings devices the hub
@@ -57989,7 +58620,8 @@ var integrationInstancesCapability = {
57989
58620
  */
57990
58621
  deleteInstance: method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), {
57991
58622
  kind: "mutation",
57992
- auth: "admin"
58623
+ auth: "protected",
58624
+ caller: "scoped"
57993
58625
  }),
57994
58626
  testInstance: optionalMethod(object({
57995
58627
  id: IntegrationInstanceIdSchema.nullable(),
@@ -57998,7 +58630,8 @@ var integrationInstancesCapability = {
57998
58630
  kind: string().min(1).optional()
57999
58631
  }), TestConnectionResultSchema, {
58000
58632
  kind: "mutation",
58001
- auth: "admin"
58633
+ auth: "protected",
58634
+ caller: "scoped"
58002
58635
  })
58003
58636
  }
58004
58637
  };
@@ -77465,6 +78098,11 @@ var SendEmailResultSchema = object({
77465
78098
  var SmtpStatusSchema = object({
77466
78099
  /** True iff the addon has successfully verified the relay. */
77467
78100
  ready: boolean(),
78101
+ /**
78102
+ * The tri-state behind `ready` (D315): true when the last send worked, false when it failed,
78103
+ * ABSENT when the relay has not sent yet (unknown, never "not verified").
78104
+ */
78105
+ verified: boolean().optional(),
77468
78106
  /** Operator-visible host string (no credentials). */
77469
78107
  host: string(),
77470
78108
  /** Last error message reported by the relay, when not ready. */
@@ -77476,10 +78114,6 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
77476
78114
  kind: "mutation",
77477
78115
  auth: "admin",
77478
78116
  access: "create"
77479
- }), method(RelayPinSchema, SmtpStatusSchema, {
77480
- kind: "mutation",
77481
- auth: "admin",
77482
- access: "view"
77483
78117
  }), method(RelayPinSchema, SmtpStatusSchema, { auth: "admin" });
77484
78118
  /**
77485
78119
  * A single device snapshot returned as base64 JPEG/PNG.
@@ -82417,6 +83051,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
82417
83051
  groupCommands: groupCommandsCapability,
82418
83052
  groupStatus: groupStatusCapability,
82419
83053
  heatingValve: heatingValveCapability,
83054
+ heatPumpStatus: heatPumpStatusCapability,
82420
83055
  humidifier: humidifierCapability,
82421
83056
  humiditySensor: humiditySensorCapability,
82422
83057
  image: imageCapability,
@@ -83700,7 +84335,7 @@ var BaseDeviceProvider = class extends BaseAddon {
83700
84335
  return toDeviceSummary(device, this.addonId);
83701
84336
  }
83702
84337
  };
83703
- DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Heater, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
84338
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Heater, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
83704
84339
  new Set(Object.values(DeviceType));
83705
84340
  /** Parses the raw slice with the cap's own schema; `undefined` when it does not parse. */
83706
84341
  function contribution(cap, schema, build) {
@@ -83836,6 +84471,13 @@ var lockContribution = contribution("lock-control", LockControlStatusSchema, (sl
83836
84471
  reading: reading("lock-control", words?.state ?? "unknown", false, words?.title),
83837
84472
  rows: [row$1("lock", state === "locked" || state === "locking" ? "lock" : "lock-open", joinText([title, since]), lockControls(state, ctx.features.includes(DeviceFeature.LockOpen)))],
83838
84473
  ...since === null || words === null ? {} : { facts: [fact("since", "clock", since, "muted")] },
84474
+ ...words === null ? {} : { tiles: { left: [{
84475
+ label: t("Door", "Porta"),
84476
+ value: title
84477
+ }, ...since === null ? [] : [{
84478
+ label: t("Since", "Da"),
84479
+ value: since
84480
+ }]] } },
83839
84481
  ...words?.door == null ? {} : { scene: { mode: words.door } }
83840
84482
  };
83841
84483
  });
@@ -83917,7 +84559,17 @@ var ACCESS_FAMILY_CONTRIBUTIONS = {
83917
84559
  return {
83918
84560
  reading: reading("alarm-panel", words?.state ?? "unknown", false, words?.title),
83919
84561
  rows: [row$1("alarm", state === "triggered" ? "shield-alert" : state === "disarmed" ? "shield-off" : "shield-check", joinText([words?.title ?? t("Unknown", "Sconosciuto"), since]), alarmControls(state, modes, slice?.requiresCode ?? false))],
83920
- ...since === null || words === null ? {} : { facts: [fact("since", "clock", since, state === "triggered" ? "danger" : "muted")] }
84562
+ ...since === null || words === null ? {} : { facts: [fact("since", "clock", since, state === "triggered" ? "danger" : "muted")] },
84563
+ ...words === null ? {} : { tiles: {
84564
+ left: [{
84565
+ label: t("Mode", "Modalità"),
84566
+ value: words.title
84567
+ }],
84568
+ ...since === null ? {} : { right: [{
84569
+ label: t("Since", "Da"),
84570
+ value: since
84571
+ }] }
84572
+ } }
83921
84573
  };
83922
84574
  }),
83923
84575
  "automation-control": contribution("automation-control", AutomationControlStatusSchema, (slice, ctx) => {
@@ -83981,7 +84633,7 @@ var ACCESS_FAMILY_CONTRIBUTIONS = {
83981
84633
  */
83982
84634
  var has$3 = (ctx, f) => ctx.features.includes(f);
83983
84635
  var unknownText = t("Unknown", "Sconosciuto");
83984
- var pct$1 = (n) => t(`${Math.round(n)}%`, `${Math.round(n)}%`);
84636
+ var pct$2 = (n) => t(`${Math.round(n)}%`, `${Math.round(n)}%`);
83985
84637
  var unit = (n) => Math.min(1, Math.max(0, n / 100));
83986
84638
  /** Battery as a fact, unless the device is also bound to the `battery` cap (it already says so). */
83987
84639
  function ownBattery(level, ctx) {
@@ -84043,7 +84695,7 @@ var fanContribution = contribution("fan-control", FanControlStatusSchema, (slice
84043
84695
  },
84044
84696
  valueArg: "direction"
84045
84697
  });
84046
- const speedText = speed === null ? null : pct$1(speed);
84698
+ const speedText = speed === null ? null : pct$2(speed);
84047
84699
  const preset = slice?.preset ?? "";
84048
84700
  const status = speed === null ? unknownText : joinText([speed === 0 ? t("Off", "Spento") : t(`Speed ${speed}%`, `Velocità ${speed}%`), preset === "" ? null : t(preset, preset)]);
84049
84701
  return {
@@ -84056,10 +84708,16 @@ var fanContribution = contribution("fan-control", FanControlStatusSchema, (slice
84056
84708
  }],
84057
84709
  ...speedText === null ? {} : {
84058
84710
  facts: [fact("speed", "fan", speedText, speed === 0 ? "muted" : "primary")],
84059
- tiles: { right: [{
84060
- label: t("Speed", "Velocità"),
84061
- value: speedText
84062
- }] },
84711
+ tiles: {
84712
+ ...slice?.oscillating == null ? {} : { left: [{
84713
+ label: t("Oscillation", "Oscilla"),
84714
+ value: slice.oscillating ? t("Yes", "Sì") : t("No", "No")
84715
+ }] },
84716
+ ...preset === "" ? {} : { right: [{
84717
+ label: t("Mode", "Modalità"),
84718
+ value: t(preset, preset)
84719
+ }] }
84720
+ },
84063
84721
  scene: { level: unit(speed ?? 0) }
84064
84722
  }
84065
84723
  };
@@ -84123,7 +84781,8 @@ var vacuumContribution = contribution("vacuum-control", VacuumControlStatusSchem
84123
84781
  const words = state === null ? null : VACUUM_WORDS[state];
84124
84782
  const title = state === "error" && slice?.errorLabel ? t(slice.errorLabel, slice.errorLabel) : words?.title;
84125
84783
  const controls = vacuumControls(state);
84126
- if (slice !== void 0 && slice.availableFanSpeeds.length > 0) controls.push({
84784
+ const suctionControls = [];
84785
+ if (slice !== void 0 && slice.availableFanSpeeds.length > 0) suctionControls.push({
84127
84786
  kind: "picker",
84128
84787
  id: "vacuum.suction",
84129
84788
  label: t("Suction", "Aspirazione"),
@@ -84138,22 +84797,31 @@ var vacuumContribution = contribution("vacuum-control", VacuumControlStatusSchem
84138
84797
  });
84139
84798
  if (state !== null) controls.push(vac("locate", "Locate", "Trova", "map-pin", "locate"));
84140
84799
  const progress = state === "cleaning" ? slice?.progress ?? null : null;
84800
+ const suction = slice?.fanSpeed == null || slice.fanSpeed === "" ? [] : [{
84801
+ label: t("Suction", "Aspirazione"),
84802
+ value: t(slice.fanSpeed, slice.fanSpeed)
84803
+ }];
84804
+ const suctionRows = suctionControls.length === 0 ? [] : [{
84805
+ id: "vacuum-suction",
84806
+ icon: "bot",
84807
+ status: null,
84808
+ controls: suctionControls
84809
+ }];
84810
+ const vacuumRows = [{
84811
+ id: "vacuum",
84812
+ icon: "bot",
84813
+ status: title ?? unknownText,
84814
+ controls
84815
+ }, ...suctionRows];
84141
84816
  return {
84142
84817
  reading: reading("vacuum-control", words?.state ?? "unknown", false, title),
84143
- rows: [{
84144
- id: "vacuum",
84145
- icon: "bot",
84146
- status: title ?? unknownText,
84147
- controls
84148
- }],
84149
- facts: [...ownBattery(slice?.batteryLevel ?? null, ctx), ...progress === null ? [] : [fact("progress", "activity", pct$1(progress), "primary")]],
84150
- ...progress === null ? {} : {
84151
- tiles: { right: [{
84152
- label: t("Progress", "Avanzamento"),
84153
- value: pct$1(progress)
84154
- }] },
84155
- scene: { level: unit(progress) }
84156
- }
84818
+ rows: vacuumRows,
84819
+ facts: [...ownBattery(slice?.batteryLevel ?? null, ctx), ...progress === null ? [] : [fact("progress", "activity", pct$2(progress), "primary")]],
84820
+ ...suction.length === 0 && progress === null ? {} : { tiles: { right: [...suction, ...progress === null ? [] : [{
84821
+ label: t("Progress", "Avanzamento"),
84822
+ value: pct$2(progress)
84823
+ }]] } },
84824
+ ...progress === null ? {} : { scene: { level: unit(progress) } }
84157
84825
  };
84158
84826
  });
84159
84827
  var MOWER_WORDS = {
@@ -84209,11 +84877,11 @@ var mowerContribution = contribution("lawn-mower-control", LawnMowerControlStatu
84209
84877
  status: title ?? unknownText,
84210
84878
  controls: mowerControls(activity)
84211
84879
  }],
84212
- facts: [...ownBattery(slice?.batteryLevel ?? null, ctx), ...progress === null ? [] : [fact("progress", "activity", pct$1(progress), "primary")]],
84880
+ facts: [...ownBattery(slice?.batteryLevel ?? null, ctx), ...progress === null ? [] : [fact("progress", "activity", pct$2(progress), "primary")]],
84213
84881
  ...progress === null ? {} : {
84214
84882
  tiles: { right: [{
84215
84883
  label: t("Progress", "Avanzamento"),
84216
- value: pct$1(progress)
84884
+ value: pct$2(progress)
84217
84885
  }] },
84218
84886
  scene: { level: unit(progress) }
84219
84887
  }
@@ -84284,10 +84952,13 @@ var feederContribution = contribution("pet-feeder", PetFeederStatusSchema, (slic
84284
84952
  ...last === null ? [] : [fact("last-run", "clock", t(`Last ${last} g`, `Ultimo ${last} g`))],
84285
84953
  ...ownBattery(slice?.powerSource === "battery" ? slice.batteryPower : null, ctx)
84286
84954
  ],
84287
- ...last === null ? {} : { tiles: { right: [{
84955
+ tiles: { left: [...slice?.foodLevel == null ? [] : [{
84956
+ label: t("Tank", "Serbatoio"),
84957
+ value: pct$2(slice.foodLevel)
84958
+ }], ...last === null ? [] : [{
84288
84959
  label: t("Last portion", "Ultima porzione"),
84289
84960
  value: t(`${last} g`, `${last} g`)
84290
- }] } }
84961
+ }]] }
84291
84962
  };
84292
84963
  });
84293
84964
  var fountainContribution = contribution("pet-fountain", PetFountainStatusSchema, (slice) => {
@@ -84315,10 +84986,13 @@ var fountainContribution = contribution("pet-fountain", PetFountainStatusSchema,
84315
84986
  }],
84316
84987
  ...level === null ? {} : {
84317
84988
  facts: [fact("level", "droplets", t(`Water ${Math.round(level)}%`, `Acqua ${Math.round(level)}%`), level <= 20 ? "warning" : "primary")],
84318
- tiles: { right: [{
84989
+ tiles: { left: [{
84319
84990
  label: t("Water", "Acqua"),
84320
- value: pct$1(level)
84321
- }] },
84991
+ value: pct$2(level)
84992
+ }, ...slice?.waterVolume == null ? [] : [{
84993
+ label: t("Volume", "Volume"),
84994
+ value: t(`${Math.round(slice.waterVolume)} ml`, `${Math.round(slice.waterVolume)} ml`)
84995
+ }]] },
84322
84996
  scene: { level: unit(level) }
84323
84997
  }
84324
84998
  };
@@ -84350,14 +85024,18 @@ var DEVICES_FAMILY_CONTRIBUTIONS = {
84350
85024
  * that does not parse reads `unknown`, never closed or off (D315/D393).
84351
85025
  */
84352
85026
  var has$2 = (ctx, f) => ctx.features.includes(f);
84353
- var pct = (n) => t(`${Math.round(n)}%`, `${Math.round(n)}%`);
85027
+ var pct$1 = (n) => t(`${Math.round(n)}%`, `${Math.round(n)}%`);
84354
85028
  var brightnessContribution = contribution("brightness", BrightnessStatusSchema, (slice) => {
84355
85029
  const level = slice?.level ?? null;
84356
85030
  return {
84357
85031
  ...level === null ? {} : { reading: reading("brightness", level > 0 ? "on" : "off") },
84358
85032
  ...level === null ? {} : {
84359
- facts: [fact("level", "sun-dim", pct(level), level > 0 ? "primary" : "muted")],
84360
- scene: { level: level / 100 }
85033
+ facts: [fact("level", "sun-dim", pct$1(level), level > 0 ? "primary" : "muted")],
85034
+ scene: { level: level / 100 },
85035
+ tiles: { left: [{
85036
+ label: t("Brightness", "Luminosità"),
85037
+ value: pct$1(level)
85038
+ }] }
84361
85039
  },
84362
85040
  rows: [row$1("brightness", "sun-dim", level === null ? null : t(`Brightness ${Math.round(level)}%`, `Luminosità ${Math.round(level)}%`), [{
84363
85041
  kind: "slider",
@@ -84566,12 +85244,16 @@ function travelFragment(s, state, position, tilt, ctx) {
84566
85244
  });
84567
85245
  return {
84568
85246
  reading: reading(s.source, words.state, false, words.title),
84569
- ...position === null ? {} : { facts: [fact("level", "arrow-up-down", pct(position))] },
85247
+ ...position === null ? {} : { facts: [fact("level", "arrow-up-down", pct$1(position))] },
85248
+ ...position === null ? {} : { tiles: { left: [{
85249
+ label: t("Opening", "Apertura"),
85250
+ value: pct$1(position)
85251
+ }] } },
84570
85252
  scene: {
84571
85253
  ...words.mode === null ? {} : { mode: words.mode },
84572
85254
  ...position === null ? {} : { level: position / 100 }
84573
85255
  },
84574
- rows: [row$1(s.rowId, "arrow-up-down", joinText([words.title, position !== null && state !== "open" ? pct(position) : null]), controls)]
85256
+ rows: [row$1(s.rowId, "arrow-up-down", joinText([words.title, position !== null && state !== "open" ? pct$1(position) : null]), controls)]
84575
85257
  };
84576
85258
  }
84577
85259
  var ACTUATORS_FAMILY_CONTRIBUTIONS = {
@@ -84649,10 +85331,10 @@ var ACTUATORS_FAMILY_CONTRIBUTIONS = {
84649
85331
  const now = slice.currentHumidity;
84650
85332
  return {
84651
85333
  reading: reading("humidifier", state, false, title),
84652
- ...now === null ? {} : { facts: [fact("level", "droplets", pct(now), "info")] },
85334
+ ...now === null ? {} : { facts: [fact("level", "droplets", pct$1(now), "info")] },
84653
85335
  ...slice.targetHumidity === null ? {} : { tiles: { right: [{
84654
85336
  label: t("Target", "Obiettivo"),
84655
- value: pct(slice.targetHumidity)
85337
+ value: pct$1(slice.targetHumidity)
84656
85338
  }] } },
84657
85339
  ...now === null ? {} : { scene: { level: now / 100 } },
84658
85340
  rows: [row$1("humidifier", "droplets", joinText([title, slice.mode === null ? null : t(slice.mode, slice.mode)]), controls)]
@@ -84900,17 +85582,16 @@ var weatherContribution = contribution("weather", WeatherStatusSchema, (slice, c
84900
85582
  });
84901
85583
  var updateContribution = contribution("update", UpdateStatusSchema, (slice) => {
84902
85584
  if (slice === void 0) return { reading: reading("update", "unknown") };
84903
- const { currentVersion, availableVersion, inProgress, updatable } = slice;
85585
+ const { currentVersion, availableVersion, inProgress } = slice;
84904
85586
  const pending = availableVersion !== null && availableVersion !== "" && availableVersion !== currentVersion;
84905
85587
  if (!pending && currentVersion === null && !inProgress) return { reading: reading("update", "unknown") };
84906
85588
  const state = inProgress ? "running" : pending ? "standby" : "idle";
84907
85589
  const title = inProgress ? t("Updating", "Aggiornamento in corso") : pending ? t("Update available", "Aggiornamento disponibile") : t("Up to date", "Aggiornato");
84908
85590
  const versions = currentVersion === null ? pending ? t(`${availableVersion}`, `${availableVersion}`) : null : pending ? t(`${currentVersion} → ${availableVersion}`, `${currentVersion} → ${availableVersion}`) : t(`Version ${currentVersion}`, `Versione ${currentVersion}`);
84909
- const install = pending && updatable && !inProgress ? [asAction(action("update.install", t("Install", "Installa"), "download", "update", "installUpdate", {}, "primary", "install"))] : [];
84910
85591
  return {
84911
85592
  reading: reading("update", state, false, title),
84912
85593
  facts: versions === null ? [] : [fact("reading", "download", versions, pending ? "warning" : "muted")],
84913
- rows: [row$1("update", "download", versions, install)]
85594
+ rows: [row$1("update", "download", versions, [])]
84914
85595
  };
84915
85596
  });
84916
85597
  var imageContribution = contribution("image", ImageStatusSchema, (slice, ctx) => {
@@ -85018,6 +85699,33 @@ var ON_MODE_PREFERENCE = [
85018
85699
  "heat",
85019
85700
  "cool"
85020
85701
  ];
85702
+ var FAN_LABEL = {
85703
+ auto: t("Auto", "Auto"),
85704
+ quiet: t("Quiet", "Silenziosa"),
85705
+ low: t("Low", "Bassa"),
85706
+ medium: t("Medium", "Media"),
85707
+ high: t("High", "Alta"),
85708
+ turbo: t("Turbo", "Turbo"),
85709
+ off: t("Off", "Spenta")
85710
+ };
85711
+ /** A readable label for a fan speed name; an unknown one is shown as the device reported it. */
85712
+ function fanLabelOf(fan) {
85713
+ return FAN_LABEL[fan] ?? t(fan, fan);
85714
+ }
85715
+ /** Short labels for a segmented control: one word each, so the segments stay on a line. */
85716
+ var MODE_SEGMENT_LABEL = {
85717
+ off: t("Off", "Spento"),
85718
+ heat: t("Heat", "Caldo"),
85719
+ cool: t("Cool", "Freddo"),
85720
+ auto: t("Auto", "Auto"),
85721
+ heat_cool: t("Heat/Cool", "Caldo/Freddo"),
85722
+ fan_only: t("Fan", "Vent."),
85723
+ dry: t("Dry", "Deum.")
85724
+ };
85725
+ /** The segment label of every available HVAC mode, keyed by the mode value. */
85726
+ function modeLabelsOf(modes) {
85727
+ return Object.fromEntries(modes.map((m) => [m, MODE_SEGMENT_LABEL[m]]));
85728
+ }
85021
85729
  /** 22 -> "22", 22.3 -> "22.3": a trailing zero is noise on a thermostat. */
85022
85730
  function formatDegrees(value) {
85023
85731
  return `${Number(value.toFixed(1))}°`;
@@ -85082,11 +85790,11 @@ function rangeFrom(min, max, step) {
85082
85790
  * contribute, types never do (D14). A slice that is absent or does not parse is
85083
85791
  * "not answered": the state reads unknown, no row is drawn (D315, D393).
85084
85792
  */
85085
- var same = (text) => t(text, text);
85086
- function tile(en, it, value) {
85793
+ var same$1 = (text) => t(text, text);
85794
+ function tile$1(en, it, value) {
85087
85795
  return {
85088
85796
  label: t(en, it),
85089
- value: same(value)
85797
+ value: same$1(value)
85090
85798
  };
85091
85799
  }
85092
85800
  function setpointSlider(id, cap, method, valueArg, value, range) {
@@ -85121,18 +85829,19 @@ var climateContribution = contribution("climate-control", ClimateControlStatusSc
85121
85829
  const status = climateStatusText(view);
85122
85830
  const facts = [];
85123
85831
  if (status !== null) facts.push(fact("climate", "thermometer", status));
85124
- if (view.humidity !== null) facts.push(fact("humidity", "droplets", same(`${Math.round(view.humidity)}%`)));
85125
- if (view.targetHumidity !== null) facts.push(fact("humidity", "droplets", same(`→ ${Math.round(view.targetHumidity)}%`)));
85832
+ if (view.humidity !== null) facts.push(fact("humidity", "droplets", same$1(`${Math.round(view.humidity)}%`)));
85833
+ if (view.targetHumidity !== null) facts.push(fact("humidity", "droplets", same$1(`→ ${Math.round(view.targetHumidity)}%`)));
85126
85834
  const left = [];
85127
85835
  const right = [];
85128
- if (view.measured !== null) left.push(tile("Indoor", "In casa", formatDegrees(view.measured)));
85129
- if (view.humidity !== null) left.push(tile("Humidity", "Umidità", `${Math.round(view.humidity)}%`));
85836
+ if (view.measured !== null) left.push(tile$1("Indoor", "In casa", formatDegrees(view.measured)));
85837
+ if (view.humidity !== null) left.push(tile$1("Humidity", "Umidità", `${Math.round(view.humidity)}%`));
85130
85838
  const modeControls = [{
85131
85839
  kind: "picker",
85132
85840
  id: "climate.mode",
85133
85841
  label: t("Mode", "Modalità"),
85134
85842
  current: view.mode,
85135
- options: [...view.availableModes],
85843
+ options: [...view.availableModes.filter((m) => m !== "off"), ...view.availableModes.filter((m) => m === "off")],
85844
+ optionLabels: modeLabelsOf(view.availableModes),
85136
85845
  call: {
85137
85846
  cap: "climate-control",
85138
85847
  method: "setMode",
@@ -85160,6 +85869,7 @@ var climateContribution = contribution("climate-control", ClimateControlStatusSc
85160
85869
  label: t("Fan", "Ventola"),
85161
85870
  current: slice.fanMode === "" ? null : slice.fanMode,
85162
85871
  options: [...slice.availableFanModes],
85872
+ optionLabels: Object.fromEntries(slice.availableFanModes.map((m) => [m, fanLabelOf(m)])),
85163
85873
  call: {
85164
85874
  cap: "climate-control",
85165
85875
  method: "setFanMode",
@@ -85185,14 +85895,15 @@ var climateContribution = contribution("climate-control", ClimateControlStatusSc
85185
85895
  if (fanControls.length > 0) rows.push({
85186
85896
  id: "climate-fan",
85187
85897
  icon: "fan",
85188
- status: slice.fanMode === "" ? null : same(slice.fanMode),
85898
+ status: slice.fanMode === "" ? null : same$1(slice.fanMode),
85189
85899
  controls: fanControls
85190
85900
  });
85191
- rows.push(...setpointRow(view.target === null ? null : same(formatDegrees(view.target)), view.range === null || view.target === null ? null : setpointSlider("climate.target", "climate-control", "setTarget", "target", view.target, view.range)));
85901
+ rows.push(...setpointRow(view.target === null ? null : same$1(formatDegrees(view.target)), view.range === null || view.target === null ? null : setpointSlider("climate.target", "climate-control", "setTarget", "target", view.target, view.range)));
85192
85902
  const sceneMode = view.sceneMode;
85193
85903
  return {
85194
85904
  reading: reading("climate-control", view.isOff ? "off" : "on"),
85195
85905
  title: view.modeLabel,
85906
+ ...view.isOff || slice.fanMode === "" ? {} : { caption: t(`${view.modeLabel.en} · fan ${fanLabelOf(slice.fanMode).en}`, `${view.modeLabel.it} · ventola ${fanLabelOf(slice.fanMode).it}`) },
85196
85907
  facts,
85197
85908
  ...left.length === 0 && right.length === 0 ? {} : { tiles: {
85198
85909
  left,
@@ -85219,7 +85930,7 @@ var WATER_ON_PREFERENCE = [
85219
85930
  "performance"
85220
85931
  ];
85221
85932
  function capitalised(word) {
85222
- return same(word.charAt(0).toUpperCase() + word.slice(1).replace(/_/g, " "));
85933
+ return same$1(word.charAt(0).toUpperCase() + word.slice(1).replace(/_/g, " "));
85223
85934
  }
85224
85935
  var waterHeaterContribution = contribution("water-heater", WaterHeaterStatusSchema, (slice) => {
85225
85936
  if (slice === void 0 || slice.operationMode === null) return { reading: reading("climate-control", "unknown") };
@@ -85256,13 +85967,13 @@ var waterHeaterContribution = contribution("water-heater", WaterHeaterStatusSche
85256
85967
  icon: "flame",
85257
85968
  status: label,
85258
85969
  controls: modeControls
85259
- }, ...setpointRow(slice.targetTemp === null ? null : same(formatDegrees(slice.targetTemp)), range === null || slice.targetTemp === null ? null : setpointSlider("water-heater.target", "water-heater", "setTargetTemp", "temp", slice.targetTemp, range))];
85970
+ }, ...setpointRow(slice.targetTemp === null ? null : same$1(formatDegrees(slice.targetTemp)), range === null || slice.targetTemp === null ? null : setpointSlider("water-heater.target", "water-heater", "setTargetTemp", "temp", slice.targetTemp, range))];
85260
85971
  const level = levelWithin(slice.targetTemp, range);
85261
85972
  return {
85262
85973
  reading: reading("climate-control", isOff ? "off" : "on"),
85263
85974
  title: label,
85264
85975
  facts: status === null ? [] : [fact("climate", "flame", status)],
85265
- ...slice.currentTemp === null ? {} : { tiles: { left: [tile("Water", "Acqua", formatDegrees(slice.currentTemp))] } },
85976
+ ...slice.currentTemp === null ? {} : { tiles: { left: [tile$1("Water", "Acqua", formatDegrees(slice.currentTemp))] } },
85266
85977
  scene: {
85267
85978
  ...isOff ? {} : { mode: "heat" },
85268
85979
  ...level === null ? {} : { level }
@@ -85280,11 +85991,12 @@ var waterHeaterContribution = contribution("water-heater", WaterHeaterStatusSche
85280
85991
  var heatingValveContribution = contribution("heating-valve", HeatingValveStatusSchema, (slice) => {
85281
85992
  const position = slice?.position ?? null;
85282
85993
  if (position === null) return { reading: reading("sensor", "unknown") };
85283
- const text = same(`${Math.round(position)}%`);
85994
+ const text = same$1(`${Math.round(position)}%`);
85284
85995
  return {
85285
85996
  reading: reading("sensor", position > 0 ? "running" : "idle"),
85286
85997
  facts: [fact("valve", "gauge", t(`Valve ${text.en}`, `Valvola ${text.it}`))],
85287
- tiles: { right: [tile("Valve", "Valvola", text.en)] },
85998
+ tiles: { right: [tile$1("Valve", "Valvola", text.en)] },
85999
+ ...position > 0 ? { caption: t(`Heating · valve ${text.en}`, `Riscalda · valvola ${text.it}`) } : {},
85288
86000
  scene: { level: position / 100 }
85289
86001
  };
85290
86002
  });
@@ -85293,9 +86005,9 @@ function sensorFragment(value, id, icon, en, it, text) {
85293
86005
  if (value === null) return { reading: reading("sensor", "unknown") };
85294
86006
  return {
85295
86007
  reading: reading("sensor", "on"),
85296
- title: same(text),
85297
- facts: [fact(id, icon, same(text))],
85298
- tiles: { right: [tile(en, it, text)] }
86008
+ title: same$1(text),
86009
+ facts: [fact(id, icon, same$1(text))],
86010
+ tiles: { right: [tile$1(en, it, text)] }
85299
86011
  };
85300
86012
  }
85301
86013
  var temperatureContribution = contribution("temperature-sensor", TemperatureSensorStatusSchema, (slice) => sensorFragment(slice?.temperature ?? null, "temperature", "thermometer", "Temperature", "Temperatura", formatDegrees(slice?.temperature ?? 0)));
@@ -85350,8 +86062,8 @@ var CLIMATE_FAMILY_CONTRIBUTIONS = {
85350
86062
  return {
85351
86063
  reading: reading("sensor", "on"),
85352
86064
  ...slice.aqi === void 0 ? {} : { title: airQualityWord(slice.aqi) },
85353
- facts: parts.map((p) => fact("air-quality", "wind", same(`${p.label} ${p.value}`))),
85354
- tiles: { right: parts.slice(0, 2).map((p) => tile(p.label, p.label, p.value)) }
86065
+ facts: parts.map((p) => fact("air-quality", "wind", same$1(`${p.label} ${p.value}`))),
86066
+ tiles: { right: parts.slice(0, 2).map((p) => tile$1(p.label, p.label, p.value)) }
85355
86067
  };
85356
86068
  }),
85357
86069
  "carbon-monoxide": contribution("carbon-monoxide", CarbonMonoxideStatusSchema, (slice) => {
@@ -85366,15 +86078,264 @@ var CLIMATE_FAMILY_CONTRIBUTIONS = {
85366
86078
  "numeric-sensor": numericContribution,
85367
86079
  "enum-sensor": enumContribution
85368
86080
  };
86081
+ /**
86082
+ * The heat pump's contribution to a hero (D1026): the compressor scene, every
86083
+ * circuit as a row with its own label and icon, the powers as pills (the
86084
+ * instantaneous COP only when both powers are known) and a "System" group with
86085
+ * one selector per mode. Values and their limits come from the slice, which the
86086
+ * gateway filled: a selector with no allowed value is not drawn, a missing
86087
+ * reading is never a 0 (D393).
86088
+ */
86089
+ var CAP = "heat-pump-control";
86090
+ var same = (text) => t(text, text);
86091
+ /** The words of the values the gateway names; a value not listed is drawn as the gateway wrote it. */
86092
+ var VALUE_LABELS = {
86093
+ off: t("Off", "Off"),
86094
+ on: t("On", "On"),
86095
+ auto: t("Auto", "Auto"),
86096
+ manual: t("Manual", "Manuale"),
86097
+ low: t("Low", "Bassa"),
86098
+ high: t("High", "Alta"),
86099
+ eco: t("Eco", "Eco"),
86100
+ heat: t("Heat", "Caldo"),
86101
+ cool: t("Cool", "Freddo"),
86102
+ heatCool: t("Heat/cool", "Caldo/freddo"),
86103
+ ownprogram: t("Programme", "Programma")
86104
+ };
86105
+ function valueLabel(value) {
86106
+ const known = VALUE_LABELS[value];
86107
+ if (known !== void 0) return known;
86108
+ const holiday = /^hm(\d+)$/.exec(value);
86109
+ if (holiday?.[1] !== void 0) return t(`Programme ${holiday[1]}`, `Programma ${holiday[1]}`);
86110
+ return same(value);
86111
+ }
86112
+ function optionLabels(allowed) {
86113
+ return Object.fromEntries(allowed.map((value) => [value, valueLabel(value)]));
86114
+ }
86115
+ function picker(id, label, setting, method, args, valueArg = "mode") {
86116
+ return {
86117
+ kind: "picker",
86118
+ id,
86119
+ label,
86120
+ current: setting.value,
86121
+ options: [...setting.allowed],
86122
+ optionLabels: optionLabels(setting.allowed),
86123
+ call: {
86124
+ cap: CAP,
86125
+ method,
86126
+ args: { ...args }
86127
+ },
86128
+ valueArg
86129
+ };
86130
+ }
86131
+ /** A selector exists only where the gateway named values to choose from. */
86132
+ function choosable(setting) {
86133
+ return setting !== null && setting.allowed.length > 0;
86134
+ }
86135
+ function circuitControl(circuit) {
86136
+ const args = { circuitId: circuit.id };
86137
+ if (circuit.kind === "dhw" && choosable(circuit.temperatureLevel)) return picker(`heat-pump.${circuit.id}.level`, t("Level", "Livello"), circuit.temperatureLevel, "setDhwTemperatureLevel", args, "level");
86138
+ if (!choosable(circuit.operationMode)) return null;
86139
+ const method = circuit.kind === "dhw" ? "setDhwOperationMode" : "setCircuitOperationMode";
86140
+ return picker(`heat-pump.${circuit.id}.mode`, t("Mode", "Modalità"), circuit.operationMode, method, args);
86141
+ }
86142
+ /** A circuit with no reading is still a row: it shows that nothing is known, not nothing. */
86143
+ var UNKNOWN_STATUS = same("—");
86144
+ function circuitRow(circuit) {
86145
+ const control = circuitControl(circuit);
86146
+ const status = thermalStatusText(circuit.targetTemp, circuit.currentTemp) ?? UNKNOWN_STATUS;
86147
+ return {
86148
+ id: "circuit",
86149
+ label: same(circuit.label),
86150
+ icon: circuit.icon,
86151
+ status,
86152
+ controls: control === null ? [] : [control]
86153
+ };
86154
+ }
86155
+ var kilowatts = (watts) => `${Number((watts / 1e3).toFixed(2))} kW`;
86156
+ function powerPills(status) {
86157
+ const { thermalW, electricalW } = status.power;
86158
+ const cop = heatPumpInstantCop(status.power);
86159
+ const pills = [];
86160
+ if (thermalW !== null) pills.push({
86161
+ icon: "flame",
86162
+ text: t(`Thermal ${kilowatts(thermalW)}`, `Termica ${kilowatts(thermalW)}`),
86163
+ tone: "muted"
86164
+ });
86165
+ if (electricalW !== null) pills.push({
86166
+ icon: "zap",
86167
+ text: t(`Electrical ${kilowatts(electricalW)}`, `Elettrica ${kilowatts(electricalW)}`),
86168
+ tone: "muted"
86169
+ });
86170
+ if (cop !== null) {
86171
+ const text = String(Number(cop.toFixed(1)));
86172
+ pills.push({
86173
+ icon: "gauge",
86174
+ text: t(`Instantaneous COP ${text}`, `COP istantaneo ${text}`),
86175
+ tone: "success"
86176
+ });
86177
+ }
86178
+ return pills;
86179
+ }
86180
+ /** The independent system modes, in the order the card draws them. */
86181
+ var SYSTEM_MODES = [
86182
+ {
86183
+ key: "silent",
86184
+ label: t("Silent", "Silenzioso"),
86185
+ icon: "volume-x",
86186
+ method: "setSilentMode"
86187
+ },
86188
+ {
86189
+ key: "away",
86190
+ label: t("Away", "Assente"),
86191
+ icon: "plane",
86192
+ method: "setAwayMode"
86193
+ },
86194
+ {
86195
+ key: "additionalHeater",
86196
+ label: t("Additional heater", "Riscaldatore aggiuntivo"),
86197
+ icon: "flame",
86198
+ method: "setAdditionalHeaterMode"
86199
+ },
86200
+ {
86201
+ key: "holiday",
86202
+ label: t("Holiday", "Vacanza"),
86203
+ icon: "tree-palm",
86204
+ method: "setHolidayMode"
86205
+ }
86206
+ ];
86207
+ var SYSTEM_GROUP = t("System", "Sistema");
86208
+ var PROGRAMME = /^hm\d+$/;
86209
+ /**
86210
+ * Holiday is two segments, off / programme. Choosing "programme" keeps the programme
86211
+ * that is set, else the first one the gateway allowed, so a write never leaves
86212
+ * `allowed`. The full hm1..hm8 pick is not in the hero (D1026). A holiday whose values
86213
+ * are not programmes keeps the plain selector.
86214
+ */
86215
+ function holidayRow(def, setting) {
86216
+ if (!setting.allowed.includes("off")) return null;
86217
+ const current = setting.value !== null && PROGRAMME.test(setting.value) ? setting.value : null;
86218
+ const programme = current ?? setting.allowed.find((v) => PROGRAMME.test(v));
86219
+ if (programme === void 0) return null;
86220
+ const control = picker("heat-pump.system.holiday", def.label, {
86221
+ value: setting.value,
86222
+ allowed: ["off", programme]
86223
+ }, def.method, {});
86224
+ const number = programme.slice(2);
86225
+ return {
86226
+ id: "system-mode",
86227
+ label: def.label,
86228
+ group: SYSTEM_GROUP,
86229
+ icon: def.icon,
86230
+ status: current === null ? null : t(`Programme ${number}`, `Programma ${number}`),
86231
+ controls: [control.kind === "picker" ? {
86232
+ ...control,
86233
+ optionLabels: {
86234
+ off: t("Off", "Off"),
86235
+ [programme]: t("Programme", "Programma")
86236
+ }
86237
+ } : control]
86238
+ };
86239
+ }
86240
+ function systemRows(status) {
86241
+ const rows = [];
86242
+ for (const def of SYSTEM_MODES) {
86243
+ const setting = status.system[def.key];
86244
+ if (!choosable(setting)) continue;
86245
+ if (def.key === "holiday") {
86246
+ const holiday = holidayRow(def, setting);
86247
+ if (holiday !== null) {
86248
+ rows.push(holiday);
86249
+ continue;
86250
+ }
86251
+ }
86252
+ rows.push({
86253
+ id: "system-mode",
86254
+ label: def.label,
86255
+ group: SYSTEM_GROUP,
86256
+ icon: def.icon,
86257
+ status: null,
86258
+ controls: [picker(`heat-pump.system.${def.key}`, def.label, setting, def.method, {})]
86259
+ });
86260
+ }
86261
+ return rows;
86262
+ }
86263
+ function tile(en, it, value) {
86264
+ return {
86265
+ label: t(en, it),
86266
+ value: same(value)
86267
+ };
86268
+ }
86269
+ function tilesOf$1(status) {
86270
+ const { supply, return: back, outdoor } = status.temperatures;
86271
+ const left = [];
86272
+ const right = [];
86273
+ if (supply !== null) left.push(tile("Supply", "Mandata", formatDegrees(supply)));
86274
+ if (back !== null) left.push(tile("Return", "Ritorno", formatDegrees(back)));
86275
+ if (outdoor !== null) right.push(tile("Outdoor", "Esterna", formatDegrees(outdoor)));
86276
+ if (status.waterPressureBar !== null) right.push(tile("Pressure", "Pressione", `${Number(status.waterPressureBar.toFixed(1))} bar`));
86277
+ return {
86278
+ left,
86279
+ right
86280
+ };
86281
+ }
86282
+ function isRunning(status) {
86283
+ const { compressorPct, electricalW } = status.power;
86284
+ if (compressorPct !== null) return compressorPct > 0;
86285
+ if (electricalW !== null) return electricalW > 0;
86286
+ return null;
86287
+ }
86288
+ var HEAT_PUMP_FAMILY_CONTRIBUTIONS = { "heat-pump-status": contribution("heat-pump-status", HeatPumpStatusSchema, (slice) => {
86289
+ if (slice === void 0) return { reading: reading("climate-control", "unknown") };
86290
+ const running = isRunning(slice);
86291
+ const facts = [];
86292
+ if (slice.power.electricalW !== null) facts.push(fact("power", "zap", same(`${Math.round(slice.power.electricalW)} W`)));
86293
+ if (slice.power.compressorPct !== null) facts.push(fact("level", "gauge", t(`Compressor ${Math.round(slice.power.compressorPct)} %`, `Compressore ${Math.round(slice.power.compressorPct)} %`)));
86294
+ const charging = slice.circuits.find((c) => c.active === true);
86295
+ const tiles = tilesOf$1(slice);
86296
+ const pills = powerPills(slice);
86297
+ const rows = [
86298
+ ...slice.circuits.map(circuitRow),
86299
+ ...pills.length === 0 ? [] : [{
86300
+ id: "heat-pump-power",
86301
+ icon: "zap",
86302
+ status: null,
86303
+ controls: [],
86304
+ pills: [...pills]
86305
+ }],
86306
+ ...systemRows(slice)
86307
+ ];
86308
+ return {
86309
+ reading: reading("climate-control", running === true ? "running" : running === false ? "idle" : "on"),
86310
+ title: t("Heat pump", "Pompa di calore"),
86311
+ ...charging === void 0 ? {} : { caption: t(`${charging.label} · heating`.toUpperCase(), `${charging.label} · in riscaldamento`.toUpperCase()) },
86312
+ facts,
86313
+ ...tiles.left.length === 0 && tiles.right.length === 0 ? {} : { tiles },
86314
+ ...slice.power.electricalW === null ? {} : { consumption: {
86315
+ priority: 1,
86316
+ slot: {
86317
+ kind: "power",
86318
+ watts: slice.power.electricalW,
86319
+ detail: slice.power.thermalW === null ? [] : [{
86320
+ label: t("Thermal", "Termica"),
86321
+ value: Math.round(slice.power.thermalW),
86322
+ unit: "W"
86323
+ }]
86324
+ }
86325
+ } },
86326
+ rows
86327
+ };
86328
+ }) };
85369
86329
  [
85370
86330
  ...Object.values(ACCESS_FAMILY_CONTRIBUTIONS),
85371
86331
  ...Object.values(DEVICES_FAMILY_CONTRIBUTIONS),
85372
86332
  ...Object.values(GROUPS_FAMILY_CONTRIBUTIONS),
85373
86333
  ...Object.values(ACTUATORS_FAMILY_CONTRIBUTIONS),
85374
86334
  ...Object.values(SENSORS_FAMILY_CONTRIBUTIONS),
85375
- ...Object.values(CLIMATE_FAMILY_CONTRIBUTIONS)
86335
+ ...Object.values(CLIMATE_FAMILY_CONTRIBUTIONS),
86336
+ ...Object.values(HEAT_PUMP_FAMILY_CONTRIBUTIONS)
85376
86337
  ];
85377
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway;
86338
+ DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway;
85378
86339
  /**
85379
86340
  * Who looks like what (D1023). One look per {@link DeviceType}, refined by a
85380
86341
  * {@link DeviceRole} where the role changes how the device reads (a leak
@@ -85764,7 +86725,7 @@ lookOf({
85764
86725
  ({ ...lookOf({
85765
86726
  icon: "shield-check",
85766
86727
  on: [
85767
- "success",
86728
+ "danger",
85768
86729
  "glow",
85769
86730
  "halo"
85770
86731
  ],
@@ -86002,8 +86963,24 @@ lookOf({
86002
86963
  idle: INFO,
86003
86964
  standby: MUTED
86004
86965
  });
86966
+ lookOf({
86967
+ icon: "thermometer-sun",
86968
+ on: [
86969
+ "warning",
86970
+ "heat-rise",
86971
+ "rise"
86972
+ ],
86973
+ off: MUTED,
86974
+ running: [
86975
+ "warning",
86976
+ "heat-rise",
86977
+ "rise"
86978
+ ],
86979
+ idle: INFO,
86980
+ standby: MUTED
86981
+ });
86005
86982
  DeviceType.Climate, DeviceType.Thermostat;
86006
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway;
86983
+ DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway;
86007
86984
  DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
86008
86985
  Object.freeze({
86009
86986
  "accessories.getStatus": {
@@ -86384,13 +87361,6 @@ Object.freeze({
86384
87361
  access: "view",
86385
87362
  role: "admin"
86386
87363
  },
86387
- "addonSettings.getIntegrationFormSchema": {
86388
- capName: "addon-settings",
86389
- capScope: "system",
86390
- addonId: null,
86391
- access: "view",
86392
- role: "admin"
86393
- },
86394
87364
  "addonSettings.getScopedSettings": {
86395
87365
  capName: "addon-settings",
86396
87366
  capScope: "system",
@@ -88176,6 +89146,13 @@ Object.freeze({
88176
89146
  access: "create",
88177
89147
  role: "admin"
88178
89148
  },
89149
+ "deviceManager.checkDeviceTemplates": {
89150
+ capName: "device-manager",
89151
+ capScope: "system",
89152
+ addonId: null,
89153
+ access: "view",
89154
+ role: "admin"
89155
+ },
88179
89156
  "deviceManager.cleanupDeletedDevicesData": {
88180
89157
  capName: "device-manager",
88181
89158
  capScope: "system",
@@ -88575,6 +89552,13 @@ Object.freeze({
88575
89552
  access: "create",
88576
89553
  role: "admin"
88577
89554
  },
89555
+ "deviceManager.renderDeviceTemplates": {
89556
+ capName: "device-manager",
89557
+ capScope: "system",
89558
+ addonId: null,
89559
+ access: "view",
89560
+ role: "admin"
89561
+ },
88578
89562
  "deviceManager.reportDiscoveryObservations": {
88579
89563
  capName: "device-manager",
88580
89564
  capScope: "system",
@@ -89506,6 +90490,111 @@ Object.freeze({
89506
90490
  access: "view",
89507
90491
  role: "viewer"
89508
90492
  },
90493
+ "heatPumpControl.setAdditionalHeaterMode": {
90494
+ capName: "heat-pump-control",
90495
+ capScope: "device",
90496
+ addonId: null,
90497
+ access: "create",
90498
+ role: "viewer"
90499
+ },
90500
+ "heatPumpControl.setAwayMode": {
90501
+ capName: "heat-pump-control",
90502
+ capScope: "device",
90503
+ addonId: null,
90504
+ access: "create",
90505
+ role: "viewer"
90506
+ },
90507
+ "heatPumpControl.setCircuitComfortSetpoint": {
90508
+ capName: "heat-pump-control",
90509
+ capScope: "device",
90510
+ addonId: null,
90511
+ access: "create",
90512
+ role: "viewer"
90513
+ },
90514
+ "heatPumpControl.setCircuitEcoSetpoint": {
90515
+ capName: "heat-pump-control",
90516
+ capScope: "device",
90517
+ addonId: null,
90518
+ access: "create",
90519
+ role: "viewer"
90520
+ },
90521
+ "heatPumpControl.setCircuitHeatCoolMode": {
90522
+ capName: "heat-pump-control",
90523
+ capScope: "device",
90524
+ addonId: null,
90525
+ access: "create",
90526
+ role: "viewer"
90527
+ },
90528
+ "heatPumpControl.setCircuitMaxFlowTemp": {
90529
+ capName: "heat-pump-control",
90530
+ capScope: "device",
90531
+ addonId: null,
90532
+ access: "create",
90533
+ role: "viewer"
90534
+ },
90535
+ "heatPumpControl.setCircuitOperationMode": {
90536
+ capName: "heat-pump-control",
90537
+ capScope: "device",
90538
+ addonId: null,
90539
+ access: "create",
90540
+ role: "viewer"
90541
+ },
90542
+ "heatPumpControl.setCircuitSummerWinter": {
90543
+ capName: "heat-pump-control",
90544
+ capScope: "device",
90545
+ addonId: null,
90546
+ access: "create",
90547
+ role: "viewer"
90548
+ },
90549
+ "heatPumpControl.setDhwChargeDuration": {
90550
+ capName: "heat-pump-control",
90551
+ capScope: "device",
90552
+ addonId: null,
90553
+ access: "create",
90554
+ role: "viewer"
90555
+ },
90556
+ "heatPumpControl.setDhwOperationMode": {
90557
+ capName: "heat-pump-control",
90558
+ capScope: "device",
90559
+ addonId: null,
90560
+ access: "create",
90561
+ role: "viewer"
90562
+ },
90563
+ "heatPumpControl.setDhwTemperatureLevel": {
90564
+ capName: "heat-pump-control",
90565
+ capScope: "device",
90566
+ addonId: null,
90567
+ access: "create",
90568
+ role: "viewer"
90569
+ },
90570
+ "heatPumpControl.setHolidayMode": {
90571
+ capName: "heat-pump-control",
90572
+ capScope: "device",
90573
+ addonId: null,
90574
+ access: "create",
90575
+ role: "viewer"
90576
+ },
90577
+ "heatPumpControl.setSilentMode": {
90578
+ capName: "heat-pump-control",
90579
+ capScope: "device",
90580
+ addonId: null,
90581
+ access: "create",
90582
+ role: "viewer"
90583
+ },
90584
+ "heatPumpControl.startExtraHotWater": {
90585
+ capName: "heat-pump-control",
90586
+ capScope: "device",
90587
+ addonId: null,
90588
+ access: "create",
90589
+ role: "viewer"
90590
+ },
90591
+ "heatPumpStatus.getStatus": {
90592
+ capName: "heat-pump-status",
90593
+ capScope: "device",
90594
+ addonId: null,
90595
+ access: "view",
90596
+ role: "viewer"
90597
+ },
89509
90598
  "heroLayout.getHeroModel": {
89510
90599
  capName: "hero-layout",
89511
90600
  capScope: "system",
@@ -94413,13 +95502,6 @@ Object.freeze({
94413
95502
  access: "create",
94414
95503
  role: "admin"
94415
95504
  },
94416
- "smtpProvider.verify": {
94417
- capName: "smtp-provider",
94418
- capScope: "system",
94419
- addonId: null,
94420
- access: "view",
94421
- role: "admin"
94422
- },
94423
95505
  "snapshot.applyDeviceSettingsPatch": {
94424
95506
  capName: "snapshot",
94425
95507
  capScope: "device",
@@ -96352,7 +97434,7 @@ function projectAction(cap, action) {
96352
97434
  };
96353
97435
  }
96354
97436
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
96355
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Heater, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
97437
+ DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Heater, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
96356
97438
  DeviceFeature.BatteryOperated;
96357
97439
  DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
96358
97440
  var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
@@ -96393,7 +97475,6 @@ function offered(received) {
96393
97475
  };
96394
97476
  }
96395
97477
  offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
96396
- string().min(1).max(4096);
96397
97478
  Object.freeze({
96398
97479
  "accessories.getStatus": [{
96399
97480
  name: "deviceId",
@@ -97476,6 +98557,81 @@ Object.freeze({
97476
98557
  form: "single",
97477
98558
  optional: false
97478
98559
  }],
98560
+ "heatPumpControl.setAdditionalHeaterMode": [{
98561
+ name: "deviceId",
98562
+ form: "single",
98563
+ optional: false
98564
+ }],
98565
+ "heatPumpControl.setAwayMode": [{
98566
+ name: "deviceId",
98567
+ form: "single",
98568
+ optional: false
98569
+ }],
98570
+ "heatPumpControl.setCircuitComfortSetpoint": [{
98571
+ name: "deviceId",
98572
+ form: "single",
98573
+ optional: false
98574
+ }],
98575
+ "heatPumpControl.setCircuitEcoSetpoint": [{
98576
+ name: "deviceId",
98577
+ form: "single",
98578
+ optional: false
98579
+ }],
98580
+ "heatPumpControl.setCircuitHeatCoolMode": [{
98581
+ name: "deviceId",
98582
+ form: "single",
98583
+ optional: false
98584
+ }],
98585
+ "heatPumpControl.setCircuitMaxFlowTemp": [{
98586
+ name: "deviceId",
98587
+ form: "single",
98588
+ optional: false
98589
+ }],
98590
+ "heatPumpControl.setCircuitOperationMode": [{
98591
+ name: "deviceId",
98592
+ form: "single",
98593
+ optional: false
98594
+ }],
98595
+ "heatPumpControl.setCircuitSummerWinter": [{
98596
+ name: "deviceId",
98597
+ form: "single",
98598
+ optional: false
98599
+ }],
98600
+ "heatPumpControl.setDhwChargeDuration": [{
98601
+ name: "deviceId",
98602
+ form: "single",
98603
+ optional: false
98604
+ }],
98605
+ "heatPumpControl.setDhwOperationMode": [{
98606
+ name: "deviceId",
98607
+ form: "single",
98608
+ optional: false
98609
+ }],
98610
+ "heatPumpControl.setDhwTemperatureLevel": [{
98611
+ name: "deviceId",
98612
+ form: "single",
98613
+ optional: false
98614
+ }],
98615
+ "heatPumpControl.setHolidayMode": [{
98616
+ name: "deviceId",
98617
+ form: "single",
98618
+ optional: false
98619
+ }],
98620
+ "heatPumpControl.setSilentMode": [{
98621
+ name: "deviceId",
98622
+ form: "single",
98623
+ optional: false
98624
+ }],
98625
+ "heatPumpControl.startExtraHotWater": [{
98626
+ name: "deviceId",
98627
+ form: "single",
98628
+ optional: false
98629
+ }],
98630
+ "heatPumpStatus.getStatus": [{
98631
+ name: "deviceId",
98632
+ form: "single",
98633
+ optional: false
98634
+ }],
97479
98635
  "heroLayout.getHeroModel": [{
97480
98636
  name: "deviceId",
97481
98637
  form: "single",
@@ -100961,4 +102117,4 @@ function keepBodyKeys(provider, keys) {
100961
102117
  };
100962
102118
  }
100963
102119
  //#endregion
100964
- export { customAction as A, number$1 as B, runningFromDishwasherOperation as C, waterHeaterCapability as D, temperatureSensorCapability as E, isSecretMarkerLike as F, unknown as G, record as H, rowsDepsOver as I, errMsg as J, DeviceRole as K, _enum as L, defineEntryKind as M, entryBody as N, RecordIntegrationInstances as O, isSecretMarker as P, array as R, quantityFromUnit as S, switchCapability as T, string as U, object as V, union as W, EventCategory as Y, heatingValveCapability as _, BaseDevice as a, numericSensorCapability as b, RUNNING_STATE_COLD as c, climateControlCapability as d, consumablesCapability as f, eventEmitterCapability as g, enumSensorCapability as h, testBoschInstance as i, defineCustomActions as j, createEvent as k, batteryCapability as l, dishwasherCapability as m, boschRowsPorts as n, BaseDeviceProvider as o, deviceAdoptionCapability as p, DeviceType as q, keepBodyKeys as r, DishwasherOperationStateSchema as s, boschRowsMigration as t, binaryCapability as u, ingestReading as v, runningStateCapability as w, powerMeterCapability as x, integrationInstancesCapability as y, literal as z };
102120
+ export { RecordIntegrationInstances as A, array as B, powerMeterCapability as C, switchCapability as D, runningStateCapability as E, entryBody as F, string as G, number$1 as H, isSecretMarker as I, DeviceRole as J, union as K, isSecretMarkerLike as L, customAction as M, defineCustomActions as N, temperatureSensorCapability as O, defineEntryKind as P, rowsDepsOver as R, numericSensorCapability as S, runningFromDishwasherOperation as T, object as U, literal as V, record as W, errMsg as X, DeviceType as Y, EventCategory as Z, heatPumpControlCapability as _, BaseDevice as a, ingestReading as b, RUNNING_STATE_COLD as c, climateControlCapability as d, consumablesCapability as f, eventEmitterCapability as g, enumSensorCapability as h, testBoschInstance as i, createEvent as j, waterHeaterCapability as k, batteryCapability as l, dishwasherCapability as m, boschRowsPorts as n, BaseDeviceProvider as o, deviceAdoptionCapability as p, unknown as q, keepBodyKeys as r, DishwasherOperationStateSchema as s, boschRowsMigration as t, binaryCapability as u, heatPumpStatusCapability as v, quantityFromUnit as w, integrationInstancesCapability as x, heatingValveCapability as y, _enum as z };