From 7c3e1173bfa1af58d3f7f2278939e90ff3fd7903 Mon Sep 17 00:00:00 2001 From: kovapatrik Date: Tue, 9 Apr 2024 02:22:17 +0200 Subject: [PATCH] init --- .vscode/settings.json | 2 +- config.schema.json | 115 ++++++++++++++++++++- package-lock.json | 93 +++++++++++++++++ package.json | 3 + src/accessory/BaseAccessory.ts | 0 src/accessory/BlueAir.ts | 0 src/accessory/BlueAirAware.ts | 0 src/accessory/BlueAirClassic.ts | 0 src/accessory/BlueAirDustProtect.ts | 0 src/api/BlueAirAws.ts | 154 ++++++++++++++++++++++++++++ src/api/BlueAirBase.ts | 13 +++ src/api/BlueAirLegacy.ts | 0 src/platform.ts | 6 +- src/platformAccessory.ts | 141 ------------------------- src/platformUtils.ts | 37 +++++++ 15 files changed, 414 insertions(+), 150 deletions(-) create mode 100644 src/accessory/BaseAccessory.ts create mode 100644 src/accessory/BlueAir.ts create mode 100644 src/accessory/BlueAirAware.ts create mode 100644 src/accessory/BlueAirClassic.ts create mode 100644 src/accessory/BlueAirDustProtect.ts create mode 100644 src/api/BlueAirAws.ts create mode 100644 src/api/BlueAirBase.ts create mode 100644 src/api/BlueAirLegacy.ts delete mode 100644 src/platformAccessory.ts create mode 100644 src/platformUtils.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 31b20d1..89d2e29 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "files.eol": "\n", "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, "editor.rulers": [ 140 ], "eslint.enable": true diff --git a/config.schema.json b/config.schema.json index 4caa4c4..b7e1288 100644 --- a/config.schema.json +++ b/config.schema.json @@ -5,12 +5,117 @@ "schema": { "type": "object", "properties": { - "name": { - "title": "Name", + "username": { + "title": "Username", + "description": "Username for BlueAir account", "type": "string", - "required": true, - "default": "Example Dynamic Platform" + "required": true + }, + "password": { + "title": "Password", + "description": "Password for BlueAir account", + "type": "string", + "required": true + }, + "verboseLogging": { + "title": "Verbose Logging", + "description": "Enable to receive detailed log messages. Useful for troubleshooting.", + "type": "boolean" + }, + "devices": { + "title": "Devices", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string", + "required": true + }, + "id": { + "title": "ID", + "type": "string", + "required": true + }, + "isAWSDevice": { + "title": "AWS Device", + "description": "Enable AWS integration. This is required for devices like DustMagnet, HealthProtect, and Blue Pure.", + "type": "boolean" + }, + "led": { + "title": "LED", + "description": "Toggles if the LED switch service is created with the accessory.", + "type": "boolean" + }, + "airQualitySensor": { + "title": "Air Quality Sensor", + "description": "Toggles if the air quality sensor service is created with the accessory.", + "type": "boolean" + }, + "co2Sensor": { + "title": "CO2 Sensor", + "description": "Toggles if the CO2 sensor service is created with the accessory.", + "type": "boolean" + }, + "temperatureSensor": { + "title": "Temperature Sensor", + "description": "Toggles if the temperature sensor service is created with the accessory.", + "type": "boolean" + }, + "humiditySensor": { + "title": "Humidity Sensor", + "description": "Toggles if the humidity sensor service is created with the accessory.", + "type": "boolean" + }, + "germShield": { + "title": "Germ Shield", + "description": "Toggles if the germ shield switch service is created with the accessory.", + "type": "boolean" + }, + "nightMode": { + "title": "Night Mode", + "description": "Toggles if the night mode switch service is created with the accessory.", + "type": "boolean" + } + } + } } } - } + }, + "layout": [ + { + "items": [ + { + "ref": "common", + "type": "fieldset", + "expandable": true, + "expanded": true, + "title": "Common Settings", + "items": [ + "username", + "password", + "verboseLogging" + ] + }, + { + "key": "devices", + "type": "tabarray", + "title": "{{ value.name || 'new device' }}", + "items": [ + "devices[].name", + "devices[].id", + "devices[].enableAWS", + "devices[].led", + "devices[].airQualitySensor", + "devices[].co2Sensor", + "devices[].temperatureSensor", + "devices[].humiditySensor", + "devices[].germShield", + "devices[].nightMode" + ] + } + ] + } + ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ffcd46f..264a629 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "homebridge-blueair-purifier", "version": "0.1.0", "license": "Apache-2.0", + "dependencies": { + "axios": "^1.6.8" + }, "devDependencies": { "@types/node": "^20.12.5", "@typescript-eslint/eslint-plugin": "^7.5.0", @@ -722,6 +725,11 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -737,6 +745,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -892,6 +910,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -1016,6 +1045,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1492,6 +1529,25 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -1517,6 +1573,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", @@ -2331,6 +2400,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -2723,6 +2811,11 @@ "node": ">= 0.8.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", diff --git a/package.json b/package.json index 6c7f913..2bfecd0 100644 --- a/package.json +++ b/package.json @@ -36,5 +36,8 @@ "rimraf": "^5.0.5", "ts-node": "^10.9.2", "typescript": "^5.4.4" + }, + "dependencies": { + "axios": "^1.6.8" } } diff --git a/src/accessory/BaseAccessory.ts b/src/accessory/BaseAccessory.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/accessory/BlueAir.ts b/src/accessory/BlueAir.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/accessory/BlueAirAware.ts b/src/accessory/BlueAirAware.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/accessory/BlueAirClassic.ts b/src/accessory/BlueAirClassic.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/accessory/BlueAirDustProtect.ts b/src/accessory/BlueAirDustProtect.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/api/BlueAirAws.ts b/src/api/BlueAirAws.ts new file mode 100644 index 0000000..126c2fe --- /dev/null +++ b/src/api/BlueAirAws.ts @@ -0,0 +1,154 @@ +import { Logger } from 'homebridge'; +import { RegionMap } from '../platformUtils'; +import { BlueAirBase, BlueAirDevice } from './BlueAirBase'; +import axios, { AxiosInstance } from 'axios'; + +type APIConfigValue = { + gigyaRegion: string; + restApiId: string; + awsRegion: string; + apiKey: string; +}; + +type APIConfig = { [key in string]: APIConfigValue }; + +const BLUEAIR_AWS_APIKEYS: APIConfig = { + 'us': { + gigyaRegion: 'us1', + restApiId: 'on1keymlmh', + awsRegion: 'us-east-2', + apiKey: '3_-xUbbrIY8QCbHDWQs1tLXE-CZBQ50SGElcOY5hF1euE11wCoIlNbjMGAFQ6UwhMY', + }, + 'eu': { + gigyaRegion: 'eu1', + restApiId: 'hkgmr8v960', + awsRegion: 'eu-west-1', + apiKey: '3_qRseYzrUJl1VyxvSJANalu_kNgQ83swB1B9uzgms58--5w1ClVNmrFdsDnWVQQCl', + }, +}; + + +export class BlueAirAws implements BlueAirBase { + + private readonly API_CONFIG: APIConfigValue; + private readonly GIGYA_API: AxiosInstance; + private readonly AWS_API: AxiosInstance; + + private access_token?: string; + + constructor( + private readonly username: string, + private readonly password: string, + region: string, + private readonly logger: Logger, + ) { + this.API_CONFIG = BLUEAIR_AWS_APIKEYS[RegionMap[region]]; + + this.GIGYA_API = axios.create({ + baseURL: `https://accounts.${this.API_CONFIG.gigyaRegion}.gigya.com`, + headers: { + 'Accept': '*/*', + 'Connection': 'keep-alive', + 'Accept-Encoding': 'gzip, deflate, br', + }, + }); + + this.AWS_API = axios.create({ + baseURL: `https://${this.API_CONFIG.restApiId}.execute-api.${this.API_CONFIG.awsRegion}.amazonaws.com`, + headers: { + 'Accept': '*/*', + 'Connection': 'keep-alive', + 'Accept-Encoding': 'gzip, deflate, br', + }, + }); + } + + async login(): Promise { + + if (this.access_token) { + return true; + } + + try { + const { token, secret } = await this.getGigyaSession(); + const { jwt } = await this.getGigyaJWT(token, secret); + const { accessToken } = await this.getAwsAccessToken(jwt); + + this.access_token = accessToken; + return true; + } catch (error) { + this.logger.error(`Login error: ${error}`); + return false; + } + } + + async getDevices(): Promise { + return []; + } + + async getDeviceStatus(uuid: string): Promise { + return uuid; + } + + async setDeviceStatus(uuid: string, status: Record): Promise { + this.logger.debug(`setDeviceStatus: ${uuid} ${JSON.stringify(status)}`); + return true; + } + + + private async getGigyaSession(): Promise<{token: string; secret: string }> { + + const params = new URLSearchParams({ + apiKey: this.API_CONFIG.apiKey, + loginID: this.username, + password: this.password, + targetEnv: 'mobile', + }); + const response = await this.GIGYA_API.post('/accounts.login', params.toString()); + + if (response.data.errorCode) { + throw new Error(`Gigya login error: ${response.data.errorCode}`); + } + + return { + token: response.data.sessionInfo.sessionToken, + secret: response.data.sessionInfo.sessionSecret, + }; + } + + private async getGigyaJWT(token: string, secret: string): Promise<{jwt: string}> { + + const params = new URLSearchParams({ + oauth_token: token, + secret: secret, + targetEnv: 'mobile', + }); + const response = await this.GIGYA_API.post('/accounts.getJWT', params.toString()); + + if (response.data.errorCode) { + throw new Error(`Gigya JWT error: ${response.data.errorCode}`); + } + + return { + jwt: response.data.id_token, + }; + } + + private async getAwsAccessToken(jwt: string): Promise<{accessToken: string}> { + + const response = await this.AWS_API.post('/prod/c/login', undefined, { + headers: { + 'Authorization': `Bearer ${jwt}`, + 'idtoken': jwt, + }, + }); + + if (response.data.errorCode) { + throw new Error(`AWS access token error: ${response.data.errorCode}`); + } + + return { + accessToken: response.data.access_token, + }; + } +} \ No newline at end of file diff --git a/src/api/BlueAirBase.ts b/src/api/BlueAirBase.ts new file mode 100644 index 0000000..5c6e4a9 --- /dev/null +++ b/src/api/BlueAirBase.ts @@ -0,0 +1,13 @@ +export type BlueAirDevice = { + uuid: string; + name: string; + userid: string; + mac: string; +}; + +export interface BlueAirBase { + login(): Promise; + getDevices(): Promise; + getDeviceStatus(uuid: string): Promise; + setDeviceStatus(uuid: string, status: Record): Promise; +} \ No newline at end of file diff --git a/src/api/BlueAirLegacy.ts b/src/api/BlueAirLegacy.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/platform.ts b/src/platform.ts index 536024f..67eab6b 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -1,7 +1,7 @@ import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service, Characteristic } from 'homebridge'; import { PLATFORM_NAME, PLUGIN_NAME } from './settings'; -import { ExamplePlatformAccessory } from './platformAccessory'; +// import { ExamplePlatformAccessory } from './platformAccessory'; /** * HomebridgePlatform @@ -87,7 +87,7 @@ export class ExampleHomebridgePlatform implements DynamicPlatformPlugin { // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` - new ExamplePlatformAccessory(this, existingAccessory); + // new ExamplePlatformAccessory(this, existingAccessory); // it is possible to remove platform accessories at any time using `api.unregisterPlatformAccessories`, eg.: // remove platform accessories when no longer present @@ -106,7 +106,7 @@ export class ExampleHomebridgePlatform implements DynamicPlatformPlugin { // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` - new ExamplePlatformAccessory(this, accessory); + // new ExamplePlatformAccessory(this, accessory); // link the accessory to your platform this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); diff --git a/src/platformAccessory.ts b/src/platformAccessory.ts deleted file mode 100644 index d885377..0000000 --- a/src/platformAccessory.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; - -import { ExampleHomebridgePlatform } from './platform'; - -/** - * Platform Accessory - * An instance of this class is created for each accessory your platform registers - * Each accessory may expose multiple services of different service types. - */ -export class ExamplePlatformAccessory { - private service: Service; - - /** - * These are just used to create a working example - * You should implement your own code to track the state of your accessory - */ - private exampleStates = { - On: false, - Brightness: 100, - }; - - constructor( - private readonly platform: ExampleHomebridgePlatform, - private readonly accessory: PlatformAccessory, - ) { - - // set accessory information - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, 'Default-Manufacturer') - .setCharacteristic(this.platform.Characteristic.Model, 'Default-Model') - .setCharacteristic(this.platform.Characteristic.SerialNumber, 'Default-Serial'); - - // get the LightBulb service if it exists, otherwise create a new LightBulb service - // you can create multiple services for each accessory - this.service = this.accessory.getService(this.platform.Service.Lightbulb) || this.accessory.addService(this.platform.Service.Lightbulb); - - // set the service name, this is what is displayed as the default name on the Home app - // in this example we are using the name we stored in the `accessory.context` in the `discoverDevices` method. - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.exampleDisplayName); - - // each service must implement at-minimum the "required characteristics" for the given service type - // see https://developers.homebridge.io/#/service/Lightbulb - - // register handlers for the On/Off Characteristic - this.service.getCharacteristic(this.platform.Characteristic.On) - .onSet(this.setOn.bind(this)) // SET - bind to the `setOn` method below - .onGet(this.getOn.bind(this)); // GET - bind to the `getOn` method below - - // register handlers for the Brightness Characteristic - this.service.getCharacteristic(this.platform.Characteristic.Brightness) - .onSet(this.setBrightness.bind(this)); // SET - bind to the 'setBrightness` method below - - /** - * Creating multiple services of the same type. - * - * To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error, - * when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id: - * this.accessory.getService('NAME') || this.accessory.addService(this.platform.Service.Lightbulb, 'NAME', 'USER_DEFINED_SUBTYPE_ID'); - * - * The USER_DEFINED_SUBTYPE must be unique to the platform accessory (if you platform exposes multiple accessories, each accessory - * can use the same sub type id.) - */ - - // Example: add two "motion sensor" services to the accessory - const motionSensorOneService = this.accessory.getService('Motion Sensor One Name') || - this.accessory.addService(this.platform.Service.MotionSensor, 'Motion Sensor One Name', 'YourUniqueIdentifier-1'); - - const motionSensorTwoService = this.accessory.getService('Motion Sensor Two Name') || - this.accessory.addService(this.platform.Service.MotionSensor, 'Motion Sensor Two Name', 'YourUniqueIdentifier-2'); - - /** - * Updating characteristics values asynchronously. - * - * Example showing how to update the state of a Characteristic asynchronously instead - * of using the `on('get')` handlers. - * Here we change update the motion sensor trigger states on and off every 10 seconds - * the `updateCharacteristic` method. - * - */ - let motionDetected = false; - setInterval(() => { - // EXAMPLE - inverse the trigger - motionDetected = !motionDetected; - - // push the new value to HomeKit - motionSensorOneService.updateCharacteristic(this.platform.Characteristic.MotionDetected, motionDetected); - motionSensorTwoService.updateCharacteristic(this.platform.Characteristic.MotionDetected, !motionDetected); - - this.platform.log.debug('Triggering motionSensorOneService:', motionDetected); - this.platform.log.debug('Triggering motionSensorTwoService:', !motionDetected); - }, 10000); - } - - /** - * Handle "SET" requests from HomeKit - * These are sent when the user changes the state of an accessory, for example, turning on a Light bulb. - */ - async setOn(value: CharacteristicValue) { - // implement your own code to turn your device on/off - this.exampleStates.On = value as boolean; - - this.platform.log.debug('Set Characteristic On ->', value); - } - - /** - * Handle the "GET" requests from HomeKit - * These are sent when HomeKit wants to know the current state of the accessory, for example, checking if a Light bulb is on. - * - * GET requests should return as fast as possbile. A long delay here will result in - * HomeKit being unresponsive and a bad user experience in general. - * - * If your device takes time to respond you should update the status of your device - * asynchronously instead using the `updateCharacteristic` method instead. - - * @example - * this.service.updateCharacteristic(this.platform.Characteristic.On, true) - */ - async getOn(): Promise { - // implement your own code to check if the device is on - const isOn = this.exampleStates.On; - - this.platform.log.debug('Get Characteristic On ->', isOn); - - // if you need to return an error to show the device as "Not Responding" in the Home app: - // throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); - - return isOn; - } - - /** - * Handle "SET" requests from HomeKit - * These are sent when the user changes the state of an accessory, for example, changing the Brightness - */ - async setBrightness(value: CharacteristicValue) { - // implement your own code to set the brightness - this.exampleStates.Brightness = value as number; - - this.platform.log.debug('Set Characteristic Brightness -> ', value); - } - -} diff --git a/src/platformUtils.ts b/src/platformUtils.ts new file mode 100644 index 0000000..9bad724 --- /dev/null +++ b/src/platformUtils.ts @@ -0,0 +1,37 @@ +export type Config = { + verboseLogging: boolean; + oauthToken: string; + oauthTokenSecret: string; + region: Region; + devices: DeviceConfig[]; +}; + +export type DeviceConfig = { + id: string; + name: string; + isAWSDevice: boolean; + led: boolean; + airQualitySensor: boolean; + co2Sensor: boolean; + temperatureSensor: boolean; + humiditySensor: boolean; + germShield: boolean; + nightMode: boolean; +}; + +export enum Region { + US = 'USA', + EU = 'Europe', + CA = 'Canada', + CN = 'China', +} + +export const RegionMap = { + [Region.US]: 'us', + [Region.EU]: 'eu', + [Region.CA]: 'eu', + [Region.CN]: 'cn', +}; + +export const LOGIN_EXPIRATION = 3600 * 1000 * 12; // n hours in milliseconds +export const DEVICE_UPDATE_INTERVAL = 1000 * 5; // n seconds in milliseconds