@camstack/addon-provider-enpal 0.1.20 → 0.1.22

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.
package/dist/addon.mjs CHANGED
@@ -814,7 +814,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
814
814
  return EventCategory;
815
815
  }({});
816
816
  //#endregion
817
- //#region ../types/dist/composed-stable-id-B3wcvtvW.mjs
817
+ //#region ../types/dist/composed-stable-id-COP6zOXx.mjs
818
818
  /**
819
819
  import { errMsg } from '@camstack/types'
820
820
  * Extract a human-readable message from an unknown error value.
@@ -827,8 +827,6 @@ function errMsg(err) {
827
827
  }
828
828
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
829
829
  DeviceType["Camera"] = "camera";
830
- /** Legacy stored value, read only by the hub-to-integration migrations; no live device carries it. */
831
- DeviceType["Hub"] = "hub";
832
830
  DeviceType["Light"] = "light";
833
831
  DeviceType["Siren"] = "siren";
834
832
  DeviceType["Switch"] = "switch";
@@ -6065,7 +6063,7 @@ var ZodIssueCode = {
6065
6063
  var ZodFirstPartyTypeKind;
6066
6064
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6067
6065
  //#endregion
6068
- //#region ../types/dist/sleep-Cykc2bzi.mjs
6066
+ //#region ../types/dist/sleep-B5XPl2YP.mjs
6069
6067
  /**
6070
6068
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6071
6069
  * window to float samples (D455).
@@ -6728,7 +6726,8 @@ async function runAddonMigrations(host, migrations, logger, options = {}) {
6728
6726
  try {
6729
6727
  declaration = await host.declareOwner({
6730
6728
  owner,
6731
- metas: migrations.map((m) => m.meta)
6729
+ metas: migrations.map((m) => m.meta),
6730
+ ...options.retired === void 0 ? {} : { retired: [...options.retired] }
6732
6731
  });
6733
6732
  } catch (err) {
6734
6733
  log.error("the migration engine refused or could not take this addon catalogue — none ran", { meta: { error: errMsg(err) } });
@@ -8721,6 +8720,14 @@ var BaseAddon = class {
8721
8720
  declareRegistrySwitch(spec) {
8722
8721
  this.registrySwitchSpec = spec;
8723
8722
  }
8723
+ /**
8724
+ * Ids this addon removed from its catalogue on purpose (`declareRetiredMigrations`): sent with
8725
+ * every declaration so the engine does not read their applied rows as a newer database.
8726
+ */
8727
+ retiredMigrationIds = [];
8728
+ declareRetiredMigrations(ids) {
8729
+ this.retiredMigrationIds = ids;
8730
+ }
8724
8731
  registrySwitchSpec = null;
8725
8732
  registrySwitchBound() {
8726
8733
  const spec = this.registrySwitchSpec;
@@ -8744,10 +8751,14 @@ var BaseAddon = class {
8744
8751
  async runBound(declared, options) {
8745
8752
  const added = this.registrySwitchBound();
8746
8753
  const migrations = [...declared, ...added];
8747
- const phased = options?.only === void 0 || added.length === 0 ? options : {
8754
+ const widened = options?.only === void 0 || added.length === 0 ? options : {
8748
8755
  ...options,
8749
8756
  only: [...options.only, ...added.map((m) => m.meta.id)]
8750
8757
  };
8758
+ const phased = this.retiredMigrationIds.length === 0 ? widened ?? {} : {
8759
+ ...widened,
8760
+ retired: this.retiredMigrationIds
8761
+ };
8751
8762
  const api = this.ctx.api.migrations;
8752
8763
  const report = await runAddonMigrations({
8753
8764
  declareOwner: (input) => api.declareOwner.mutate(input),
@@ -10376,6 +10387,8 @@ var READING_QUANTITIES = [
10376
10387
  "information",
10377
10388
  "sound-level",
10378
10389
  "signal-level",
10390
+ "signal-ratio",
10391
+ "frequency",
10379
10392
  "colour-temperature",
10380
10393
  "angle",
10381
10394
  "bitrate",
@@ -10419,6 +10432,8 @@ var QUANTITY_BASE = {
10419
10432
  information: "B",
10420
10433
  "sound-level": "dBFS",
10421
10434
  "signal-level": "dBm",
10435
+ "signal-ratio": "dB",
10436
+ frequency: "Hz",
10422
10437
  "colour-temperature": "mired",
10423
10438
  angle: "°",
10424
10439
  bitrate: "kbps",
@@ -10771,41 +10786,57 @@ var CAP_VOCABULARY_BY_NAME = {
10771
10786
  en: "Switch",
10772
10787
  it: "Interruttore"
10773
10788
  },
10774
- actions: [{
10775
- id: "switch.on",
10776
- method: "setState",
10777
- label: {
10778
- en: "Turn on",
10779
- it: "Accendi"
10780
- },
10781
- phrase: {
10782
- en: "Turn on {device}",
10783
- it: "Accendi {device}"
10784
- },
10785
- icon: "power",
10786
- args: { on: true },
10787
- effect: {
10788
- field: "on",
10789
- value: true
10790
- }
10791
- }, {
10792
- id: "switch.off",
10793
- method: "setState",
10794
- label: {
10795
- en: "Turn off",
10796
- it: "Spegni"
10789
+ actions: [
10790
+ {
10791
+ id: "switch.on",
10792
+ method: "setState",
10793
+ label: {
10794
+ en: "Turn on",
10795
+ it: "Accendi"
10796
+ },
10797
+ phrase: {
10798
+ en: "Turn on {device}",
10799
+ it: "Accendi {device}"
10800
+ },
10801
+ icon: "power",
10802
+ args: { on: true },
10803
+ effect: {
10804
+ field: "on",
10805
+ value: true
10806
+ }
10797
10807
  },
10798
- phrase: {
10799
- en: "Turn off {device}",
10800
- it: "Spegni {device}"
10808
+ {
10809
+ id: "switch.off",
10810
+ method: "setState",
10811
+ label: {
10812
+ en: "Turn off",
10813
+ it: "Spegni"
10814
+ },
10815
+ phrase: {
10816
+ en: "Turn off {device}",
10817
+ it: "Spegni {device}"
10818
+ },
10819
+ icon: "power-off",
10820
+ args: { on: false },
10821
+ effect: {
10822
+ field: "on",
10823
+ value: false
10824
+ }
10801
10825
  },
10802
- icon: "power-off",
10803
- args: { on: false },
10804
- effect: {
10805
- field: "on",
10806
- value: false
10826
+ {
10827
+ id: "switch.toggle",
10828
+ method: "toggle",
10829
+ label: {
10830
+ en: "Toggle",
10831
+ it: "Commuta"
10832
+ },
10833
+ phrase: {
10834
+ en: "Toggle {device}",
10835
+ it: "Commuta {device}"
10836
+ },
10837
+ icon: "toggle-right"
10807
10838
  }
10808
- }],
10839
+ ],
10809
10840
  state: [{
10810
10841
  kind: "boolean",
10811
10842
  field: "on",
@@ -20338,6 +20369,10 @@ Object.freeze({
20338
20369
  {
20339
20370
  "path": "storage.dischargedTodayKwh",
20340
20371
  "valueType": "number"
20372
+ },
20373
+ {
20374
+ "path": "storage.healthPct",
20375
+ "valueType": "number"
20341
20376
  }
20342
20377
  ],
20343
20378
  "enum-sensor": [{
@@ -20718,6 +20753,10 @@ Object.freeze({
20718
20753
  "path": "isMuted",
20719
20754
  "valueType": "boolean"
20720
20755
  },
20756
+ {
20757
+ "path": "muteReadback",
20758
+ "valueType": "boolean"
20759
+ },
20721
20760
  {
20722
20761
  "path": "source",
20723
20762
  "valueType": "string"
@@ -21654,7 +21693,7 @@ Object.freeze({
21654
21693
  }
21655
21694
  });
21656
21695
  //#endregion
21657
- //#region ../types/dist/implemented-methods-CgUBu6uM.mjs
21696
+ //#region ../types/dist/implemented-methods-0xxMOekw.mjs
21658
21697
  /**
21659
21698
  * The ffmpeg build every CamStack node downloads, per platform and arch.
21660
21699
  *
@@ -22838,7 +22877,7 @@ var NodeColorIdSchema = _enum([
22838
22877
  "node-h"
22839
22878
  ]);
22840
22879
  //#endregion
22841
- //#region ../types/dist/convert-BAzH15HU.mjs
22880
+ //#region ../types/dist/convert-BwMfbk5b.mjs
22842
22881
  /**
22843
22882
  * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
22844
22883
  * unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
@@ -23171,6 +23210,21 @@ var UNIT_TABLE = {
23171
23210
  factor: 1,
23172
23211
  offset: 0
23173
23212
  },
23213
+ dB: {
23214
+ dimension: "signal-ratio",
23215
+ factor: 1,
23216
+ offset: 0
23217
+ },
23218
+ Hz: {
23219
+ dimension: "frequency",
23220
+ factor: 1,
23221
+ offset: 0
23222
+ },
23223
+ kHz: {
23224
+ dimension: "frequency",
23225
+ factor: 1e3,
23226
+ offset: 0
23227
+ },
23174
23228
  mired: {
23175
23229
  dimension: "colour-temperature",
23176
23230
  factor: 1,
@@ -55493,7 +55547,9 @@ var ClusterUpdateFailureReasonSchema = _enum([
55493
55547
  "status-unreadable",
55494
55548
  "addon-update-failed",
55495
55549
  "addons-not-converged",
55496
- "interrupted"
55550
+ "interrupted",
55551
+ "timeout-stall",
55552
+ "timeout-ceiling"
55497
55553
  ]);
55498
55554
  var ClusterUpdateStepSchema = object({
55499
55555
  kind: ClusterUpdateStepKindSchema,
@@ -57060,6 +57116,12 @@ var MediaPlayerStatusSchema = object({
57060
57116
  /** Mute toggle distinct from volume=0. Null when no mute surface.
57061
57117
  * Pair with `DeviceFeature.MediaPlayerMute`. */
57062
57118
  isMuted: boolean().nullable(),
57119
+ /**
57120
+ * True only when the provider READS the mute state back from the device.
57121
+ * Absent or false: mute is a momentary key. A non-null `isMuted` is not a
57122
+ * reading on its own — an infrared send can leave a value without this flag.
57123
+ */
57124
+ muteReadback: boolean().optional(),
57063
57125
  /** Active source / input. Empty when no source surface. */
57064
57126
  source: string(),
57065
57127
  /** Selectable sources. Empty when no source surface.
@@ -57501,7 +57563,7 @@ var mediaPlayerCapability = {
57501
57563
  ]
57502
57564
  }
57503
57565
  };
57504
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Climate, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.Dryer, DeviceType.Gateway, DeviceType.HeatPump, DeviceType.EnergySystem, DeviceType.Camera, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Gateway, 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.PetFeeder, DeviceType.MediaPlayer;
57566
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Climate, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.Dryer, DeviceType.Gateway, DeviceType.HeatPump, DeviceType.EnergySystem, DeviceType.Camera, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Gateway, 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.PetFeeder, DeviceType.MediaPlayer;
57505
57567
  var NUMBER$1 = {
57506
57568
  kind: "fixed",
57507
57569
  result: ["number"]
@@ -59763,9 +59825,15 @@ var SiteZoneSchema = object({
59763
59825
  locationKeys: array(string())
59764
59826
  });
59765
59827
  var DeviceSearchRequiresSchema = object({
59766
- /** Capability names the field wants a value of. */
59767
- caps: array(string()).readonly(),
59768
- /** `any` (default): a device with one of them is compatible. `all`: it must hold every one. */
59828
+ /** Capability names the field wants a value of. Absent when the field wants actions only. */
59829
+ caps: array(string()).readonly().optional(),
59830
+ /**
59831
+ * Vocabulary action ids (`cover.open`) the device must offer. Compatible means
59832
+ * the device's bindings resolve those actions; `values` stays empty when no
59833
+ * cap was required. `mode` applies the same way as for `caps`.
59834
+ */
59835
+ actions: array(string()).readonly().optional(),
59836
+ /** `any` (default): one of the named caps, and one of the named actions, is enough. `all`: every one. */
59769
59837
  mode: _enum(["any", "all"]).optional()
59770
59838
  });
59771
59839
  var DeviceSearchFiltersSchema = object({
@@ -61822,7 +61890,7 @@ var TransitionsStatusSchema = object({
61822
61890
  /** Refused rows since start, by reason. */
61823
61891
  refused: record(string(), number$1())
61824
61892
  });
61825
- Object.values(DeviceType).filter((type) => type !== DeviceType.Hub && type !== DeviceType.Container), method(object({
61893
+ Object.values(DeviceType).filter((type) => type !== DeviceType.Container), method(object({
61826
61894
  deviceId: number$1().int(),
61827
61895
  capName: string().optional(),
61828
61896
  actorKind: TransitionActorKindSchema.optional(),
@@ -63116,7 +63184,9 @@ var EnergyStorageSchema = object({
63116
63184
  /** Lifetime discharged, kWh. */
63117
63185
  dischargedKwh: number$1().nullable().meta(readingMeta("energy")),
63118
63186
  chargedTodayKwh: number$1().nullable().meta(readingMeta("energy")),
63119
- dischargedTodayKwh: number$1().nullable().meta(readingMeta("energy"))
63187
+ dischargedTodayKwh: number$1().nullable().meta(readingMeta("energy")),
63188
+ /** State of health, 0..100. Null when the system did not report it. */
63189
+ healthPct: number$1().nullable().meta(readingMeta("percentage"))
63120
63190
  });
63121
63191
  var EnergySystemStatusSchema = object({
63122
63192
  solar: EnergySolarSchema.nullable(),
@@ -63228,6 +63298,12 @@ var energySystemCapability = {
63228
63298
  kind: "counter",
63229
63299
  unit: "kWh",
63230
63300
  displayName: "Storage discharged"
63301
+ },
63302
+ "storage.healthPct": {
63303
+ kind: "gauge",
63304
+ unit: "%",
63305
+ deadband: 1,
63306
+ displayName: "Storage health"
63231
63307
  }
63232
63308
  } },
63233
63309
  volatileStateFields: ["lastFetchedAt"],
@@ -65761,7 +65837,7 @@ var HeroSceneSchema = object({
65761
65837
  /** 0..1: volume, cover position, cycle progress. */
65762
65838
  level: number$1().min(0).max(1).nullable()
65763
65839
  });
65764
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.EnergySystem, 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.Dryer, DeviceType.Gateway;
65840
+ DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.EnergySystem, 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.Dryer, DeviceType.Gateway;
65765
65841
  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.SwitchHeater, DeviceRole.SwitchFan, DeviceRole.SwitchLight, DeviceRole.ClimateAc, DeviceRole.ClimateThermostat, DeviceRole.ClimateFloor, DeviceRole.ClimateRadiator, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit, 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;
65766
65842
  var HeroRowIdSchema = _enum([
65767
65843
  "progress",
@@ -66122,7 +66198,7 @@ var HeroModelSchema = object({
66122
66198
  *
66123
66199
  * - `extras` are the further inline controls of the types that carry more than one
66124
66200
  * ({@link ROW_EXTRAS_BY_TYPE}): climate setpoint, a robot's return to base,
66125
- * a player's volume and remote. Each is a control the model already has, named by id,
66201
+ * a player's volume and remote, an automation's enabled switch. Each is a control the model already has, named by id,
66126
66202
  * in that order and at most {@link ROW_EXTRAS_MAX}; one the model lacks, or lists in
66127
66203
  * `gated`, is absent, and the one `primary` already is never repeats. Empty = the
66128
66204
  * row of an older server: `primary` alone.
@@ -66182,7 +66258,7 @@ new Set([
66182
66258
  "valve"
66183
66259
  ]);
66184
66260
  DeviceType.Climate;
66185
- DeviceType.Climate, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer;
66261
+ DeviceType.Climate, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.Automation;
66186
66262
  /**
66187
66263
  * hero-layout — what a device's hero says (D1023). Read-only: the FIXED
66188
66264
  * layouts live in `@camstack/types` and nothing here stores or edits one.
@@ -70517,7 +70593,9 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
70517
70593
  auth: "admin"
70518
70594
  }), method(object({
70519
70595
  owner: string().min(1),
70520
- metas: array(MigrationMetaSchema)
70596
+ metas: array(MigrationMetaSchema),
70597
+ /** Ids the owner deliberately removed: an applied row of one is fine, never "newer". */
70598
+ retired: array(string().min(1)).optional()
70521
70599
  }), object({
70522
70600
  bootId: string(),
70523
70601
  snapshot: _enum([
@@ -79305,6 +79383,13 @@ var NcHistoryEntrySchema = object({
79305
79383
  error: string().optional(),
79306
79384
  subject: NcHistorySubjectSchema,
79307
79385
  /**
79386
+ * The title and body actually sent, frozen at delivery. Absent on a row
79387
+ * still pending, a row suppressed before a notification was built, and a
79388
+ * row delivered before this field shipped.
79389
+ */
79390
+ title: string().optional(),
79391
+ body: string().optional(),
79392
+ /**
79308
79393
  * Ids of the artefacts (still, then gif, then clip) this row's successful
79309
79394
  * delivery indexed in the artefact library — a REFERENCE, never the bytes
79310
79395
  * (an artefact is often megabytes; this row is durable JSON rewritten on
@@ -87615,7 +87700,8 @@ var ServerUpdateActionResultSchema = object({
87615
87700
  outcome: ServerUpdateOutcomeSchema.optional(),
87616
87701
  /**
87617
87702
  * Stable, greppable cause for `refused` / `failed` (the engine's
87618
- * `UpdateRefusalReason`, `staging-failed`, …). `null` on success.
87703
+ * `UpdateRefusalReason`, `staging-failed`, `timeout-stall`,
87704
+ * `timeout-ceiling`). `null` on success.
87619
87705
  */
87620
87706
  reason: string().nullable().optional(),
87621
87707
  /**
@@ -87737,6 +87823,14 @@ var smokeCapability = {
87737
87823
  }]
87738
87824
  }
87739
87825
  };
87826
+ var EmailImagePartSchema = object({
87827
+ ticket: PeerBytesTicketSchema,
87828
+ mime: string().min(1),
87829
+ name: string().min(1),
87830
+ contentId: string().min(1),
87831
+ placement: _enum(["inline", "attachment"]),
87832
+ deviceId: number$1().int().nonnegative().optional()
87833
+ });
87740
87834
  /**
87741
87835
  * `smtp-provider` — pluggable email delivery surface.
87742
87836
  *
@@ -87746,8 +87840,8 @@ var smokeCapability = {
87746
87840
  * ready provider OR target a specific addonId.
87747
87841
  *
87748
87842
  * The shape stays small on purpose — `sendEmail` is enough for the
87749
- * 90% case. Attachments / HTML templating / multi-part are room for
87750
- * future fields without breaking the existing call sites.
87843
+ * 90% case. An image is not a byte field on this method (D613): `images`
87844
+ * names `peerBytes` tickets the sender opens and folds into one MIME body.
87751
87845
  */
87752
87846
  var EmailAddressSchema = email();
87753
87847
  /**
@@ -87774,7 +87868,12 @@ var SendEmailInputSchema = object({
87774
87868
  * for clients that strip HTML — including most spam filters). */
87775
87869
  text: string(),
87776
87870
  /** Optional HTML body. Renders alongside `text` as multi-part. */
87777
- html: string().optional()
87871
+ html: string().optional(),
87872
+ /**
87873
+ * Images the caller already offered over `peerBytes`. Absent = a mail with
87874
+ * no image. The sender opens each ticket; one it cannot read is left out.
87875
+ */
87876
+ images: array(EmailImagePartSchema).max(3).optional()
87778
87877
  });
87779
87878
  var SendEmailResultSchema = object({
87780
87879
  messageId: string(),
@@ -88804,34 +88903,46 @@ var switchCapability = {
88804
88903
  DeviceType.Light,
88805
88904
  DeviceType.Fan
88806
88905
  ],
88807
- methods: { setState: method(object({
88808
- deviceId: number$1(),
88809
- on: boolean()
88810
- }), _void(), {
88811
- kind: "mutation",
88812
- auth: "protected",
88813
- actions: [{
88814
- id: "switch.on",
88815
- label: t("Turn on", "Accendi"),
88816
- phrase: t("Turn on {device}", "Accendi {device}"),
88817
- icon: "power",
88818
- effect: {
88819
- field: "on",
88820
- value: true
88821
- },
88822
- args: { on: true }
88823
- }, {
88824
- id: "switch.off",
88825
- label: t("Turn off", "Spegni"),
88826
- phrase: t("Turn off {device}", "Spegni {device}"),
88827
- icon: "power-off",
88828
- effect: {
88829
- field: "on",
88830
- value: false
88831
- },
88832
- args: { on: false }
88833
- }]
88834
- }) },
88906
+ methods: {
88907
+ setState: method(object({
88908
+ deviceId: number$1(),
88909
+ on: boolean()
88910
+ }), _void(), {
88911
+ kind: "mutation",
88912
+ auth: "protected",
88913
+ actions: [{
88914
+ id: "switch.on",
88915
+ label: t("Turn on", "Accendi"),
88916
+ phrase: t("Turn on {device}", "Accendi {device}"),
88917
+ icon: "power",
88918
+ effect: {
88919
+ field: "on",
88920
+ value: true
88921
+ },
88922
+ args: { on: true }
88923
+ }, {
88924
+ id: "switch.off",
88925
+ label: t("Turn off", "Spegni"),
88926
+ phrase: t("Turn off {device}", "Spegni {device}"),
88927
+ icon: "power-off",
88928
+ effect: {
88929
+ field: "on",
88930
+ value: false
88931
+ },
88932
+ args: { on: false }
88933
+ }]
88934
+ }),
88935
+ toggle: optionalMethod(object({ deviceId: number$1() }), _void(), {
88936
+ kind: "mutation",
88937
+ auth: "protected",
88938
+ actions: [{
88939
+ id: "switch.toggle",
88940
+ label: t("Toggle", "Commuta"),
88941
+ phrase: t("Toggle {device}", "Commuta {device}"),
88942
+ icon: "toggle-right"
88943
+ }]
88944
+ })
88945
+ },
88835
88946
  events: { onStateChanged: {
88836
88947
  data: object({
88837
88948
  deviceId: number$1(),
@@ -94931,7 +95042,7 @@ var BaseDeviceProvider = class extends BaseAddon {
94931
95042
  }
94932
95043
  };
94933
95044
  DeviceType.HeatPump;
94934
- DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.EnergySystem, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, 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.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
95045
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.EnergySystem, DeviceType.Camera, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, 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.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
94935
95046
  new Set(Object.values(DeviceType));
94936
95047
  /** Parses the raw slice with the cap's own schema; `undefined` when it does not parse. */
94937
95048
  function contribution(cap, schema, build) {
@@ -96645,7 +96756,11 @@ var temperatureContribution = contribution("temperature-sensor", TemperatureSens
96645
96756
  var humidityContribution = contribution("humidity-sensor", HumiditySensorStatusSchema, (slice) => measured(sensorFragment(slice?.humidity ?? null, "humidity", "droplets", "Humidity", "Umidità", `${Math.round(slice?.humidity ?? 0)}%`), slice?.humidity == null ? emptyMeasurement("humidity") : humidityMeasurement(slice.humidity, slice.lastFetchedAt)));
96646
96757
  var pressureContribution = contribution("pressure-sensor", PressureSensorStatusSchema, (slice) => sensorFragment(slice?.pressure ?? null, "pressure", "gauge", "Pressure", "Pressione", `${Math.round(slice?.pressure ?? 0)} hPa`));
96647
96758
  var illuminanceContribution = contribution("ambient-light-sensor", AmbientLightSensorStatusSchema, (slice) => sensorFragment(slice?.illuminance ?? null, "illuminance", "sun", "Light", "Luce", `${Math.round(slice?.illuminance ?? 0)} lx`));
96648
- var numericContribution = contribution("numeric-sensor", NumericSensorStatusSchema, (slice) => sensorFragment(slice?.value ?? null, "reading", "gauge", "Value", "Valore", String(Number((slice?.value ?? 0).toFixed(2)))));
96759
+ var numericContribution = contribution("numeric-sensor", NumericSensorStatusSchema, (slice, ctx) => {
96760
+ const unit = ctx.quantity == null ? void 0 : baseUnitOf(ctx.quantity);
96761
+ const text = String(Number((slice?.value ?? 0).toFixed(2)));
96762
+ return sensorFragment(slice?.value ?? null, "reading", "gauge", "Value", "Valore", unit === void 0 ? text : `${text} ${unit}`);
96763
+ });
96649
96764
  var enumContribution = contribution("enum-sensor", EnumSensorStatusSchema, (slice) => {
96650
96765
  if (slice === void 0 || slice.value === "") return measured({ reading: reading("sensor", "unknown") }, emptyMeasurement("text"));
96651
96766
  if (slice.timestamp === true) return { reading: reading("sensor", "on") };
@@ -96972,7 +97087,7 @@ var HEAT_PUMP_FAMILY_CONTRIBUTIONS = { "heat-pump-status": contribution("heat-pu
96972
97087
  ...Object.values(CLIMATE_FAMILY_CONTRIBUTIONS),
96973
97088
  ...Object.values(HEAT_PUMP_FAMILY_CONTRIBUTIONS)
96974
97089
  ];
96975
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.EnergySystem, 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.Dryer, DeviceType.Gateway;
97090
+ DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.EnergySystem, 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.Dryer, DeviceType.Gateway;
96976
97091
  /**
96977
97092
  * Who looks like what (D1023). One look per {@link DeviceType}, refined by a
96978
97093
  * {@link DeviceRole} where the role changes how the device reads (a leak
@@ -97616,7 +97731,7 @@ lookOf({
97616
97731
  standby: MUTED
97617
97732
  });
97618
97733
  DeviceType.Climate;
97619
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.EnergySystem, 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.Dryer, DeviceType.Gateway;
97734
+ DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.EnergySystem, 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.Dryer, DeviceType.Gateway;
97620
97735
  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.SwitchHeater, DeviceRole.SwitchFan, DeviceRole.SwitchLight, DeviceRole.ClimateAc, DeviceRole.ClimateThermostat, DeviceRole.ClimateFloor, DeviceRole.ClimateRadiator, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit, 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;
97621
97736
  Object.freeze({
97622
97737
  "accessories.getStatus": {
@@ -106915,6 +107030,13 @@ Object.freeze({
106915
107030
  access: "create",
106916
107031
  role: "viewer"
106917
107032
  },
107033
+ "switch.toggle": {
107034
+ capName: "switch",
107035
+ capScope: "device",
107036
+ addonId: null,
107037
+ access: "create",
107038
+ role: "viewer"
107039
+ },
106918
107040
  "system.debugWatch": {
106919
107041
  capName: "system",
106920
107042
  capScope: "system",
@@ -108080,7 +108202,7 @@ function offered(received) {
108080
108202
  };
108081
108203
  }
108082
108204
  offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
108083
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Climate, DeviceType.HeatPump, DeviceType.EnergySystem, 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.Dryer, DeviceType.Gateway;
108205
+ DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Climate, DeviceType.HeatPump, DeviceType.EnergySystem, 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.Dryer, DeviceType.Gateway;
108084
108206
  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.SwitchHeater, DeviceRole.SwitchFan, DeviceRole.SwitchLight, DeviceRole.ClimateAc, DeviceRole.ClimateThermostat, DeviceRole.ClimateFloor, DeviceRole.ClimateRadiator, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit, 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;
108085
108207
  new Set(Object.values(DeviceType));
108086
108208
  new Set(Object.values(DeviceRole));
@@ -110742,6 +110864,12 @@ Object.freeze({
110742
110864
  form: "single",
110743
110865
  optional: false
110744
110866
  }],
110867
+ "smtpProvider.sendEmail": [{
110868
+ name: "images",
110869
+ form: "object-array",
110870
+ optional: true,
110871
+ itemField: "deviceId"
110872
+ }],
110745
110873
  "snapshot.applyDeviceSettingsPatch": [{
110746
110874
  name: "deviceId",
110747
110875
  form: "single",
@@ -110908,6 +111036,11 @@ Object.freeze({
110908
111036
  form: "single",
110909
111037
  optional: false
110910
111038
  }],
111039
+ "switch.toggle": [{
111040
+ name: "deviceId",
111041
+ form: "single",
111042
+ optional: false
111043
+ }],
110911
111044
  "system.debugWatch": [{
110912
111045
  name: "deviceIds",
110913
111046
  form: "array",
@@ -111771,6 +111904,28 @@ var STATE_SUMMARY_VARS = [
111771
111904
  systemEventKinds: [...SUMMARISABLE],
111772
111905
  systemEventDeliveries: ["summary"]
111773
111906
  },
111907
+ {
111908
+ name: "offlineCount",
111909
+ label: "Summary: how many went offline",
111910
+ example: "2",
111911
+ group: "system",
111912
+ families: ["rule"],
111913
+ fields: [...RULE_TEXT],
111914
+ kinds: ["system"],
111915
+ systemEventKinds: [...SUMMARISABLE],
111916
+ systemEventDeliveries: ["summary"]
111917
+ },
111918
+ {
111919
+ name: "onlineCount",
111920
+ label: "Summary: how many came back",
111921
+ example: "5",
111922
+ group: "system",
111923
+ families: ["rule"],
111924
+ fields: [...RULE_TEXT],
111925
+ kinds: ["system"],
111926
+ systemEventKinds: [...SUMMARISABLE],
111927
+ systemEventDeliveries: ["summary"]
111928
+ },
111774
111929
  {
111775
111930
  name: "summaryCount",
111776
111931
  label: "Summary: items reported",
@@ -111812,6 +111967,7 @@ var STATE_SUMMARY_VARS = [
111812
111967
  ([...RULE_TEXT], [...COMBINED_ALARM]),
111813
111968
  ([...RULE_TEXT], [...COMBINED_ALARM]),
111814
111969
  [...RULE_TEXT],
111970
+ [...RULE_TEXT],
111815
111971
  ([...RULE_TEXT], [...COMBINED_ALARM]),
111816
111972
  ([...RULE_TEXT], [...PACKAGE_UPDATES]),
111817
111973
  [...RULE_TEXT],
@@ -112929,6 +113085,21 @@ function toBase(value, unit, quantity) {
112929
113085
  }
112930
113086
  //#endregion
112931
113087
  //#region src/enpal-catalog.ts
113088
+ /**
113089
+ * The two meters the box both called "Consumption". English is the device
113090
+ * name (one string, like every other catalog entry); Italian is the addon
113091
+ * catalogue key `catalog.<id>.name`.
113092
+ */
113093
+ var ENPAL_CONSUMPTION_NAMES = {
113094
+ house: {
113095
+ en: "House consumption",
113096
+ it: "Consumo casa"
113097
+ },
113098
+ heatpump: {
113099
+ en: "Heat pump consumption",
113100
+ it: "Consumo pompa di calore"
113101
+ }
113102
+ };
112932
113103
  var point = (quantity, ...sources) => ({
112933
113104
  quantity,
112934
113105
  sources: sources.map((s) => typeof s === "string" ? { key: s } : s)
@@ -113029,7 +113200,7 @@ var ENPAL_SPECS = [
113029
113200
  {
113030
113201
  kind: "power-meter",
113031
113202
  id: "house",
113032
- name: "Consumption",
113203
+ name: ENPAL_CONSUMPTION_NAMES.house.en,
113033
113204
  role: DeviceRole.PowerSensor,
113034
113205
  power: point("power", "Power.Consumption.Total"),
113035
113206
  kwh: point("energy", "Energy.Consumption.Total.Lifetime")
@@ -113037,7 +113208,7 @@ var ENPAL_SPECS = [
113037
113208
  {
113038
113209
  kind: "power-meter",
113039
113210
  id: "heatpump",
113040
- name: "Consumption",
113211
+ name: ENPAL_CONSUMPTION_NAMES.heatpump.en,
113041
113212
  role: DeviceRole.PowerSensor,
113042
113213
  power: point("power", "Heatpump.Power.Consumption.Total"),
113043
113214
  kwh: point("energy", "Heatpump.Energy.Consumption.Total.Lifetime")
@@ -113114,11 +113285,8 @@ var ENPAL_SPECS = [
113114
113285
  id: "grid-frequency",
113115
113286
  name: "Frequency",
113116
113287
  role: DeviceRole.NumericSensor,
113117
- quantity: "index",
113118
- value: {
113119
- ...point("index", "Frequency.Grid"),
113120
- ignoreUnit: true
113121
- }
113288
+ quantity: "frequency",
113289
+ value: point("frequency", "Frequency.Grid")
113122
113290
  },
113123
113291
  numeric("grid-feed-max", "Maximum feed-in power", "power", "Power.Grid.Maximum.Feed", DeviceRole.PowerSensor),
113124
113292
  numeric("house-energy-day", "Consumption today", "energy", "Energy.Consumption.Total.Day", DeviceRole.EnergySensor),
@@ -113152,9 +113320,9 @@ var ENPAL_SPECS = [
113152
113320
  text("lte-connection-type", "Connection type", "LTE.Connection.Type"),
113153
113321
  numeric("lte-quality", "LTE quality", "percentage", "LTE.Quality"),
113154
113322
  numeric("lte-rsrp", "RSRP", "signal-level", "LTE.RSRP"),
113155
- numeric("lte-rsrq", "RSRQ", "index", "LTE.RSRQ"),
113323
+ numeric("lte-rsrq", "RSRQ", "signal-ratio", "LTE.RSRQ"),
113156
113324
  numeric("lte-rssi", "RSSI", "signal-level", "LTE.RSSI"),
113157
- numeric("lte-snr", "SNR", "index", "LTE.SNR"),
113325
+ numeric("lte-snr", "SNR", "signal-ratio", "LTE.SNR"),
113158
113326
  text("lte-failover", "Failover result", "LTE.Failover.Result"),
113159
113327
  text("lte-signal-check", "Signal check", "LTE.Cronny.Result"),
113160
113328
  text("wifi-state", "Wi-Fi state", "WiFi.State"),
@@ -113195,7 +113363,7 @@ function resolve(target, snapshot) {
113195
113363
  for (const source of target.sources) {
113196
113364
  const reading = snapshot.readings.get(source.key);
113197
113365
  if (reading === void 0) continue;
113198
- const converted = toBase(reading.value, target.ignoreUnit === true ? void 0 : reading.unit, target.quantity);
113366
+ const converted = toBase(reading.value, reading.unit, target.quantity);
113199
113367
  if (!converted.ok) {
113200
113368
  refusal = `${source.key}: ${converted.reason} (unit ${reading.unit ?? "none"})`;
113201
113369
  continue;
@@ -113668,7 +113836,8 @@ function computeEnergySystemStatus(snapshot, options, now) {
113668
113836
  chargedKwh: read.meter("battery-charged"),
113669
113837
  dischargedKwh: read.meter("battery-discharged"),
113670
113838
  chargedTodayKwh: read.numeric("battery-charge-day"),
113671
- dischargedTodayKwh: read.numeric("battery-discharge-day")
113839
+ dischargedTodayKwh: read.numeric("battery-discharge-day"),
113840
+ healthPct: read.numeric("battery-health")
113672
113841
  };
113673
113842
  return {
113674
113843
  status: {
@@ -114061,6 +114230,53 @@ function accessoryChildSpecs(config) {
114061
114230
  return out;
114062
114231
  }
114063
114232
  //#endregion
114233
+ //#region src/devices/enpal-catalog-rename.ts
114234
+ /**
114235
+ * House and heat-pump meters used to be created as "Consumption". A restore
114236
+ * may replace that default; a name the operator chose stays.
114237
+ */
114238
+ /** Keep unless the stored name is still the old catalogue default for this meter. */
114239
+ function catalogNameWrite(input) {
114240
+ const spec = findSpec(input.specId);
114241
+ if (spec === void 0) return { kind: "keep" };
114242
+ if (input.specId !== "house" && input.specId !== "heatpump") return { kind: "keep" };
114243
+ if (input.name === spec.name) return { kind: "keep" };
114244
+ if (input.name !== "Consumption") return { kind: "keep" };
114245
+ return {
114246
+ kind: "rename",
114247
+ name: spec.name,
114248
+ stampUserNamed: input.userNamed === true
114249
+ };
114250
+ }
114251
+ /** Rename each child that still carries the old default. Logs every rename. */
114252
+ async function renameLegacyCatalogDevices(input) {
114253
+ for (const child of input.children) {
114254
+ const decision = catalogNameWrite(child);
114255
+ if (decision.kind === "keep") continue;
114256
+ try {
114257
+ await input.rename(child.deviceId, decision.name, decision.stampUserNamed);
114258
+ } catch (err) {
114259
+ input.logger.warn("enpal: could not rename a device still carrying the old catalogue name", {
114260
+ tags: { deviceId: child.deviceId },
114261
+ meta: {
114262
+ from: child.name,
114263
+ to: decision.name,
114264
+ error: errMsg(err)
114265
+ }
114266
+ });
114267
+ continue;
114268
+ }
114269
+ input.logger.info("enpal: renamed a device that still had the old catalogue name", {
114270
+ tags: { deviceId: child.deviceId },
114271
+ meta: {
114272
+ specId: child.specId,
114273
+ from: child.name,
114274
+ to: decision.name
114275
+ }
114276
+ });
114277
+ }
114278
+ }
114279
+ //#endregion
114064
114280
  //#region src/devices/enpal-metadata-sync.ts
114065
114281
  /**
114066
114282
  * Writes the identity facts a piece reported (model, serial, firmware) as device attributes.
@@ -114176,6 +114392,7 @@ var EnpalEquipmentDevice = class extends BaseDevice {
114176
114392
  },
114177
114393
  logger: this.ctx.logger
114178
114394
  });
114395
+ await this.renameLegacyNames();
114179
114396
  const metadata = new EnpalMetadataSync({
114180
114397
  equipment: this.equipment,
114181
114398
  deviceId: this.id,
@@ -114193,6 +114410,45 @@ var EnpalEquipmentDevice = class extends BaseDevice {
114193
114410
  });
114194
114411
  await metadata.apply();
114195
114412
  }
114413
+ /**
114414
+ * A meter created as "Consumption" takes the current catalogue name. A name
114415
+ * the operator changed is left as it is.
114416
+ */
114417
+ async renameLegacyNames() {
114418
+ const manager = this.ctx.api.deviceManager;
114419
+ try {
114420
+ const legacy = (await manager.getChildren.query({ parentDeviceId: this.id })).flatMap((child) => {
114421
+ const specId = child.config["specId"];
114422
+ if (typeof specId !== "string" || child.name !== "Consumption") return [];
114423
+ return [{
114424
+ deviceId: child.id,
114425
+ specId,
114426
+ name: child.name
114427
+ }];
114428
+ });
114429
+ await renameLegacyCatalogDevices({
114430
+ children: await Promise.all(legacy.map(async (child) => {
114431
+ const meta = await manager.loadMeta.query({ deviceId: child.deviceId });
114432
+ return {
114433
+ ...child,
114434
+ name: meta?.name ?? child.name,
114435
+ userNamed: meta?.userNamed
114436
+ };
114437
+ })),
114438
+ rename: (deviceId, name, stampUserNamed) => manager.setName.mutate({
114439
+ deviceId,
114440
+ name,
114441
+ ...stampUserNamed ? {} : { stampUserNamed: false }
114442
+ }),
114443
+ logger: this.ctx.logger
114444
+ });
114445
+ } catch (err) {
114446
+ this.ctx.logger.warn("enpal: could not rename devices still carrying the old catalogue name", {
114447
+ tags: { deviceId: this.id },
114448
+ meta: { error: errMsg(err) }
114449
+ });
114450
+ }
114451
+ }
114196
114452
  async removeDevice() {
114197
114453
  this.snapshotUnsub?.();
114198
114454
  this.snapshotUnsub = null;