@camstack/addon-provider-gree 0.1.5 → 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.
- package/dist/addon.js +186 -33
- package/dist/addon.mjs +186 -33
- package/package.json +2 -2
package/dist/addon.js
CHANGED
|
@@ -10080,15 +10080,18 @@ var humiditySensorCapability = {
|
|
|
10080
10080
|
runtimeState: HumiditySensorStatusSchema
|
|
10081
10081
|
};
|
|
10082
10082
|
/**
|
|
10083
|
-
* Image display cap. Models
|
|
10084
|
-
*
|
|
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.
|
|
10085
10085
|
*
|
|
10086
|
-
* Read-only: there are no setters. The provider resolves
|
|
10087
|
-
*
|
|
10088
|
-
*
|
|
10089
|
-
*
|
|
10090
|
-
*
|
|
10091
|
-
*
|
|
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.
|
|
10092
10095
|
*/
|
|
10093
10096
|
var ImageStatusSchema = object({
|
|
10094
10097
|
/** Absolute signed URL the browser loads directly. Null when the
|
|
@@ -10114,18 +10117,47 @@ var imageCapability = {
|
|
|
10114
10117
|
*/
|
|
10115
10118
|
runtimeState: ImageStatusSchema
|
|
10116
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
|
+
]);
|
|
10117
10144
|
var LawnMowerControlStatusSchema = object({
|
|
10118
10145
|
/** Lifecycle activity of the mower. */
|
|
10119
|
-
activity:
|
|
10120
|
-
"idle",
|
|
10121
|
-
"mowing",
|
|
10122
|
-
"paused",
|
|
10123
|
-
"docked",
|
|
10124
|
-
"error"
|
|
10125
|
-
]),
|
|
10146
|
+
activity: LawnMowerActivitySchema,
|
|
10126
10147
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
10127
10148
|
* reading. */
|
|
10128
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,
|
|
10129
10161
|
/** Ms epoch when the slice was last updated. */
|
|
10130
10162
|
lastChangedAt: number()
|
|
10131
10163
|
});
|
|
@@ -12351,6 +12383,7 @@ var VacuumStateSchema = _enum([
|
|
|
12351
12383
|
"paused",
|
|
12352
12384
|
"returning",
|
|
12353
12385
|
"docked",
|
|
12386
|
+
"drying",
|
|
12354
12387
|
"error"
|
|
12355
12388
|
]);
|
|
12356
12389
|
/**
|
|
@@ -12389,6 +12422,12 @@ var VacuumControlStatusSchema = object({
|
|
|
12389
12422
|
detergent: TankStatusSchema.nullable(),
|
|
12390
12423
|
/** Dust bin. Null when the hardware has no dust bin. */
|
|
12391
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(),
|
|
12392
12431
|
/** Ms epoch when the slice was last updated. */
|
|
12393
12432
|
lastChangedAt: number()
|
|
12394
12433
|
});
|
|
@@ -16904,6 +16943,9 @@ method(object({
|
|
|
16904
16943
|
}), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
|
|
16905
16944
|
kind: "mutation",
|
|
16906
16945
|
auth: "admin"
|
|
16946
|
+
}), method(ResyncInputSchema, ResyncResultSchema, {
|
|
16947
|
+
kind: "mutation",
|
|
16948
|
+
auth: "admin"
|
|
16907
16949
|
}), method(object({
|
|
16908
16950
|
deviceId: number(),
|
|
16909
16951
|
key: string(),
|
|
@@ -20666,6 +20708,12 @@ Object.freeze({
|
|
|
20666
20708
|
addonId: null,
|
|
20667
20709
|
access: "create"
|
|
20668
20710
|
},
|
|
20711
|
+
"deviceManager.adoptionResync": {
|
|
20712
|
+
capName: "device-manager",
|
|
20713
|
+
capScope: "system",
|
|
20714
|
+
addonId: null,
|
|
20715
|
+
access: "create"
|
|
20716
|
+
},
|
|
20669
20717
|
"deviceManager.allocateDeviceId": {
|
|
20670
20718
|
capName: "device-manager",
|
|
20671
20719
|
capScope: "system",
|
|
@@ -25537,6 +25585,90 @@ function swingToOscillating(vertical, horizontal) {
|
|
|
25537
25585
|
function oscillatingToVerticalSwing(oscillating) {
|
|
25538
25586
|
return oscillating ? VerticalSwing.FullSwing : VerticalSwing.Default;
|
|
25539
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
|
+
}
|
|
25540
25672
|
//#endregion
|
|
25541
25673
|
//#region src/devices/gree-ac-device.ts
|
|
25542
25674
|
var CLIMATE_CAP = "climate-control";
|
|
@@ -25545,9 +25677,9 @@ var CLIMATE_COLD_START = {
|
|
|
25545
25677
|
mode: "off",
|
|
25546
25678
|
availableModes: [...ADVERTISED_CAP_MODES],
|
|
25547
25679
|
fanMode: "",
|
|
25548
|
-
availableFanModes: [],
|
|
25680
|
+
availableFanModes: [...GREE_FAN_MODES],
|
|
25549
25681
|
preset: "",
|
|
25550
|
-
availablePresets: [],
|
|
25682
|
+
availablePresets: [...GREE_PRESETS],
|
|
25551
25683
|
target: null,
|
|
25552
25684
|
targetHigh: null,
|
|
25553
25685
|
targetLow: null,
|
|
@@ -25583,17 +25715,21 @@ var greeAcSchema = object({
|
|
|
25583
25715
|
* `stateChanged` push (and seeds on activate).
|
|
25584
25716
|
*
|
|
25585
25717
|
* Climate commands route to the bound handle: setMode `off`→`setPower(false)`,
|
|
25586
|
-
* any other→`setPower(true)`+`setMode()`; setTarget→`setTargetTemperature()
|
|
25587
|
-
*
|
|
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);
|
|
25588
25723
|
* setOscillating→`setSwingVertical(FullSwing|Default)`.
|
|
25589
25724
|
*
|
|
25590
|
-
* TODO (deferred):
|
|
25591
|
-
*
|
|
25592
|
-
*
|
|
25593
|
-
*
|
|
25594
|
-
*
|
|
25595
|
-
*
|
|
25596
|
-
*
|
|
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).
|
|
25597
25733
|
*/
|
|
25598
25734
|
var GreeAcDevice = class extends BaseDevice$1 {
|
|
25599
25735
|
features = [];
|
|
@@ -25671,10 +25807,19 @@ var GreeAcDevice = class extends BaseDevice$1 {
|
|
|
25671
25807
|
await ac.setMode(libMode);
|
|
25672
25808
|
},
|
|
25673
25809
|
setFanMode: async ({ fanMode }) => {
|
|
25674
|
-
|
|
25810
|
+
const speed = fanModeToFanSpeed(fanMode);
|
|
25811
|
+
if (speed === null) throw new Error(`gree ac: unsupported fan mode "${fanMode}"`);
|
|
25812
|
+
await this.requireAc().setFanSpeed(speed);
|
|
25675
25813
|
},
|
|
25676
25814
|
setPreset: async ({ preset }) => {
|
|
25677
|
-
|
|
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);
|
|
25678
25823
|
},
|
|
25679
25824
|
setTarget: async ({ target }) => {
|
|
25680
25825
|
await this.requireAc().setTargetTemperature(target);
|
|
@@ -25708,13 +25853,21 @@ var GreeAcDevice = class extends BaseDevice$1 {
|
|
|
25708
25853
|
const ac = this.resolveAc();
|
|
25709
25854
|
if (ac === null) return;
|
|
25710
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
|
+
});
|
|
25711
25864
|
const climateSlice = {
|
|
25712
|
-
mode
|
|
25865
|
+
mode,
|
|
25713
25866
|
availableModes: [...ADVERTISED_CAP_MODES],
|
|
25714
|
-
fanMode:
|
|
25715
|
-
availableFanModes: [],
|
|
25716
|
-
preset
|
|
25717
|
-
availablePresets: [],
|
|
25867
|
+
fanMode: fanSpeedToFanMode(ac.fanSpeed),
|
|
25868
|
+
availableFanModes: [...GREE_FAN_MODES],
|
|
25869
|
+
preset,
|
|
25870
|
+
availablePresets: [...GREE_PRESETS],
|
|
25718
25871
|
target: ac.targetTemperature,
|
|
25719
25872
|
targetHigh: null,
|
|
25720
25873
|
targetLow: null,
|
package/dist/addon.mjs
CHANGED
|
@@ -10079,15 +10079,18 @@ var humiditySensorCapability = {
|
|
|
10079
10079
|
runtimeState: HumiditySensorStatusSchema
|
|
10080
10080
|
};
|
|
10081
10081
|
/**
|
|
10082
|
-
* Image display cap. Models
|
|
10083
|
-
*
|
|
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.
|
|
10084
10084
|
*
|
|
10085
|
-
* Read-only: there are no setters. The provider resolves
|
|
10086
|
-
*
|
|
10087
|
-
*
|
|
10088
|
-
*
|
|
10089
|
-
*
|
|
10090
|
-
*
|
|
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.
|
|
10091
10094
|
*/
|
|
10092
10095
|
var ImageStatusSchema = object({
|
|
10093
10096
|
/** Absolute signed URL the browser loads directly. Null when the
|
|
@@ -10113,18 +10116,47 @@ var imageCapability = {
|
|
|
10113
10116
|
*/
|
|
10114
10117
|
runtimeState: ImageStatusSchema
|
|
10115
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
|
+
]);
|
|
10116
10143
|
var LawnMowerControlStatusSchema = object({
|
|
10117
10144
|
/** Lifecycle activity of the mower. */
|
|
10118
|
-
activity:
|
|
10119
|
-
"idle",
|
|
10120
|
-
"mowing",
|
|
10121
|
-
"paused",
|
|
10122
|
-
"docked",
|
|
10123
|
-
"error"
|
|
10124
|
-
]),
|
|
10145
|
+
activity: LawnMowerActivitySchema,
|
|
10125
10146
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
10126
10147
|
* reading. */
|
|
10127
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,
|
|
10128
10160
|
/** Ms epoch when the slice was last updated. */
|
|
10129
10161
|
lastChangedAt: number()
|
|
10130
10162
|
});
|
|
@@ -12350,6 +12382,7 @@ var VacuumStateSchema = _enum([
|
|
|
12350
12382
|
"paused",
|
|
12351
12383
|
"returning",
|
|
12352
12384
|
"docked",
|
|
12385
|
+
"drying",
|
|
12353
12386
|
"error"
|
|
12354
12387
|
]);
|
|
12355
12388
|
/**
|
|
@@ -12388,6 +12421,12 @@ var VacuumControlStatusSchema = object({
|
|
|
12388
12421
|
detergent: TankStatusSchema.nullable(),
|
|
12389
12422
|
/** Dust bin. Null when the hardware has no dust bin. */
|
|
12390
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(),
|
|
12391
12430
|
/** Ms epoch when the slice was last updated. */
|
|
12392
12431
|
lastChangedAt: number()
|
|
12393
12432
|
});
|
|
@@ -16903,6 +16942,9 @@ method(object({
|
|
|
16903
16942
|
}), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
|
|
16904
16943
|
kind: "mutation",
|
|
16905
16944
|
auth: "admin"
|
|
16945
|
+
}), method(ResyncInputSchema, ResyncResultSchema, {
|
|
16946
|
+
kind: "mutation",
|
|
16947
|
+
auth: "admin"
|
|
16906
16948
|
}), method(object({
|
|
16907
16949
|
deviceId: number(),
|
|
16908
16950
|
key: string(),
|
|
@@ -20665,6 +20707,12 @@ Object.freeze({
|
|
|
20665
20707
|
addonId: null,
|
|
20666
20708
|
access: "create"
|
|
20667
20709
|
},
|
|
20710
|
+
"deviceManager.adoptionResync": {
|
|
20711
|
+
capName: "device-manager",
|
|
20712
|
+
capScope: "system",
|
|
20713
|
+
addonId: null,
|
|
20714
|
+
access: "create"
|
|
20715
|
+
},
|
|
20668
20716
|
"deviceManager.allocateDeviceId": {
|
|
20669
20717
|
capName: "device-manager",
|
|
20670
20718
|
capScope: "system",
|
|
@@ -25536,6 +25584,90 @@ function swingToOscillating(vertical, horizontal) {
|
|
|
25536
25584
|
function oscillatingToVerticalSwing(oscillating) {
|
|
25537
25585
|
return oscillating ? VerticalSwing.FullSwing : VerticalSwing.Default;
|
|
25538
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
|
+
}
|
|
25539
25671
|
//#endregion
|
|
25540
25672
|
//#region src/devices/gree-ac-device.ts
|
|
25541
25673
|
var CLIMATE_CAP = "climate-control";
|
|
@@ -25544,9 +25676,9 @@ var CLIMATE_COLD_START = {
|
|
|
25544
25676
|
mode: "off",
|
|
25545
25677
|
availableModes: [...ADVERTISED_CAP_MODES],
|
|
25546
25678
|
fanMode: "",
|
|
25547
|
-
availableFanModes: [],
|
|
25679
|
+
availableFanModes: [...GREE_FAN_MODES],
|
|
25548
25680
|
preset: "",
|
|
25549
|
-
availablePresets: [],
|
|
25681
|
+
availablePresets: [...GREE_PRESETS],
|
|
25550
25682
|
target: null,
|
|
25551
25683
|
targetHigh: null,
|
|
25552
25684
|
targetLow: null,
|
|
@@ -25582,17 +25714,21 @@ var greeAcSchema = object({
|
|
|
25582
25714
|
* `stateChanged` push (and seeds on activate).
|
|
25583
25715
|
*
|
|
25584
25716
|
* Climate commands route to the bound handle: setMode `off`→`setPower(false)`,
|
|
25585
|
-
* any other→`setPower(true)`+`setMode()`; setTarget→`setTargetTemperature()
|
|
25586
|
-
*
|
|
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);
|
|
25587
25722
|
* setOscillating→`setSwingVertical(FullSwing|Default)`.
|
|
25588
25723
|
*
|
|
25589
|
-
* TODO (deferred):
|
|
25590
|
-
*
|
|
25591
|
-
*
|
|
25592
|
-
*
|
|
25593
|
-
*
|
|
25594
|
-
*
|
|
25595
|
-
*
|
|
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).
|
|
25596
25732
|
*/
|
|
25597
25733
|
var GreeAcDevice = class extends BaseDevice$1 {
|
|
25598
25734
|
features = [];
|
|
@@ -25670,10 +25806,19 @@ var GreeAcDevice = class extends BaseDevice$1 {
|
|
|
25670
25806
|
await ac.setMode(libMode);
|
|
25671
25807
|
},
|
|
25672
25808
|
setFanMode: async ({ fanMode }) => {
|
|
25673
|
-
|
|
25809
|
+
const speed = fanModeToFanSpeed(fanMode);
|
|
25810
|
+
if (speed === null) throw new Error(`gree ac: unsupported fan mode "${fanMode}"`);
|
|
25811
|
+
await this.requireAc().setFanSpeed(speed);
|
|
25674
25812
|
},
|
|
25675
25813
|
setPreset: async ({ preset }) => {
|
|
25676
|
-
|
|
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);
|
|
25677
25822
|
},
|
|
25678
25823
|
setTarget: async ({ target }) => {
|
|
25679
25824
|
await this.requireAc().setTargetTemperature(target);
|
|
@@ -25707,13 +25852,21 @@ var GreeAcDevice = class extends BaseDevice$1 {
|
|
|
25707
25852
|
const ac = this.resolveAc();
|
|
25708
25853
|
if (ac === null) return;
|
|
25709
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
|
+
});
|
|
25710
25863
|
const climateSlice = {
|
|
25711
|
-
mode
|
|
25864
|
+
mode,
|
|
25712
25865
|
availableModes: [...ADVERTISED_CAP_MODES],
|
|
25713
|
-
fanMode:
|
|
25714
|
-
availableFanModes: [],
|
|
25715
|
-
preset
|
|
25716
|
-
availablePresets: [],
|
|
25866
|
+
fanMode: fanSpeedToFanMode(ac.fanSpeed),
|
|
25867
|
+
availableFanModes: [...GREE_FAN_MODES],
|
|
25868
|
+
preset,
|
|
25869
|
+
availablePresets: [...GREE_PRESETS],
|
|
25717
25870
|
target: ac.targetTemperature,
|
|
25718
25871
|
targetHigh: null,
|
|
25719
25872
|
targetLow: null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-gree",
|
|
3
|
-
"version": "0.1.
|
|
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": "
|
|
47
|
+
"placement": "hub-only"
|
|
48
48
|
},
|
|
49
49
|
"capabilities": [
|
|
50
50
|
{
|