homebridge-blueair-purifier 0.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.
Files changed (44) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +191 -0
  3. package/config.schema.json +121 -0
  4. package/dist/accessory/AirPurifierAccessory.d.ts +26 -0
  5. package/dist/accessory/AirPurifierAccessory.d.ts.map +1 -0
  6. package/dist/accessory/AirPurifierAccessory.js +92 -0
  7. package/dist/accessory/AirPurifierAccessory.js.map +1 -0
  8. package/dist/api/BlueAirAwsApi.d.ts +69 -0
  9. package/dist/api/BlueAirAwsApi.d.ts.map +1 -0
  10. package/dist/api/BlueAirAwsApi.js +148 -0
  11. package/dist/api/BlueAirAwsApi.js.map +1 -0
  12. package/dist/api/Consts.d.ts +40 -0
  13. package/dist/api/Consts.d.ts.map +1 -0
  14. package/dist/api/Consts.js +50 -0
  15. package/dist/api/Consts.js.map +1 -0
  16. package/dist/api/GigyaApi.d.ts +18 -0
  17. package/dist/api/GigyaApi.d.ts.map +1 -0
  18. package/dist/api/GigyaApi.js +78 -0
  19. package/dist/api/GigyaApi.js.map +1 -0
  20. package/dist/device/BlueAirDevice.d.ts +12 -0
  21. package/dist/device/BlueAirDevice.d.ts.map +1 -0
  22. package/dist/device/BlueAirDevice.js +31 -0
  23. package/dist/device/BlueAirDevice.js.map +1 -0
  24. package/dist/index.d.ts +7 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +7 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/platform.d.ts +19 -0
  29. package/dist/platform.d.ts.map +1 -0
  30. package/dist/platform.js +87 -0
  31. package/dist/platform.js.map +1 -0
  32. package/dist/platformUtils.d.ts +37 -0
  33. package/dist/platformUtils.d.ts.map +1 -0
  34. package/dist/platformUtils.js +39 -0
  35. package/dist/platformUtils.js.map +1 -0
  36. package/dist/settings.d.ts +9 -0
  37. package/dist/settings.d.ts.map +1 -0
  38. package/dist/settings.js +12 -0
  39. package/dist/settings.js.map +1 -0
  40. package/homebridge-ui/public/css/bootstrap.min.css +6 -0
  41. package/homebridge-ui/public/index.html +263 -0
  42. package/homebridge-ui/public/js/bootstrap.min.js +7 -0
  43. package/homebridge-ui/server.js +120 -0
  44. package/package.json +45 -0
@@ -0,0 +1,69 @@
1
+ import { Logger } from 'homebridge';
2
+ type BlueAirDeviceDiscovery = {
3
+ mac: string;
4
+ 'mcu-firmware': string;
5
+ name: string;
6
+ type: string;
7
+ 'user-type': string;
8
+ uuid: string;
9
+ 'wifi-firmware': string;
10
+ };
11
+ export type BlueAirDeviceState = {
12
+ cfv?: string;
13
+ germshield?: boolean;
14
+ gsnm?: boolean;
15
+ standby?: boolean;
16
+ fanspeed?: number;
17
+ childlock?: boolean;
18
+ nightmode?: boolean;
19
+ mfv?: string;
20
+ automode?: boolean;
21
+ ofv?: string;
22
+ brightness?: number;
23
+ safetyswitch?: boolean;
24
+ filterusage?: number;
25
+ disinfection?: boolean;
26
+ disinftime?: number;
27
+ };
28
+ export type BlueAirDeviceSensorData = {
29
+ fanspeed?: number;
30
+ hcho?: number;
31
+ humidity?: number;
32
+ pm1?: number;
33
+ pm10?: number;
34
+ pm25?: number;
35
+ temperature?: number;
36
+ voc?: number;
37
+ };
38
+ export type BlueAirDeviceStatus = {
39
+ id: string;
40
+ name: string;
41
+ state: BlueAirDeviceState;
42
+ sensorData: BlueAirDeviceSensorData;
43
+ };
44
+ export declare const BlueAirDeviceSensorDataMap: {
45
+ fsp0: string;
46
+ hcho: string;
47
+ h: string;
48
+ pm1: string;
49
+ pm10: string;
50
+ pm2_5: string;
51
+ t: string;
52
+ tVOC: string;
53
+ };
54
+ export default class BlueAirAwsApi {
55
+ private readonly logger;
56
+ private readonly gigyaApi;
57
+ private readonly blueairAxios;
58
+ private last_login;
59
+ constructor(username: string, password: string, region: string, logger: Logger);
60
+ login(): Promise<void>;
61
+ checkTokenExpiration(): Promise<void>;
62
+ getDevices(): Promise<BlueAirDeviceDiscovery[]>;
63
+ getDeviceStatus(accountUuid: string, uuids: string[]): Promise<BlueAirDeviceStatus[]>;
64
+ setDeviceStatus(uuid: string, state: keyof BlueAirDeviceState, value: number | boolean): Promise<void>;
65
+ private getAwsAccessToken;
66
+ private apiCall;
67
+ }
68
+ export {};
69
+ //# sourceMappingURL=BlueAirAwsApi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlueAirAwsApi.d.ts","sourceRoot":"","sources":["../../src/api/BlueAirAwsApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAMpC,KAAK,sBAAsB,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,uBAAuB,CAAC;CACrC,CAAC;AAQF,eAAO,MAAM,0BAA0B;;;;;;;;;CAStC,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,aAAa;IAW9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IATzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgB;IAE7C,OAAO,CAAC,UAAU,CAAS;gBAGzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACG,MAAM,EAAE,MAAM;IAkB3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrC,UAAU,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC;IAa/C,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAwCrF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;YAoB9F,iBAAiB;YAmBjB,OAAO;CAoBtB"}
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BlueAirDeviceSensorDataMap = void 0;
7
+ const platformUtils_1 = require("../platformUtils");
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const GigyaApi_1 = __importDefault(require("./GigyaApi"));
10
+ const Consts_1 = require("./Consts");
11
+ exports.BlueAirDeviceSensorDataMap = {
12
+ fsp0: 'fanspeed',
13
+ hcho: 'hcho',
14
+ h: 'humidity',
15
+ pm1: 'pm1',
16
+ pm10: 'pm10',
17
+ pm2_5: 'pm25',
18
+ t: 'temperature',
19
+ tVOC: 'voc',
20
+ };
21
+ class BlueAirAwsApi {
22
+ constructor(username, password, region, logger) {
23
+ this.logger = logger;
24
+ const config = Consts_1.BLUEAIR_CONFIG[platformUtils_1.RegionMap[region]].awsConfig;
25
+ this.gigyaApi = new GigyaApi_1.default(username, password, region, logger);
26
+ this.blueairAxios = axios_1.default.create({
27
+ baseURL: `https://${config.restApiId}.execute-api.${config.awsRegion}.amazonaws.com/prod/c`,
28
+ headers: {
29
+ 'Accept': '*/*',
30
+ 'Connection': 'keep-alive',
31
+ 'Accept-Encoding': 'gzip, deflate, br',
32
+ },
33
+ timeout: Consts_1.BLUEAIR_API_TIMEOUT,
34
+ });
35
+ this.last_login = 0;
36
+ }
37
+ async login() {
38
+ const { token, secret } = await this.gigyaApi.getGigyaSession();
39
+ const { jwt } = await this.gigyaApi.getGigyaJWT(token, secret);
40
+ const { accessToken } = await this.getAwsAccessToken(jwt);
41
+ this.last_login = Date.now();
42
+ this.blueairAxios.defaults.headers['Authorization'] = `Bearer ${accessToken}`;
43
+ this.blueairAxios.defaults.headers['idtoken'] = accessToken;
44
+ }
45
+ async checkTokenExpiration() {
46
+ if (Consts_1.LOGIN_EXPIRATION < Date.now() - this.last_login) {
47
+ return await this.login();
48
+ }
49
+ return;
50
+ }
51
+ async getDevices() {
52
+ await this.checkTokenExpiration();
53
+ const response = await this.apiCall('/registered-devices', undefined);
54
+ if (!response.data.devices) {
55
+ throw new Error('getDevices error: no devices in response');
56
+ }
57
+ const devices = response.data.devices;
58
+ return devices;
59
+ }
60
+ async getDeviceStatus(accountUuid, uuids) {
61
+ await this.checkTokenExpiration();
62
+ const body = {
63
+ deviceconfigquery: uuids.map((uuid) => ({ id: uuid })),
64
+ };
65
+ const response = await this.apiCall(`/${accountUuid}/r/initial`, body);
66
+ const { data } = response;
67
+ if (!data.deviceInfo) {
68
+ throw new Error('getDeviceStatus error: no deviceInfo in response');
69
+ }
70
+ const deviceStatuses = data.deviceInfo.map(device => {
71
+ return {
72
+ id: device.id,
73
+ name: device.configuration.di.name,
74
+ sensorData: device.sensordata.reduce((acc, sensor) => {
75
+ const key = exports.BlueAirDeviceSensorDataMap[sensor.n];
76
+ if (key) {
77
+ acc[key] = sensor.v;
78
+ }
79
+ return acc;
80
+ }, {}),
81
+ state: device.states.reduce((acc, state) => {
82
+ if (state.v) {
83
+ acc[state.n] = state.v;
84
+ }
85
+ else if (state.vb) {
86
+ acc[state.n] = state.vb;
87
+ }
88
+ else {
89
+ this.logger.warn(`getDeviceStatus: unknown state ${JSON.stringify(state)}`);
90
+ }
91
+ return acc;
92
+ }, {}),
93
+ };
94
+ });
95
+ return deviceStatuses;
96
+ }
97
+ async setDeviceStatus(uuid, state, value) {
98
+ await this.checkTokenExpiration();
99
+ this.logger.debug(`setDeviceStatus: ${uuid} ${state} ${value}`);
100
+ const body = {
101
+ n: state,
102
+ };
103
+ if (typeof value === 'number') {
104
+ body.v = value;
105
+ }
106
+ else if (typeof value === 'boolean') {
107
+ body.vb = value;
108
+ }
109
+ else {
110
+ throw new Error(`setDeviceStatus: unknown value type ${typeof value}`);
111
+ }
112
+ await this.apiCall(`/${uuid}/a/${state}`, body);
113
+ }
114
+ async getAwsAccessToken(jwt) {
115
+ this.logger.debug('Getting AWS access token...');
116
+ const response = await this.apiCall('/login', undefined, {
117
+ 'Authorization': `Bearer ${jwt}`,
118
+ 'idtoken': jwt,
119
+ });
120
+ if (!response.data.access_token) {
121
+ throw new Error(`AWS access token error: ${JSON.stringify(response.data)}`);
122
+ }
123
+ this.logger.debug('AWS access token received');
124
+ return {
125
+ accessToken: response.data.access_token,
126
+ };
127
+ }
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ async apiCall(url, data, headers, retries = 3) {
130
+ try {
131
+ const response = await this.blueairAxios.post(url, data, { headers });
132
+ if (response.status !== 200) {
133
+ throw new Error(`API call error with status ${response.status}: ${response.statusText}, ${JSON.stringify(response.data)}`);
134
+ }
135
+ return response;
136
+ }
137
+ catch (error) {
138
+ if (retries > 0) {
139
+ return this.apiCall(url, data, headers, retries - 1);
140
+ }
141
+ else {
142
+ throw new Error(`API call failed after ${retries} retries`);
143
+ }
144
+ }
145
+ }
146
+ }
147
+ exports.default = BlueAirAwsApi;
148
+ //# sourceMappingURL=BlueAirAwsApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlueAirAwsApi.js","sourceRoot":"","sources":["../../src/api/BlueAirAwsApi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ type AWSConfigValue = {
2
+ restApiId: string;
3
+ awsRegion: string;
4
+ };
5
+ type GigyaConfigValue = {
6
+ gigyaRegion: string;
7
+ apiKey: string;
8
+ };
9
+ type APIConfig = {
10
+ [key: string]: {
11
+ awsConfig: AWSConfigValue;
12
+ gigyaConfig: GigyaConfigValue;
13
+ };
14
+ };
15
+ export declare const BLUEAIR_CONFIG: APIConfig;
16
+ export declare const LOGIN_EXPIRATION: number;
17
+ export declare const BLUEAIR_API_TIMEOUT: number;
18
+ export type BlueAirDeviceStatusResponse = {
19
+ deviceInfo: {
20
+ id: string;
21
+ configuration: {
22
+ di: {
23
+ name: string;
24
+ };
25
+ };
26
+ sensordata: {
27
+ n: string;
28
+ t: number;
29
+ v: number;
30
+ }[];
31
+ states: {
32
+ n: string;
33
+ t: number;
34
+ v?: number;
35
+ vb?: boolean;
36
+ }[];
37
+ }[];
38
+ };
39
+ export {};
40
+ //# sourceMappingURL=Consts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Consts.d.ts","sourceRoot":"","sources":["../../src/api/Consts.ts"],"names":[],"mappings":"AAEA,KAAK,cAAc,GAAG;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAkBF,KAAK,gBAAgB,GAAG;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AA0BF,KAAK,SAAS,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG;QAChC,SAAS,EAAE,cAAc,CAAC;QAC1B,WAAW,EAAE,gBAAgB,CAAC;KAC/B,CAAC;CAAE,CAAC;AAEL,eAAO,MAAM,cAAc,WAMP,CAAC;AAErB,eAAO,MAAM,gBAAgB,QAAmB,CAAC;AACjD,eAAO,MAAM,mBAAmB,QAAW,CAAC;AAE5C,MAAM,MAAM,2BAA2B,GAAG;IACxC,UAAU,EAAE;QACV,EAAE,EAAE,MAAM,CAAC;QACX,aAAa,EAAE;YACb,EAAE,EAAE;gBACF,IAAI,EAAE,MAAM,CAAC;aACd,CAAC;SACH,CAAC;QACF,UAAU,EAAE;YACV,CAAC,EAAE,MAAM,CAAC;YACV,CAAC,EAAE,MAAM,CAAC;YACV,CAAC,EAAE,MAAM,CAAC;SACX,EAAE,CAAC;QACJ,MAAM,EAAE;YACN,CAAC,EAAE,MAAM,CAAC;YACV,CAAC,EAAE,MAAM,CAAC;YACV,CAAC,CAAC,EAAE,MAAM,CAAC;YACX,EAAE,CAAC,EAAE,OAAO,CAAC;SACd,EAAE,CAAC;KAEL,EAAE,CAAC;CACL,CAAC"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BLUEAIR_API_TIMEOUT = exports.LOGIN_EXPIRATION = exports.BLUEAIR_CONFIG = void 0;
4
+ const platformUtils_1 = require("../platformUtils");
5
+ const AWS_CONFIG = {
6
+ 'us': {
7
+ restApiId: 'on1keymlmh',
8
+ awsRegion: 'us-east-2',
9
+ },
10
+ 'eu': {
11
+ restApiId: 'hkgmr8v960',
12
+ awsRegion: 'eu-west-1',
13
+ },
14
+ 'cn': {
15
+ restApiId: 'ftbkyp79si',
16
+ awsRegion: 'cn-north-1',
17
+ },
18
+ };
19
+ const GIGYA_CONFIG = {
20
+ 'us': {
21
+ gigyaRegion: 'us1',
22
+ apiKey: '3_-xUbbrIY8QCbHDWQs1tLXE-CZBQ50SGElcOY5hF1euE11wCoIlNbjMGAFQ6UwhMY',
23
+ },
24
+ 'eu': {
25
+ gigyaRegion: 'eu1',
26
+ apiKey: '3_qRseYzrUJl1VyxvSJANalu_kNgQ83swB1B9uzgms58--5w1ClVNmrFdsDnWVQQCl',
27
+ },
28
+ 'cn': {
29
+ gigyaRegion: 'cn1',
30
+ apiKey: '3_h3UEfJnA-zDpFPR9L4412HO7Mz2VVeN4wprbWYafPN1gX0kSnLcZ9VSfFi7bEIIU',
31
+ },
32
+ 'au': {
33
+ gigyaRegion: 'au1',
34
+ apiKey: '3_Z2N0mIFC6j2fx1z2sq76R3pwkCMaMX2y9btPb0_PgI_3wfjSJoofFnBbxbtuQksN',
35
+ },
36
+ 'ru': {
37
+ gigyaRegion: 'ru1',
38
+ apiKey: '3_wYhHEBaOcS_w6idVM3mh8UjyjOP-3Dwn3w9Z6AYc0FhGf-uIwUkrcoCdsYarND2k',
39
+ },
40
+ };
41
+ exports.BLUEAIR_CONFIG = Object.values(platformUtils_1.RegionMap).reduce((acc, region) => ({
42
+ ...acc,
43
+ [region]: {
44
+ awsConfig: region in AWS_CONFIG ? AWS_CONFIG[region] : AWS_CONFIG['eu'],
45
+ gigyaConfig: region in GIGYA_CONFIG ? GIGYA_CONFIG[region] : GIGYA_CONFIG['eu'],
46
+ },
47
+ }), {});
48
+ exports.LOGIN_EXPIRATION = 3600 * 1000 * 24; // n hours in milliseconds
49
+ exports.BLUEAIR_API_TIMEOUT = 1000 * 5; // n seconds in milliseconds
50
+ //# sourceMappingURL=Consts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Consts.js","sourceRoot":"","sources":["../../src/api/Consts.ts"],"names":[],"mappings":";;;AAAA,oDAA6C;AAQ7C,MAAM,UAAU,GAAe;IAC7B,IAAI,EAAE;QACJ,SAAS,EAAE,YAAY;QACvB,SAAS,EAAE,WAAW;KACvB;IACD,IAAI,EAAE;QACJ,SAAS,EAAE,YAAY;QACvB,SAAS,EAAE,WAAW;KACvB;IACD,IAAI,EAAE;QACJ,SAAS,EAAE,YAAY;QACvB,SAAS,EAAE,YAAY;KACxB;CACF,CAAC;AAQF,MAAM,YAAY,GAAiB;IACjC,IAAI,EAAE;QACJ,WAAW,EAAE,KAAK;QAClB,MAAM,EAAE,oEAAoE;KAC7E;IACD,IAAI,EAAE;QACJ,WAAW,EAAE,KAAK;QAClB,MAAM,EAAE,oEAAoE;KAC7E;IACD,IAAI,EAAE;QACJ,WAAW,EAAE,KAAK;QAClB,MAAM,EAAE,oEAAoE;KAC7E;IACD,IAAI,EAAE;QACJ,WAAW,EAAE,KAAK;QAClB,MAAM,EAAE,oEAAoE;KAC7E;IACD,IAAI,EAAE;QACJ,WAAW,EAAE,KAAK;QAClB,MAAM,EAAE,oEAAoE;KAC7E;CACF,CAAC;AAOW,QAAA,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,yBAAS,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAc,EAAE,EAAE,CAAC,CAAC;IACtF,GAAG,GAAG;IACN,CAAC,MAAM,CAAC,EAAE;QACR,SAAS,EAAE,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QACvE,WAAW,EAAE,MAAM,IAAI,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC;KAChF;CACF,CAAC,EAAE,EAAe,CAAC,CAAC;AAER,QAAA,gBAAgB,GAAG,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,0BAA0B;AAC/D,QAAA,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,4BAA4B"}
@@ -0,0 +1,18 @@
1
+ import { Logger } from 'homebridge';
2
+ export default class GigyaApi {
3
+ private readonly username;
4
+ private readonly password;
5
+ private readonly logger;
6
+ private api_key;
7
+ private readonly gigyaAxios;
8
+ constructor(username: string, password: string, region: string, logger: Logger);
9
+ getGigyaSession(): Promise<{
10
+ token: string;
11
+ secret: string;
12
+ }>;
13
+ getGigyaJWT(token: string, secret: string): Promise<{
14
+ jwt: string;
15
+ }>;
16
+ private apiCall;
17
+ }
18
+ //# sourceMappingURL=GigyaApi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GigyaApi.d.ts","sourceRoot":"","sources":["../../src/api/GigyaApi.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAIpC,MAAM,CAAC,OAAO,OAAO,QAAQ;IAMzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAEzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;gBAGxB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EACjC,MAAM,EAAE,MAAM,EACG,MAAM,EAAE,MAAM;IAepB,eAAe,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAqB7D,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,CAAC;YAmBjE,OAAO;CAiBtB"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const axios_1 = __importDefault(require("axios"));
7
+ const Consts_1 = require("./Consts");
8
+ const platformUtils_1 = require("../platformUtils");
9
+ class GigyaApi {
10
+ constructor(username, password, region, logger) {
11
+ this.username = username;
12
+ this.password = password;
13
+ this.logger = logger;
14
+ const config = Consts_1.BLUEAIR_CONFIG[platformUtils_1.RegionMap[region]].gigyaConfig;
15
+ this.api_key = config.apiKey;
16
+ this.gigyaAxios = axios_1.default.create({
17
+ baseURL: `https://accounts.${config.gigyaRegion}.gigya.com`,
18
+ headers: {
19
+ 'Accept': '*/*',
20
+ 'Connection': 'keep-alive',
21
+ 'Accept-Encoding': 'gzip, deflate, br',
22
+ },
23
+ });
24
+ }
25
+ async getGigyaSession() {
26
+ const params = new URLSearchParams({
27
+ apiKey: this.api_key,
28
+ loginID: this.username,
29
+ password: this.password,
30
+ targetEnv: 'mobile',
31
+ });
32
+ const response = await this.apiCall('/accounts.login', params.toString());
33
+ if (!response.data.oauth_token || !response.data.oauth_token_secret) {
34
+ throw new Error('Gigya session error: no oauth_token or oauth_token_secret in response');
35
+ }
36
+ this.logger.debug('Gigya session received');
37
+ return {
38
+ token: response.data.oauth_token,
39
+ secret: response.data.oauth_token_secret,
40
+ };
41
+ }
42
+ async getGigyaJWT(token, secret) {
43
+ const params = new URLSearchParams({
44
+ oauth_token: token,
45
+ secret: secret,
46
+ targetEnv: 'mobile',
47
+ });
48
+ const response = await this.apiCall('/accounts.getJWT', params.toString());
49
+ if (!response.data.id_token) {
50
+ throw new Error('Gigya JWT error: no id_token in response');
51
+ }
52
+ this.logger.debug('Gigya JWT received');
53
+ return {
54
+ jwt: response.data.id_token,
55
+ };
56
+ }
57
+ async apiCall(url, data, retries = 3) {
58
+ try {
59
+ const response = await this.gigyaAxios.post(url, data);
60
+ if (response.status !== 200) {
61
+ throw new Error(`API call error with status ${response.status}: ${response.statusText}, ${JSON.stringify(response.data)}`);
62
+ }
63
+ return response;
64
+ }
65
+ catch (error) {
66
+ this.logger.error(`API call failed: ${error}`);
67
+ if (retries > 0) {
68
+ this.logger.debug(`Retrying API call (${retries} retries left)...`);
69
+ return this.apiCall(url, data, retries - 1);
70
+ }
71
+ else {
72
+ throw new Error(`API call failed after ${retries} retries`);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ exports.default = GigyaApi;
78
+ //# sourceMappingURL=GigyaApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GigyaApi.js","sourceRoot":"","sources":["../../src/api/GigyaApi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /// <reference types="node" />
2
+ import EventEmitter from 'events';
3
+ import { BlueAirDeviceSensorData, BlueAirDeviceState } from '../api/BlueAirAwsApi';
4
+ export declare class BlueAirDevice extends EventEmitter {
5
+ private readonly id;
6
+ state: BlueAirDeviceState;
7
+ sensorData: BlueAirDeviceSensorData;
8
+ constructor(id: string);
9
+ setState(state: string, value: number | boolean | string): void;
10
+ private updateState;
11
+ }
12
+ //# sourceMappingURL=BlueAirDevice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlueAirDevice.d.ts","sourceRoot":"","sources":["../../src/device/BlueAirDevice.ts"],"names":[],"mappings":";AAAA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAuB,MAAM,sBAAsB,CAAC;AAExG,qBAAa,aAAc,SAAQ,YAAY;IAM3C,OAAO,CAAC,QAAQ,CAAC,EAAE;IAJd,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,uBAAuB,CAAC;gBAGxB,EAAE,EAAE,MAAM;IAWtB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM;IAS/D,OAAO,CAAC,WAAW;CAIpB"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BlueAirDevice = void 0;
7
+ const events_1 = __importDefault(require("events"));
8
+ class BlueAirDevice extends events_1.default {
9
+ constructor(id) {
10
+ super();
11
+ this.id = id;
12
+ this.state = {};
13
+ this.sensorData = {};
14
+ this.on('state', (state) => {
15
+ this.updateState(state);
16
+ });
17
+ }
18
+ setState(state, value) {
19
+ if (state in this.state === false) {
20
+ throw new Error(`Invalid state: ${state}`);
21
+ }
22
+ this.state[state] = value;
23
+ this.emit('setState', { id: this.id, state, value });
24
+ }
25
+ updateState(state) {
26
+ this.state = state.state;
27
+ this.sensorData = state.sensorData;
28
+ }
29
+ }
30
+ exports.BlueAirDevice = BlueAirDevice;
31
+ //# sourceMappingURL=BlueAirDevice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlueAirDevice.js","sourceRoot":"","sources":["../../src/device/BlueAirDevice.ts"],"names":[],"mappings":";;;;;;AAAA,oDAAkC;AAGlC,MAAa,aAAc,SAAQ,gBAAY;IAK7C,YACmB,EAAU;QAE3B,KAAK,EAAE,CAAC;QAFS,OAAE,GAAF,EAAE,CAAQ;QAG3B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;QAErB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACzB,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,QAAQ,CAAC,KAAa,EAAE,KAAgC;QAC7D,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;QAE1B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAC,CAAC,CAAC;IACrD,CAAC;IAEO,WAAW,CAAC,KAA0B;QAC5C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACrC,CAAC;CACF;AA9BD,sCA8BC"}
@@ -0,0 +1,7 @@
1
+ import { API } from 'homebridge';
2
+ /**
3
+ * This method registers the platform with Homebridge
4
+ */
5
+ declare const _default: (api: API) => void;
6
+ export = _default;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAKjC;;GAEG;8BACY,GAAG;AAAlB,kBAEE"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ const settings_1 = require("./settings");
3
+ const platform_1 = require("./platform");
4
+ module.exports = (api) => {
5
+ api.registerPlatform(settings_1.PLATFORM_NAME, platform_1.BlueAirPlatform);
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,yCAA2C;AAC3C,yCAA6C;AAK7C,iBAAS,CAAC,GAAQ,EAAE,EAAE;IACpB,GAAG,CAAC,gBAAgB,CAAC,wBAAa,EAAE,0BAAe,CAAC,CAAC;AACvD,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /// <reference types="node" />
2
+ import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service, Characteristic } from 'homebridge';
3
+ import { BlueAirDeviceStatus } from './api/BlueAirAwsApi';
4
+ import EventEmitter from 'events';
5
+ export declare class BlueAirPlatform extends EventEmitter implements DynamicPlatformPlugin {
6
+ readonly log: Logger;
7
+ readonly config: PlatformConfig;
8
+ readonly api: API;
9
+ readonly Service: typeof Service;
10
+ readonly Characteristic: typeof Characteristic;
11
+ readonly accessories: PlatformAccessory[];
12
+ private readonly platformConfig;
13
+ private readonly blueAirApi;
14
+ constructor(log: Logger, config: PlatformConfig, api: API);
15
+ configureAccessory(accessory: PlatformAccessory): void;
16
+ getInitialDeviceStates(): Promise<void>;
17
+ addDevice(device: BlueAirDeviceStatus): void;
18
+ }
19
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,qBAAqB,EAAE,MAAM,EAAE,iBAAiB,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAK5H,OAAsB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAGzE,OAAO,YAAY,MAAM,QAAQ,CAAC;AAElC,qBAAa,eAAgB,SAAQ,YAAa,YAAW,qBAAqB;aAW9D,GAAG,EAAE,MAAM;aACX,MAAM,EAAE,cAAc;aACtB,GAAG,EAAE,GAAG;IAZ1B,SAAgB,OAAO,EAAE,OAAO,OAAO,CAAwB;IAC/D,SAAgB,cAAc,EAAE,OAAO,cAAc,CAA+B;IAGpF,SAAgB,WAAW,EAAE,iBAAiB,EAAE,CAAM;IAEtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;gBAGzB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,GAAG;IA2B1B,kBAAkB,CAAC,SAAS,EAAE,iBAAiB;IAOzC,sBAAsB;IAmB5B,SAAS,CAAC,MAAM,EAAE,mBAAmB;CAsBtC"}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BlueAirPlatform = void 0;
7
+ const settings_1 = require("./settings");
8
+ const platformUtils_1 = require("./platformUtils");
9
+ const lodash_1 = require("lodash");
10
+ const BlueAirAwsApi_1 = __importDefault(require("./api/BlueAirAwsApi"));
11
+ const BlueAirDevice_1 = require("./device/BlueAirDevice");
12
+ const AirPurifierAccessory_1 = require("./accessory/AirPurifierAccessory");
13
+ const events_1 = __importDefault(require("events"));
14
+ class BlueAirPlatform extends events_1.default {
15
+ constructor(log, config, api) {
16
+ super();
17
+ this.log = log;
18
+ this.config = config;
19
+ this.api = api;
20
+ this.Service = this.api.hap.Service;
21
+ this.Characteristic = this.api.hap.Characteristic;
22
+ // this is used to track restored cached accessories
23
+ this.accessories = [];
24
+ this.platformConfig = (0, lodash_1.defaultsDeep)(config, platformUtils_1.defaultConfig);
25
+ if (!this.platformConfig.username || !this.platformConfig.password || !this.platformConfig.accountUuid) {
26
+ this.log.error('Missing required configuration options! Please do the device discovery in the configuration UI and/or check your\
27
+ config.json file');
28
+ }
29
+ this.blueAirApi = new BlueAirAwsApi_1.default(this.platformConfig.username, this.platformConfig.password, this.platformConfig.region, log);
30
+ this.api.on('didFinishLaunching', async () => {
31
+ await this.getInitialDeviceStates();
32
+ });
33
+ this.on('setState', async ({ id, state, value }) => {
34
+ this.log.info(`[${id}] Setting state: ${state} = ${value}`);
35
+ try {
36
+ await this.blueAirApi.setDeviceStatus(id, state, value);
37
+ }
38
+ catch (error) {
39
+ this.log.error(`[${id}] Error setting state: ${state} = ${value}`, error);
40
+ }
41
+ });
42
+ }
43
+ configureAccessory(accessory) {
44
+ this.log.info('Loading accessory from cache:', accessory.displayName);
45
+ // add the restored accessory to the accessories cache so we can track if it has already been registered
46
+ this.accessories.push(accessory);
47
+ }
48
+ async getInitialDeviceStates() {
49
+ try {
50
+ await this.blueAirApi.login();
51
+ let uuids = this.platformConfig.devices.map(device => device.id);
52
+ const devices = await this.blueAirApi.getDeviceStatus(this.platformConfig.accountUuid, uuids);
53
+ for (const device of devices) {
54
+ this.addDevice(device);
55
+ uuids = uuids.filter(uuid => uuid !== device.id);
56
+ }
57
+ for (const uuid of uuids) {
58
+ this.log.warn(`[${uuid}] Device not found in AWS API response!`);
59
+ }
60
+ }
61
+ catch (error) {
62
+ this.log.error('Error getting initial device states:', error);
63
+ }
64
+ }
65
+ addDevice(device) {
66
+ const uuid = this.api.hap.uuid.generate(device.id);
67
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
68
+ const deviceConfig = this.platformConfig.devices.find(config => config.id === device.id);
69
+ if (!deviceConfig) {
70
+ this.log.error(`[${device.id}] Device configuration not found!`);
71
+ return;
72
+ }
73
+ const blueAirDevice = new BlueAirDevice_1.BlueAirDevice(device.id);
74
+ if (existingAccessory) {
75
+ this.log.info(`[${deviceConfig.name}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
76
+ new AirPurifierAccessory_1.AirPurifierAccessory(this, existingAccessory, blueAirDevice, deviceConfig);
77
+ }
78
+ else {
79
+ this.log.info('Adding new accessory:', device.name);
80
+ const accessory = new this.api.platformAccessory(device.name, uuid);
81
+ new AirPurifierAccessory_1.AirPurifierAccessory(this, accessory, blueAirDevice, deviceConfig);
82
+ this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
83
+ }
84
+ }
85
+ }
86
+ exports.BlueAirPlatform = BlueAirPlatform;
87
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}