homebridge-winix-purifiers 2.2.4 → 2.2.5
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/device.js +10 -1
- package/dist/winix.js +13 -2
- package/package.json +3 -2
package/dist/device.js
CHANGED
|
@@ -168,9 +168,18 @@ class Device {
|
|
|
168
168
|
this.schedulePoll(this.pollIntervalMs);
|
|
169
169
|
return;
|
|
170
170
|
}
|
|
171
|
+
// Winix occasionally returns "no data" for a healthy device. Treat as
|
|
172
|
+
// transient: keep last-known state and don't count toward unreachable.
|
|
173
|
+
if (e instanceof winix_api_1.NoDataError) {
|
|
174
|
+
this.log.debug('device:poll() got NoDataError, retaining last-known state');
|
|
175
|
+
this.schedulePoll(this.pollIntervalMs);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
171
178
|
this.consecutiveFailures++;
|
|
172
179
|
const backoffMs = Math.min(this.pollIntervalMs * Math.pow(2, this.consecutiveFailures), MAX_BACKOFF_MS);
|
|
173
|
-
|
|
180
|
+
const err = e;
|
|
181
|
+
this.log.error(`device:poll() error: ${err.constructor?.name ?? 'Error'}: ${err.message} ` +
|
|
182
|
+
`(retry in ${Math.round(backoffMs / 1000)}s)`);
|
|
174
183
|
this.schedulePoll(backoffMs);
|
|
175
184
|
return;
|
|
176
185
|
}
|
package/dist/winix.js
CHANGED
|
@@ -10,6 +10,7 @@ const promises_1 = require("node:fs/promises");
|
|
|
10
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
11
11
|
const TOKEN_DIRECTORY_NAME = 'winix-purifiers';
|
|
12
12
|
const TOKEN_FILE_NAME = 'token.json';
|
|
13
|
+
const MIN_RELOGIN_INTERVAL_MS = 5 * 60 * 1000;
|
|
13
14
|
class NotConfiguredError extends Error {
|
|
14
15
|
constructor() {
|
|
15
16
|
super('Account not configured');
|
|
@@ -25,6 +26,7 @@ exports.UnauthenticatedError = UnauthenticatedError;
|
|
|
25
26
|
class WinixHandler {
|
|
26
27
|
constructor(storagePath, encryptionKey) {
|
|
27
28
|
this.encryptionKey = encryptionKey;
|
|
29
|
+
this.lastReloginAt = 0;
|
|
28
30
|
this.refreshTokenPath = node_path_1.default.join(storagePath, TOKEN_DIRECTORY_NAME, TOKEN_FILE_NAME);
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
@@ -110,10 +112,19 @@ class WinixHandler {
|
|
|
110
112
|
devices = await this.winix.getDevices();
|
|
111
113
|
}
|
|
112
114
|
catch (e) {
|
|
113
|
-
if (
|
|
115
|
+
if (e instanceof winix_api_1.RefreshTokenExpiredError) {
|
|
116
|
+
await this.login(this.auth.username, this.auth.password);
|
|
117
|
+
return await this.winix.getDevices();
|
|
118
|
+
}
|
|
119
|
+
if (!(e instanceof winix_api_1.MobileSessionInvalidError)) {
|
|
120
|
+
throw e;
|
|
121
|
+
}
|
|
122
|
+
// Mobile session invalidated (MULTI LOGIN / "user is not valid"). Re-login
|
|
123
|
+
// is throttled to prevent ping-pong with another active session.
|
|
124
|
+
if (Date.now() - this.lastReloginAt < MIN_RELOGIN_INTERVAL_MS) {
|
|
114
125
|
throw e;
|
|
115
126
|
}
|
|
116
|
-
|
|
127
|
+
this.lastReloginAt = Date.now();
|
|
117
128
|
await this.login(this.auth.username, this.auth.password);
|
|
118
129
|
return await this.winix.getDevices();
|
|
119
130
|
}
|
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.5",
|
|
5
5
|
"description": "Homebridge plugin for Winix air purifiers",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"test": "vitest run",
|
|
47
47
|
"test:integration": "vitest run --config vitest.integration.config.ts tests/integration/integration.test.ts --reporter=verbose",
|
|
48
48
|
"test:integration:rate-limit": "vitest run --config vitest.integration.config.ts tests/integration/integration-rate-limit.test.ts --reporter=verbose",
|
|
49
|
+
"test:integration:session-recovery": "node --env-file-if-exists=.env ./node_modules/vitest/vitest.mjs run --config vitest.integration.config.ts tests/integration/integration-session-recovery.test.ts --reporter=verbose",
|
|
49
50
|
"test:watch": "vitest",
|
|
50
51
|
"test:coverage": "vitest run --coverage",
|
|
51
52
|
"validate": "yarn lint && yarn build && yarn test",
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
],
|
|
66
67
|
"dependencies": {
|
|
67
68
|
"@homebridge/plugin-ui-utils": "1.0.3",
|
|
68
|
-
"winix-api": "2.0.
|
|
69
|
+
"winix-api": "2.0.1"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
72
|
"@types/node": "20.11.0",
|