homebridge-winix-purifiers 2.2.1 → 2.2.2
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/accessory.js +8 -3
- package/dist/device.js +17 -7
- package/dist/platform.js +6 -4
- package/package.json +2 -2
package/dist/accessory.js
CHANGED
|
@@ -14,7 +14,7 @@ const DEFAULT_POLL_INTERVAL_SECONDS = 30;
|
|
|
14
14
|
const MIN_POLL_INTERVAL_SECONDS = 15;
|
|
15
15
|
const MIN_AMBIENT_LIGHT = 0.0001;
|
|
16
16
|
class WinixPurifierAccessory {
|
|
17
|
-
constructor(platform, config, accessory, override, log) {
|
|
17
|
+
constructor(platform, config, accessory, override, log, deviceCount) {
|
|
18
18
|
this.platform = platform;
|
|
19
19
|
this.config = config;
|
|
20
20
|
this.accessory = accessory;
|
|
@@ -24,8 +24,13 @@ class WinixPurifierAccessory {
|
|
|
24
24
|
this.Characteristic = this.platform.Characteristic;
|
|
25
25
|
const { deviceId, deviceAlias } = accessory.context.device;
|
|
26
26
|
const configuredSeconds = config.pollIntervalSeconds ?? DEFAULT_POLL_INTERVAL_SECONDS;
|
|
27
|
-
const
|
|
28
|
-
|
|
27
|
+
const minForDeviceCount = deviceCount * 10;
|
|
28
|
+
const effectiveSeconds = Math.max(configuredSeconds, MIN_POLL_INTERVAL_SECONDS, minForDeviceCount);
|
|
29
|
+
if (effectiveSeconds > configuredSeconds) {
|
|
30
|
+
this.log.info(`Poll interval adjusted from ${configuredSeconds}s to ${effectiveSeconds}s to avoid Winix API rate limiting with ${deviceCount} device(s)`);
|
|
31
|
+
}
|
|
32
|
+
const pollIntervalMs = effectiveSeconds * 1000;
|
|
33
|
+
this.device = new device_1.Device(deviceId, pollIntervalMs, this.log, this.platform.client);
|
|
29
34
|
this.servicesInUse = new Set();
|
|
30
35
|
const deviceSerial = override?.serialNumber ?? 'WNXAI00000000';
|
|
31
36
|
const deviceName = override?.nameDevice ?? deviceAlias;
|
package/dist/device.js
CHANGED
|
@@ -6,10 +6,11 @@ const MAX_BACKOFF_MS = 5 * 60 * 1000;
|
|
|
6
6
|
const COMMAND_DELAY_MS = 1500;
|
|
7
7
|
const UNREACHABLE_THRESHOLD = 3;
|
|
8
8
|
class Device {
|
|
9
|
-
constructor(deviceId, pollIntervalMs, log) {
|
|
9
|
+
constructor(deviceId, pollIntervalMs, log, client) {
|
|
10
10
|
this.deviceId = deviceId;
|
|
11
11
|
this.pollIntervalMs = pollIntervalMs;
|
|
12
12
|
this.log = log;
|
|
13
|
+
this.client = client;
|
|
13
14
|
this.hasReceivedData = false;
|
|
14
15
|
this.pollTimer = null;
|
|
15
16
|
this.consecutiveFailures = 0;
|
|
@@ -33,13 +34,17 @@ class Device {
|
|
|
33
34
|
async initialFetch() {
|
|
34
35
|
try {
|
|
35
36
|
this.log.debug('device:initialFetch()');
|
|
36
|
-
const newState = await
|
|
37
|
+
const newState = await this.client.getDeviceStatus(this.deviceId);
|
|
37
38
|
Object.assign(this.state, newState);
|
|
38
39
|
this.hasReceivedData = true;
|
|
39
40
|
this.consecutiveFailures = 0;
|
|
40
41
|
this.log.debug('device:initialFetch()', JSON.stringify(this.state));
|
|
41
42
|
}
|
|
42
43
|
catch (e) {
|
|
44
|
+
if (e instanceof winix_api_1.RateLimitError) {
|
|
45
|
+
this.log.warn('device:initialFetch() rate limited, using defaults');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
43
48
|
this.log.warn('device:initialFetch() failed, using defaults:', e.message);
|
|
44
49
|
}
|
|
45
50
|
}
|
|
@@ -92,7 +97,7 @@ class Device {
|
|
|
92
97
|
return;
|
|
93
98
|
}
|
|
94
99
|
this.log.debug('device:setPower()', initialPower, value);
|
|
95
|
-
await
|
|
100
|
+
await this.client.setPower(this.deviceId, value);
|
|
96
101
|
this.state.power = value;
|
|
97
102
|
// Side effects observed from device testing
|
|
98
103
|
if (value === winix_api_1.Power.Off) {
|
|
@@ -110,7 +115,7 @@ class Device {
|
|
|
110
115
|
return;
|
|
111
116
|
}
|
|
112
117
|
this.log.debug('device:setMode(%s)', value);
|
|
113
|
-
await
|
|
118
|
+
await this.client.setMode(this.deviceId, value);
|
|
114
119
|
this.state.mode = value;
|
|
115
120
|
// Side effects observed from device testing
|
|
116
121
|
if (value === winix_api_1.Mode.Auto) {
|
|
@@ -127,7 +132,7 @@ class Device {
|
|
|
127
132
|
await this.setMode(winix_api_1.Mode.Manual);
|
|
128
133
|
await new Promise(r => setTimeout(r, COMMAND_DELAY_MS));
|
|
129
134
|
}
|
|
130
|
-
await
|
|
135
|
+
await this.client.setAirflow(this.deviceId, value);
|
|
131
136
|
this.state.airflow = value;
|
|
132
137
|
// Side effects observed from device testing
|
|
133
138
|
if (value === winix_api_1.Airflow.Sleep) {
|
|
@@ -137,7 +142,7 @@ class Device {
|
|
|
137
142
|
async setPlasmawave(value) {
|
|
138
143
|
this.log.debug('device:setPlasmawave()', value);
|
|
139
144
|
await this.ensureOn();
|
|
140
|
-
await
|
|
145
|
+
await this.client.setPlasmawave(this.deviceId, value);
|
|
141
146
|
this.state.plasmawave = value;
|
|
142
147
|
}
|
|
143
148
|
// Private methods
|
|
@@ -150,7 +155,7 @@ class Device {
|
|
|
150
155
|
async poll() {
|
|
151
156
|
try {
|
|
152
157
|
this.log.debug('device:poll()');
|
|
153
|
-
const newState = await
|
|
158
|
+
const newState = await this.client.getDeviceStatus(this.deviceId);
|
|
154
159
|
Object.assign(this.state, newState);
|
|
155
160
|
this.hasReceivedData = true;
|
|
156
161
|
this.consecutiveFailures = 0;
|
|
@@ -158,6 +163,11 @@ class Device {
|
|
|
158
163
|
this.onUpdate?.();
|
|
159
164
|
}
|
|
160
165
|
catch (e) {
|
|
166
|
+
if (e instanceof winix_api_1.RateLimitError) {
|
|
167
|
+
this.log.warn('device:poll() rate limited, retrying on next interval');
|
|
168
|
+
this.schedulePoll(this.pollIntervalMs);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
161
171
|
this.consecutiveFailures++;
|
|
162
172
|
const backoffMs = Math.min(this.pollIntervalMs * Math.pow(2, this.consecutiveFailures), MAX_BACKOFF_MS);
|
|
163
173
|
this.log.error(`device:poll() error: ${e.message} (retry in ${Math.round(backoffMs / 1000)}s)`);
|
package/dist/platform.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.WinixPurifierPlatform = void 0;
|
|
|
4
4
|
const winix_1 = require("./winix");
|
|
5
5
|
const settings_1 = require("./settings");
|
|
6
6
|
const accessory_1 = require("./accessory");
|
|
7
|
+
const winix_api_1 = require("winix-api");
|
|
7
8
|
const logger_1 = require("./logger");
|
|
8
9
|
const errors_1 = require("./errors");
|
|
9
10
|
const encryption_1 = require("./encryption");
|
|
@@ -21,6 +22,7 @@ class WinixPurifierPlatform {
|
|
|
21
22
|
this.handlers = new Map();
|
|
22
23
|
this.deviceOverrides = (this.config.deviceOverrides ?? [])
|
|
23
24
|
.reduce((m, o) => m.set(o.deviceId, o), new Map());
|
|
25
|
+
this.client = new winix_api_1.WinixClient();
|
|
24
26
|
this.winix = new winix_1.WinixHandler(api.user.storagePath(), settings_1.ENCRYPTION_KEY);
|
|
25
27
|
this.api.on("didFinishLaunching" /* APIEvent.DID_FINISH_LAUNCHING */, this.onFinishLaunching.bind(this));
|
|
26
28
|
}
|
|
@@ -81,14 +83,14 @@ class WinixPurifierPlatform {
|
|
|
81
83
|
this.log.debug('Found', accessory ? 'existing' : 'new', 'accessory:', this.logName(device));
|
|
82
84
|
if (accessory) {
|
|
83
85
|
accessory.context.device = device;
|
|
84
|
-
const handler = await this.createNewAccessoryHandler(accessory);
|
|
86
|
+
const handler = await this.createNewAccessoryHandler(accessory, devices.length);
|
|
85
87
|
this.handlers.set(uuid, handler);
|
|
86
88
|
this.api.updatePlatformAccessories([accessory]);
|
|
87
89
|
}
|
|
88
90
|
else {
|
|
89
91
|
accessory = new this.api.platformAccessory(device.deviceAlias, uuid, 19 /* Categories.AIR_PURIFIER */);
|
|
90
92
|
accessory.context.device = device;
|
|
91
|
-
const handler = await this.createNewAccessoryHandler(accessory);
|
|
93
|
+
const handler = await this.createNewAccessoryHandler(accessory, devices.length);
|
|
92
94
|
this.accessories.set(uuid, accessory);
|
|
93
95
|
this.handlers.set(uuid, handler);
|
|
94
96
|
accessoriesToAdd.push(accessory);
|
|
@@ -100,12 +102,12 @@ class WinixPurifierPlatform {
|
|
|
100
102
|
}
|
|
101
103
|
this.removeOldDevices(discoveredUUIDs);
|
|
102
104
|
}
|
|
103
|
-
async createNewAccessoryHandler(accessory) {
|
|
105
|
+
async createNewAccessoryHandler(accessory, deviceCount) {
|
|
104
106
|
// 🫣 suppress warning message about adding characteristics which aren't required / optional, since it isn't accurate
|
|
105
107
|
this.suppressCharacteristicWarnings(accessory);
|
|
106
108
|
const deviceOverride = this.deviceOverrides.get(accessory.context.device.deviceId);
|
|
107
109
|
const log = new logger_1.DeviceLogger(this.log, accessory.context.device);
|
|
108
|
-
const handler = new accessory_1.WinixPurifierAccessory(this, this.config, accessory, deviceOverride, log);
|
|
110
|
+
const handler = new accessory_1.WinixPurifierAccessory(this, this.config, accessory, deviceOverride, log, deviceCount);
|
|
109
111
|
this.unsuppressCharacteristicWarnings(accessory);
|
|
110
112
|
await handler.initialize();
|
|
111
113
|
return handler;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "Winix Air Purifiers",
|
|
3
3
|
"name": "homebridge-winix-purifiers",
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.2",
|
|
5
5
|
"description": "Homebridge plugin for Winix air purifiers",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
],
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@homebridge/plugin-ui-utils": "1.0.3",
|
|
66
|
-
"winix-api": "1.
|
|
66
|
+
"winix-api": "1.9.0"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@types/node": "20.11.0",
|