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