homebridge-winix-purifiers 2.0.1 → 2.0.4
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/README.md +6 -2
- package/config.schema.json +8 -1
- package/dist/accessory.js +84 -31
- package/dist/device.js +10 -10
- package/dist/homebridge-ui/public/index-ta8s453r.js +1 -0
- package/dist/homebridge-ui/public/index.html +1 -1
- package/package.json +1 -1
- package/dist/homebridge-ui/public/index-S5C9rjtx.js +0 -1
package/README.md
CHANGED
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
## Features
|
|
25
25
|
|
|
26
26
|
- **Dynamic Device Discovery**: Automatically discovers and configures Winix purifiers linked to your account.
|
|
27
|
-
- **Control**: Power on/off, switch between auto/manual modes, and adjust airflow speed.
|
|
28
|
-
- **Customization**: Optionally expose Air Quality, Ambient Light, Plasmawave, and
|
|
27
|
+
- **Control**: Power on/off, switch between sleep/auto/manual modes, and adjust airflow speed.
|
|
28
|
+
- **Customization**: Optionally expose Air Quality, Ambient Light, Plasmawave, Auto Mode, and Sleep Mode switches to
|
|
29
|
+
HomeKit.
|
|
29
30
|
- **Filter Management**: Exposes the remaining filter life and provides an alert when it's time to change the filter,
|
|
30
31
|
configurable to trigger at a specified percentage of remaining filter life.
|
|
31
32
|
- **Efficiency**: Features Winix API response caching to minimize requests and avoid rate limiting.
|
|
@@ -75,6 +76,7 @@ While not recommended, if manual setup is required, add the following to the `pl
|
|
|
75
76
|
"exposeAmbientLight": true,
|
|
76
77
|
"exposePlasmawave": false,
|
|
77
78
|
"exposeAutoSwitch": false,
|
|
79
|
+
"exposeSleepSwitch": false,
|
|
78
80
|
"filterReplacementIndicatorPercentage": 10,
|
|
79
81
|
"cacheIntervalSeconds": 300,
|
|
80
82
|
"deviceRefreshIntervalMinutes": 60,
|
|
@@ -111,6 +113,7 @@ While not recommended, if manual setup is required, add the following to the `pl
|
|
|
111
113
|
| `exposeAmbientLight` | `false` | Whether to expose the ambient light sensors to HomeKit. |
|
|
112
114
|
| `exposePlasmawave` | `false` | Whether to expose switches for Plasmawave on/off. |
|
|
113
115
|
| `exposeAutoSwitch` | `false` | Whether to expose switches for Auto mode on/off. |
|
|
116
|
+
| `exposeSleepSwitch` | `false` | Whether to expose switches for Sleep mode on/off. |
|
|
114
117
|
| `filterReplacementIndicatorPercentage` | `10` | Percentage of filter life remaining to trigger a filter replacement alert. |
|
|
115
118
|
| `cacheIntervalSeconds` | `60` | Time, in seconds, for how long to reuse cached responses from Winix. |
|
|
116
119
|
| `deviceRefreshIntervalMinutes` | `60` | Time, in minutes, for how often to poll Winix to refresh the device list. |
|
|
@@ -124,6 +127,7 @@ While not recommended, if manual setup is required, add the following to the `pl
|
|
|
124
127
|
| `deviceOverrides[].nameAmbientLightSensor` | `"Ambient Light"` | The display name of the ambient light sensor. Optional. |
|
|
125
128
|
| `deviceOverrides[].namePlasmawaveSwitch` | `"Plasmawave"` | The display name of the Plasmawave switch. Optional. |
|
|
126
129
|
| `deviceOverrides[].nameAutoSwitch` | `"Auto Mode"` | The display name of the Auto switch. Optional. |
|
|
130
|
+
| `deviceOverrides[].nameSleepSwitch` | `"Sleep"` | The display name of the Sleep switch. Optional. |
|
|
127
131
|
| `platform` | `"WinixPurifiers"` | Must always be `"WinixPurifiers"` in order for the plugin to load this config. |
|
|
128
132
|
|
|
129
133
|
## FAQ
|
package/config.schema.json
CHANGED
|
@@ -27,7 +27,13 @@
|
|
|
27
27
|
},
|
|
28
28
|
"exposeAutoSwitch": {
|
|
29
29
|
"title": "Auto Switch",
|
|
30
|
-
"description": "Expose the
|
|
30
|
+
"description": "Expose the Auto on/off switch",
|
|
31
|
+
"type": "boolean",
|
|
32
|
+
"default": false
|
|
33
|
+
},
|
|
34
|
+
"exposeSleepSwitch": {
|
|
35
|
+
"title": "Sleep Switch",
|
|
36
|
+
"description": "Expose the Sleep on/off switch",
|
|
31
37
|
"type": "boolean",
|
|
32
38
|
"default": false
|
|
33
39
|
},
|
|
@@ -158,6 +164,7 @@
|
|
|
158
164
|
"exposeAmbientLight",
|
|
159
165
|
"exposePlasmawave",
|
|
160
166
|
"exposeAutoSwitch",
|
|
167
|
+
"exposeSleepSwitch",
|
|
161
168
|
"filterReplacementIndicatorPercentage",
|
|
162
169
|
"cacheIntervalSeconds",
|
|
163
170
|
"deviceRefreshIntervalMinutes"
|
package/dist/accessory.js
CHANGED
|
@@ -48,7 +48,7 @@ class WinixPurifierAccessory {
|
|
|
48
48
|
.onSet(this.setTargetState.bind(this));
|
|
49
49
|
characteristics.get(this.purifier, this.Characteristic.RotationSpeed)
|
|
50
50
|
.onGet(this.getRotationSpeed.bind(this))
|
|
51
|
-
.onSet(this.setRotationSpeed.bind(this));
|
|
51
|
+
.onSet(this.debounce(this.setRotationSpeed.bind(this), 500));
|
|
52
52
|
characteristics.get(this.purifier, this.Characteristic.FilterLifeLevel)
|
|
53
53
|
.onGet(this.getFilterLifeLevel.bind(this));
|
|
54
54
|
characteristics.get(this.purifier, this.Characteristic.FilterChangeIndication)
|
|
@@ -102,6 +102,16 @@ class WinixPurifierAccessory {
|
|
|
102
102
|
.onGet(this.getAutoSwitchState.bind(this))
|
|
103
103
|
.onSet(this.setAutoSwitchState.bind(this));
|
|
104
104
|
}
|
|
105
|
+
if (config.exposeSleepSwitch) {
|
|
106
|
+
this.sleepSwitch = accessory.getServiceById(this.ServiceType.Switch, 'switch-sleep') ||
|
|
107
|
+
accessory.addService(this.ServiceType.Switch, 'Sleep', 'switch-sleep');
|
|
108
|
+
this.servicesInUse.add(this.sleepSwitch);
|
|
109
|
+
characteristics.set(this.sleepSwitch, this.Characteristic.Name, 'Sleep');
|
|
110
|
+
characteristics.set(this.sleepSwitch, this.Characteristic.ConfiguredName, 'Sleep');
|
|
111
|
+
characteristics.get(this.sleepSwitch, this.Characteristic.On)
|
|
112
|
+
.onGet(this.getSleepSwitchState.bind(this))
|
|
113
|
+
.onSet(this.setSleepSwitchState.bind(this));
|
|
114
|
+
}
|
|
105
115
|
this.pruneUnusedServices();
|
|
106
116
|
}
|
|
107
117
|
/**
|
|
@@ -124,7 +134,7 @@ class WinixPurifierAccessory {
|
|
|
124
134
|
*/
|
|
125
135
|
async getActiveState() {
|
|
126
136
|
const power = await this.device.getPower();
|
|
127
|
-
this.log.debug('getActiveState()', power);
|
|
137
|
+
this.log.debug('accessory:getActiveState()', power);
|
|
128
138
|
return this.toActiveState(power);
|
|
129
139
|
}
|
|
130
140
|
/**
|
|
@@ -133,7 +143,7 @@ class WinixPurifierAccessory {
|
|
|
133
143
|
*/
|
|
134
144
|
async setActiveState(state) {
|
|
135
145
|
const power = state === this.Characteristic.Active.ACTIVE ? winix_api_1.Power.On : winix_api_1.Power.Off;
|
|
136
|
-
this.log.debug(`setActiveState(${state})`, power);
|
|
146
|
+
this.log.debug(`accessory:setActiveState(${state})`, power);
|
|
137
147
|
await this.device.setPower(power);
|
|
138
148
|
await this.sendHomekitUpdate();
|
|
139
149
|
}
|
|
@@ -143,7 +153,7 @@ class WinixPurifierAccessory {
|
|
|
143
153
|
*/
|
|
144
154
|
async getCurrentState() {
|
|
145
155
|
const power = await this.device.getPower();
|
|
146
|
-
this.log.debug('getCurrentState()', power);
|
|
156
|
+
this.log.debug('accessory:getCurrentState()', power);
|
|
147
157
|
return this.toCurrentState(power);
|
|
148
158
|
}
|
|
149
159
|
/**
|
|
@@ -151,7 +161,7 @@ class WinixPurifierAccessory {
|
|
|
151
161
|
*/
|
|
152
162
|
async getTargetState() {
|
|
153
163
|
const mode = await this.device.getMode();
|
|
154
|
-
this.log.debug('getTargetState()', mode);
|
|
164
|
+
this.log.debug('accessory:getTargetState()', mode);
|
|
155
165
|
return this.toTargetState(mode);
|
|
156
166
|
}
|
|
157
167
|
/**
|
|
@@ -159,25 +169,21 @@ class WinixPurifierAccessory {
|
|
|
159
169
|
*/
|
|
160
170
|
async setTargetState(state) {
|
|
161
171
|
const newMode = state === this.Characteristic.TargetAirPurifierState.AUTO ? winix_api_1.Mode.Auto : winix_api_1.Mode.Manual;
|
|
162
|
-
this.log.debug(`setTargetState(${state})`, newMode);
|
|
172
|
+
this.log.debug(`accessory:setTargetState(${state})`, newMode);
|
|
163
173
|
await this.device.setMode(newMode);
|
|
164
174
|
if (newMode === winix_api_1.Mode.Manual) {
|
|
165
175
|
return;
|
|
166
176
|
}
|
|
167
177
|
// If we're switching back to auto, the airflow speed will most likely change on the Winix device itself.
|
|
168
178
|
// Pause, get the latest airflow speed, then send the update to Homekit
|
|
169
|
-
this.
|
|
170
|
-
setTimeout(async () => {
|
|
171
|
-
await this.device.update();
|
|
172
|
-
await this.sendHomekitUpdate();
|
|
173
|
-
}, 2000);
|
|
179
|
+
this.scheduleHomekitUpdate();
|
|
174
180
|
}
|
|
175
181
|
/**
|
|
176
182
|
* Get the rotation speed of the purifier.
|
|
177
183
|
*/
|
|
178
184
|
async getRotationSpeed() {
|
|
179
185
|
const airflow = await this.device.getAirflow();
|
|
180
|
-
this.log.debug('getRotationSpeed()', airflow);
|
|
186
|
+
this.log.debug('accessory:getRotationSpeed()', airflow);
|
|
181
187
|
return this.toRotationSpeed(airflow);
|
|
182
188
|
}
|
|
183
189
|
/**
|
|
@@ -185,7 +191,11 @@ class WinixPurifierAccessory {
|
|
|
185
191
|
*/
|
|
186
192
|
async setRotationSpeed(state) {
|
|
187
193
|
const airflow = this.toAirflow(state);
|
|
188
|
-
this.log.debug(`setRotationSpeed(${state}):`, airflow);
|
|
194
|
+
this.log.debug(`accessory:setRotationSpeed(${state}):`, airflow);
|
|
195
|
+
// Don't set the airflow if it's null. this means state was 0 - this is a power-off signal
|
|
196
|
+
if (!airflow) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
189
199
|
await this.device.setAirflow(airflow);
|
|
190
200
|
await this.sendHomekitUpdate();
|
|
191
201
|
}
|
|
@@ -194,7 +204,7 @@ class WinixPurifierAccessory {
|
|
|
194
204
|
*/
|
|
195
205
|
async getAirQuality() {
|
|
196
206
|
const airQuality = await this.device.getAirQuality();
|
|
197
|
-
this.log.debug('getAirQuality():', airQuality);
|
|
207
|
+
this.log.debug('accessory:getAirQuality():', airQuality);
|
|
198
208
|
return this.toAirQuality(airQuality);
|
|
199
209
|
}
|
|
200
210
|
/**
|
|
@@ -202,7 +212,7 @@ class WinixPurifierAccessory {
|
|
|
202
212
|
*/
|
|
203
213
|
async getPlasmawave() {
|
|
204
214
|
const plasmawave = await this.device.getPlasmawave();
|
|
205
|
-
this.log.debug('getPlasmawave():', plasmawave);
|
|
215
|
+
this.log.debug('accessory:getPlasmawave():', plasmawave);
|
|
206
216
|
return this.toSwitch(plasmawave);
|
|
207
217
|
}
|
|
208
218
|
/**
|
|
@@ -210,7 +220,7 @@ class WinixPurifierAccessory {
|
|
|
210
220
|
*/
|
|
211
221
|
async setPlasmawave(state) {
|
|
212
222
|
const plasmawave = this.toPlasmawave(state);
|
|
213
|
-
this.log.debug(`setPlasmawave(${state}):`, plasmawave);
|
|
223
|
+
this.log.debug(`accessory:setPlasmawave(${state}):`, plasmawave);
|
|
214
224
|
await this.device.setPlasmawave(plasmawave);
|
|
215
225
|
await this.sendHomekitUpdate();
|
|
216
226
|
}
|
|
@@ -220,8 +230,8 @@ class WinixPurifierAccessory {
|
|
|
220
230
|
async getAmbientLight() {
|
|
221
231
|
const ambientLight = await this.device.getAmbientLight();
|
|
222
232
|
// Fix ambient light value under 0.0001 warning
|
|
223
|
-
const fixedAmbientLight =
|
|
224
|
-
this.log.debug('getAmbientLight():', 'measured:', ambientLight, 'fixed:', fixedAmbientLight);
|
|
233
|
+
const fixedAmbientLight = this.toAmbientLight(ambientLight);
|
|
234
|
+
this.log.debug('accessory:getAmbientLight():', 'measured:', ambientLight, 'fixed:', fixedAmbientLight);
|
|
225
235
|
return fixedAmbientLight;
|
|
226
236
|
}
|
|
227
237
|
/**
|
|
@@ -230,9 +240,8 @@ class WinixPurifierAccessory {
|
|
|
230
240
|
async getAutoSwitchState() {
|
|
231
241
|
const targetState = await this.getTargetState();
|
|
232
242
|
// Translate target state (auto/manual mode) to auto switch state
|
|
233
|
-
const result = targetState === this.Characteristic.TargetAirPurifierState.AUTO
|
|
234
|
-
|
|
235
|
-
this.log.debug('getAutoSwitchState()', 'target', targetState, 'result', result);
|
|
243
|
+
const result = targetState === this.Characteristic.TargetAirPurifierState.AUTO;
|
|
244
|
+
this.log.debug('accessory:getAutoSwitchState()', 'target', targetState, 'result', result);
|
|
236
245
|
return result;
|
|
237
246
|
}
|
|
238
247
|
/**
|
|
@@ -243,8 +252,26 @@ class WinixPurifierAccessory {
|
|
|
243
252
|
const proxyState = state ?
|
|
244
253
|
this.Characteristic.TargetAirPurifierState.AUTO :
|
|
245
254
|
this.Characteristic.TargetAirPurifierState.MANUAL;
|
|
246
|
-
this.log.debug(`setAutoSwitchState(${state})`, proxyState);
|
|
247
|
-
return this.setTargetState(proxyState);
|
|
255
|
+
this.log.debug(`accessory:setAutoSwitchState(${state})`, proxyState);
|
|
256
|
+
return await this.setTargetState(proxyState);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Get the sleep switch state of the purifier.
|
|
260
|
+
*/
|
|
261
|
+
async getSleepSwitchState() {
|
|
262
|
+
const airflow = await this.device.getAirflow();
|
|
263
|
+
const isInSleep = airflow === winix_api_1.Airflow.Sleep;
|
|
264
|
+
this.log.debug('accessory:getSleepSwitchState()', isInSleep);
|
|
265
|
+
return isInSleep;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Set the sleep switch state of the purifier.
|
|
269
|
+
*/
|
|
270
|
+
async setSleepSwitchState(state) {
|
|
271
|
+
const airflow = state ? winix_api_1.Airflow.Sleep : winix_api_1.Airflow.Low;
|
|
272
|
+
this.log.debug(`accessory:setSleepSwitchState(${state})`, airflow);
|
|
273
|
+
await this.device.setAirflow(airflow);
|
|
274
|
+
this.scheduleHomekitUpdate();
|
|
248
275
|
}
|
|
249
276
|
/**
|
|
250
277
|
* Get the filter life level of the purifier.
|
|
@@ -252,12 +279,12 @@ class WinixPurifierAccessory {
|
|
|
252
279
|
async getFilterLifeLevel() {
|
|
253
280
|
const currentFilterHours = await this.device.getFilterHours();
|
|
254
281
|
if (currentFilterHours <= 0) {
|
|
255
|
-
this.log.debug('getFilterLifeLevel(): currentFilterHours is not a positive number:', currentFilterHours);
|
|
282
|
+
this.log.debug('accessory:getFilterLifeLevel(): currentFilterHours is not a positive number:', currentFilterHours);
|
|
256
283
|
return 100;
|
|
257
284
|
}
|
|
258
285
|
const remainingLife = Math.max(MAX_FILTER_HOURS - currentFilterHours, 0);
|
|
259
286
|
const remainingPercentage = Math.round((remainingLife / MAX_FILTER_HOURS) * 100);
|
|
260
|
-
this.log.debug('getFilterLifeLevel()', remainingPercentage);
|
|
287
|
+
this.log.debug('accessory:getFilterLifeLevel()', remainingPercentage);
|
|
261
288
|
return remainingPercentage;
|
|
262
289
|
}
|
|
263
290
|
/**
|
|
@@ -269,16 +296,23 @@ class WinixPurifierAccessory {
|
|
|
269
296
|
const shouldReplaceFilter = filterLife <= replacementPercentage ?
|
|
270
297
|
this.Characteristic.FilterChangeIndication.CHANGE_FILTER :
|
|
271
298
|
this.Characteristic.FilterChangeIndication.FILTER_OK;
|
|
272
|
-
this.log.debug('getFilterChangeIndication() filterLife:', filterLife, 'replacementPercentage:', replacementPercentage, 'shouldReplaceFilter:', shouldReplaceFilter);
|
|
299
|
+
this.log.debug('accessory:getFilterChangeIndication() filterLife:', filterLife, 'replacementPercentage:', replacementPercentage, 'shouldReplaceFilter:', shouldReplaceFilter);
|
|
273
300
|
return shouldReplaceFilter;
|
|
274
301
|
}
|
|
302
|
+
scheduleHomekitUpdate() {
|
|
303
|
+
this.log.debug('scheduling homekit update');
|
|
304
|
+
setTimeout(async () => {
|
|
305
|
+
await this.device.update();
|
|
306
|
+
await this.sendHomekitUpdate();
|
|
307
|
+
}, 1000);
|
|
308
|
+
}
|
|
275
309
|
/**
|
|
276
310
|
* Send an update to Homekit with the latest device status.
|
|
277
311
|
*/
|
|
278
312
|
async sendHomekitUpdate() {
|
|
279
|
-
this.log.debug('sendHomekitUpdate()');
|
|
313
|
+
this.log.debug('accessory:sendHomekitUpdate()');
|
|
280
314
|
if (!this.device.hasData()) {
|
|
281
|
-
this.log.debug('sendHomekitUpdate(): skipping update, no status');
|
|
315
|
+
this.log.debug('accessory:sendHomekitUpdate(): skipping update, no status');
|
|
282
316
|
return;
|
|
283
317
|
}
|
|
284
318
|
const { power, mode, airflow, airQuality, plasmawave, ambientLight, } = await this.device.getState();
|
|
@@ -293,11 +327,14 @@ class WinixPurifierAccessory {
|
|
|
293
327
|
this.plasmawave?.updateCharacteristic(this.Characteristic.On, this.toSwitch(plasmawave));
|
|
294
328
|
}
|
|
295
329
|
if (this.ambientLight !== undefined) {
|
|
296
|
-
this.ambientLight?.updateCharacteristic(this.Characteristic.CurrentAmbientLightLevel, ambientLight);
|
|
330
|
+
this.ambientLight?.updateCharacteristic(this.Characteristic.CurrentAmbientLightLevel, this.toAmbientLight(ambientLight));
|
|
297
331
|
}
|
|
298
332
|
if (this.autoSwitch !== undefined) {
|
|
299
333
|
this.autoSwitch?.updateCharacteristic(this.Characteristic.On, this.toTargetState(mode) === this.Characteristic.TargetAirPurifierState.AUTO);
|
|
300
334
|
}
|
|
335
|
+
if (this.sleepSwitch !== undefined) {
|
|
336
|
+
this.sleepSwitch?.updateCharacteristic(this.Characteristic.On, airflow === winix_api_1.Airflow.Sleep);
|
|
337
|
+
}
|
|
301
338
|
}
|
|
302
339
|
toActiveState(power) {
|
|
303
340
|
switch (power) {
|
|
@@ -326,7 +363,7 @@ class WinixPurifierAccessory {
|
|
|
326
363
|
toRotationSpeed(airflow) {
|
|
327
364
|
switch (airflow) {
|
|
328
365
|
case winix_api_1.Airflow.Sleep:
|
|
329
|
-
return
|
|
366
|
+
return 1;
|
|
330
367
|
case winix_api_1.Airflow.Low:
|
|
331
368
|
return 25;
|
|
332
369
|
case winix_api_1.Airflow.Medium:
|
|
@@ -338,9 +375,13 @@ class WinixPurifierAccessory {
|
|
|
338
375
|
}
|
|
339
376
|
}
|
|
340
377
|
toAirflow(state) {
|
|
378
|
+
// Don't return any airflow if the state is explicitly 0, this is a power-off signal
|
|
379
|
+
if (state === 0) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
341
382
|
// Round to nearest 25
|
|
342
383
|
const nearestState = Math.round(state / 25) * 25;
|
|
343
|
-
this.log.debug(`toAirflow(${state}): ${nearestState}`);
|
|
384
|
+
this.log.debug(`accessory:toAirflow(${state}): ${nearestState}`);
|
|
344
385
|
switch (nearestState) {
|
|
345
386
|
case 0:
|
|
346
387
|
return winix_api_1.Airflow.Sleep;
|
|
@@ -373,5 +414,17 @@ class WinixPurifierAccessory {
|
|
|
373
414
|
toPlasmawave(state) {
|
|
374
415
|
return state ? winix_api_1.Plasmawave.On : winix_api_1.Plasmawave.Off;
|
|
375
416
|
}
|
|
417
|
+
toAmbientLight(ambientLight) {
|
|
418
|
+
return Math.max(ambientLight, MIN_AMBIENT_LIGHT);
|
|
419
|
+
}
|
|
420
|
+
debounce(func, delay) {
|
|
421
|
+
let timeoutId = null;
|
|
422
|
+
return async (arg) => {
|
|
423
|
+
if (timeoutId) {
|
|
424
|
+
clearTimeout(timeoutId);
|
|
425
|
+
}
|
|
426
|
+
timeoutId = setTimeout(async () => await func(arg), delay);
|
|
427
|
+
};
|
|
428
|
+
}
|
|
376
429
|
}
|
|
377
430
|
exports.WinixPurifierAccessory = WinixPurifierAccessory;
|
package/dist/device.js
CHANGED
|
@@ -57,10 +57,10 @@ class Device {
|
|
|
57
57
|
async setPower(value) {
|
|
58
58
|
const initialPower = await this.getPower();
|
|
59
59
|
if (initialPower === value) {
|
|
60
|
-
this.log.debug('setPower(%s)', value, '(no change)');
|
|
60
|
+
this.log.debug('device:setPower(%s)', value, '(no change)');
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
|
-
this.log.debug('setPower()', initialPower, value);
|
|
63
|
+
this.log.debug('device:setPower()', initialPower, value);
|
|
64
64
|
await winix_api_1.WinixAPI.setPower(this.deviceId, value);
|
|
65
65
|
this.state.power = value;
|
|
66
66
|
// default to auto mode when turning on from off
|
|
@@ -73,17 +73,17 @@ class Device {
|
|
|
73
73
|
// Don't try to set the mode if it's already set to the same value
|
|
74
74
|
// Fixes issues with this being set right around the time of power on
|
|
75
75
|
if (!turnedOn && value === await this.getMode()) {
|
|
76
|
-
this.log.debug('setMode(%s)', value, '(no change)');
|
|
76
|
+
this.log.debug('device:setMode(%s)', value, '(no change)');
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
|
-
this.log.debug('setMode(%s)', value);
|
|
79
|
+
this.log.debug('device:setMode(%s)', value);
|
|
80
80
|
await winix_api_1.WinixAPI.setMode(this.deviceId, value);
|
|
81
81
|
this.state.mode = value;
|
|
82
82
|
// default to low airflow when switching modes
|
|
83
83
|
this.state.airflow = winix_api_1.Airflow.Low;
|
|
84
84
|
}
|
|
85
85
|
async setAirflow(value) {
|
|
86
|
-
this.log.debug('setAirflow(%s)', value);
|
|
86
|
+
this.log.debug('device:setAirflow(%s)', value);
|
|
87
87
|
// Device must be on and in manual mode to set airflow
|
|
88
88
|
await this.ensureOn();
|
|
89
89
|
await this.setMode(winix_api_1.Mode.Manual);
|
|
@@ -91,7 +91,7 @@ class Device {
|
|
|
91
91
|
this.state.airflow = value;
|
|
92
92
|
}
|
|
93
93
|
async setPlasmawave(value) {
|
|
94
|
-
this.log.debug('setPlasmawave()', value);
|
|
94
|
+
this.log.debug('device:setPlasmawave()', value);
|
|
95
95
|
await this.ensureOn();
|
|
96
96
|
await winix_api_1.WinixAPI.setPlasmawave(this.deviceId, value);
|
|
97
97
|
this.state.plasmawave = value;
|
|
@@ -109,9 +109,9 @@ class Device {
|
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
111
|
async update() {
|
|
112
|
-
this.log.debug('update()');
|
|
112
|
+
this.log.debug('device:update()');
|
|
113
113
|
this.state = await winix_api_1.WinixAPI.getDeviceStatus(this.deviceId);
|
|
114
|
-
this.log.debug('update()', JSON.stringify(this.state));
|
|
114
|
+
this.log.debug('device:update()', JSON.stringify(this.state));
|
|
115
115
|
this.lastWinixPoll = Date.now();
|
|
116
116
|
}
|
|
117
117
|
async ensureUpdated() {
|
|
@@ -127,10 +127,10 @@ class Device {
|
|
|
127
127
|
}
|
|
128
128
|
async ensureOn() {
|
|
129
129
|
if (await this.getPower() === winix_api_1.Power.On) {
|
|
130
|
-
this.log.debug('ensureOn()', 'already on');
|
|
130
|
+
this.log.debug('device:ensureOn()', 'already on');
|
|
131
131
|
return false;
|
|
132
132
|
}
|
|
133
|
-
this.log.debug('ensureOn()');
|
|
133
|
+
this.log.debug('device:ensureOn()');
|
|
134
134
|
await this.setPower(winix_api_1.Power.On);
|
|
135
135
|
return true;
|
|
136
136
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))c(t);new MutationObserver(t=>{for(const i of t)if(i.type==="childList")for(const a of i.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&c(a)}).observe(document,{childList:!0,subtree:!0});function o(t){const i={};return t.integrity&&(i.integrity=t.integrity),t.referrerPolicy&&(i.referrerPolicy=t.referrerPolicy),t.crossOrigin==="use-credentials"?i.credentials="include":t.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function c(t){if(t.ep)return;t.ep=!0;const i=o(t);fetch(t.href,i)}})();const h={schema:{type:"object",properties:{email:{title:"Email",type:"string","x-schema-form":{type:"email"},required:!0},password:{title:"Password",type:"string","x-schema-form":{type:"password"},required:!0}}}},v=r=>({schema:{deviceOverrides:{type:"array",items:{type:"object",properties:{deviceId:{type:"string",title:"Device ID",required:!0},deviceAlias:{type:"string",title:"Device Alias",required:!0},modelName:{type:"string",title:"Model Name",required:!0},serialNumber:{type:"string",title:"Serial Number",description:"The serial number of the device",placeholder:"WNXAI00000000",required:!1},nameDevice:{type:"string",title:"Device Name",description:"The display name of the device",required:!1},nameAirQualitySensor:{type:"string",title:"Air Quality Sensor Name",description:"The display name of the air quality sensor",placeholder:"Air Quality",required:!1},nameAmbientLightSensor:{type:"string",title:"Ambient Light Sensor Name",description:"The display name of the ambient light sensor",placeholder:"Ambient Light",required:!1},namePlasmawaveSwitch:{type:"string",title:"Plasmawave Switch Name",description:"The display name of the Plasmawave switch",placeholder:"Plasmawave",required:!1},nameAutoSwitch:{type:"string",title:"Auto Switch Name",description:"The display name of the Auto switch",placeholder:"Auto Mode",required:!1},nameSleepSwitch:{type:"string",title:"Sleep Switch Name",description:"The display name of the Sleep switch",placeholder:"Sleep",required:!1}}}}},form:[{type:"tabarray",key:"deviceOverrides",legend:"{{value.deviceAlias}} ({{value.modelName}})",maxItems:r,removable:!1,items:["deviceOverrides[].serialNumber","deviceOverrides[].nameDevice","deviceOverrides[].nameAirQualitySensor","deviceOverrides[].nameAmbientLightSensor","deviceOverrides[].namePlasmawaveSwitch","deviceOverrides[].nameAutoSwitch","deviceOverrides[].nameSleepSwitch"]}]}),{homebridge:e}=window,d=document.getElementById("header-winix-buttons"),w=document.getElementById("btn-winix-new-token"),S=document.getElementById("btn-winix-device-overrides"),m=document.getElementById("header-winix-link-account"),u=document.getElementById("header-winix-device-overrides"),p=document.getElementById("txt-auth-issues");w.addEventListener("click",()=>y());S.addEventListener("click",()=>b());e.showSpinner();P();async function P(){var o;const[r]=await e.getPluginConfig();((o=r==null?void 0:r.auth)==null?void 0:o.refreshToken)?g():await y()}async function y(){e.hideSchemaForm(),d==null||d.style.setProperty("display","none"),m==null||m.style.setProperty("display","block");const r=await O();r&&(p==null||p.style.setProperty("display","block")),e.hideSpinner();const n=e.createForm(h,{},"Log In",r?"Back":void 0);n.onSubmit(async({email:o,password:c})=>{e.showSpinner();try{const t=await e.request("/login",{email:o,password:c});await N(t),e.toast.success("Linked with Winix account","Winix Purifiers"),g()}catch(t){const i=t;console.error("error logging in",i.message),e.toast.error("Login Failed: "+i.message,"Winix Purifiers")}finally{e.hideSpinner()}}),n.onCancel(()=>g())}async function b(){e.showSpinner();const r=await x();let n;try{n=await e.request("/discover",r)}catch(s){const l=s;console.error("error discovering devices",l.message),e.toast.error("Device Discovery Failed: "+l.message,"Winix Purifiers"),e.hideSpinner();return}const o=n.devices.map(s=>({deviceId:s.deviceId,deviceAlias:s.deviceAlias,modelName:s.modelName})).reduce((s,l)=>s.set(l.deviceId,l),new Map),[c]=await e.getPluginConfig();((c==null?void 0:c.deviceOverrides)??[]).forEach(s=>{const l=o.get(s.deviceId);l&&o.set(s.deviceId,{...l,...s})});const i=Array.from(o.values());d==null||d.style.setProperty("display","none"),u==null||u.style.setProperty("display","block"),e.hideSchemaForm(),e.hideSpinner();const a=e.createForm(v(i.length),{deviceOverrides:i},"Save All","Back");a.onSubmit(async({deviceOverrides:s})=>{s=A(s);const[l,...f]=await e.getPluginConfig();await e.updatePluginConfig([{...l,deviceOverrides:s},...f]),await e.savePluginConfig(),e.toast.success("Device configurations updated successfully","Winix Purifiers"),g()}),a.onCancel(()=>{e.toast.warning("Device configurations not updated","Winix Purifiers"),g()})}function A(r){return r.filter(n=>{const{deviceId:o,deviceAlias:c,modelName:t,...i}=n;return Object.values(i).some(a=>a!==void 0&&a!=="")}).map(n=>{const{deviceId:o,deviceAlias:c,modelName:t,...i}=n;return Object.keys(i).forEach(a=>{if(!(a in i))return;const s=i[a];(s===void 0||s==="")&&(i[a]=void 0)}),{deviceId:o,...i}})}function g(){d==null||d.style.setProperty("display","block"),m==null||m.style.setProperty("display","none"),p==null||p.style.setProperty("display","none"),u==null||u.style.setProperty("display","none"),e.showSchemaForm(),e.hideSpinner()}async function N(r){const[n,...o]=await e.getPluginConfig();await e.updatePluginConfig([{...n,auth:r},...o]),await e.savePluginConfig()}async function O(){var n;const[r]=await e.getPluginConfig();return!!((n=r==null?void 0:r.auth)!=null&&n.refreshToken)}async function x(){const[r]=await e.getPluginConfig();return r.auth}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script type="module" crossorigin src="./index-
|
|
1
|
+
<script type="module" crossorigin src="./index-ta8s453r.js"></script>
|
|
2
2
|
<div id="header-winix-buttons" class="container-fluid" style="display: none;">
|
|
3
3
|
<div class="row">
|
|
4
4
|
<!-- Left-aligned button -->
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))c(t);new MutationObserver(t=>{for(const i of t)if(i.type==="childList")for(const a of i.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&c(a)}).observe(document,{childList:!0,subtree:!0});function o(t){const i={};return t.integrity&&(i.integrity=t.integrity),t.referrerPolicy&&(i.referrerPolicy=t.referrerPolicy),t.crossOrigin==="use-credentials"?i.credentials="include":t.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function c(t){if(t.ep)return;t.ep=!0;const i=o(t);fetch(t.href,i)}})();const h={schema:{type:"object",properties:{email:{title:"Email",type:"string","x-schema-form":{type:"email"},required:!0},password:{title:"Password",type:"string","x-schema-form":{type:"password"},required:!0}}}},v=r=>({schema:{deviceOverrides:{type:"array",items:{type:"object",properties:{deviceId:{type:"string",title:"Device ID",required:!0},deviceAlias:{type:"string",title:"Device Alias",required:!0},modelName:{type:"string",title:"Model Name",required:!0},serialNumber:{type:"string",title:"Serial Number",description:"The serial number of the device",placeholder:"WNXAI00000000",required:!1},nameDevice:{type:"string",title:"Device Name",description:"The display name of the device",required:!1},nameAirQualitySensor:{type:"string",title:"Air Quality Sensor Name",description:"The display name of the air quality sensor",placeholder:"Air Quality",required:!1},nameAmbientLightSensor:{type:"string",title:"Ambient Light Sensor Name",description:"The display name of the ambient light sensor",placeholder:"Ambient Light",required:!1},namePlasmawaveSwitch:{type:"string",title:"Plasmawave Switch Name",description:"The display name of the Plasmawave switch",placeholder:"Plasmawave",required:!1},nameAutoSwitch:{type:"string",title:"Auto Switch Name",description:"The display name of the Auto switch",placeholder:"Auto Mode",required:!1}}}}},form:[{type:"tabarray",key:"deviceOverrides",legend:"{{value.deviceAlias}} ({{value.modelName}})",maxItems:r,removable:!1,items:["deviceOverrides[].serialNumber","deviceOverrides[].nameDevice","deviceOverrides[].nameAirQualitySensor","deviceOverrides[].nameAmbientLightSensor","deviceOverrides[].namePlasmawaveSwitch","deviceOverrides[].nameAutoSwitch"]}]}),{homebridge:e}=window,d=document.getElementById("header-winix-buttons"),w=document.getElementById("btn-winix-new-token"),P=document.getElementById("btn-winix-device-overrides"),u=document.getElementById("header-winix-link-account"),m=document.getElementById("header-winix-device-overrides"),p=document.getElementById("txt-auth-issues");w.addEventListener("click",()=>y());P.addEventListener("click",()=>S());e.showSpinner();b();async function b(){var o;const[r]=await e.getPluginConfig();((o=r==null?void 0:r.auth)==null?void 0:o.refreshToken)?g():await y()}async function y(){e.hideSchemaForm(),d==null||d.style.setProperty("display","none"),u==null||u.style.setProperty("display","block");const r=await x();r&&(p==null||p.style.setProperty("display","block")),e.hideSpinner();const n=e.createForm(h,{},"Log In",r?"Back":void 0);n.onSubmit(async({email:o,password:c})=>{e.showSpinner();try{const t=await e.request("/login",{email:o,password:c});await N(t),e.toast.success("Linked with Winix account","Winix Purifiers"),g()}catch(t){const i=t;console.error("error logging in",i.message),e.toast.error("Login Failed: "+i.message,"Winix Purifiers")}finally{e.hideSpinner()}}),n.onCancel(()=>g())}async function S(){e.showSpinner();const r=await O();let n;try{n=await e.request("/discover",r)}catch(s){const l=s;console.error("error discovering devices",l.message),e.toast.error("Device Discovery Failed: "+l.message,"Winix Purifiers"),e.hideSpinner();return}const o=n.devices.map(s=>({deviceId:s.deviceId,deviceAlias:s.deviceAlias,modelName:s.modelName})).reduce((s,l)=>s.set(l.deviceId,l),new Map),[c]=await e.getPluginConfig();((c==null?void 0:c.deviceOverrides)??[]).forEach(s=>{const l=o.get(s.deviceId);l&&o.set(s.deviceId,{...l,...s})});const i=Array.from(o.values());d==null||d.style.setProperty("display","none"),m==null||m.style.setProperty("display","block"),e.hideSchemaForm(),e.hideSpinner();const a=e.createForm(v(i.length),{deviceOverrides:i},"Save All","Back");a.onSubmit(async({deviceOverrides:s})=>{s=A(s);const[l,...f]=await e.getPluginConfig();await e.updatePluginConfig([{...l,deviceOverrides:s},...f]),await e.savePluginConfig(),e.toast.success("Device configurations updated successfully","Winix Purifiers"),g()}),a.onCancel(()=>{e.toast.warning("Device configurations not updated","Winix Purifiers"),g()})}function A(r){return r.filter(n=>{const{deviceId:o,deviceAlias:c,modelName:t,...i}=n;return Object.values(i).some(a=>a!==void 0&&a!=="")}).map(n=>{const{deviceId:o,deviceAlias:c,modelName:t,...i}=n;return Object.keys(i).forEach(a=>{if(!(a in i))return;const s=i[a];(s===void 0||s==="")&&(i[a]=void 0)}),{deviceId:o,...i}})}function g(){d==null||d.style.setProperty("display","block"),u==null||u.style.setProperty("display","none"),p==null||p.style.setProperty("display","none"),m==null||m.style.setProperty("display","none"),e.showSchemaForm(),e.hideSpinner()}async function N(r){const[n,...o]=await e.getPluginConfig();await e.updatePluginConfig([{...n,auth:r},...o]),await e.savePluginConfig()}async function x(){var n;const[r]=await e.getPluginConfig();return!!((n=r==null?void 0:r.auth)!=null&&n.refreshToken)}async function O(){const[r]=await e.getPluginConfig();return r.auth}
|