homebridge-blueair-plugin 1.1.0
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/.eslintignore +3 -0
- package/.gitattributes +3 -0
- package/CHANGELOG.md +12 -0
- package/LICENSE +176 -0
- package/README.md +99 -0
- package/branding/Blueair.png +0 -0
- package/branding/Homebridge_x_Blueair.svg +82 -0
- package/dist/accessory.d.ts +111 -0
- package/dist/accessory.d.ts.map +1 -0
- package/dist/accessory.js +821 -0
- package/dist/accessory.js.map +1 -0
- package/dist/api/BlueAirAwsApi.d.ts +79 -0
- package/dist/api/BlueAirAwsApi.d.ts.map +1 -0
- package/dist/api/BlueAirAwsApi.js +216 -0
- package/dist/api/BlueAirAwsApi.js.map +1 -0
- package/dist/api/Consts.d.ts +36 -0
- package/dist/api/Consts.d.ts.map +1 -0
- package/dist/api/Consts.js +52 -0
- package/dist/api/Consts.js.map +1 -0
- package/dist/api/GigyaApi.d.ts +19 -0
- package/dist/api/GigyaApi.d.ts.map +1 -0
- package/dist/api/GigyaApi.js +79 -0
- package/dist/api/GigyaApi.js.map +1 -0
- package/dist/constants.d.ts +40 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +146 -0
- package/dist/constants.js.map +1 -0
- package/dist/device.d.ts +49 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +136 -0
- package/dist/device.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +35 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +277 -0
- package/dist/platform.js.map +1 -0
- package/dist/utils.d.ts +96 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +98 -0
- package/dist/utils.js.map +1 -0
- package/package.json +54 -0
package/dist/platform.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.BlueAirPlatform = void 0;
|
|
40
|
+
const utils_1 = require("./utils");
|
|
41
|
+
const lodash_1 = require("lodash");
|
|
42
|
+
const BlueAirAwsApi_1 = __importStar(require("./api/BlueAirAwsApi"));
|
|
43
|
+
const accessory_1 = require("./accessory");
|
|
44
|
+
const events_1 = __importDefault(require("events"));
|
|
45
|
+
class BlueAirPlatform extends events_1.default {
|
|
46
|
+
constructor(log, config, api) {
|
|
47
|
+
super();
|
|
48
|
+
this.log = log;
|
|
49
|
+
this.config = config;
|
|
50
|
+
this.api = api;
|
|
51
|
+
// this is used to track restored cached accessories
|
|
52
|
+
this.accessories = [];
|
|
53
|
+
this.existingUuids = [];
|
|
54
|
+
this.devices = [];
|
|
55
|
+
this.polling = null;
|
|
56
|
+
this.retryCount = 0;
|
|
57
|
+
this.MAX_RETRY_COUNT = 5;
|
|
58
|
+
this.Service = api.hap.Service;
|
|
59
|
+
this.Characteristic = api.hap.Characteristic;
|
|
60
|
+
this.platformConfig = (0, lodash_1.defaultsDeep)(config, utils_1.defaultConfig);
|
|
61
|
+
this.log.debug("Finished initializing platform:", this.platformConfig.name);
|
|
62
|
+
if (!this.platformConfig.username ||
|
|
63
|
+
!this.platformConfig.password ||
|
|
64
|
+
!this.platformConfig.accountUuid) {
|
|
65
|
+
this.log.error("Missing required configuration options! Please do the device discovery in the configuration UI and/or check your\
|
|
66
|
+
config.json file");
|
|
67
|
+
}
|
|
68
|
+
this.blueAirApi = new BlueAirAwsApi_1.default(this.platformConfig.username, this.platformConfig.password, this.platformConfig.region, log);
|
|
69
|
+
this.api.on("didFinishLaunching", async () => {
|
|
70
|
+
await this.getInitialDeviceStates();
|
|
71
|
+
// Add a small delay before starting polling to reduce API pressure
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
this.getValidDevicesStatus();
|
|
74
|
+
}, 5000); // 5 second delay
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
configureAccessory(accessory) {
|
|
78
|
+
this.log.info("Loading accessory from cache:", accessory.displayName);
|
|
79
|
+
this.accessories.push(accessory);
|
|
80
|
+
}
|
|
81
|
+
async getValidDevicesStatus() {
|
|
82
|
+
this.log.debug("Updating devices states...");
|
|
83
|
+
try {
|
|
84
|
+
const devices = await this.blueAirApi.getDeviceStatus(this.platformConfig.accountUuid, this.existingUuids);
|
|
85
|
+
for (const device of devices) {
|
|
86
|
+
const blueAirDevice = this.devices.find((d) => d.id === device.id);
|
|
87
|
+
if (!blueAirDevice) {
|
|
88
|
+
this.log.error(`[${device.name}] Device not found in cache!`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
this.log.debug(`[${device.name}] Updating device state...`);
|
|
92
|
+
blueAirDevice.emit("update", device);
|
|
93
|
+
}
|
|
94
|
+
this.log.debug("Devices states updated!");
|
|
95
|
+
// Reset retry count on success
|
|
96
|
+
this.retryCount = 0;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const err = error;
|
|
100
|
+
let retryDelay = this.platformConfig.pollingInterval * 1000;
|
|
101
|
+
// Check if this is a rate limit error
|
|
102
|
+
if (err.message.includes("rate limit") ||
|
|
103
|
+
err.message.includes("too many calls")) {
|
|
104
|
+
this.retryCount++;
|
|
105
|
+
// Exponential backoff for rate limit: double the interval each time, up to 10 minutes
|
|
106
|
+
retryDelay = Math.min(this.platformConfig.pollingInterval *
|
|
107
|
+
1000 *
|
|
108
|
+
Math.pow(2, this.retryCount), 600000);
|
|
109
|
+
this.log.warn(`Rate limit exceeded (attempt ${this.retryCount}/${this.MAX_RETRY_COUNT}). ` +
|
|
110
|
+
`Backing off and retrying in ${Math.round(retryDelay / 1000)} seconds...`);
|
|
111
|
+
if (this.retryCount >= this.MAX_RETRY_COUNT) {
|
|
112
|
+
this.log.error(`Rate limit retry limit reached. Please increase the polling interval in your config ` +
|
|
113
|
+
`(current: ${this.platformConfig.pollingInterval}s, recommended: ${this.platformConfig.pollingInterval * 2}s or more)`);
|
|
114
|
+
// Reset counter but keep long delay
|
|
115
|
+
this.retryCount = 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
retryDelay = 5000; // 5 seconds for non-rate-limit errors
|
|
120
|
+
this.log.warn("Error getting valid devices status: " +
|
|
121
|
+
err.message +
|
|
122
|
+
`. Retrying in ${retryDelay / 1000} seconds...`);
|
|
123
|
+
this.log.debug("Error stack:", err.stack);
|
|
124
|
+
}
|
|
125
|
+
this.polling = setTimeout(this.getValidDevicesStatus.bind(this), retryDelay);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// Schedule next update with normal interval
|
|
129
|
+
this.polling = setTimeout(this.getValidDevicesStatus.bind(this), this.platformConfig.pollingInterval * 1000);
|
|
130
|
+
}
|
|
131
|
+
async getInitialDeviceStates(retryCount = 0, maxRetries = 5) {
|
|
132
|
+
this.log.info("Getting initial device states...");
|
|
133
|
+
try {
|
|
134
|
+
await this.blueAirApi.login();
|
|
135
|
+
let uuids = this.platformConfig.devices.map((device) => device.id);
|
|
136
|
+
const devices = await this.blueAirApi.getDeviceStatus(this.platformConfig.accountUuid, uuids);
|
|
137
|
+
// Log status at startup for visibility
|
|
138
|
+
this.log.info(`Found ${devices.length} device(s) in API response`);
|
|
139
|
+
for (const device of devices) {
|
|
140
|
+
const config = this.platformConfig.devices.find((c) => c.id === device.id);
|
|
141
|
+
const deviceType = config && this.isHumidifierDevice(config)
|
|
142
|
+
? "Humidifier"
|
|
143
|
+
: "Air Purifier";
|
|
144
|
+
this.log.info(`[${device.name}] Type: ${deviceType}, Model: ${(config === null || config === void 0 ? void 0 : config.model) || "Unknown"}`);
|
|
145
|
+
}
|
|
146
|
+
for (const device of devices) {
|
|
147
|
+
this.addDevice(device);
|
|
148
|
+
uuids = uuids.filter((uuid) => uuid !== device.id);
|
|
149
|
+
}
|
|
150
|
+
for (const uuid of uuids) {
|
|
151
|
+
const device = this.platformConfig.devices.find((device) => device.id === uuid);
|
|
152
|
+
this.log.warn(`[${device.name}] Device not found in AWS API response!`);
|
|
153
|
+
}
|
|
154
|
+
this.log.info("All configured devices have been added!");
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const err = error;
|
|
158
|
+
// Check if this is a rate limit error
|
|
159
|
+
if (err.message.includes("rate limit") ||
|
|
160
|
+
err.message.includes("too many calls")) {
|
|
161
|
+
if (retryCount < maxRetries) {
|
|
162
|
+
// Exponential backoff: 30s, 60s, 120s, 240s, 480s
|
|
163
|
+
const retryDelay = 30000 * Math.pow(2, retryCount);
|
|
164
|
+
this.log.warn(`Rate limit during initialization (attempt ${retryCount + 1}/${maxRetries}). ` +
|
|
165
|
+
`Retrying in ${retryDelay / 1000} seconds...`);
|
|
166
|
+
setTimeout(async () => {
|
|
167
|
+
await this.getInitialDeviceStates(retryCount + 1, maxRetries);
|
|
168
|
+
}, retryDelay);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
this.log.error(`Failed to initialize devices after ${maxRetries} attempts due to rate limiting. ` +
|
|
173
|
+
`Please wait a few minutes and restart Homebridge, or increase the polling interval.`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// For non-rate-limit errors, retry once after 10 seconds
|
|
178
|
+
if (retryCount === 0) {
|
|
179
|
+
this.log.error("Error getting initial device states:", err.message);
|
|
180
|
+
this.log.warn("Retrying in 10 seconds...");
|
|
181
|
+
setTimeout(async () => {
|
|
182
|
+
await this.getInitialDeviceStates(1, 1);
|
|
183
|
+
}, 10000);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
this.log.error("Failed to get initial device states after retry:", err.message);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async addDevice(device) {
|
|
191
|
+
const uuid = this.api.hap.uuid.generate(device.id);
|
|
192
|
+
const existingAccessory = this.accessories.find((accessory) => accessory.UUID === uuid);
|
|
193
|
+
const deviceConfig = this.platformConfig.devices.find((config) => config.id === device.id);
|
|
194
|
+
this.existingUuids.push(device.id);
|
|
195
|
+
if (!deviceConfig) {
|
|
196
|
+
this.log.error(`[${device.name}] Device configuration not found!`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const blueAirDevice = new accessory_1.BlueAirDevice(device);
|
|
200
|
+
this.devices.push(blueAirDevice);
|
|
201
|
+
blueAirDevice.on("setState", async ({ id, name, attribute, value }) => {
|
|
202
|
+
this.log.info(`[${name}] API → Setting ${attribute} = ${value}`);
|
|
203
|
+
// Clear polling to avoid conflicts
|
|
204
|
+
this.polling && clearTimeout(this.polling);
|
|
205
|
+
let success = false;
|
|
206
|
+
try {
|
|
207
|
+
await this.blueAirApi.setDeviceStatus(id, attribute, value);
|
|
208
|
+
this.log.info(`[${name}] API ✓ Successfully set ${attribute} = ${value}`);
|
|
209
|
+
success = true;
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
this.log.error(`[${name}] API ✗ Failed to set ${attribute} = ${value}`, error);
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
blueAirDevice.emit("setStateDone", success);
|
|
216
|
+
// Have to clear polling again to avoid conflicts
|
|
217
|
+
this.polling && clearTimeout(this.polling);
|
|
218
|
+
this.polling = setTimeout(this.getValidDevicesStatus.bind(this), this.platformConfig.pollingInterval * 1000);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
// Determine if this is a humidifier based on model name or device config
|
|
222
|
+
const isHumidifier = this.isHumidifierDevice(deviceConfig);
|
|
223
|
+
if (existingAccessory) {
|
|
224
|
+
this.log.info(`[${deviceConfig.name}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
|
|
225
|
+
new accessory_1.BlueAirAccessory(this, existingAccessory, blueAirDevice, deviceConfig, isHumidifier ? "humidifier" : "air-purifier");
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
this.log.info("Adding new accessory:", device.name);
|
|
229
|
+
const accessory = new this.api.platformAccessory(device.name, uuid);
|
|
230
|
+
new accessory_1.BlueAirAccessory(this, accessory, blueAirDevice, deviceConfig, isHumidifier ? "humidifier" : "air-purifier");
|
|
231
|
+
this.api.registerPlatformAccessories(utils_1.PLUGIN_NAME, utils_1.PLATFORM_NAME, [
|
|
232
|
+
accessory,
|
|
233
|
+
]);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get available devices from API for auto-discovery
|
|
238
|
+
* Used by the configuration UI to populate available devices
|
|
239
|
+
*/
|
|
240
|
+
async getAvailableDevices() {
|
|
241
|
+
try {
|
|
242
|
+
await this.blueAirApi.login();
|
|
243
|
+
const devices = await this.blueAirApi.getDevices();
|
|
244
|
+
return devices.map((d) => ({
|
|
245
|
+
uuid: d.uuid,
|
|
246
|
+
name: d.name,
|
|
247
|
+
type: d.type,
|
|
248
|
+
mac: d.mac,
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
this.log.error("Error getting available devices:", error);
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
isHumidifierDevice(deviceConfig) {
|
|
257
|
+
const deviceName = (deviceConfig.model ||
|
|
258
|
+
deviceConfig.name ||
|
|
259
|
+
"").toLowerCase();
|
|
260
|
+
const deviceType = (deviceConfig.type || "").toLowerCase();
|
|
261
|
+
// Check device type first (from API)
|
|
262
|
+
if (deviceType.includes(BlueAirAwsApi_1.DEVICE_TYPES.HUMIDIFIER) ||
|
|
263
|
+
deviceType.includes("humidity")) {
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
// Check model name for humidifier patterns
|
|
267
|
+
const humidifierPatterns = [
|
|
268
|
+
"humidifier",
|
|
269
|
+
"humidify",
|
|
270
|
+
"moisture",
|
|
271
|
+
"hygrostat",
|
|
272
|
+
];
|
|
273
|
+
return humidifierPatterns.some((pattern) => deviceName.includes(pattern));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
exports.BlueAirPlatform = BlueAirPlatform;
|
|
277
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { BlueAirDeviceState, BlueAirDeviceSensorData } from "./api/BlueAirAwsApi";
|
|
2
|
+
export type Config = {
|
|
3
|
+
name: string;
|
|
4
|
+
username: string;
|
|
5
|
+
password: string;
|
|
6
|
+
region: Region;
|
|
7
|
+
accountUuid: string;
|
|
8
|
+
verboseLogging: boolean;
|
|
9
|
+
uiDebug: boolean;
|
|
10
|
+
pollingInterval: number;
|
|
11
|
+
devices: DeviceConfig[];
|
|
12
|
+
discoveredDevices?: DiscoveredDevice[];
|
|
13
|
+
};
|
|
14
|
+
export type DiscoveredDevice = {
|
|
15
|
+
uuid: string;
|
|
16
|
+
name: string;
|
|
17
|
+
type: string;
|
|
18
|
+
mac: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Device capabilities detected dynamically from state keys
|
|
22
|
+
*/
|
|
23
|
+
export type DeviceCapabilities = {
|
|
24
|
+
hasBrightness: boolean;
|
|
25
|
+
hasNightLight: boolean;
|
|
26
|
+
hasNightMode: boolean;
|
|
27
|
+
hasAutoMode: boolean;
|
|
28
|
+
hasHumidity: boolean;
|
|
29
|
+
hasHumidityTarget: boolean;
|
|
30
|
+
hasWaterLevel: boolean;
|
|
31
|
+
hasTemperature: boolean;
|
|
32
|
+
hasAirQuality: boolean;
|
|
33
|
+
hasGermShield: boolean;
|
|
34
|
+
hasFilterUsage: boolean;
|
|
35
|
+
hasChildLock: boolean;
|
|
36
|
+
hasFanSpeed: boolean;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Detect device capabilities from available state keys and sensor data
|
|
40
|
+
*/
|
|
41
|
+
export declare function detectCapabilities(state: Partial<BlueAirDeviceState>, sensors: Partial<BlueAirDeviceSensorData>): DeviceCapabilities;
|
|
42
|
+
/**
|
|
43
|
+
* Format capabilities for logging
|
|
44
|
+
*/
|
|
45
|
+
export declare function formatCapabilities(capabilities: DeviceCapabilities): string;
|
|
46
|
+
export type DeviceConfig = {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
model: string;
|
|
50
|
+
serialNumber: string;
|
|
51
|
+
room?: string;
|
|
52
|
+
filterChangeLevel: number;
|
|
53
|
+
targetHumidity?: number;
|
|
54
|
+
targetHumidityAttribute?: string;
|
|
55
|
+
showFanTile?: boolean;
|
|
56
|
+
defaultTargetHumidity?: number;
|
|
57
|
+
led: boolean;
|
|
58
|
+
nightLight?: boolean;
|
|
59
|
+
airQualitySensor: boolean;
|
|
60
|
+
co2Sensor: boolean;
|
|
61
|
+
temperatureSensor: boolean;
|
|
62
|
+
humiditySensor: boolean;
|
|
63
|
+
germShield: boolean;
|
|
64
|
+
nightMode: boolean;
|
|
65
|
+
};
|
|
66
|
+
export declare enum Region {
|
|
67
|
+
EU = "Default (all other regions)",
|
|
68
|
+
AU = "Australia",
|
|
69
|
+
CN = "China",
|
|
70
|
+
RU = "Russia",
|
|
71
|
+
US = "USA"
|
|
72
|
+
}
|
|
73
|
+
export declare const defaultConfig: Config;
|
|
74
|
+
export declare const PLATFORM_NAME = "blueair-purifier";
|
|
75
|
+
export declare const PLUGIN_NAME = "homebridge-blueair-purifier";
|
|
76
|
+
/**
|
|
77
|
+
* Generic debounce utility for HomeKit slider/control interactions.
|
|
78
|
+
* Stores pending value and only executes action after delay with no new calls.
|
|
79
|
+
*/
|
|
80
|
+
export declare class Debouncer<T> {
|
|
81
|
+
private readonly delay;
|
|
82
|
+
private readonly action;
|
|
83
|
+
private timer?;
|
|
84
|
+
private pendingValue?;
|
|
85
|
+
constructor(delay: number, action: (value: T) => Promise<void>);
|
|
86
|
+
/**
|
|
87
|
+
* Schedule the action with the given value.
|
|
88
|
+
* Cancels any pending execution and reschedules.
|
|
89
|
+
*/
|
|
90
|
+
call(value: T): void;
|
|
91
|
+
/** Get the currently pending value (if any) */
|
|
92
|
+
get pending(): T | undefined;
|
|
93
|
+
/** Cancel any pending execution */
|
|
94
|
+
cancel(): void;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Combined utils from platformUtils.ts and settings.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Debouncer = exports.PLUGIN_NAME = exports.PLATFORM_NAME = exports.defaultConfig = exports.Region = void 0;
|
|
5
|
+
exports.detectCapabilities = detectCapabilities;
|
|
6
|
+
exports.formatCapabilities = formatCapabilities;
|
|
7
|
+
/**
|
|
8
|
+
* Detect device capabilities from available state keys and sensor data
|
|
9
|
+
*/
|
|
10
|
+
function detectCapabilities(state, sensors) {
|
|
11
|
+
return {
|
|
12
|
+
hasBrightness: "brightness" in state,
|
|
13
|
+
hasNightLight: "nlbrightness" in state,
|
|
14
|
+
hasNightMode: "nightmode" in state,
|
|
15
|
+
hasAutoMode: "automode" in state,
|
|
16
|
+
hasWaterLevel: "wlevel" in state,
|
|
17
|
+
hasGermShield: "germshield" in state,
|
|
18
|
+
hasFilterUsage: "filterusage" in state,
|
|
19
|
+
hasChildLock: "childlock" in state,
|
|
20
|
+
hasFanSpeed: "fanspeed" in state,
|
|
21
|
+
hasHumidityTarget: "autorh" in state ||
|
|
22
|
+
Object.keys(state).some((k) => /target.*hum|hum.*target|humidity.*set/i.test(k)),
|
|
23
|
+
hasTemperature: "temperature" in sensors && sensors.temperature !== undefined,
|
|
24
|
+
hasHumidity: "humidity" in sensors && sensors.humidity !== undefined,
|
|
25
|
+
hasAirQuality: "pm2_5" in sensors || "pm10" in sensors || "voc" in sensors,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Format capabilities for logging
|
|
30
|
+
*/
|
|
31
|
+
function formatCapabilities(capabilities) {
|
|
32
|
+
return (Object.entries(capabilities)
|
|
33
|
+
.filter(([, v]) => v)
|
|
34
|
+
.map(([k]) => k.replace("has", ""))
|
|
35
|
+
.join(", ") || "none");
|
|
36
|
+
}
|
|
37
|
+
var Region;
|
|
38
|
+
(function (Region) {
|
|
39
|
+
Region["EU"] = "Default (all other regions)";
|
|
40
|
+
Region["AU"] = "Australia";
|
|
41
|
+
Region["CN"] = "China";
|
|
42
|
+
Region["RU"] = "Russia";
|
|
43
|
+
Region["US"] = "USA";
|
|
44
|
+
})(Region || (exports.Region = Region = {}));
|
|
45
|
+
exports.defaultConfig = {
|
|
46
|
+
name: "BlueAir Platform",
|
|
47
|
+
uiDebug: false,
|
|
48
|
+
verboseLogging: true,
|
|
49
|
+
username: "",
|
|
50
|
+
password: "",
|
|
51
|
+
region: Region.EU,
|
|
52
|
+
accountUuid: "",
|
|
53
|
+
pollingInterval: 120,
|
|
54
|
+
devices: [],
|
|
55
|
+
};
|
|
56
|
+
exports.PLATFORM_NAME = "blueair-purifier";
|
|
57
|
+
exports.PLUGIN_NAME = "homebridge-blueair-purifier";
|
|
58
|
+
/**
|
|
59
|
+
* Generic debounce utility for HomeKit slider/control interactions.
|
|
60
|
+
* Stores pending value and only executes action after delay with no new calls.
|
|
61
|
+
*/
|
|
62
|
+
class Debouncer {
|
|
63
|
+
constructor(delay, action) {
|
|
64
|
+
this.delay = delay;
|
|
65
|
+
this.action = action;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Schedule the action with the given value.
|
|
69
|
+
* Cancels any pending execution and reschedules.
|
|
70
|
+
*/
|
|
71
|
+
call(value) {
|
|
72
|
+
this.pendingValue = value;
|
|
73
|
+
if (this.timer) {
|
|
74
|
+
clearTimeout(this.timer);
|
|
75
|
+
}
|
|
76
|
+
this.timer = setTimeout(async () => {
|
|
77
|
+
const valueToUse = this.pendingValue;
|
|
78
|
+
if (valueToUse !== undefined) {
|
|
79
|
+
this.pendingValue = undefined;
|
|
80
|
+
await this.action(valueToUse);
|
|
81
|
+
}
|
|
82
|
+
}, this.delay);
|
|
83
|
+
}
|
|
84
|
+
/** Get the currently pending value (if any) */
|
|
85
|
+
get pending() {
|
|
86
|
+
return this.pendingValue;
|
|
87
|
+
}
|
|
88
|
+
/** Cancel any pending execution */
|
|
89
|
+
cancel() {
|
|
90
|
+
if (this.timer) {
|
|
91
|
+
clearTimeout(this.timer);
|
|
92
|
+
this.timer = undefined;
|
|
93
|
+
}
|
|
94
|
+
this.pendingValue = undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.Debouncer = Debouncer;
|
|
98
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"displayName": "Homebridge BlueAir Plugin",
|
|
3
|
+
"name": "homebridge-blueair-plugin",
|
|
4
|
+
"version": "1.1.0",
|
|
5
|
+
"description": "Homebridge plugin for BlueAir devices",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/robertstepner-del/homebridge-blueair-plugin.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/robertstepner-del/homebridge-blueair-plugin#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/robertstepner-del/homebridge-blueair-plugin/issues"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": "^20.18.0 || ^22.10.0 || ^24.0.0",
|
|
17
|
+
"homebridge": "^1.8.0 || ^2.0.0-beta.0"
|
|
18
|
+
},
|
|
19
|
+
"main": "dist/index.js",
|
|
20
|
+
"scripts": {
|
|
21
|
+
"lint": "eslint . --cache --ext .ts --config config/eslintrc.json",
|
|
22
|
+
"watch": "npm run build && npm link && nodemon --config config/nodemon.json",
|
|
23
|
+
"build": "rimraf ./dist && tsc --project config/tsconfig.json",
|
|
24
|
+
"prepublishOnly": "npm run lint && npm run build",
|
|
25
|
+
"format": "prettier --log-level warn --write \"**/*.ts\" --config config/prettierrc.json && npm run lint -- --fix"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"homebridge-plugin",
|
|
29
|
+
"blueair",
|
|
30
|
+
"purifier",
|
|
31
|
+
"air purifier"
|
|
32
|
+
],
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/lodash": "^4.17.0",
|
|
35
|
+
"@types/node": "^20.12.5",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
|
37
|
+
"@typescript-eslint/parser": "^7.5.0",
|
|
38
|
+
"eslint": "^8.57.0",
|
|
39
|
+
"eslint-config-prettier": "^9.1.0",
|
|
40
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
41
|
+
"homebridge": "^1.11.1",
|
|
42
|
+
"homebridge-config-ui-x": "^5.15.2",
|
|
43
|
+
"nodemon": "^3.1.0",
|
|
44
|
+
"prettier": "^3.2.5",
|
|
45
|
+
"rimraf": "^5.0.5",
|
|
46
|
+
"ts-node": "^10.9.2",
|
|
47
|
+
"typescript": "^5.9.3"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@homebridge/plugin-ui-utils": "^1.0.3",
|
|
51
|
+
"async-mutex": "^0.5.0",
|
|
52
|
+
"lodash": "^4.17.21"
|
|
53
|
+
}
|
|
54
|
+
}
|