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 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 pollIntervalMs = Math.max(configuredSeconds, MIN_POLL_INTERVAL_SECONDS) * 1000;
28
- this.device = new device_1.Device(deviceId, pollIntervalMs, this.log);
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 winix_api_1.WinixAPI.getDeviceStatus(this.deviceId);
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 winix_api_1.WinixAPI.setPower(this.deviceId, value);
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 winix_api_1.WinixAPI.setMode(this.deviceId, value);
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 winix_api_1.WinixAPI.setAirflow(this.deviceId, value);
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 winix_api_1.WinixAPI.setPlasmawave(this.deviceId, value);
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 winix_api_1.WinixAPI.getDeviceStatus(this.deviceId);
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.1",
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.8.0"
66
+ "winix-api": "1.9.0"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@types/node": "20.11.0",