@camstack/addon-provider-bosch 0.1.44 → 0.1.45

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
@@ -45356,10 +45474,6 @@ method(object({
45356
45474
  scope: ScopedSettingsScopeSchema,
45357
45475
  nodeId: string().optional()
45358
45476
  }), record(string(), unknown()).nullable()), method(object({
45359
- addonId: string(),
45360
- nodeId: string().optional(),
45361
- instance: IntegrationInstanceRefSchema
45362
- }), SettingsSchemaWithValuesSchema.nullable()), method(object({
45363
45477
  addonId: string(),
45364
45478
  nodeId: string().optional()
45365
45479
  }), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
@@ -50599,7 +50713,7 @@ var mediaPlayerCapability = {
50599
50713
  isMuted: { kind: "boolean" }
50600
50714
  } }
50601
50715
  };
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;
50716
+ 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
50717
  var NUMBER = {
50604
50718
  kind: "fixed",
50605
50719
  result: ["number"]
@@ -56639,6 +56753,332 @@ var groupStatusCapability = {
56639
56753
  durability: "session",
56640
56754
  history: "none"
56641
56755
  };
56756
+ /**
56757
+ * Heat pump settable surface (D1026): one typed method per parameter, never a
56758
+ * generic `set(key, value)`. The values a mode takes and the limits of a number
56759
+ * are the ones `heat-pump-status` carries (`allowed`, `min`, `max`, `step`),
56760
+ * read from the gateway; a provider refuses a value outside them. The state is
56761
+ * `heat-pump-status` — this cap has none.
56762
+ */
56763
+ var deviceId = number$1().int().nonnegative();
56764
+ var circuitId = string().min(1);
56765
+ var mode = string().min(1);
56766
+ var temp = number$1().finite();
56767
+ var heatPumpControlCapability = {
56768
+ name: "heat-pump-control",
56769
+ scope: "device",
56770
+ deviceNative: true,
56771
+ mode: "singleton",
56772
+ deviceTypes: [DeviceType.HeatPump],
56773
+ methods: {
56774
+ /** off / auto / on. */
56775
+ setSilentMode: method(object({
56776
+ deviceId,
56777
+ mode
56778
+ }), _void(), {
56779
+ kind: "mutation",
56780
+ auth: "protected"
56781
+ }),
56782
+ /** off / on. */
56783
+ setAwayMode: method(object({
56784
+ deviceId,
56785
+ mode
56786
+ }), _void(), {
56787
+ kind: "mutation",
56788
+ auth: "protected"
56789
+ }),
56790
+ /** off / manual / auto. */
56791
+ setAdditionalHeaterMode: method(object({
56792
+ deviceId,
56793
+ mode
56794
+ }), _void(), {
56795
+ kind: "mutation",
56796
+ auth: "protected"
56797
+ }),
56798
+ /** off / hm1 .. hm8 (the programmes the gateway lists). */
56799
+ setHolidayMode: method(object({
56800
+ deviceId,
56801
+ mode
56802
+ }), _void(), {
56803
+ kind: "mutation",
56804
+ auth: "protected"
56805
+ }),
56806
+ setCircuitOperationMode: method(object({
56807
+ deviceId,
56808
+ circuitId,
56809
+ mode
56810
+ }), _void(), {
56811
+ kind: "mutation",
56812
+ auth: "protected"
56813
+ }),
56814
+ setCircuitHeatCoolMode: method(object({
56815
+ deviceId,
56816
+ circuitId,
56817
+ mode
56818
+ }), _void(), {
56819
+ kind: "mutation",
56820
+ auth: "protected"
56821
+ }),
56822
+ setCircuitSummerWinter: method(object({
56823
+ deviceId,
56824
+ circuitId,
56825
+ mode
56826
+ }), _void(), {
56827
+ kind: "mutation",
56828
+ auth: "protected"
56829
+ }),
56830
+ setCircuitComfortSetpoint: method(object({
56831
+ deviceId,
56832
+ circuitId,
56833
+ temp
56834
+ }), _void(), {
56835
+ kind: "mutation",
56836
+ auth: "protected"
56837
+ }),
56838
+ setCircuitEcoSetpoint: method(object({
56839
+ deviceId,
56840
+ circuitId,
56841
+ temp
56842
+ }), _void(), {
56843
+ kind: "mutation",
56844
+ auth: "protected"
56845
+ }),
56846
+ setCircuitMaxFlowTemp: method(object({
56847
+ deviceId,
56848
+ circuitId,
56849
+ temp
56850
+ }), _void(), {
56851
+ kind: "mutation",
56852
+ auth: "protected"
56853
+ }),
56854
+ setDhwOperationMode: method(object({
56855
+ deviceId,
56856
+ circuitId,
56857
+ mode
56858
+ }), _void(), {
56859
+ kind: "mutation",
56860
+ auth: "protected"
56861
+ }),
56862
+ setDhwTemperatureLevel: method(object({
56863
+ deviceId,
56864
+ circuitId,
56865
+ level: mode
56866
+ }), _void(), {
56867
+ kind: "mutation",
56868
+ auth: "protected"
56869
+ }),
56870
+ setDhwChargeDuration: method(object({
56871
+ deviceId,
56872
+ circuitId,
56873
+ minutes: number$1().int().positive()
56874
+ }), _void(), {
56875
+ kind: "mutation",
56876
+ auth: "protected"
56877
+ }),
56878
+ /** Start an extra-hot-water charge now: an action, not a stored mode. */
56879
+ startExtraHotWater: method(object({
56880
+ deviceId,
56881
+ circuitId
56882
+ }), _void(), {
56883
+ kind: "mutation",
56884
+ auth: "protected"
56885
+ })
56886
+ }
56887
+ };
56888
+ var HeatPumpCircuitKindSchema = _enum([
56889
+ "heating",
56890
+ "dhw",
56891
+ "cooling",
56892
+ "solar"
56893
+ ]);
56894
+ /** A setting that takes one of a list of values the gateway named. */
56895
+ var HeatPumpOptionSettingSchema = object({
56896
+ /** The active value. Null when the gateway did not answer. */
56897
+ value: string().nullable(),
56898
+ /** Values the gateway accepts, in its order. Empty = read-only here. */
56899
+ allowed: array(string())
56900
+ });
56901
+ /** A numeric setting with the limits the gateway named. */
56902
+ var HeatPumpRangeSettingSchema = object({
56903
+ value: number$1().nullable(),
56904
+ min: number$1().nullable(),
56905
+ max: number$1().nullable(),
56906
+ step: number$1().nullable()
56907
+ });
56908
+ var HeatPumpCircuitSchema = object({
56909
+ /** Gateway id of the circuit (`hc1`, `dhw1`): the key of the series. */
56910
+ id: string().min(1),
56911
+ kind: HeatPumpCircuitKindSchema,
56912
+ label: string(),
56913
+ icon: IconKeySchema,
56914
+ /** Measured temperature of the circuit (room, tank, collector). */
56915
+ currentTemp: number$1().nullable().meta(readingMeta("temperature")),
56916
+ /** The setpoint in force now. */
56917
+ targetTemp: number$1().nullable().meta(readingMeta("temperature")),
56918
+ /** The operation mode in force (mirror of `operationMode.value`, for the series). */
56919
+ mode: string().nullable(),
56920
+ /** Whether the circuit is calling for heat right now. Null = unknown. */
56921
+ active: boolean().nullable(),
56922
+ operationMode: HeatPumpOptionSettingSchema.nullable(),
56923
+ /** Heating circuits: heat / cool / heatCool. */
56924
+ heatCoolMode: HeatPumpOptionSettingSchema.nullable(),
56925
+ /** Heating circuits: summer / winter changeover. */
56926
+ summerWinter: HeatPumpOptionSettingSchema.nullable(),
56927
+ comfortSetpoint: HeatPumpRangeSettingSchema.nullable(),
56928
+ ecoSetpoint: HeatPumpRangeSettingSchema.nullable(),
56929
+ maxFlowTemp: HeatPumpRangeSettingSchema.nullable(),
56930
+ /** Hot water: the temperature level (off / low / high / eco). */
56931
+ temperatureLevel: HeatPumpOptionSettingSchema.nullable(),
56932
+ /** Hot water: how long a charge lasts, minutes. */
56933
+ chargeDuration: HeatPumpRangeSettingSchema.nullable(),
56934
+ /** Hot water: whether an extra-hot-water charge is running. Null = no such surface. */
56935
+ extraHotWater: boolean().nullable()
56936
+ });
56937
+ /** The independent system modes, each with its real values. A null mode = the gateway has none. */
56938
+ var HeatPumpSystemSchema = object({
56939
+ silent: HeatPumpOptionSettingSchema.nullable(),
56940
+ away: HeatPumpOptionSettingSchema.nullable(),
56941
+ additionalHeater: HeatPumpOptionSettingSchema.nullable(),
56942
+ holiday: HeatPumpOptionSettingSchema.nullable()
56943
+ });
56944
+ var HeatPumpPowerSchema = object({
56945
+ thermalW: number$1().nullable().meta(readingMeta("power")),
56946
+ electricalW: number$1().nullable().meta(readingMeta("power")),
56947
+ /** Compressor modulation, percent. */
56948
+ compressorPct: number$1().nullable().meta(readingMeta("percentage"))
56949
+ });
56950
+ var HeatPumpTemperaturesSchema = object({
56951
+ supply: number$1().nullable().meta(readingMeta("temperature")),
56952
+ return: number$1().nullable().meta(readingMeta("temperature")),
56953
+ outdoor: number$1().nullable().meta(readingMeta("temperature"))
56954
+ });
56955
+ /** Lifetime meters, kWh: the energy of a period is computed at read time from their difference (D944). */
56956
+ var HeatPumpEnergySchema = object({
56957
+ thermalKwh: number$1().nullable().meta(readingMeta("energy")),
56958
+ electricalKwh: number$1().nullable().meta(readingMeta("energy"))
56959
+ });
56960
+ var HeatPumpStatusSchema = object({
56961
+ circuits: array(HeatPumpCircuitSchema),
56962
+ system: HeatPumpSystemSchema,
56963
+ power: HeatPumpPowerSchema,
56964
+ temperatures: HeatPumpTemperaturesSchema,
56965
+ energy: HeatPumpEnergySchema,
56966
+ /** Water pressure of the heating loop, bar. */
56967
+ waterPressureBar: number$1().nullable(),
56968
+ /** Compressor starts since commissioning. */
56969
+ starts: number$1().nullable(),
56970
+ /** Ms epoch when the slice was last read from the gateway. */
56971
+ lastFetchedAt: number$1()
56972
+ });
56973
+ /**
56974
+ * The instantaneous COP: thermal over electrical power. Null unless BOTH powers
56975
+ * are known and the unit draws power — a missing reading is never a 0 or a
56976
+ * division by it (D393). Computed on every read; there is no stored value.
56977
+ */
56978
+ function heatPumpInstantCop(power) {
56979
+ const { thermalW, electricalW } = power;
56980
+ if (thermalW === null || electricalW === null || electricalW <= 0) return null;
56981
+ return thermalW / electricalW;
56982
+ }
56983
+ var heatPumpStatusCapability = {
56984
+ name: "heat-pump-status",
56985
+ scope: "device",
56986
+ deviceNative: true,
56987
+ mode: "singleton",
56988
+ deviceTypes: [DeviceType.HeatPump],
56989
+ methods: {},
56990
+ status: {
56991
+ schema: HeatPumpStatusSchema,
56992
+ kind: "push"
56993
+ },
56994
+ runtimeState: HeatPumpStatusSchema,
56995
+ /**
56996
+ * Runtime-state durability: **session** — a heat pump's gateway answers on the
56997
+ * next poll, so a restart that clears the slice loses nothing a reader needs
56998
+ * (the series are recorded). See `RuntimeStateDurability`.
56999
+ */
57000
+ durability: "session",
57001
+ /**
57002
+ * History (D850, D917): `circuits[]` is a keyed array — one series per circuit and
57003
+ * field, named by the circuit's label. The mode is a state (D896); the energy
57004
+ * totals are lifetime counters, the kWh of a period is computed at read time (D944).
57005
+ * The COP is never recorded: it derives from the two powers.
57006
+ */
57007
+ history: {
57008
+ fields: {
57009
+ "power.thermalW": {
57010
+ kind: "gauge",
57011
+ unit: "W",
57012
+ deadband: 50,
57013
+ displayName: "Thermal power"
57014
+ },
57015
+ "power.electricalW": {
57016
+ kind: "gauge",
57017
+ unit: "W",
57018
+ deadband: 50,
57019
+ displayName: "Electrical power"
57020
+ },
57021
+ "power.compressorPct": {
57022
+ kind: "gauge",
57023
+ unit: "%",
57024
+ deadband: 2,
57025
+ displayName: "Compressor"
57026
+ },
57027
+ "temperatures.supply": {
57028
+ kind: "gauge",
57029
+ unit: "°C",
57030
+ deadband: .5,
57031
+ displayName: "Supply"
57032
+ },
57033
+ "temperatures.return": {
57034
+ kind: "gauge",
57035
+ unit: "°C",
57036
+ deadband: .5,
57037
+ displayName: "Return"
57038
+ },
57039
+ "temperatures.outdoor": {
57040
+ kind: "gauge",
57041
+ unit: "°C",
57042
+ deadband: .5,
57043
+ displayName: "Outdoor"
57044
+ },
57045
+ "energy.thermalKwh": {
57046
+ kind: "counter",
57047
+ unit: "kWh",
57048
+ displayName: "Thermal energy"
57049
+ },
57050
+ "energy.electricalKwh": {
57051
+ kind: "counter",
57052
+ unit: "kWh",
57053
+ displayName: "Electrical energy"
57054
+ }
57055
+ },
57056
+ items: {
57057
+ path: "circuits",
57058
+ key: "id",
57059
+ label: "label",
57060
+ fields: {
57061
+ currentTemp: {
57062
+ kind: "gauge",
57063
+ unit: "°C",
57064
+ deadband: .5,
57065
+ displayName: "Temperature"
57066
+ },
57067
+ targetTemp: {
57068
+ kind: "gauge",
57069
+ unit: "°C",
57070
+ minIntervalMs: 0,
57071
+ displayName: "Setpoint"
57072
+ },
57073
+ mode: {
57074
+ kind: "enum",
57075
+ displayName: "Mode"
57076
+ }
57077
+ }
57078
+ }
57079
+ },
57080
+ volatileStateFields: ["lastFetchedAt"]
57081
+ };
56642
57082
  var HeatingValveStatusSchema = object({
56643
57083
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
56644
57084
  position: number$1().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -56853,6 +57293,7 @@ var HeroSceneSchema = object({
56853
57293
  "ac",
56854
57294
  "thermostat",
56855
57295
  "heater",
57296
+ "heat-pump",
56856
57297
  "fan",
56857
57298
  "washer",
56858
57299
  "dryer",
@@ -56895,7 +57336,7 @@ var HeroSceneSchema = object({
56895
57336
  /** 0..1: volume, cover position, cycle progress. */
56896
57337
  level: number$1().min(0).max(1).nullable()
56897
57338
  });
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;
57339
+ 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
57340
  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
57341
  /** A string in both languages, as the cap vocabulary writes it (D920): no `t()` in the model. */
56901
57342
  var HeroTextSchema = object({
@@ -56903,6 +57344,7 @@ var HeroTextSchema = object({
56903
57344
  it: string()
56904
57345
  });
56905
57346
  var HeroRowIdSchema = _enum([
57347
+ "progress",
56906
57348
  "run-for",
56907
57349
  "cycle",
56908
57350
  "program",
@@ -56917,6 +57359,7 @@ var HeroRowIdSchema = _enum([
56917
57359
  "alarm",
56918
57360
  "fan",
56919
57361
  "vacuum",
57362
+ "vacuum-suction",
56920
57363
  "mower",
56921
57364
  "feeder",
56922
57365
  "fountain",
@@ -56935,7 +57378,10 @@ var HeroRowIdSchema = _enum([
56935
57378
  "climate-fan",
56936
57379
  "setpoint",
56937
57380
  "channel",
56938
- "control"
57381
+ "control",
57382
+ "circuit",
57383
+ "heat-pump-power",
57384
+ "system-mode"
56939
57385
  ]);
56940
57386
  var HeroArgValueSchema = lazy(() => union([
56941
57387
  string(),
@@ -57000,16 +57446,32 @@ var HeroControlSchema = discriminatedUnion("kind", [
57000
57446
  label: HeroTextSchema,
57001
57447
  current: string().nullable(),
57002
57448
  options: array(string()),
57449
+ /** A readable label per option value; an option without one is drawn as its raw value. */
57450
+ optionLabels: record(string(), HeroTextSchema).optional(),
57003
57451
  call: HeroCallSchema,
57004
57452
  valueArg: string()
57005
57453
  })
57006
57454
  ]);
57455
+ /** Declared by its interface: a `z.infer` of one more icon union pushes the cap's inferred type past what the compiler can print. */
57456
+ var HeroPillSchema = object({
57457
+ icon: IconKeySchema.nullable(),
57458
+ text: HeroTextSchema,
57459
+ tone: HeroToneSchema
57460
+ });
57007
57461
  var HeroRowSchema = object({
57008
57462
  id: HeroRowIdSchema,
57463
+ /** The row's own name when the kind's label would not tell rows of one kind apart (a circuit's label). */
57464
+ label: HeroTextSchema.optional(),
57465
+ /** A group header drawn above the row when it differs from the previous row's ("System"). */
57466
+ group: HeroTextSchema.optional(),
57467
+ /** Values drawn as pills across the row (a heat pump's powers). */
57468
+ pills: array(HeroPillSchema).optional(),
57009
57469
  icon: IconKeySchema,
57010
57470
  /** The row's status, in words. `null` = nothing to say. */
57011
57471
  status: HeroTextSchema.nullable(),
57012
- controls: array(HeroControlSchema)
57472
+ controls: array(HeroControlSchema),
57473
+ /** A bar under the row's label, 0..1; `null` / absent = no bar. */
57474
+ progress: number$1().min(0).max(1).nullable().optional()
57013
57475
  });
57014
57476
  var HeroDetailSchema = object({
57015
57477
  label: HeroTextSchema,
@@ -57017,7 +57479,11 @@ var HeroDetailSchema = object({
57017
57479
  unit: string().nullable()
57018
57480
  });
57019
57481
  var HeroBadgeSchema = object({
57020
- id: _enum(["battery", "network-link"]),
57482
+ id: _enum([
57483
+ "battery",
57484
+ "network-link",
57485
+ "integration"
57486
+ ]),
57021
57487
  icon: IconKeySchema,
57022
57488
  tone: HeroToneSchema,
57023
57489
  /** Percent for a battery, link quality for a link; `null` = not known. */
@@ -57117,7 +57583,12 @@ var HeroFactSchema = object({
57117
57583
  var HeroSideTileSchema = object({
57118
57584
  label: HeroTextSchema,
57119
57585
  value: HeroTextSchema,
57120
- tone: HeroToneSchema.optional()
57586
+ tone: HeroToneSchema.optional(),
57587
+ /**
57588
+ * Epoch ms when the tile reads a clock time (an appliance's end): a client prints it in
57589
+ * the viewer's own time zone; `value` is the fallback for one that cannot.
57590
+ */
57591
+ at: number$1().optional()
57121
57592
  });
57122
57593
  var HeroSideTilesSchema = object({
57123
57594
  left: array(HeroSideTileSchema).max(2),
@@ -57153,8 +57624,12 @@ var HeroModelSchema = object({
57153
57624
  sideTiles: HeroSideTilesSchema,
57154
57625
  /** The status line under the main icon. */
57155
57626
  title: HeroTextSchema,
57627
+ /** The scene's top line when it says more than `title`; absent = draw the title. */
57628
+ caption: HeroTextSchema.optional(),
57156
57629
  badges: array(HeroBadgeSchema),
57157
57630
  remote: HeroRemoteSlotSchema.nullable(),
57631
+ /** The foot button that opens the device's history: "History", "Cycles", "Meals", "Access". */
57632
+ historyLabel: HeroTextSchema,
57158
57633
  consumables: HeroConsumablesSlotSchema.nullable(),
57159
57634
  consumption: HeroConsumptionSlotSchema.nullable(),
57160
57635
  ownPower: HeroOwnPowerSlotSchema.nullable(),
@@ -57612,6 +58087,12 @@ var IntegrationWithStateSchema = object({
57612
58087
  name: string(),
57613
58088
  /** A projection of the instance body's switch (`setInstanceEnabled` is the only writer). */
57614
58089
  enabled: boolean(),
58090
+ /**
58091
+ * True when the addon that holds the switch did not answer (down, failed, or keeps no record for
58092
+ * this row): `enabled` is then the stored registry flag, a stale value, not the state. A reader
58093
+ * must not draw it as on or off (D315).
58094
+ */
58095
+ switchUnknown: literal(true).optional(),
57615
58096
  info: record(string(), unknown()).readonly(),
57616
58097
  /** The discovery candidate this integration was set up from (D984). */
57617
58098
  originStableId: string().optional(),
@@ -57625,6 +58106,12 @@ var IntegrationLiteSchema = object({
57625
58106
  name: string(),
57626
58107
  /** A projection of the instance body's switch (`setInstanceEnabled` is the only writer). */
57627
58108
  enabled: boolean(),
58109
+ /**
58110
+ * True when the addon that holds the switch did not answer (down, failed, or keeps no record for
58111
+ * this row): `enabled` is then the stored registry flag, a stale value, not the state. A reader
58112
+ * must not draw it as on or off (D315).
58113
+ */
58114
+ switchUnknown: literal(true).optional(),
57628
58115
  info: record(string(), unknown()).readonly(),
57629
58116
  /** The discovery candidate this integration was set up from (D984). */
57630
58117
  originStableId: string().optional(),
@@ -57723,9 +58210,9 @@ var UpdateIntegrationInputSchema = object({
57723
58210
  id: string(),
57724
58211
  name: string().optional(),
57725
58212
  /**
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`.
58213
+ * Honoured ONLY for a fixed builtin (`info.fixed`), whose switch is the hub's own registry flag
58214
+ * and works while its addon is down. Any other row is refused (a typed `BAD_REQUEST`): its
58215
+ * switch is `integration-instances.setInstanceEnabled`.
57729
58216
  */
57730
58217
  enabled: boolean().optional(),
57731
58218
  info: record(string(), unknown()).optional()
@@ -57759,6 +58246,12 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
57759
58246
  * the form as a `SecretFieldView`, never text (D789). A patch value omitted, `''`
57760
58247
  * or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
57761
58248
  * clears it.
58249
+ *
58250
+ * Who may call (epic #440): every method but `describe` and `instanceFormSchema` is open to any
58251
+ * session (`caller: 'scoped'`) and default-denies. An admin passes everywhere. A non-admin passes
58252
+ * only on a call pinned to one addon whose `describe().userScoped` is true, and that provider
58253
+ * filters every row on the `caller` the router injected (a notification target is somebody's
58254
+ * phone: a user sees and changes only their own). An addon that says nothing keeps the admin door.
57762
58255
  */
57763
58256
  var ConfigSchemaPassthrough$2 = custom();
57764
58257
  var IntegrationInstanceIdSchema = string().min(1);
@@ -57872,7 +58365,15 @@ var IntegrationInstanceDescriptionSchema = object({
57872
58365
  */
57873
58366
  mintsId: boolean().optional(),
57874
58367
  /** The links to other instances the provider's kinds declare; absent means none. */
57875
- links: array(IntegrationInstanceLinkSchema).optional()
58368
+ links: array(IntegrationInstanceLinkSchema).optional(),
58369
+ /**
58370
+ * The rows belong to individual users (a notification target is somebody's phone). The router
58371
+ * then opens `listInstances`, `getInstance`, `createInstance`, `updateInstance`,
58372
+ * `setInstanceEnabled`, `deleteInstance` and `testInstance` to a NON-admin session on a call
58373
+ * pinned to this addon, and hands the provider the `caller` so it keeps a user to their own rows.
58374
+ * Absent means admin only, as it always was.
58375
+ */
58376
+ userScoped: boolean().optional()
57876
58377
  });
57877
58378
  var IntegrationInstanceStatusSchema = object({
57878
58379
  state: _enum([
@@ -57930,8 +58431,14 @@ var integrationInstancesCapability = {
57930
58431
  /** The discovery candidate the wizard was seeded from (`stableId`), if any. */
57931
58432
  candidateStableId: string().min(1).optional()
57932
58433
  }), ConfigSchemaPassthrough$2, { auth: "admin" }),
57933
- listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
57934
- getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
58434
+ listInstances: method(looseObject({}), array(IntegrationInstanceSchema).readonly(), {
58435
+ auth: "protected",
58436
+ caller: "scoped"
58437
+ }),
58438
+ getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), {
58439
+ auth: "protected",
58440
+ caller: "scoped"
58441
+ }),
57935
58442
  /**
57936
58443
  * Insert the typed record. With `id` it is stored under that id (a duplicate id is refused);
57937
58444
  * without one the PROVIDER mints it and answers it. `kind` picks one of `describe().kinds`.
@@ -57964,11 +58471,13 @@ var integrationInstancesCapability = {
57964
58471
  seed: object({ info: record(string(), unknown()) }).optional()
57965
58472
  }), object({ id: string() }), {
57966
58473
  kind: "mutation",
57967
- auth: "admin"
58474
+ auth: "protected",
58475
+ caller: "scoped"
57968
58476
  }),
57969
58477
  updateInstance: method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), {
57970
58478
  kind: "mutation",
57971
- auth: "admin"
58479
+ auth: "protected",
58480
+ caller: "scoped"
57972
58481
  }),
57973
58482
  /**
57974
58483
  * Switch ONE instance on or off, for every role; a missing record is created from the addon's
@@ -57980,7 +58489,8 @@ var integrationInstancesCapability = {
57980
58489
  enabled: boolean()
57981
58490
  }), object({ ok: literal(true) }), {
57982
58491
  kind: "mutation",
57983
- auth: "admin"
58492
+ auth: "protected",
58493
+ caller: "scoped"
57984
58494
  }),
57985
58495
  /**
57986
58496
  * Delete the record and its keychain refs together. For an instance that brings devices the hub
@@ -57989,7 +58499,8 @@ var integrationInstancesCapability = {
57989
58499
  */
57990
58500
  deleteInstance: method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), {
57991
58501
  kind: "mutation",
57992
- auth: "admin"
58502
+ auth: "protected",
58503
+ caller: "scoped"
57993
58504
  }),
57994
58505
  testInstance: optionalMethod(object({
57995
58506
  id: IntegrationInstanceIdSchema.nullable(),
@@ -57998,7 +58509,8 @@ var integrationInstancesCapability = {
57998
58509
  kind: string().min(1).optional()
57999
58510
  }), TestConnectionResultSchema, {
58000
58511
  kind: "mutation",
58001
- auth: "admin"
58512
+ auth: "protected",
58513
+ caller: "scoped"
58002
58514
  })
58003
58515
  }
58004
58516
  };
@@ -77465,6 +77977,11 @@ var SendEmailResultSchema = object({
77465
77977
  var SmtpStatusSchema = object({
77466
77978
  /** True iff the addon has successfully verified the relay. */
77467
77979
  ready: boolean(),
77980
+ /**
77981
+ * The tri-state behind `ready` (D315): true when the last send worked, false when it failed,
77982
+ * ABSENT when the relay has not sent yet (unknown, never "not verified").
77983
+ */
77984
+ verified: boolean().optional(),
77468
77985
  /** Operator-visible host string (no credentials). */
77469
77986
  host: string(),
77470
77987
  /** Last error message reported by the relay, when not ready. */
@@ -77476,10 +77993,6 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
77476
77993
  kind: "mutation",
77477
77994
  auth: "admin",
77478
77995
  access: "create"
77479
- }), method(RelayPinSchema, SmtpStatusSchema, {
77480
- kind: "mutation",
77481
- auth: "admin",
77482
- access: "view"
77483
77996
  }), method(RelayPinSchema, SmtpStatusSchema, { auth: "admin" });
77484
77997
  /**
77485
77998
  * A single device snapshot returned as base64 JPEG/PNG.
@@ -82417,6 +82930,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
82417
82930
  groupCommands: groupCommandsCapability,
82418
82931
  groupStatus: groupStatusCapability,
82419
82932
  heatingValve: heatingValveCapability,
82933
+ heatPumpStatus: heatPumpStatusCapability,
82420
82934
  humidifier: humidifierCapability,
82421
82935
  humiditySensor: humiditySensorCapability,
82422
82936
  image: imageCapability,
@@ -83700,7 +84214,7 @@ var BaseDeviceProvider = class extends BaseAddon {
83700
84214
  return toDeviceSummary(device, this.addonId);
83701
84215
  }
83702
84216
  };
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;
84217
+ 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
84218
  new Set(Object.values(DeviceType));
83705
84219
  /** Parses the raw slice with the cap's own schema; `undefined` when it does not parse. */
83706
84220
  function contribution(cap, schema, build) {
@@ -83836,6 +84350,13 @@ var lockContribution = contribution("lock-control", LockControlStatusSchema, (sl
83836
84350
  reading: reading("lock-control", words?.state ?? "unknown", false, words?.title),
83837
84351
  rows: [row$1("lock", state === "locked" || state === "locking" ? "lock" : "lock-open", joinText([title, since]), lockControls(state, ctx.features.includes(DeviceFeature.LockOpen)))],
83838
84352
  ...since === null || words === null ? {} : { facts: [fact("since", "clock", since, "muted")] },
84353
+ ...words === null ? {} : { tiles: { left: [{
84354
+ label: t("Door", "Porta"),
84355
+ value: title
84356
+ }, ...since === null ? [] : [{
84357
+ label: t("Since", "Da"),
84358
+ value: since
84359
+ }]] } },
83839
84360
  ...words?.door == null ? {} : { scene: { mode: words.door } }
83840
84361
  };
83841
84362
  });
@@ -83917,7 +84438,17 @@ var ACCESS_FAMILY_CONTRIBUTIONS = {
83917
84438
  return {
83918
84439
  reading: reading("alarm-panel", words?.state ?? "unknown", false, words?.title),
83919
84440
  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")] }
84441
+ ...since === null || words === null ? {} : { facts: [fact("since", "clock", since, state === "triggered" ? "danger" : "muted")] },
84442
+ ...words === null ? {} : { tiles: {
84443
+ left: [{
84444
+ label: t("Mode", "Modalità"),
84445
+ value: words.title
84446
+ }],
84447
+ ...since === null ? {} : { right: [{
84448
+ label: t("Since", "Da"),
84449
+ value: since
84450
+ }] }
84451
+ } }
83921
84452
  };
83922
84453
  }),
83923
84454
  "automation-control": contribution("automation-control", AutomationControlStatusSchema, (slice, ctx) => {
@@ -83981,7 +84512,7 @@ var ACCESS_FAMILY_CONTRIBUTIONS = {
83981
84512
  */
83982
84513
  var has$3 = (ctx, f) => ctx.features.includes(f);
83983
84514
  var unknownText = t("Unknown", "Sconosciuto");
83984
- var pct$1 = (n) => t(`${Math.round(n)}%`, `${Math.round(n)}%`);
84515
+ var pct$2 = (n) => t(`${Math.round(n)}%`, `${Math.round(n)}%`);
83985
84516
  var unit = (n) => Math.min(1, Math.max(0, n / 100));
83986
84517
  /** Battery as a fact, unless the device is also bound to the `battery` cap (it already says so). */
83987
84518
  function ownBattery(level, ctx) {
@@ -84043,7 +84574,7 @@ var fanContribution = contribution("fan-control", FanControlStatusSchema, (slice
84043
84574
  },
84044
84575
  valueArg: "direction"
84045
84576
  });
84046
- const speedText = speed === null ? null : pct$1(speed);
84577
+ const speedText = speed === null ? null : pct$2(speed);
84047
84578
  const preset = slice?.preset ?? "";
84048
84579
  const status = speed === null ? unknownText : joinText([speed === 0 ? t("Off", "Spento") : t(`Speed ${speed}%`, `Velocità ${speed}%`), preset === "" ? null : t(preset, preset)]);
84049
84580
  return {
@@ -84056,10 +84587,16 @@ var fanContribution = contribution("fan-control", FanControlStatusSchema, (slice
84056
84587
  }],
84057
84588
  ...speedText === null ? {} : {
84058
84589
  facts: [fact("speed", "fan", speedText, speed === 0 ? "muted" : "primary")],
84059
- tiles: { right: [{
84060
- label: t("Speed", "Velocità"),
84061
- value: speedText
84062
- }] },
84590
+ tiles: {
84591
+ ...slice?.oscillating == null ? {} : { left: [{
84592
+ label: t("Oscillation", "Oscilla"),
84593
+ value: slice.oscillating ? t("Yes", "Sì") : t("No", "No")
84594
+ }] },
84595
+ ...preset === "" ? {} : { right: [{
84596
+ label: t("Mode", "Modalità"),
84597
+ value: t(preset, preset)
84598
+ }] }
84599
+ },
84063
84600
  scene: { level: unit(speed ?? 0) }
84064
84601
  }
84065
84602
  };
@@ -84123,7 +84660,8 @@ var vacuumContribution = contribution("vacuum-control", VacuumControlStatusSchem
84123
84660
  const words = state === null ? null : VACUUM_WORDS[state];
84124
84661
  const title = state === "error" && slice?.errorLabel ? t(slice.errorLabel, slice.errorLabel) : words?.title;
84125
84662
  const controls = vacuumControls(state);
84126
- if (slice !== void 0 && slice.availableFanSpeeds.length > 0) controls.push({
84663
+ const suctionControls = [];
84664
+ if (slice !== void 0 && slice.availableFanSpeeds.length > 0) suctionControls.push({
84127
84665
  kind: "picker",
84128
84666
  id: "vacuum.suction",
84129
84667
  label: t("Suction", "Aspirazione"),
@@ -84138,22 +84676,31 @@ var vacuumContribution = contribution("vacuum-control", VacuumControlStatusSchem
84138
84676
  });
84139
84677
  if (state !== null) controls.push(vac("locate", "Locate", "Trova", "map-pin", "locate"));
84140
84678
  const progress = state === "cleaning" ? slice?.progress ?? null : null;
84679
+ const suction = slice?.fanSpeed == null || slice.fanSpeed === "" ? [] : [{
84680
+ label: t("Suction", "Aspirazione"),
84681
+ value: t(slice.fanSpeed, slice.fanSpeed)
84682
+ }];
84683
+ const suctionRows = suctionControls.length === 0 ? [] : [{
84684
+ id: "vacuum-suction",
84685
+ icon: "bot",
84686
+ status: null,
84687
+ controls: suctionControls
84688
+ }];
84689
+ const vacuumRows = [{
84690
+ id: "vacuum",
84691
+ icon: "bot",
84692
+ status: title ?? unknownText,
84693
+ controls
84694
+ }, ...suctionRows];
84141
84695
  return {
84142
84696
  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
- }
84697
+ rows: vacuumRows,
84698
+ facts: [...ownBattery(slice?.batteryLevel ?? null, ctx), ...progress === null ? [] : [fact("progress", "activity", pct$2(progress), "primary")]],
84699
+ ...suction.length === 0 && progress === null ? {} : { tiles: { right: [...suction, ...progress === null ? [] : [{
84700
+ label: t("Progress", "Avanzamento"),
84701
+ value: pct$2(progress)
84702
+ }]] } },
84703
+ ...progress === null ? {} : { scene: { level: unit(progress) } }
84157
84704
  };
84158
84705
  });
84159
84706
  var MOWER_WORDS = {
@@ -84209,11 +84756,11 @@ var mowerContribution = contribution("lawn-mower-control", LawnMowerControlStatu
84209
84756
  status: title ?? unknownText,
84210
84757
  controls: mowerControls(activity)
84211
84758
  }],
84212
- facts: [...ownBattery(slice?.batteryLevel ?? null, ctx), ...progress === null ? [] : [fact("progress", "activity", pct$1(progress), "primary")]],
84759
+ facts: [...ownBattery(slice?.batteryLevel ?? null, ctx), ...progress === null ? [] : [fact("progress", "activity", pct$2(progress), "primary")]],
84213
84760
  ...progress === null ? {} : {
84214
84761
  tiles: { right: [{
84215
84762
  label: t("Progress", "Avanzamento"),
84216
- value: pct$1(progress)
84763
+ value: pct$2(progress)
84217
84764
  }] },
84218
84765
  scene: { level: unit(progress) }
84219
84766
  }
@@ -84284,10 +84831,13 @@ var feederContribution = contribution("pet-feeder", PetFeederStatusSchema, (slic
84284
84831
  ...last === null ? [] : [fact("last-run", "clock", t(`Last ${last} g`, `Ultimo ${last} g`))],
84285
84832
  ...ownBattery(slice?.powerSource === "battery" ? slice.batteryPower : null, ctx)
84286
84833
  ],
84287
- ...last === null ? {} : { tiles: { right: [{
84834
+ tiles: { left: [...slice?.foodLevel == null ? [] : [{
84835
+ label: t("Tank", "Serbatoio"),
84836
+ value: pct$2(slice.foodLevel)
84837
+ }], ...last === null ? [] : [{
84288
84838
  label: t("Last portion", "Ultima porzione"),
84289
84839
  value: t(`${last} g`, `${last} g`)
84290
- }] } }
84840
+ }]] }
84291
84841
  };
84292
84842
  });
84293
84843
  var fountainContribution = contribution("pet-fountain", PetFountainStatusSchema, (slice) => {
@@ -84315,10 +84865,13 @@ var fountainContribution = contribution("pet-fountain", PetFountainStatusSchema,
84315
84865
  }],
84316
84866
  ...level === null ? {} : {
84317
84867
  facts: [fact("level", "droplets", t(`Water ${Math.round(level)}%`, `Acqua ${Math.round(level)}%`), level <= 20 ? "warning" : "primary")],
84318
- tiles: { right: [{
84868
+ tiles: { left: [{
84319
84869
  label: t("Water", "Acqua"),
84320
- value: pct$1(level)
84321
- }] },
84870
+ value: pct$2(level)
84871
+ }, ...slice?.waterVolume == null ? [] : [{
84872
+ label: t("Volume", "Volume"),
84873
+ value: t(`${Math.round(slice.waterVolume)} ml`, `${Math.round(slice.waterVolume)} ml`)
84874
+ }]] },
84322
84875
  scene: { level: unit(level) }
84323
84876
  }
84324
84877
  };
@@ -84350,14 +84903,18 @@ var DEVICES_FAMILY_CONTRIBUTIONS = {
84350
84903
  * that does not parse reads `unknown`, never closed or off (D315/D393).
84351
84904
  */
84352
84905
  var has$2 = (ctx, f) => ctx.features.includes(f);
84353
- var pct = (n) => t(`${Math.round(n)}%`, `${Math.round(n)}%`);
84906
+ var pct$1 = (n) => t(`${Math.round(n)}%`, `${Math.round(n)}%`);
84354
84907
  var brightnessContribution = contribution("brightness", BrightnessStatusSchema, (slice) => {
84355
84908
  const level = slice?.level ?? null;
84356
84909
  return {
84357
84910
  ...level === null ? {} : { reading: reading("brightness", level > 0 ? "on" : "off") },
84358
84911
  ...level === null ? {} : {
84359
- facts: [fact("level", "sun-dim", pct(level), level > 0 ? "primary" : "muted")],
84360
- scene: { level: level / 100 }
84912
+ facts: [fact("level", "sun-dim", pct$1(level), level > 0 ? "primary" : "muted")],
84913
+ scene: { level: level / 100 },
84914
+ tiles: { left: [{
84915
+ label: t("Brightness", "Luminosità"),
84916
+ value: pct$1(level)
84917
+ }] }
84361
84918
  },
84362
84919
  rows: [row$1("brightness", "sun-dim", level === null ? null : t(`Brightness ${Math.round(level)}%`, `Luminosità ${Math.round(level)}%`), [{
84363
84920
  kind: "slider",
@@ -84566,12 +85123,16 @@ function travelFragment(s, state, position, tilt, ctx) {
84566
85123
  });
84567
85124
  return {
84568
85125
  reading: reading(s.source, words.state, false, words.title),
84569
- ...position === null ? {} : { facts: [fact("level", "arrow-up-down", pct(position))] },
85126
+ ...position === null ? {} : { facts: [fact("level", "arrow-up-down", pct$1(position))] },
85127
+ ...position === null ? {} : { tiles: { left: [{
85128
+ label: t("Opening", "Apertura"),
85129
+ value: pct$1(position)
85130
+ }] } },
84570
85131
  scene: {
84571
85132
  ...words.mode === null ? {} : { mode: words.mode },
84572
85133
  ...position === null ? {} : { level: position / 100 }
84573
85134
  },
84574
- rows: [row$1(s.rowId, "arrow-up-down", joinText([words.title, position !== null && state !== "open" ? pct(position) : null]), controls)]
85135
+ rows: [row$1(s.rowId, "arrow-up-down", joinText([words.title, position !== null && state !== "open" ? pct$1(position) : null]), controls)]
84575
85136
  };
84576
85137
  }
84577
85138
  var ACTUATORS_FAMILY_CONTRIBUTIONS = {
@@ -84649,10 +85210,10 @@ var ACTUATORS_FAMILY_CONTRIBUTIONS = {
84649
85210
  const now = slice.currentHumidity;
84650
85211
  return {
84651
85212
  reading: reading("humidifier", state, false, title),
84652
- ...now === null ? {} : { facts: [fact("level", "droplets", pct(now), "info")] },
85213
+ ...now === null ? {} : { facts: [fact("level", "droplets", pct$1(now), "info")] },
84653
85214
  ...slice.targetHumidity === null ? {} : { tiles: { right: [{
84654
85215
  label: t("Target", "Obiettivo"),
84655
- value: pct(slice.targetHumidity)
85216
+ value: pct$1(slice.targetHumidity)
84656
85217
  }] } },
84657
85218
  ...now === null ? {} : { scene: { level: now / 100 } },
84658
85219
  rows: [row$1("humidifier", "droplets", joinText([title, slice.mode === null ? null : t(slice.mode, slice.mode)]), controls)]
@@ -84900,17 +85461,16 @@ var weatherContribution = contribution("weather", WeatherStatusSchema, (slice, c
84900
85461
  });
84901
85462
  var updateContribution = contribution("update", UpdateStatusSchema, (slice) => {
84902
85463
  if (slice === void 0) return { reading: reading("update", "unknown") };
84903
- const { currentVersion, availableVersion, inProgress, updatable } = slice;
85464
+ const { currentVersion, availableVersion, inProgress } = slice;
84904
85465
  const pending = availableVersion !== null && availableVersion !== "" && availableVersion !== currentVersion;
84905
85466
  if (!pending && currentVersion === null && !inProgress) return { reading: reading("update", "unknown") };
84906
85467
  const state = inProgress ? "running" : pending ? "standby" : "idle";
84907
85468
  const title = inProgress ? t("Updating", "Aggiornamento in corso") : pending ? t("Update available", "Aggiornamento disponibile") : t("Up to date", "Aggiornato");
84908
85469
  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
85470
  return {
84911
85471
  reading: reading("update", state, false, title),
84912
85472
  facts: versions === null ? [] : [fact("reading", "download", versions, pending ? "warning" : "muted")],
84913
- rows: [row$1("update", "download", versions, install)]
85473
+ rows: [row$1("update", "download", versions, [])]
84914
85474
  };
84915
85475
  });
84916
85476
  var imageContribution = contribution("image", ImageStatusSchema, (slice, ctx) => {
@@ -85018,6 +85578,33 @@ var ON_MODE_PREFERENCE = [
85018
85578
  "heat",
85019
85579
  "cool"
85020
85580
  ];
85581
+ var FAN_LABEL = {
85582
+ auto: t("Auto", "Auto"),
85583
+ quiet: t("Quiet", "Silenziosa"),
85584
+ low: t("Low", "Bassa"),
85585
+ medium: t("Medium", "Media"),
85586
+ high: t("High", "Alta"),
85587
+ turbo: t("Turbo", "Turbo"),
85588
+ off: t("Off", "Spenta")
85589
+ };
85590
+ /** A readable label for a fan speed name; an unknown one is shown as the device reported it. */
85591
+ function fanLabelOf(fan) {
85592
+ return FAN_LABEL[fan] ?? t(fan, fan);
85593
+ }
85594
+ /** Short labels for a segmented control: one word each, so the segments stay on a line. */
85595
+ var MODE_SEGMENT_LABEL = {
85596
+ off: t("Off", "Spento"),
85597
+ heat: t("Heat", "Caldo"),
85598
+ cool: t("Cool", "Freddo"),
85599
+ auto: t("Auto", "Auto"),
85600
+ heat_cool: t("Heat/Cool", "Caldo/Freddo"),
85601
+ fan_only: t("Fan", "Vent."),
85602
+ dry: t("Dry", "Deum.")
85603
+ };
85604
+ /** The segment label of every available HVAC mode, keyed by the mode value. */
85605
+ function modeLabelsOf(modes) {
85606
+ return Object.fromEntries(modes.map((m) => [m, MODE_SEGMENT_LABEL[m]]));
85607
+ }
85021
85608
  /** 22 -> "22", 22.3 -> "22.3": a trailing zero is noise on a thermostat. */
85022
85609
  function formatDegrees(value) {
85023
85610
  return `${Number(value.toFixed(1))}°`;
@@ -85082,11 +85669,11 @@ function rangeFrom(min, max, step) {
85082
85669
  * contribute, types never do (D14). A slice that is absent or does not parse is
85083
85670
  * "not answered": the state reads unknown, no row is drawn (D315, D393).
85084
85671
  */
85085
- var same = (text) => t(text, text);
85086
- function tile(en, it, value) {
85672
+ var same$1 = (text) => t(text, text);
85673
+ function tile$1(en, it, value) {
85087
85674
  return {
85088
85675
  label: t(en, it),
85089
- value: same(value)
85676
+ value: same$1(value)
85090
85677
  };
85091
85678
  }
85092
85679
  function setpointSlider(id, cap, method, valueArg, value, range) {
@@ -85121,18 +85708,19 @@ var climateContribution = contribution("climate-control", ClimateControlStatusSc
85121
85708
  const status = climateStatusText(view);
85122
85709
  const facts = [];
85123
85710
  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)}%`)));
85711
+ if (view.humidity !== null) facts.push(fact("humidity", "droplets", same$1(`${Math.round(view.humidity)}%`)));
85712
+ if (view.targetHumidity !== null) facts.push(fact("humidity", "droplets", same$1(`→ ${Math.round(view.targetHumidity)}%`)));
85126
85713
  const left = [];
85127
85714
  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)}%`));
85715
+ if (view.measured !== null) left.push(tile$1("Indoor", "In casa", formatDegrees(view.measured)));
85716
+ if (view.humidity !== null) left.push(tile$1("Humidity", "Umidità", `${Math.round(view.humidity)}%`));
85130
85717
  const modeControls = [{
85131
85718
  kind: "picker",
85132
85719
  id: "climate.mode",
85133
85720
  label: t("Mode", "Modalità"),
85134
85721
  current: view.mode,
85135
- options: [...view.availableModes],
85722
+ options: [...view.availableModes.filter((m) => m !== "off"), ...view.availableModes.filter((m) => m === "off")],
85723
+ optionLabels: modeLabelsOf(view.availableModes),
85136
85724
  call: {
85137
85725
  cap: "climate-control",
85138
85726
  method: "setMode",
@@ -85160,6 +85748,7 @@ var climateContribution = contribution("climate-control", ClimateControlStatusSc
85160
85748
  label: t("Fan", "Ventola"),
85161
85749
  current: slice.fanMode === "" ? null : slice.fanMode,
85162
85750
  options: [...slice.availableFanModes],
85751
+ optionLabels: Object.fromEntries(slice.availableFanModes.map((m) => [m, fanLabelOf(m)])),
85163
85752
  call: {
85164
85753
  cap: "climate-control",
85165
85754
  method: "setFanMode",
@@ -85185,14 +85774,15 @@ var climateContribution = contribution("climate-control", ClimateControlStatusSc
85185
85774
  if (fanControls.length > 0) rows.push({
85186
85775
  id: "climate-fan",
85187
85776
  icon: "fan",
85188
- status: slice.fanMode === "" ? null : same(slice.fanMode),
85777
+ status: slice.fanMode === "" ? null : same$1(slice.fanMode),
85189
85778
  controls: fanControls
85190
85779
  });
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)));
85780
+ 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
85781
  const sceneMode = view.sceneMode;
85193
85782
  return {
85194
85783
  reading: reading("climate-control", view.isOff ? "off" : "on"),
85195
85784
  title: view.modeLabel,
85785
+ ...view.isOff || slice.fanMode === "" ? {} : { caption: t(`${view.modeLabel.en} · fan ${fanLabelOf(slice.fanMode).en}`, `${view.modeLabel.it} · ventola ${fanLabelOf(slice.fanMode).it}`) },
85196
85786
  facts,
85197
85787
  ...left.length === 0 && right.length === 0 ? {} : { tiles: {
85198
85788
  left,
@@ -85219,7 +85809,7 @@ var WATER_ON_PREFERENCE = [
85219
85809
  "performance"
85220
85810
  ];
85221
85811
  function capitalised(word) {
85222
- return same(word.charAt(0).toUpperCase() + word.slice(1).replace(/_/g, " "));
85812
+ return same$1(word.charAt(0).toUpperCase() + word.slice(1).replace(/_/g, " "));
85223
85813
  }
85224
85814
  var waterHeaterContribution = contribution("water-heater", WaterHeaterStatusSchema, (slice) => {
85225
85815
  if (slice === void 0 || slice.operationMode === null) return { reading: reading("climate-control", "unknown") };
@@ -85256,13 +85846,13 @@ var waterHeaterContribution = contribution("water-heater", WaterHeaterStatusSche
85256
85846
  icon: "flame",
85257
85847
  status: label,
85258
85848
  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))];
85849
+ }, ...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
85850
  const level = levelWithin(slice.targetTemp, range);
85261
85851
  return {
85262
85852
  reading: reading("climate-control", isOff ? "off" : "on"),
85263
85853
  title: label,
85264
85854
  facts: status === null ? [] : [fact("climate", "flame", status)],
85265
- ...slice.currentTemp === null ? {} : { tiles: { left: [tile("Water", "Acqua", formatDegrees(slice.currentTemp))] } },
85855
+ ...slice.currentTemp === null ? {} : { tiles: { left: [tile$1("Water", "Acqua", formatDegrees(slice.currentTemp))] } },
85266
85856
  scene: {
85267
85857
  ...isOff ? {} : { mode: "heat" },
85268
85858
  ...level === null ? {} : { level }
@@ -85280,11 +85870,12 @@ var waterHeaterContribution = contribution("water-heater", WaterHeaterStatusSche
85280
85870
  var heatingValveContribution = contribution("heating-valve", HeatingValveStatusSchema, (slice) => {
85281
85871
  const position = slice?.position ?? null;
85282
85872
  if (position === null) return { reading: reading("sensor", "unknown") };
85283
- const text = same(`${Math.round(position)}%`);
85873
+ const text = same$1(`${Math.round(position)}%`);
85284
85874
  return {
85285
85875
  reading: reading("sensor", position > 0 ? "running" : "idle"),
85286
85876
  facts: [fact("valve", "gauge", t(`Valve ${text.en}`, `Valvola ${text.it}`))],
85287
- tiles: { right: [tile("Valve", "Valvola", text.en)] },
85877
+ tiles: { right: [tile$1("Valve", "Valvola", text.en)] },
85878
+ ...position > 0 ? { caption: t(`Heating · valve ${text.en}`, `Riscalda · valvola ${text.it}`) } : {},
85288
85879
  scene: { level: position / 100 }
85289
85880
  };
85290
85881
  });
@@ -85293,9 +85884,9 @@ function sensorFragment(value, id, icon, en, it, text) {
85293
85884
  if (value === null) return { reading: reading("sensor", "unknown") };
85294
85885
  return {
85295
85886
  reading: reading("sensor", "on"),
85296
- title: same(text),
85297
- facts: [fact(id, icon, same(text))],
85298
- tiles: { right: [tile(en, it, text)] }
85887
+ title: same$1(text),
85888
+ facts: [fact(id, icon, same$1(text))],
85889
+ tiles: { right: [tile$1(en, it, text)] }
85299
85890
  };
85300
85891
  }
85301
85892
  var temperatureContribution = contribution("temperature-sensor", TemperatureSensorStatusSchema, (slice) => sensorFragment(slice?.temperature ?? null, "temperature", "thermometer", "Temperature", "Temperatura", formatDegrees(slice?.temperature ?? 0)));
@@ -85350,8 +85941,8 @@ var CLIMATE_FAMILY_CONTRIBUTIONS = {
85350
85941
  return {
85351
85942
  reading: reading("sensor", "on"),
85352
85943
  ...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)) }
85944
+ facts: parts.map((p) => fact("air-quality", "wind", same$1(`${p.label} ${p.value}`))),
85945
+ tiles: { right: parts.slice(0, 2).map((p) => tile$1(p.label, p.label, p.value)) }
85355
85946
  };
85356
85947
  }),
85357
85948
  "carbon-monoxide": contribution("carbon-monoxide", CarbonMonoxideStatusSchema, (slice) => {
@@ -85366,15 +85957,264 @@ var CLIMATE_FAMILY_CONTRIBUTIONS = {
85366
85957
  "numeric-sensor": numericContribution,
85367
85958
  "enum-sensor": enumContribution
85368
85959
  };
85960
+ /**
85961
+ * The heat pump's contribution to a hero (D1026): the compressor scene, every
85962
+ * circuit as a row with its own label and icon, the powers as pills (the
85963
+ * instantaneous COP only when both powers are known) and a "System" group with
85964
+ * one selector per mode. Values and their limits come from the slice, which the
85965
+ * gateway filled: a selector with no allowed value is not drawn, a missing
85966
+ * reading is never a 0 (D393).
85967
+ */
85968
+ var CAP = "heat-pump-control";
85969
+ var same = (text) => t(text, text);
85970
+ /** The words of the values the gateway names; a value not listed is drawn as the gateway wrote it. */
85971
+ var VALUE_LABELS = {
85972
+ off: t("Off", "Off"),
85973
+ on: t("On", "On"),
85974
+ auto: t("Auto", "Auto"),
85975
+ manual: t("Manual", "Manuale"),
85976
+ low: t("Low", "Bassa"),
85977
+ high: t("High", "Alta"),
85978
+ eco: t("Eco", "Eco"),
85979
+ heat: t("Heat", "Caldo"),
85980
+ cool: t("Cool", "Freddo"),
85981
+ heatCool: t("Heat/cool", "Caldo/freddo"),
85982
+ ownprogram: t("Programme", "Programma")
85983
+ };
85984
+ function valueLabel(value) {
85985
+ const known = VALUE_LABELS[value];
85986
+ if (known !== void 0) return known;
85987
+ const holiday = /^hm(\d+)$/.exec(value);
85988
+ if (holiday?.[1] !== void 0) return t(`Programme ${holiday[1]}`, `Programma ${holiday[1]}`);
85989
+ return same(value);
85990
+ }
85991
+ function optionLabels(allowed) {
85992
+ return Object.fromEntries(allowed.map((value) => [value, valueLabel(value)]));
85993
+ }
85994
+ function picker(id, label, setting, method, args, valueArg = "mode") {
85995
+ return {
85996
+ kind: "picker",
85997
+ id,
85998
+ label,
85999
+ current: setting.value,
86000
+ options: [...setting.allowed],
86001
+ optionLabels: optionLabels(setting.allowed),
86002
+ call: {
86003
+ cap: CAP,
86004
+ method,
86005
+ args: { ...args }
86006
+ },
86007
+ valueArg
86008
+ };
86009
+ }
86010
+ /** A selector exists only where the gateway named values to choose from. */
86011
+ function choosable(setting) {
86012
+ return setting !== null && setting.allowed.length > 0;
86013
+ }
86014
+ function circuitControl(circuit) {
86015
+ const args = { circuitId: circuit.id };
86016
+ if (circuit.kind === "dhw" && choosable(circuit.temperatureLevel)) return picker(`heat-pump.${circuit.id}.level`, t("Level", "Livello"), circuit.temperatureLevel, "setDhwTemperatureLevel", args, "level");
86017
+ if (!choosable(circuit.operationMode)) return null;
86018
+ const method = circuit.kind === "dhw" ? "setDhwOperationMode" : "setCircuitOperationMode";
86019
+ return picker(`heat-pump.${circuit.id}.mode`, t("Mode", "Modalità"), circuit.operationMode, method, args);
86020
+ }
86021
+ /** A circuit with no reading is still a row: it shows that nothing is known, not nothing. */
86022
+ var UNKNOWN_STATUS = same("—");
86023
+ function circuitRow(circuit) {
86024
+ const control = circuitControl(circuit);
86025
+ const status = thermalStatusText(circuit.targetTemp, circuit.currentTemp) ?? UNKNOWN_STATUS;
86026
+ return {
86027
+ id: "circuit",
86028
+ label: same(circuit.label),
86029
+ icon: circuit.icon,
86030
+ status,
86031
+ controls: control === null ? [] : [control]
86032
+ };
86033
+ }
86034
+ var kilowatts = (watts) => `${Number((watts / 1e3).toFixed(2))} kW`;
86035
+ function powerPills(status) {
86036
+ const { thermalW, electricalW } = status.power;
86037
+ const cop = heatPumpInstantCop(status.power);
86038
+ const pills = [];
86039
+ if (thermalW !== null) pills.push({
86040
+ icon: "flame",
86041
+ text: t(`Thermal ${kilowatts(thermalW)}`, `Termica ${kilowatts(thermalW)}`),
86042
+ tone: "muted"
86043
+ });
86044
+ if (electricalW !== null) pills.push({
86045
+ icon: "zap",
86046
+ text: t(`Electrical ${kilowatts(electricalW)}`, `Elettrica ${kilowatts(electricalW)}`),
86047
+ tone: "muted"
86048
+ });
86049
+ if (cop !== null) {
86050
+ const text = String(Number(cop.toFixed(1)));
86051
+ pills.push({
86052
+ icon: "gauge",
86053
+ text: t(`Instantaneous COP ${text}`, `COP istantaneo ${text}`),
86054
+ tone: "success"
86055
+ });
86056
+ }
86057
+ return pills;
86058
+ }
86059
+ /** The independent system modes, in the order the card draws them. */
86060
+ var SYSTEM_MODES = [
86061
+ {
86062
+ key: "silent",
86063
+ label: t("Silent", "Silenzioso"),
86064
+ icon: "volume-x",
86065
+ method: "setSilentMode"
86066
+ },
86067
+ {
86068
+ key: "away",
86069
+ label: t("Away", "Assente"),
86070
+ icon: "plane",
86071
+ method: "setAwayMode"
86072
+ },
86073
+ {
86074
+ key: "additionalHeater",
86075
+ label: t("Additional heater", "Riscaldatore aggiuntivo"),
86076
+ icon: "flame",
86077
+ method: "setAdditionalHeaterMode"
86078
+ },
86079
+ {
86080
+ key: "holiday",
86081
+ label: t("Holiday", "Vacanza"),
86082
+ icon: "tree-palm",
86083
+ method: "setHolidayMode"
86084
+ }
86085
+ ];
86086
+ var SYSTEM_GROUP = t("System", "Sistema");
86087
+ var PROGRAMME = /^hm\d+$/;
86088
+ /**
86089
+ * Holiday is two segments, off / programme. Choosing "programme" keeps the programme
86090
+ * that is set, else the first one the gateway allowed, so a write never leaves
86091
+ * `allowed`. The full hm1..hm8 pick is not in the hero (D1026). A holiday whose values
86092
+ * are not programmes keeps the plain selector.
86093
+ */
86094
+ function holidayRow(def, setting) {
86095
+ if (!setting.allowed.includes("off")) return null;
86096
+ const current = setting.value !== null && PROGRAMME.test(setting.value) ? setting.value : null;
86097
+ const programme = current ?? setting.allowed.find((v) => PROGRAMME.test(v));
86098
+ if (programme === void 0) return null;
86099
+ const control = picker("heat-pump.system.holiday", def.label, {
86100
+ value: setting.value,
86101
+ allowed: ["off", programme]
86102
+ }, def.method, {});
86103
+ const number = programme.slice(2);
86104
+ return {
86105
+ id: "system-mode",
86106
+ label: def.label,
86107
+ group: SYSTEM_GROUP,
86108
+ icon: def.icon,
86109
+ status: current === null ? null : t(`Programme ${number}`, `Programma ${number}`),
86110
+ controls: [control.kind === "picker" ? {
86111
+ ...control,
86112
+ optionLabels: {
86113
+ off: t("Off", "Off"),
86114
+ [programme]: t("Programme", "Programma")
86115
+ }
86116
+ } : control]
86117
+ };
86118
+ }
86119
+ function systemRows(status) {
86120
+ const rows = [];
86121
+ for (const def of SYSTEM_MODES) {
86122
+ const setting = status.system[def.key];
86123
+ if (!choosable(setting)) continue;
86124
+ if (def.key === "holiday") {
86125
+ const holiday = holidayRow(def, setting);
86126
+ if (holiday !== null) {
86127
+ rows.push(holiday);
86128
+ continue;
86129
+ }
86130
+ }
86131
+ rows.push({
86132
+ id: "system-mode",
86133
+ label: def.label,
86134
+ group: SYSTEM_GROUP,
86135
+ icon: def.icon,
86136
+ status: null,
86137
+ controls: [picker(`heat-pump.system.${def.key}`, def.label, setting, def.method, {})]
86138
+ });
86139
+ }
86140
+ return rows;
86141
+ }
86142
+ function tile(en, it, value) {
86143
+ return {
86144
+ label: t(en, it),
86145
+ value: same(value)
86146
+ };
86147
+ }
86148
+ function tilesOf$1(status) {
86149
+ const { supply, return: back, outdoor } = status.temperatures;
86150
+ const left = [];
86151
+ const right = [];
86152
+ if (supply !== null) left.push(tile("Supply", "Mandata", formatDegrees(supply)));
86153
+ if (back !== null) left.push(tile("Return", "Ritorno", formatDegrees(back)));
86154
+ if (outdoor !== null) right.push(tile("Outdoor", "Esterna", formatDegrees(outdoor)));
86155
+ if (status.waterPressureBar !== null) right.push(tile("Pressure", "Pressione", `${Number(status.waterPressureBar.toFixed(1))} bar`));
86156
+ return {
86157
+ left,
86158
+ right
86159
+ };
86160
+ }
86161
+ function isRunning(status) {
86162
+ const { compressorPct, electricalW } = status.power;
86163
+ if (compressorPct !== null) return compressorPct > 0;
86164
+ if (electricalW !== null) return electricalW > 0;
86165
+ return null;
86166
+ }
86167
+ var HEAT_PUMP_FAMILY_CONTRIBUTIONS = { "heat-pump-status": contribution("heat-pump-status", HeatPumpStatusSchema, (slice) => {
86168
+ if (slice === void 0) return { reading: reading("climate-control", "unknown") };
86169
+ const running = isRunning(slice);
86170
+ const facts = [];
86171
+ if (slice.power.electricalW !== null) facts.push(fact("power", "zap", same(`${Math.round(slice.power.electricalW)} W`)));
86172
+ if (slice.power.compressorPct !== null) facts.push(fact("level", "gauge", t(`Compressor ${Math.round(slice.power.compressorPct)} %`, `Compressore ${Math.round(slice.power.compressorPct)} %`)));
86173
+ const charging = slice.circuits.find((c) => c.active === true);
86174
+ const tiles = tilesOf$1(slice);
86175
+ const pills = powerPills(slice);
86176
+ const rows = [
86177
+ ...slice.circuits.map(circuitRow),
86178
+ ...pills.length === 0 ? [] : [{
86179
+ id: "heat-pump-power",
86180
+ icon: "zap",
86181
+ status: null,
86182
+ controls: [],
86183
+ pills: [...pills]
86184
+ }],
86185
+ ...systemRows(slice)
86186
+ ];
86187
+ return {
86188
+ reading: reading("climate-control", running === true ? "running" : running === false ? "idle" : "on"),
86189
+ title: t("Heat pump", "Pompa di calore"),
86190
+ ...charging === void 0 ? {} : { caption: t(`${charging.label} · heating`.toUpperCase(), `${charging.label} · in riscaldamento`.toUpperCase()) },
86191
+ facts,
86192
+ ...tiles.left.length === 0 && tiles.right.length === 0 ? {} : { tiles },
86193
+ ...slice.power.electricalW === null ? {} : { consumption: {
86194
+ priority: 1,
86195
+ slot: {
86196
+ kind: "power",
86197
+ watts: slice.power.electricalW,
86198
+ detail: slice.power.thermalW === null ? [] : [{
86199
+ label: t("Thermal", "Termica"),
86200
+ value: Math.round(slice.power.thermalW),
86201
+ unit: "W"
86202
+ }]
86203
+ }
86204
+ } },
86205
+ rows
86206
+ };
86207
+ }) };
85369
86208
  [
85370
86209
  ...Object.values(ACCESS_FAMILY_CONTRIBUTIONS),
85371
86210
  ...Object.values(DEVICES_FAMILY_CONTRIBUTIONS),
85372
86211
  ...Object.values(GROUPS_FAMILY_CONTRIBUTIONS),
85373
86212
  ...Object.values(ACTUATORS_FAMILY_CONTRIBUTIONS),
85374
86213
  ...Object.values(SENSORS_FAMILY_CONTRIBUTIONS),
85375
- ...Object.values(CLIMATE_FAMILY_CONTRIBUTIONS)
86214
+ ...Object.values(CLIMATE_FAMILY_CONTRIBUTIONS),
86215
+ ...Object.values(HEAT_PUMP_FAMILY_CONTRIBUTIONS)
85376
86216
  ];
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;
86217
+ 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
86218
  /**
85379
86219
  * Who looks like what (D1023). One look per {@link DeviceType}, refined by a
85380
86220
  * {@link DeviceRole} where the role changes how the device reads (a leak
@@ -85764,7 +86604,7 @@ lookOf({
85764
86604
  ({ ...lookOf({
85765
86605
  icon: "shield-check",
85766
86606
  on: [
85767
- "success",
86607
+ "danger",
85768
86608
  "glow",
85769
86609
  "halo"
85770
86610
  ],
@@ -86002,8 +86842,24 @@ lookOf({
86002
86842
  idle: INFO,
86003
86843
  standby: MUTED
86004
86844
  });
86845
+ lookOf({
86846
+ icon: "thermometer-sun",
86847
+ on: [
86848
+ "warning",
86849
+ "heat-rise",
86850
+ "rise"
86851
+ ],
86852
+ off: MUTED,
86853
+ running: [
86854
+ "warning",
86855
+ "heat-rise",
86856
+ "rise"
86857
+ ],
86858
+ idle: INFO,
86859
+ standby: MUTED
86860
+ });
86005
86861
  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;
86862
+ 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
86863
  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
86864
  Object.freeze({
86009
86865
  "accessories.getStatus": {
@@ -86384,13 +87240,6 @@ Object.freeze({
86384
87240
  access: "view",
86385
87241
  role: "admin"
86386
87242
  },
86387
- "addonSettings.getIntegrationFormSchema": {
86388
- capName: "addon-settings",
86389
- capScope: "system",
86390
- addonId: null,
86391
- access: "view",
86392
- role: "admin"
86393
- },
86394
87243
  "addonSettings.getScopedSettings": {
86395
87244
  capName: "addon-settings",
86396
87245
  capScope: "system",
@@ -89506,6 +90355,111 @@ Object.freeze({
89506
90355
  access: "view",
89507
90356
  role: "viewer"
89508
90357
  },
90358
+ "heatPumpControl.setAdditionalHeaterMode": {
90359
+ capName: "heat-pump-control",
90360
+ capScope: "device",
90361
+ addonId: null,
90362
+ access: "create",
90363
+ role: "viewer"
90364
+ },
90365
+ "heatPumpControl.setAwayMode": {
90366
+ capName: "heat-pump-control",
90367
+ capScope: "device",
90368
+ addonId: null,
90369
+ access: "create",
90370
+ role: "viewer"
90371
+ },
90372
+ "heatPumpControl.setCircuitComfortSetpoint": {
90373
+ capName: "heat-pump-control",
90374
+ capScope: "device",
90375
+ addonId: null,
90376
+ access: "create",
90377
+ role: "viewer"
90378
+ },
90379
+ "heatPumpControl.setCircuitEcoSetpoint": {
90380
+ capName: "heat-pump-control",
90381
+ capScope: "device",
90382
+ addonId: null,
90383
+ access: "create",
90384
+ role: "viewer"
90385
+ },
90386
+ "heatPumpControl.setCircuitHeatCoolMode": {
90387
+ capName: "heat-pump-control",
90388
+ capScope: "device",
90389
+ addonId: null,
90390
+ access: "create",
90391
+ role: "viewer"
90392
+ },
90393
+ "heatPumpControl.setCircuitMaxFlowTemp": {
90394
+ capName: "heat-pump-control",
90395
+ capScope: "device",
90396
+ addonId: null,
90397
+ access: "create",
90398
+ role: "viewer"
90399
+ },
90400
+ "heatPumpControl.setCircuitOperationMode": {
90401
+ capName: "heat-pump-control",
90402
+ capScope: "device",
90403
+ addonId: null,
90404
+ access: "create",
90405
+ role: "viewer"
90406
+ },
90407
+ "heatPumpControl.setCircuitSummerWinter": {
90408
+ capName: "heat-pump-control",
90409
+ capScope: "device",
90410
+ addonId: null,
90411
+ access: "create",
90412
+ role: "viewer"
90413
+ },
90414
+ "heatPumpControl.setDhwChargeDuration": {
90415
+ capName: "heat-pump-control",
90416
+ capScope: "device",
90417
+ addonId: null,
90418
+ access: "create",
90419
+ role: "viewer"
90420
+ },
90421
+ "heatPumpControl.setDhwOperationMode": {
90422
+ capName: "heat-pump-control",
90423
+ capScope: "device",
90424
+ addonId: null,
90425
+ access: "create",
90426
+ role: "viewer"
90427
+ },
90428
+ "heatPumpControl.setDhwTemperatureLevel": {
90429
+ capName: "heat-pump-control",
90430
+ capScope: "device",
90431
+ addonId: null,
90432
+ access: "create",
90433
+ role: "viewer"
90434
+ },
90435
+ "heatPumpControl.setHolidayMode": {
90436
+ capName: "heat-pump-control",
90437
+ capScope: "device",
90438
+ addonId: null,
90439
+ access: "create",
90440
+ role: "viewer"
90441
+ },
90442
+ "heatPumpControl.setSilentMode": {
90443
+ capName: "heat-pump-control",
90444
+ capScope: "device",
90445
+ addonId: null,
90446
+ access: "create",
90447
+ role: "viewer"
90448
+ },
90449
+ "heatPumpControl.startExtraHotWater": {
90450
+ capName: "heat-pump-control",
90451
+ capScope: "device",
90452
+ addonId: null,
90453
+ access: "create",
90454
+ role: "viewer"
90455
+ },
90456
+ "heatPumpStatus.getStatus": {
90457
+ capName: "heat-pump-status",
90458
+ capScope: "device",
90459
+ addonId: null,
90460
+ access: "view",
90461
+ role: "viewer"
90462
+ },
89509
90463
  "heroLayout.getHeroModel": {
89510
90464
  capName: "hero-layout",
89511
90465
  capScope: "system",
@@ -94413,13 +95367,6 @@ Object.freeze({
94413
95367
  access: "create",
94414
95368
  role: "admin"
94415
95369
  },
94416
- "smtpProvider.verify": {
94417
- capName: "smtp-provider",
94418
- capScope: "system",
94419
- addonId: null,
94420
- access: "view",
94421
- role: "admin"
94422
- },
94423
95370
  "snapshot.applyDeviceSettingsPatch": {
94424
95371
  capName: "snapshot",
94425
95372
  capScope: "device",
@@ -96352,7 +97299,7 @@ function projectAction(cap, action) {
96352
97299
  };
96353
97300
  }
96354
97301
  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;
97302
+ 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
97303
  DeviceFeature.BatteryOperated;
96357
97304
  DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
96358
97305
  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.";
@@ -97476,6 +98423,81 @@ Object.freeze({
97476
98423
  form: "single",
97477
98424
  optional: false
97478
98425
  }],
98426
+ "heatPumpControl.setAdditionalHeaterMode": [{
98427
+ name: "deviceId",
98428
+ form: "single",
98429
+ optional: false
98430
+ }],
98431
+ "heatPumpControl.setAwayMode": [{
98432
+ name: "deviceId",
98433
+ form: "single",
98434
+ optional: false
98435
+ }],
98436
+ "heatPumpControl.setCircuitComfortSetpoint": [{
98437
+ name: "deviceId",
98438
+ form: "single",
98439
+ optional: false
98440
+ }],
98441
+ "heatPumpControl.setCircuitEcoSetpoint": [{
98442
+ name: "deviceId",
98443
+ form: "single",
98444
+ optional: false
98445
+ }],
98446
+ "heatPumpControl.setCircuitHeatCoolMode": [{
98447
+ name: "deviceId",
98448
+ form: "single",
98449
+ optional: false
98450
+ }],
98451
+ "heatPumpControl.setCircuitMaxFlowTemp": [{
98452
+ name: "deviceId",
98453
+ form: "single",
98454
+ optional: false
98455
+ }],
98456
+ "heatPumpControl.setCircuitOperationMode": [{
98457
+ name: "deviceId",
98458
+ form: "single",
98459
+ optional: false
98460
+ }],
98461
+ "heatPumpControl.setCircuitSummerWinter": [{
98462
+ name: "deviceId",
98463
+ form: "single",
98464
+ optional: false
98465
+ }],
98466
+ "heatPumpControl.setDhwChargeDuration": [{
98467
+ name: "deviceId",
98468
+ form: "single",
98469
+ optional: false
98470
+ }],
98471
+ "heatPumpControl.setDhwOperationMode": [{
98472
+ name: "deviceId",
98473
+ form: "single",
98474
+ optional: false
98475
+ }],
98476
+ "heatPumpControl.setDhwTemperatureLevel": [{
98477
+ name: "deviceId",
98478
+ form: "single",
98479
+ optional: false
98480
+ }],
98481
+ "heatPumpControl.setHolidayMode": [{
98482
+ name: "deviceId",
98483
+ form: "single",
98484
+ optional: false
98485
+ }],
98486
+ "heatPumpControl.setSilentMode": [{
98487
+ name: "deviceId",
98488
+ form: "single",
98489
+ optional: false
98490
+ }],
98491
+ "heatPumpControl.startExtraHotWater": [{
98492
+ name: "deviceId",
98493
+ form: "single",
98494
+ optional: false
98495
+ }],
98496
+ "heatPumpStatus.getStatus": [{
98497
+ name: "deviceId",
98498
+ form: "single",
98499
+ optional: false
98500
+ }],
97479
98501
  "heroLayout.getHeroModel": [{
97480
98502
  name: "deviceId",
97481
98503
  form: "single",
@@ -101117,6 +102139,18 @@ Object.defineProperty(exports, "eventEmitterCapability", {
101117
102139
  return eventEmitterCapability;
101118
102140
  }
101119
102141
  });
102142
+ Object.defineProperty(exports, "heatPumpControlCapability", {
102143
+ enumerable: true,
102144
+ get: function() {
102145
+ return heatPumpControlCapability;
102146
+ }
102147
+ });
102148
+ Object.defineProperty(exports, "heatPumpStatusCapability", {
102149
+ enumerable: true,
102150
+ get: function() {
102151
+ return heatPumpStatusCapability;
102152
+ }
102153
+ });
101120
102154
  Object.defineProperty(exports, "heatingValveCapability", {
101121
102155
  enumerable: true,
101122
102156
  get: function() {