@camstack/addon-provider-gree 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +198 -34
  2. package/dist/addon.mjs +198 -34
  3. package/package.json +2 -2
package/dist/addon.js CHANGED
@@ -4645,7 +4645,7 @@ function preprocess(fn, schema) {
4645
4645
  });
4646
4646
  }
4647
4647
  //#endregion
4648
- //#region ../types/dist/sleep-NOH4yRwj.mjs
4648
+ //#region ../types/dist/sleep-DVmKHFGi.mjs
4649
4649
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4650
4650
  EventCategory["SystemBoot"] = "system.boot";
4651
4651
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -6445,6 +6445,17 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
6445
6445
  DeviceRole["HumiditySensor"] = "humidity-sensor";
6446
6446
  DeviceRole["AmbientLightSensor"] = "ambient-light-sensor";
6447
6447
  DeviceRole["PressureSensor"] = "pressure-sensor";
6448
+ /** Wind speed or direction (weather-station `wind-sensor` cap). */
6449
+ DeviceRole["WindSensor"] = "wind-sensor";
6450
+ /** Rain accumulation or rate (weather-station `rain-sensor` cap). */
6451
+ DeviceRole["RainSensor"] = "rain-sensor";
6452
+ /** UV index (weather-station `uv-sensor` cap). */
6453
+ DeviceRole["UvSensor"] = "uv-sensor";
6454
+ /** Solar irradiance W/m² (weather-station `solar-radiation-sensor` cap).
6455
+ * Distinct from AmbientLightSensor (lux). */
6456
+ DeviceRole["SolarRadiationSensor"] = "solar-radiation-sensor";
6457
+ /** Soil moisture % (garden/weather `soil-moisture-sensor` cap). */
6458
+ DeviceRole["SoilMoistureSensor"] = "soil-moisture-sensor";
6448
6459
  DeviceRole["PowerSensor"] = "power-sensor";
6449
6460
  DeviceRole["EnergySensor"] = "energy-sensor";
6450
6461
  DeviceRole["VoltageSensor"] = "voltage-sensor";
@@ -10069,15 +10080,18 @@ var humiditySensorCapability = {
10069
10080
  runtimeState: HumiditySensorStatusSchema
10070
10081
  };
10071
10082
  /**
10072
- * Image display cap. Models HA `image.*` entities a single still image
10073
- * exposed by an integration (a snapshot, a chart, a generated picture).
10083
+ * Image display cap. Models a single still image exposed by an integration
10084
+ * a snapshot, a chart, a generated picture, or a robot's cleaning-map render.
10074
10085
  *
10075
- * Read-only: there are no setters. The provider resolves the HA
10076
- * `entity_picture` (a relative, signed-token path) into an ABSOLUTE URL
10077
- * the browser loads directly the token stays in the query string so no
10078
- * auth header is required. The slice carries that URL plus the upstream
10079
- * last-updated timestamp; the image changes when the entity state (a
10080
- * timestamp) changes.
10086
+ * Read-only: there are no setters. The provider resolves whatever upstream
10087
+ * source it has into an ABSOLUTE URL the browser loads directly:
10088
+ * - HA `image.*` entities the `entity_picture` signed-token path
10089
+ * (token stays in the query string, so no auth header is needed);
10090
+ * - a Dreame/robot map the cloud/OSS map-image URL (or an addon
10091
+ * data-plane URL serving the rendered map bytes), exposed as its own
10092
+ * Image child device grouped under the robot's container.
10093
+ * The slice carries that URL plus the upstream last-updated timestamp; the
10094
+ * image changes when the source's last-updated marker changes.
10081
10095
  */
10082
10096
  var ImageStatusSchema = object({
10083
10097
  /** Absolute signed URL the browser loads directly. Null when the
@@ -10103,18 +10117,47 @@ var imageCapability = {
10103
10117
  */
10104
10118
  runtimeState: ImageStatusSchema
10105
10119
  };
10120
+ /**
10121
+ * Robotic lawn-mower cap. Models HA `lawn_mower.*` entities — anything
10122
+ * with a mowing lifecycle plus a dock action.
10123
+ *
10124
+ * Activity follows HA's canonical lawn-mower lifecycle: `idle` /
10125
+ * `mowing` / `paused` / `docked` / `error`. `batteryLevel` (0..100) is
10126
+ * nullable — some mowers don't report a battery percentage.
10127
+ *
10128
+ * `startMowing` begins a mowing run, `pause` halts it in place, and
10129
+ * `dock` sends the mower back to its charging station.
10130
+ */
10131
+ var LawnMowerActivitySchema = _enum([
10132
+ "idle",
10133
+ "mowing",
10134
+ "paused",
10135
+ "docked",
10136
+ "error"
10137
+ ]);
10138
+ /** Severity of the current device/error code — info (status), warning, error. */
10139
+ var DeviceCodeSeveritySchema = _enum([
10140
+ "info",
10141
+ "warning",
10142
+ "error"
10143
+ ]);
10106
10144
  var LawnMowerControlStatusSchema = object({
10107
10145
  /** Lifecycle activity of the mower. */
10108
- activity: _enum([
10109
- "idle",
10110
- "mowing",
10111
- "paused",
10112
- "docked",
10113
- "error"
10114
- ]),
10146
+ activity: LawnMowerActivitySchema,
10115
10147
  /** 0..100 battery percentage. Null when the device has no battery
10116
10148
  * reading. */
10117
10149
  batteryLevel: number().min(0).max(100).nullable(),
10150
+ /** 0..100 mowing-completion percentage of the current task, or null when no
10151
+ * task is active / progress is unavailable. */
10152
+ progressPercent: number().min(0).max(100).nullable(),
10153
+ /** Current device/event code (dynamic — mostly status, sometimes an error),
10154
+ * or null when unknown. */
10155
+ currentCode: number().nullable(),
10156
+ /** Human label for {@link currentCode}, or null when undecodable. */
10157
+ currentCodeLabel: string().nullable(),
10158
+ /** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
10159
+ * attention; `info` is normal status. */
10160
+ severity: DeviceCodeSeveritySchema,
10118
10161
  /** Ms epoch when the slice was last updated. */
10119
10162
  lastChangedAt: number()
10120
10163
  });
@@ -12340,6 +12383,7 @@ var VacuumStateSchema = _enum([
12340
12383
  "paused",
12341
12384
  "returning",
12342
12385
  "docked",
12386
+ "drying",
12343
12387
  "error"
12344
12388
  ]);
12345
12389
  /**
@@ -12378,6 +12422,12 @@ var VacuumControlStatusSchema = object({
12378
12422
  detergent: TankStatusSchema.nullable(),
12379
12423
  /** Dust bin. Null when the hardware has no dust bin. */
12380
12424
  dustBin: TankStatusSchema.nullable(),
12425
+ /** 0..100 cleaning-completion percentage of the current task, or null. */
12426
+ progressPercent: number().min(0).max(100).nullable(),
12427
+ /** Current error code (0 / null = no error). */
12428
+ errorCode: number().nullable(),
12429
+ /** Human label for {@link errorCode}, or null when none / undecodable. */
12430
+ errorLabel: string().nullable(),
12381
12431
  /** Ms epoch when the slice was last updated. */
12382
12432
  lastChangedAt: number()
12383
12433
  });
@@ -16893,6 +16943,9 @@ method(object({
16893
16943
  }), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
16894
16944
  kind: "mutation",
16895
16945
  auth: "admin"
16946
+ }), method(ResyncInputSchema, ResyncResultSchema, {
16947
+ kind: "mutation",
16948
+ auth: "admin"
16896
16949
  }), method(object({
16897
16950
  deviceId: number(),
16898
16951
  key: string(),
@@ -20655,6 +20708,12 @@ Object.freeze({
20655
20708
  addonId: null,
20656
20709
  access: "create"
20657
20710
  },
20711
+ "deviceManager.adoptionResync": {
20712
+ capName: "device-manager",
20713
+ capScope: "system",
20714
+ addonId: null,
20715
+ access: "create"
20716
+ },
20658
20717
  "deviceManager.allocateDeviceId": {
20659
20718
  capName: "device-manager",
20660
20719
  capScope: "system",
@@ -25526,6 +25585,90 @@ function swingToOscillating(vertical, horizontal) {
25526
25585
  function oscillatingToVerticalSwing(oscillating) {
25527
25586
  return oscillating ? VerticalSwing.FullSwing : VerticalSwing.Default;
25528
25587
  }
25588
+ /**
25589
+ * Gree fan-speed names surfaced on the `climate-control` cap's `fanMode` /
25590
+ * `availableFanModes` strings, mirroring the HA Gree integration's `fan_modes`
25591
+ * attribute (`auto`/`low`/`medium_low`/`medium`/`medium_high`/`high`).
25592
+ *
25593
+ * NOTE: HA's Gree integration also folds `turbo` and `quiet` INTO `fan_modes`.
25594
+ * We deliberately keep turbo/quiet on the dedicated `preset` surface instead
25595
+ * (they are independent boolean device flags in the protocol — `Tur` / `Quiet`
25596
+ * — not points on the `WdSpd` speed axis), so the speed picker stays a pure
25597
+ * speed axis and the two flags stay independently togglable. The `fan-control`
25598
+ * percentage surface continues to mirror the same `WdSpd` speed for the
25599
+ * fan-style UI.
25600
+ */
25601
+ var GREE_FAN_MODES = [
25602
+ "auto",
25603
+ "low",
25604
+ "medium_low",
25605
+ "medium",
25606
+ "medium_high",
25607
+ "high"
25608
+ ];
25609
+ /** Map a library {@link FanSpeed} to its `climate-control` `fanMode` string. Pure. */
25610
+ function fanSpeedToFanMode(speed) {
25611
+ switch (speed) {
25612
+ case FanSpeed.Auto: return "auto";
25613
+ case FanSpeed.Low: return "low";
25614
+ case FanSpeed.MediumLow: return "medium_low";
25615
+ case FanSpeed.Medium: return "medium";
25616
+ case FanSpeed.MediumHigh: return "medium_high";
25617
+ case FanSpeed.High: return "high";
25618
+ }
25619
+ }
25620
+ /**
25621
+ * Map a `climate-control` `fanMode` string back to a library {@link FanSpeed},
25622
+ * or null when the string is not a known Gree speed. Pure.
25623
+ */
25624
+ function fanModeToFanSpeed(fanMode) {
25625
+ switch (fanMode) {
25626
+ case "auto": return FanSpeed.Auto;
25627
+ case "low": return FanSpeed.Low;
25628
+ case "medium_low": return FanSpeed.MediumLow;
25629
+ case "medium": return FanSpeed.Medium;
25630
+ case "medium_high": return FanSpeed.MediumHigh;
25631
+ case "high": return FanSpeed.High;
25632
+ default: return null;
25633
+ }
25634
+ }
25635
+ var GREE_PRESETS = [
25636
+ "none",
25637
+ "turbo",
25638
+ "quiet",
25639
+ "sleep",
25640
+ "eco",
25641
+ "8c_heat"
25642
+ ];
25643
+ /**
25644
+ * Derive the single active `preset` string from the device's comfort flags.
25645
+ * Precedence is fixed (turbo > quiet > sleep > eco > 8c_heat) so a coherent
25646
+ * single value is reported even if the device has more than one flag set.
25647
+ * Returns `'none'` when no flag is set. Pure.
25648
+ */
25649
+ function presetFromFlags(flags) {
25650
+ if (flags.turbo) return "turbo";
25651
+ if (flags.quiet) return "quiet";
25652
+ if (flags.sleep) return "sleep";
25653
+ if (flags.powerSave) return "eco";
25654
+ if (flags.steadyHeat) return "8c_heat";
25655
+ return "none";
25656
+ }
25657
+ /**
25658
+ * Resolve a requested `preset` string to the set of flag writes needed to make
25659
+ * it the active one — the chosen flag (if any) true, every other flag false.
25660
+ * Returns null when the string is not a known Gree preset. Pure.
25661
+ */
25662
+ function presetToFlagWrites(preset) {
25663
+ if (!GREE_PRESETS.includes(preset)) return null;
25664
+ return {
25665
+ turbo: preset === "turbo",
25666
+ quiet: preset === "quiet",
25667
+ sleep: preset === "sleep",
25668
+ powerSave: preset === "eco",
25669
+ steadyHeat: preset === "8c_heat"
25670
+ };
25671
+ }
25529
25672
  //#endregion
25530
25673
  //#region src/devices/gree-ac-device.ts
25531
25674
  var CLIMATE_CAP = "climate-control";
@@ -25534,9 +25677,9 @@ var CLIMATE_COLD_START = {
25534
25677
  mode: "off",
25535
25678
  availableModes: [...ADVERTISED_CAP_MODES],
25536
25679
  fanMode: "",
25537
- availableFanModes: [],
25680
+ availableFanModes: [...GREE_FAN_MODES],
25538
25681
  preset: "",
25539
- availablePresets: [],
25682
+ availablePresets: [...GREE_PRESETS],
25540
25683
  target: null,
25541
25684
  targetHigh: null,
25542
25685
  targetLow: null,
@@ -25572,17 +25715,21 @@ var greeAcSchema = object({
25572
25715
  * `stateChanged` push (and seeds on activate).
25573
25716
  *
25574
25717
  * Climate commands route to the bound handle: setMode `off`→`setPower(false)`,
25575
- * any other→`setPower(true)`+`setMode()`; setTarget→`setTargetTemperature()`.
25576
- * Fan commands: setPercentage→`setFanSpeed()` (percentageFanSpeed bucket);
25718
+ * any other→`setPower(true)`+`setMode()`; setTarget→`setTargetTemperature()`;
25719
+ * setFanMode→`setFanSpeed()` (climate `fanMode` string Gree speed); setPreset→
25720
+ * the comfort-flag setters (turbo/quiet/sleep/eco/8c_heat → `setTurbo`/`setQuiet`/
25721
+ * `setSleep`/`setPowerSave`/`setSteadyHeat`, exclusive). Fan commands:
25722
+ * setPercentage→`setFanSpeed()` (percentage↔FanSpeed bucket);
25577
25723
  * setOscillating→`setSwingVertical(FullSwing|Default)`.
25578
25724
  *
25579
- * TODO (deferred): the cap's `heat_cool` dual-setpoint, `setTargetHumidity`,
25580
- * fan-mode strings, and presets (turbo/quiet/sleep/powerSave/steadyHeat which the
25581
- * library DOES expose) are not yet mapped fan speed already occupies the
25582
- * `fan-control` percentage surface, and the cap preset/fan-mode surfaces are left
25583
- * empty. The fine-grained 12-position vertical / 7-position horizontal swing is
25584
- * collapsed to a single `oscillating` boolean. A later phase could add a vendor
25585
- * preset / swing-position cap.
25725
+ * TODO (deferred — needs protocol RE / live confirmation): `heat_cool`
25726
+ * dual-setpoint (Gree single-setpoint ACs have none), `setTargetHumidity`
25727
+ * (`Dwet`/`DwatSen` dehumidifier props exist in PROPS but the library exposes no
25728
+ * getter/setter yet), the light toggle (`setLight`), X-Fan / fresh-air / health
25729
+ * toggles (`setXfan`/`setFreshAir`/`setHealth`) all real library setters with
25730
+ * no cap surface (a vendor toggle cap would carry them), and the fine-grained
25731
+ * 12-position vertical / 7-position horizontal swing (collapsed to a single
25732
+ * `oscillating` boolean — a vendor swing-position cap would carry them).
25586
25733
  */
25587
25734
  var GreeAcDevice = class extends BaseDevice$1 {
25588
25735
  features = [];
@@ -25660,10 +25807,19 @@ var GreeAcDevice = class extends BaseDevice$1 {
25660
25807
  await ac.setMode(libMode);
25661
25808
  },
25662
25809
  setFanMode: async ({ fanMode }) => {
25663
- throw new Error(`gree ac: fan-mode strings not supported (use fan-control); got "${fanMode}"`);
25810
+ const speed = fanModeToFanSpeed(fanMode);
25811
+ if (speed === null) throw new Error(`gree ac: unsupported fan mode "${fanMode}"`);
25812
+ await this.requireAc().setFanSpeed(speed);
25664
25813
  },
25665
25814
  setPreset: async ({ preset }) => {
25666
- throw new Error(`gree ac: presets not yet supported (got "${preset}")`);
25815
+ const writes = presetToFlagWrites(preset);
25816
+ if (writes === null) throw new Error(`gree ac: unsupported preset "${preset}"`);
25817
+ const ac = this.requireAc();
25818
+ if (ac.turbo !== writes.turbo) await ac.setTurbo(writes.turbo);
25819
+ if (ac.quiet !== writes.quiet) await ac.setQuiet(writes.quiet);
25820
+ if (ac.sleep !== writes.sleep) await ac.setSleep(writes.sleep);
25821
+ if (ac.powerSave !== writes.powerSave) await ac.setPowerSave(writes.powerSave);
25822
+ if (ac.steadyHeat !== writes.steadyHeat) await ac.setSteadyHeat(writes.steadyHeat);
25667
25823
  },
25668
25824
  setTarget: async ({ target }) => {
25669
25825
  await this.requireAc().setTargetTemperature(target);
@@ -25697,13 +25853,21 @@ var GreeAcDevice = class extends BaseDevice$1 {
25697
25853
  const ac = this.resolveAc();
25698
25854
  if (ac === null) return;
25699
25855
  const now = Date.now();
25856
+ const mode = ac.power ? libModeToCapMode(ac.mode) : "off";
25857
+ const preset = presetFromFlags({
25858
+ turbo: ac.turbo,
25859
+ quiet: ac.quiet,
25860
+ sleep: ac.sleep,
25861
+ powerSave: ac.powerSave,
25862
+ steadyHeat: ac.steadyHeat
25863
+ });
25700
25864
  const climateSlice = {
25701
- mode: ac.power ? libModeToCapMode(ac.mode) : "off",
25865
+ mode,
25702
25866
  availableModes: [...ADVERTISED_CAP_MODES],
25703
- fanMode: "",
25704
- availableFanModes: [],
25705
- preset: "",
25706
- availablePresets: [],
25867
+ fanMode: fanSpeedToFanMode(ac.fanSpeed),
25868
+ availableFanModes: [...GREE_FAN_MODES],
25869
+ preset,
25870
+ availablePresets: [...GREE_PRESETS],
25707
25871
  target: ac.targetTemperature,
25708
25872
  targetHigh: null,
25709
25873
  targetLow: null,
package/dist/addon.mjs CHANGED
@@ -4644,7 +4644,7 @@ function preprocess(fn, schema) {
4644
4644
  });
4645
4645
  }
4646
4646
  //#endregion
4647
- //#region ../types/dist/sleep-NOH4yRwj.mjs
4647
+ //#region ../types/dist/sleep-DVmKHFGi.mjs
4648
4648
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4649
4649
  EventCategory["SystemBoot"] = "system.boot";
4650
4650
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -6444,6 +6444,17 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
6444
6444
  DeviceRole["HumiditySensor"] = "humidity-sensor";
6445
6445
  DeviceRole["AmbientLightSensor"] = "ambient-light-sensor";
6446
6446
  DeviceRole["PressureSensor"] = "pressure-sensor";
6447
+ /** Wind speed or direction (weather-station `wind-sensor` cap). */
6448
+ DeviceRole["WindSensor"] = "wind-sensor";
6449
+ /** Rain accumulation or rate (weather-station `rain-sensor` cap). */
6450
+ DeviceRole["RainSensor"] = "rain-sensor";
6451
+ /** UV index (weather-station `uv-sensor` cap). */
6452
+ DeviceRole["UvSensor"] = "uv-sensor";
6453
+ /** Solar irradiance W/m² (weather-station `solar-radiation-sensor` cap).
6454
+ * Distinct from AmbientLightSensor (lux). */
6455
+ DeviceRole["SolarRadiationSensor"] = "solar-radiation-sensor";
6456
+ /** Soil moisture % (garden/weather `soil-moisture-sensor` cap). */
6457
+ DeviceRole["SoilMoistureSensor"] = "soil-moisture-sensor";
6447
6458
  DeviceRole["PowerSensor"] = "power-sensor";
6448
6459
  DeviceRole["EnergySensor"] = "energy-sensor";
6449
6460
  DeviceRole["VoltageSensor"] = "voltage-sensor";
@@ -10068,15 +10079,18 @@ var humiditySensorCapability = {
10068
10079
  runtimeState: HumiditySensorStatusSchema
10069
10080
  };
10070
10081
  /**
10071
- * Image display cap. Models HA `image.*` entities a single still image
10072
- * exposed by an integration (a snapshot, a chart, a generated picture).
10082
+ * Image display cap. Models a single still image exposed by an integration
10083
+ * a snapshot, a chart, a generated picture, or a robot's cleaning-map render.
10073
10084
  *
10074
- * Read-only: there are no setters. The provider resolves the HA
10075
- * `entity_picture` (a relative, signed-token path) into an ABSOLUTE URL
10076
- * the browser loads directly the token stays in the query string so no
10077
- * auth header is required. The slice carries that URL plus the upstream
10078
- * last-updated timestamp; the image changes when the entity state (a
10079
- * timestamp) changes.
10085
+ * Read-only: there are no setters. The provider resolves whatever upstream
10086
+ * source it has into an ABSOLUTE URL the browser loads directly:
10087
+ * - HA `image.*` entities the `entity_picture` signed-token path
10088
+ * (token stays in the query string, so no auth header is needed);
10089
+ * - a Dreame/robot map the cloud/OSS map-image URL (or an addon
10090
+ * data-plane URL serving the rendered map bytes), exposed as its own
10091
+ * Image child device grouped under the robot's container.
10092
+ * The slice carries that URL plus the upstream last-updated timestamp; the
10093
+ * image changes when the source's last-updated marker changes.
10080
10094
  */
10081
10095
  var ImageStatusSchema = object({
10082
10096
  /** Absolute signed URL the browser loads directly. Null when the
@@ -10102,18 +10116,47 @@ var imageCapability = {
10102
10116
  */
10103
10117
  runtimeState: ImageStatusSchema
10104
10118
  };
10119
+ /**
10120
+ * Robotic lawn-mower cap. Models HA `lawn_mower.*` entities — anything
10121
+ * with a mowing lifecycle plus a dock action.
10122
+ *
10123
+ * Activity follows HA's canonical lawn-mower lifecycle: `idle` /
10124
+ * `mowing` / `paused` / `docked` / `error`. `batteryLevel` (0..100) is
10125
+ * nullable — some mowers don't report a battery percentage.
10126
+ *
10127
+ * `startMowing` begins a mowing run, `pause` halts it in place, and
10128
+ * `dock` sends the mower back to its charging station.
10129
+ */
10130
+ var LawnMowerActivitySchema = _enum([
10131
+ "idle",
10132
+ "mowing",
10133
+ "paused",
10134
+ "docked",
10135
+ "error"
10136
+ ]);
10137
+ /** Severity of the current device/error code — info (status), warning, error. */
10138
+ var DeviceCodeSeveritySchema = _enum([
10139
+ "info",
10140
+ "warning",
10141
+ "error"
10142
+ ]);
10105
10143
  var LawnMowerControlStatusSchema = object({
10106
10144
  /** Lifecycle activity of the mower. */
10107
- activity: _enum([
10108
- "idle",
10109
- "mowing",
10110
- "paused",
10111
- "docked",
10112
- "error"
10113
- ]),
10145
+ activity: LawnMowerActivitySchema,
10114
10146
  /** 0..100 battery percentage. Null when the device has no battery
10115
10147
  * reading. */
10116
10148
  batteryLevel: number().min(0).max(100).nullable(),
10149
+ /** 0..100 mowing-completion percentage of the current task, or null when no
10150
+ * task is active / progress is unavailable. */
10151
+ progressPercent: number().min(0).max(100).nullable(),
10152
+ /** Current device/event code (dynamic — mostly status, sometimes an error),
10153
+ * or null when unknown. */
10154
+ currentCode: number().nullable(),
10155
+ /** Human label for {@link currentCode}, or null when undecodable. */
10156
+ currentCodeLabel: string().nullable(),
10157
+ /** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
10158
+ * attention; `info` is normal status. */
10159
+ severity: DeviceCodeSeveritySchema,
10117
10160
  /** Ms epoch when the slice was last updated. */
10118
10161
  lastChangedAt: number()
10119
10162
  });
@@ -12339,6 +12382,7 @@ var VacuumStateSchema = _enum([
12339
12382
  "paused",
12340
12383
  "returning",
12341
12384
  "docked",
12385
+ "drying",
12342
12386
  "error"
12343
12387
  ]);
12344
12388
  /**
@@ -12377,6 +12421,12 @@ var VacuumControlStatusSchema = object({
12377
12421
  detergent: TankStatusSchema.nullable(),
12378
12422
  /** Dust bin. Null when the hardware has no dust bin. */
12379
12423
  dustBin: TankStatusSchema.nullable(),
12424
+ /** 0..100 cleaning-completion percentage of the current task, or null. */
12425
+ progressPercent: number().min(0).max(100).nullable(),
12426
+ /** Current error code (0 / null = no error). */
12427
+ errorCode: number().nullable(),
12428
+ /** Human label for {@link errorCode}, or null when none / undecodable. */
12429
+ errorLabel: string().nullable(),
12380
12430
  /** Ms epoch when the slice was last updated. */
12381
12431
  lastChangedAt: number()
12382
12432
  });
@@ -16892,6 +16942,9 @@ method(object({
16892
16942
  }), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
16893
16943
  kind: "mutation",
16894
16944
  auth: "admin"
16945
+ }), method(ResyncInputSchema, ResyncResultSchema, {
16946
+ kind: "mutation",
16947
+ auth: "admin"
16895
16948
  }), method(object({
16896
16949
  deviceId: number(),
16897
16950
  key: string(),
@@ -20654,6 +20707,12 @@ Object.freeze({
20654
20707
  addonId: null,
20655
20708
  access: "create"
20656
20709
  },
20710
+ "deviceManager.adoptionResync": {
20711
+ capName: "device-manager",
20712
+ capScope: "system",
20713
+ addonId: null,
20714
+ access: "create"
20715
+ },
20657
20716
  "deviceManager.allocateDeviceId": {
20658
20717
  capName: "device-manager",
20659
20718
  capScope: "system",
@@ -25525,6 +25584,90 @@ function swingToOscillating(vertical, horizontal) {
25525
25584
  function oscillatingToVerticalSwing(oscillating) {
25526
25585
  return oscillating ? VerticalSwing.FullSwing : VerticalSwing.Default;
25527
25586
  }
25587
+ /**
25588
+ * Gree fan-speed names surfaced on the `climate-control` cap's `fanMode` /
25589
+ * `availableFanModes` strings, mirroring the HA Gree integration's `fan_modes`
25590
+ * attribute (`auto`/`low`/`medium_low`/`medium`/`medium_high`/`high`).
25591
+ *
25592
+ * NOTE: HA's Gree integration also folds `turbo` and `quiet` INTO `fan_modes`.
25593
+ * We deliberately keep turbo/quiet on the dedicated `preset` surface instead
25594
+ * (they are independent boolean device flags in the protocol — `Tur` / `Quiet`
25595
+ * — not points on the `WdSpd` speed axis), so the speed picker stays a pure
25596
+ * speed axis and the two flags stay independently togglable. The `fan-control`
25597
+ * percentage surface continues to mirror the same `WdSpd` speed for the
25598
+ * fan-style UI.
25599
+ */
25600
+ var GREE_FAN_MODES = [
25601
+ "auto",
25602
+ "low",
25603
+ "medium_low",
25604
+ "medium",
25605
+ "medium_high",
25606
+ "high"
25607
+ ];
25608
+ /** Map a library {@link FanSpeed} to its `climate-control` `fanMode` string. Pure. */
25609
+ function fanSpeedToFanMode(speed) {
25610
+ switch (speed) {
25611
+ case FanSpeed.Auto: return "auto";
25612
+ case FanSpeed.Low: return "low";
25613
+ case FanSpeed.MediumLow: return "medium_low";
25614
+ case FanSpeed.Medium: return "medium";
25615
+ case FanSpeed.MediumHigh: return "medium_high";
25616
+ case FanSpeed.High: return "high";
25617
+ }
25618
+ }
25619
+ /**
25620
+ * Map a `climate-control` `fanMode` string back to a library {@link FanSpeed},
25621
+ * or null when the string is not a known Gree speed. Pure.
25622
+ */
25623
+ function fanModeToFanSpeed(fanMode) {
25624
+ switch (fanMode) {
25625
+ case "auto": return FanSpeed.Auto;
25626
+ case "low": return FanSpeed.Low;
25627
+ case "medium_low": return FanSpeed.MediumLow;
25628
+ case "medium": return FanSpeed.Medium;
25629
+ case "medium_high": return FanSpeed.MediumHigh;
25630
+ case "high": return FanSpeed.High;
25631
+ default: return null;
25632
+ }
25633
+ }
25634
+ var GREE_PRESETS = [
25635
+ "none",
25636
+ "turbo",
25637
+ "quiet",
25638
+ "sleep",
25639
+ "eco",
25640
+ "8c_heat"
25641
+ ];
25642
+ /**
25643
+ * Derive the single active `preset` string from the device's comfort flags.
25644
+ * Precedence is fixed (turbo > quiet > sleep > eco > 8c_heat) so a coherent
25645
+ * single value is reported even if the device has more than one flag set.
25646
+ * Returns `'none'` when no flag is set. Pure.
25647
+ */
25648
+ function presetFromFlags(flags) {
25649
+ if (flags.turbo) return "turbo";
25650
+ if (flags.quiet) return "quiet";
25651
+ if (flags.sleep) return "sleep";
25652
+ if (flags.powerSave) return "eco";
25653
+ if (flags.steadyHeat) return "8c_heat";
25654
+ return "none";
25655
+ }
25656
+ /**
25657
+ * Resolve a requested `preset` string to the set of flag writes needed to make
25658
+ * it the active one — the chosen flag (if any) true, every other flag false.
25659
+ * Returns null when the string is not a known Gree preset. Pure.
25660
+ */
25661
+ function presetToFlagWrites(preset) {
25662
+ if (!GREE_PRESETS.includes(preset)) return null;
25663
+ return {
25664
+ turbo: preset === "turbo",
25665
+ quiet: preset === "quiet",
25666
+ sleep: preset === "sleep",
25667
+ powerSave: preset === "eco",
25668
+ steadyHeat: preset === "8c_heat"
25669
+ };
25670
+ }
25528
25671
  //#endregion
25529
25672
  //#region src/devices/gree-ac-device.ts
25530
25673
  var CLIMATE_CAP = "climate-control";
@@ -25533,9 +25676,9 @@ var CLIMATE_COLD_START = {
25533
25676
  mode: "off",
25534
25677
  availableModes: [...ADVERTISED_CAP_MODES],
25535
25678
  fanMode: "",
25536
- availableFanModes: [],
25679
+ availableFanModes: [...GREE_FAN_MODES],
25537
25680
  preset: "",
25538
- availablePresets: [],
25681
+ availablePresets: [...GREE_PRESETS],
25539
25682
  target: null,
25540
25683
  targetHigh: null,
25541
25684
  targetLow: null,
@@ -25571,17 +25714,21 @@ var greeAcSchema = object({
25571
25714
  * `stateChanged` push (and seeds on activate).
25572
25715
  *
25573
25716
  * Climate commands route to the bound handle: setMode `off`→`setPower(false)`,
25574
- * any other→`setPower(true)`+`setMode()`; setTarget→`setTargetTemperature()`.
25575
- * Fan commands: setPercentage→`setFanSpeed()` (percentageFanSpeed bucket);
25717
+ * any other→`setPower(true)`+`setMode()`; setTarget→`setTargetTemperature()`;
25718
+ * setFanMode→`setFanSpeed()` (climate `fanMode` string Gree speed); setPreset→
25719
+ * the comfort-flag setters (turbo/quiet/sleep/eco/8c_heat → `setTurbo`/`setQuiet`/
25720
+ * `setSleep`/`setPowerSave`/`setSteadyHeat`, exclusive). Fan commands:
25721
+ * setPercentage→`setFanSpeed()` (percentage↔FanSpeed bucket);
25576
25722
  * setOscillating→`setSwingVertical(FullSwing|Default)`.
25577
25723
  *
25578
- * TODO (deferred): the cap's `heat_cool` dual-setpoint, `setTargetHumidity`,
25579
- * fan-mode strings, and presets (turbo/quiet/sleep/powerSave/steadyHeat which the
25580
- * library DOES expose) are not yet mapped fan speed already occupies the
25581
- * `fan-control` percentage surface, and the cap preset/fan-mode surfaces are left
25582
- * empty. The fine-grained 12-position vertical / 7-position horizontal swing is
25583
- * collapsed to a single `oscillating` boolean. A later phase could add a vendor
25584
- * preset / swing-position cap.
25724
+ * TODO (deferred — needs protocol RE / live confirmation): `heat_cool`
25725
+ * dual-setpoint (Gree single-setpoint ACs have none), `setTargetHumidity`
25726
+ * (`Dwet`/`DwatSen` dehumidifier props exist in PROPS but the library exposes no
25727
+ * getter/setter yet), the light toggle (`setLight`), X-Fan / fresh-air / health
25728
+ * toggles (`setXfan`/`setFreshAir`/`setHealth`) all real library setters with
25729
+ * no cap surface (a vendor toggle cap would carry them), and the fine-grained
25730
+ * 12-position vertical / 7-position horizontal swing (collapsed to a single
25731
+ * `oscillating` boolean — a vendor swing-position cap would carry them).
25585
25732
  */
25586
25733
  var GreeAcDevice = class extends BaseDevice$1 {
25587
25734
  features = [];
@@ -25659,10 +25806,19 @@ var GreeAcDevice = class extends BaseDevice$1 {
25659
25806
  await ac.setMode(libMode);
25660
25807
  },
25661
25808
  setFanMode: async ({ fanMode }) => {
25662
- throw new Error(`gree ac: fan-mode strings not supported (use fan-control); got "${fanMode}"`);
25809
+ const speed = fanModeToFanSpeed(fanMode);
25810
+ if (speed === null) throw new Error(`gree ac: unsupported fan mode "${fanMode}"`);
25811
+ await this.requireAc().setFanSpeed(speed);
25663
25812
  },
25664
25813
  setPreset: async ({ preset }) => {
25665
- throw new Error(`gree ac: presets not yet supported (got "${preset}")`);
25814
+ const writes = presetToFlagWrites(preset);
25815
+ if (writes === null) throw new Error(`gree ac: unsupported preset "${preset}"`);
25816
+ const ac = this.requireAc();
25817
+ if (ac.turbo !== writes.turbo) await ac.setTurbo(writes.turbo);
25818
+ if (ac.quiet !== writes.quiet) await ac.setQuiet(writes.quiet);
25819
+ if (ac.sleep !== writes.sleep) await ac.setSleep(writes.sleep);
25820
+ if (ac.powerSave !== writes.powerSave) await ac.setPowerSave(writes.powerSave);
25821
+ if (ac.steadyHeat !== writes.steadyHeat) await ac.setSteadyHeat(writes.steadyHeat);
25666
25822
  },
25667
25823
  setTarget: async ({ target }) => {
25668
25824
  await this.requireAc().setTargetTemperature(target);
@@ -25696,13 +25852,21 @@ var GreeAcDevice = class extends BaseDevice$1 {
25696
25852
  const ac = this.resolveAc();
25697
25853
  if (ac === null) return;
25698
25854
  const now = Date.now();
25855
+ const mode = ac.power ? libModeToCapMode(ac.mode) : "off";
25856
+ const preset = presetFromFlags({
25857
+ turbo: ac.turbo,
25858
+ quiet: ac.quiet,
25859
+ sleep: ac.sleep,
25860
+ powerSave: ac.powerSave,
25861
+ steadyHeat: ac.steadyHeat
25862
+ });
25699
25863
  const climateSlice = {
25700
- mode: ac.power ? libModeToCapMode(ac.mode) : "off",
25864
+ mode,
25701
25865
  availableModes: [...ADVERTISED_CAP_MODES],
25702
- fanMode: "",
25703
- availableFanModes: [],
25704
- preset: "",
25705
- availablePresets: [],
25866
+ fanMode: fanSpeedToFanMode(ac.fanSpeed),
25867
+ availableFanModes: [...GREE_FAN_MODES],
25868
+ preset,
25869
+ availablePresets: [...GREE_PRESETS],
25706
25870
  target: ac.targetTemperature,
25707
25871
  targetHigh: null,
25708
25872
  targetLow: null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-gree",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Gree air-conditioner device-provider addon for CamStack — wraps the @apocaliss92/nodegree local-UDP client (LAN discovery + AES control), exposing climate-control and fan-control",
5
5
  "keywords": [
6
6
  "camstack",
@@ -44,7 +44,7 @@
44
44
  "instanceMode": "multiple",
45
45
  "brokerKind": "gree",
46
46
  "execution": {
47
- "placement": "any-node"
47
+ "placement": "hub-only"
48
48
  },
49
49
  "capabilities": [
50
50
  {