From 6ff55f4c99ae2978be42c21f7606db57d8f7811d Mon Sep 17 00:00:00 2001 From: Andrii Vynohradov Date: Sat, 19 Oct 2024 19:56:35 -0700 Subject: [PATCH] Added hue and color temperature support --- .eslintrc | 19 ++- .prettierrc | 6 + .vscode/extensions.json | 6 +- .vscode/settings.json | 4 +- CHANGELOG.md | 2 +- README.md | 33 ++-- config.schema.json | 2 +- nodemon.json | 6 +- package-lock.json | 37 ++++- package.json | 6 +- src/accessory/accessory-controller.ts | 116 ++++++++++--- src/accessory/accessory.ts | 1 - src/accessory/blind.ts | 80 ++++++--- src/accessory/contact-sensor.ts | 79 ++++++--- src/accessory/dimmable-switch.ts | 98 ++++++++--- src/accessory/doorbell.ts | 44 +++-- src/accessory/fan.ts | 99 ++++++++--- src/accessory/garage-door.ts | 109 ++++++++---- src/accessory/light.ts | 210 +++++++++++++++++++---- src/accessory/lock.ts | 99 ++++++++--- src/accessory/motion-sensor.ts | 69 ++++++-- src/accessory/switch.ts | 69 ++++++-- src/accessory/temperature-sensor.ts | 94 ++++++++--- src/accessory/thermostat.ts | 231 +++++++++++++++++++------- src/accessory/tv.ts | 64 +++++-- src/accessory/window-ac-unit.ts | 229 ++++++++++++++++++------- src/api-client.ts | 162 ++++++++++++------ src/constants.ts | 11 +- src/model/sinricpro-device.ts | 30 ++-- src/model/sse.ts | 44 ++--- src/platform.ts | 84 +++++++--- src/settings.ts | 2 +- src/sse-client.ts | 31 +++- src/utils/color-converter.ts | 111 +++++++++++++ src/utils/guid.ts | 6 +- tsconfig.json | 15 +- 36 files changed, 1735 insertions(+), 573 deletions(-) create mode 100644 .prettierrc create mode 100644 src/utils/color-converter.ts diff --git a/.eslintrc b/.eslintrc index d9f3095..7fd08ec 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,16 +2,15 @@ "parser": "@typescript-eslint/parser", "extends": [ "eslint:recommended", + "prettier", "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" // uses the recommended rules from the @typescript-eslint/eslint-plugin + "plugin:@typescript-eslint/recommended", // uses the recommended rules from the @typescript-eslint/eslint-plugin ], "parserOptions": { "ecmaVersion": 2018, - "sourceType": "module" + "sourceType": "module", }, - "ignorePatterns": [ - "dist" - ], + "ignorePatterns": ["dist"], "rules": { "quotes": ["warn", "single"], "indent": ["warn", 2, { "SwitchCase": 1 }], @@ -28,13 +27,17 @@ "comma-spacing": ["error"], "no-multi-spaces": ["warn", { "ignoreEOLComments": true }], "no-trailing-spaces": ["warn"], - "lines-between-class-members": ["warn", "always", {"exceptAfterSingleLine": true}], + "lines-between-class-members": [ + "warn", + "always", + { "exceptAfterSingleLine": true }, + ], "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/semi": ["warn"], "@typescript-eslint/member-delimiter-style": ["warn"], "@typescript-eslint/no-inferrable-types": "off", - "@typescript-eslint/no-explicit-any": "off" - } + "@typescript-eslint/no-explicit-any": "off", + }, } diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..120fa49 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all" +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index fd1d9bf..940260d 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,3 @@ { - "recommendations": [ - "dbaeumer.vscode-eslint" - ] -} \ No newline at end of file + "recommendations": ["dbaeumer.vscode-eslint"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 89d2e29..3ccc820 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,6 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, - "editor.rulers": [ 140 ], + "editor.rulers": [140], "eslint.enable": true -} \ No newline at end of file +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b469db..9824560 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,3 @@ # CHANGELOG -1.0.0 - Support all device types. \ No newline at end of file +1.0.0 - Support all device types. diff --git a/README.md b/README.md index b4b6114..a90c8f3 100644 --- a/README.md +++ b/README.md @@ -20,23 +20,21 @@ This plugin seamlessly exposes all your SinricPro devices to HomeKit, enabling y #### Following devices types are supported: -|Sinric Pro |HomeKit Accessory |Notes -|--- |--- |--- -| `Switch` | Switch | - -| `Blinds` | WindowCovering | - -| `Dimmable Switch` | Lightbulb | - -| `Fan` | Fan | - -| `Garage Door` | GarageDoorOpener | - -| `Light` | Lightbulb | - -| `Lock` | LockMechanism | - -| `Thermostat` | Thermostat | - -| `TV` | Television | On and Off only. -| `AC Unit` | Thermostat | - -| `Temperature Sensor` | TemperatureSensor and HumiditySensor | - -| `Motion Sensor` | MotionSensor | - -| `Contact Sensor` | ContactSensor | - - - +| Sinric Pro | HomeKit Accessory | Notes | +| -------------------- | ------------------------------------ | ---------------- | +| `Switch` | Switch | - | +| `Blinds` | WindowCovering | - | +| `Dimmable Switch` | Lightbulb | - | +| `Fan` | Fan | - | +| `Garage Door` | GarageDoorOpener | - | +| `Light` | Lightbulb | - | +| `Lock` | LockMechanism | - | +| `Thermostat` | Thermostat | - | +| `TV` | Television | On and Off only. | +| `AC Unit` | Thermostat | - | +| `Temperature Sensor` | TemperatureSensor and HumiditySensor | - | +| `Motion Sensor` | MotionSensor | - | +| `Contact Sensor` | ContactSensor | - | #### For Developments @@ -46,6 +44,7 @@ This plugin seamlessly exposes all your SinricPro devices to HomeKit, enabling y 4. npm run watch ## Join the community! + Join us on our [Official Discord Server](https://discord.gg/rq9vcRcSqA)! ### Limitations diff --git a/config.schema.json b/config.schema.json index 2e38408..ecd33d6 100644 --- a/config.schema.json +++ b/config.schema.json @@ -18,4 +18,4 @@ } } } -} \ No newline at end of file +} diff --git a/nodemon.json b/nodemon.json index 6dd7df6..0e0e5ba 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,7 +1,5 @@ { - "watch": [ - "src" - ], + "watch": ["src"], "ext": "ts", "ignore": [], "exec": "tsc && homebridge -I -D", @@ -9,4 +7,4 @@ "env": { "NODE_OPTIONS": "--trace-warnings" } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index e9fbec0..3f170d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "homebridge-sinricpro", - "version": "0.0.1", + "version": "1.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "homebridge-sinricpro", - "version": "0.0.1", - "license": "Apache-2.0", + "version": "1.1.3", + "license": "CC-BY-SA", "dependencies": { "axios": "^1.6.2", "eventsource": "^2.0.2" @@ -17,8 +17,10 @@ "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "eslint": "^8.45.0", + "eslint-config-prettier": "^9.1.0", "homebridge": "^1.6.0", "nodemon": "^2.0.22", + "prettier": "^3.3.3", "rimraf": "^3.0.2", "ts-node": "^10.9.1", "typescript": "^4.9.5" @@ -1054,6 +1056,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -2514,6 +2529,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", diff --git a/package.json b/package.json index ab576d5..9496ff7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "SinricPro", "name": "homebridge-sinricpro", - "version": "1.1.2", + "version": "1.1.3", "description": "Sinric Pro devices support for HomeKit.", "license": "CC-BY-SA", "repository": { @@ -30,7 +30,7 @@ "smartthings", "esp32", "esp8266", - "homebridge-plugin" + "homebridge-plugin" ], "dependencies": { "axios": "^1.6.2", @@ -41,8 +41,10 @@ "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "eslint": "^8.45.0", + "eslint-config-prettier": "^9.1.0", "homebridge": "^1.6.0", "nodemon": "^2.0.22", + "prettier": "^3.3.3", "rimraf": "^3.0.2", "ts-node": "^10.9.1", "typescript": "^4.9.5" diff --git a/src/accessory/accessory-controller.ts b/src/accessory/accessory-controller.ts index 448435e..87dd2a4 100644 --- a/src/accessory/accessory-controller.ts +++ b/src/accessory/accessory-controller.ts @@ -7,13 +7,16 @@ import { CharacteristicValue, PlatformAccessory } from 'homebridge'; import { SinricProPlatform } from '../platform'; +import { ColorConverter } from '../utils/color-converter'; /* Base class to control devices SinricPro via API */ export class AccessoryController { public sinricProDeviceId = ''; - constructor(private readonly _platform: SinricProPlatform, - private readonly _accessory: PlatformAccessory) { + constructor( + private readonly _platform: SinricProPlatform, + private readonly _accessory: PlatformAccessory, + ) { this.sinricProDeviceId = _accessory.context.device.id; } @@ -21,59 +24,134 @@ export class AccessoryController { async setPowerState(value: CharacteristicValue) { const newState = value as boolean; const powerState = newState === true ? 'On' : 'Off'; - this._platform.log.debug(`[setPowerState()]: Set device:${this.sinricProDeviceId} power state to:${powerState}`); - this._platform.sinricProApiClient.setPowerState(this.sinricProDeviceId, powerState ); + this._platform.log.debug( + `[setPowerState()]: Set device:${this.sinricProDeviceId} power state to:${powerState}`, + ); + await this._platform.sinricProApiClient.setPowerState( + this.sinricProDeviceId, + powerState, + ); } /* Set device's brightness */ async setBrightness(value: CharacteristicValue) { const brightness = value as number; - this._platform.sinricProApiClient.setBrightness(this.sinricProDeviceId, brightness); - this._platform.log.debug(`[setBrightness()]: Set brightness (${this.sinricProDeviceId}) brightness to: ${JSON.stringify(value)}`); + await this._platform.sinricProApiClient.setBrightness( + this.sinricProDeviceId, + brightness, + ); + this._platform.log.debug( + `[setBrightness()]: Set brightness (${this.sinricProDeviceId}) to: ${brightness}`, + ); + } + + /* Set device's color */ + async setColor(value: CharacteristicValue) { + const rgb = value as { r: number; g: number; b: number }; + + await this._platform.sinricProApiClient.setColor( + this.sinricProDeviceId, + rgb, + ); + + this._platform.log.debug( + `[setColor()]: Set color (${this.sinricProDeviceId}) to: ${JSON.stringify( + rgb, + )}`, + ); + } + + /* Set device's color temperature */ + async setColorTemperature(value: CharacteristicValue) { + const colorTemperature = value as number; + + await this._platform.sinricProApiClient.setColorTemperature( + this.sinricProDeviceId, + ColorConverter.miredsToKelvin(colorTemperature), + ); + + this._platform.log.debug( + `[setColorTemperature()]: Set color temperature (${this.sinricProDeviceId}) to: ${colorTemperature}`, + ); } /* Set device's power level */ async setPowerLevel(value: CharacteristicValue) { const powerLevel = value as number; - this._platform.sinricProApiClient.setPowerLevel(this.sinricProDeviceId, powerLevel); - this._platform.log.debug(`[setPowerLevel()]: Set powerLevel (${this.sinricProDeviceId}) to: ${JSON.stringify(value)}`); + this._platform.sinricProApiClient.setPowerLevel( + this.sinricProDeviceId, + powerLevel, + ); + this._platform.log.debug( + `[setPowerLevel()]: Set powerLevel (${ + this.sinricProDeviceId + }) to: ${JSON.stringify(value)}`, + ); } /* Set device's numeric property */ async setRangeValue(value: CharacteristicValue) { const rangeValue = value as number; - this._platform.sinricProApiClient.setRangeValue(this.sinricProDeviceId, rangeValue); - this._platform.log.debug(`[setRangeValue()]: Set (${this.sinricProDeviceId}) range value to: ${JSON.stringify(value)}`); + this._platform.sinricProApiClient.setRangeValue( + this.sinricProDeviceId, + rangeValue, + ); + this._platform.log.debug( + `[setRangeValue()]: Set (${ + this.sinricProDeviceId + }) range value to: ${JSON.stringify(value)}`, + ); } /* Set device's target temperature */ async targetTemperature(value: CharacteristicValue) { const temperature = value as number; - this._platform.sinricProApiClient.setTargetTemperature(this.sinricProDeviceId, temperature); - this._platform.log.debug(`[targetTemperature()]: Set targetTemperature (${this.sinricProDeviceId}) to: ${JSON.stringify(value)}`); + this._platform.sinricProApiClient.setTargetTemperature( + this.sinricProDeviceId, + temperature, + ); + this._platform.log.debug( + `[targetTemperature()]: Set targetTemperature (${ + this.sinricProDeviceId + }) to: ${JSON.stringify(value)}`, + ); } /* Set device's mode */ async setMode(mode: string) { this._platform.sinricProApiClient.setMode(this.sinricProDeviceId, mode); - this._platform.log.debug(`[setMode()]: Set mode (${this.sinricProDeviceId}) to: ${mode}`); + this._platform.log.debug( + `[setMode()]: Set mode (${this.sinricProDeviceId}) to: ${mode}`, + ); } /* Set device's lock state */ async setLockState(state: string) { - this._platform.sinricProApiClient.setLockState(this.sinricProDeviceId, state); - this._platform.log.debug(`[setLockState()]: Set lock state (${this.sinricProDeviceId}) to: ${state}`); + this._platform.sinricProApiClient.setLockState( + this.sinricProDeviceId, + state, + ); + this._platform.log.debug( + `[setLockState()]: Set lock state (${this.sinricProDeviceId}) to: ${state}`, + ); } /* Send door-bell notification */ async setDoorbellPress() { this._platform.sinricProApiClient.setDoorbellPress(this.sinricProDeviceId); - this._platform.log.debug(`[setDoorbellPress()]: Send doorbell (${this.sinricProDeviceId}) pressed`); + this._platform.log.debug( + `[setDoorbellPress()]: Send doorbell (${this.sinricProDeviceId}) pressed`, + ); } /* Set thermostat mode */ async setThermostatMode(value: string) { - this._platform.sinricProApiClient.setThermostatMode(this.sinricProDeviceId, value); - this._platform.log.debug(`[setThermostatMode()]: Set thermostat mode (${this.sinricProDeviceId}) to: ${value}`); + this._platform.sinricProApiClient.setThermostatMode( + this.sinricProDeviceId, + value, + ); + this._platform.log.debug( + `[setThermostatMode()]: Set thermostat mode (${this.sinricProDeviceId}) to: ${value}`, + ); } -} \ No newline at end of file +} diff --git a/src/accessory/accessory.ts b/src/accessory/accessory.ts index 9d107f6..707a6b2 100644 --- a/src/accessory/accessory.ts +++ b/src/accessory/accessory.ts @@ -9,4 +9,3 @@ export interface SinricProAccessory { sinricProDeviceId: string; updateState(action: string, value: any): void; } - diff --git a/src/accessory/blind.ts b/src/accessory/blind.ts index 5c726c0..61880f1 100644 --- a/src/accessory/blind.ts +++ b/src/accessory/blind.ts @@ -16,7 +16,10 @@ import { ActionConstants, ModelConstants } from '../constants'; * Sinric Pro - Blind * homebridge https://developers.homebridge.io/#/service/WindowCovering */ -export class SinricProBlind extends AccessoryController implements SinricProAccessory { +export class SinricProBlind + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private states = { @@ -30,24 +33,44 @@ export class SinricProBlind extends AccessoryController implements SinricProAcce ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.SWITCH_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); - - this.platform.log.debug('[SinricProBlind()]: Adding device:', this.accessory.displayName, accessory.context.device); - - this.service = this.accessory.getService(this.platform.Service.WindowCovering) - ?? this.accessory.addService(this.platform.Service.WindowCovering); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.SWITCH_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); + + this.platform.log.debug( + '[SinricProBlind()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); + + this.service = + this.accessory.getService(this.platform.Service.WindowCovering) ?? + this.accessory.addService(this.platform.Service.WindowCovering); this.service.setPrimaryService(true); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); // register handlers for the characteristic this.service.getCharacteristic(this.platform.Characteristic.PositionState); - this.service.getCharacteristic(this.platform.Characteristic.CurrentPosition) + this.service + .getCharacteristic(this.platform.Characteristic.CurrentPosition) .onGet(this.getCurrentPostion.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.TargetPosition) + this.service + .getCharacteristic(this.platform.Characteristic.TargetPosition) .onSet(this.setTargetPostion.bind(this)); // restore present device state. @@ -60,18 +83,33 @@ export class SinricProBlind extends AccessoryController implements SinricProAcce * @param value - {"rangeValue":100} */ public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); if (action === ActionConstants.SET_RANGE_VALUE) { this.states.currentPosition = value.rangeValue; this.accessory.context.device.rangeValue = value.rangeValue; - this.service.updateCharacteristic(this.platform.Characteristic.CurrentPosition, value.rangeValue); + this.service.updateCharacteristic( + this.platform.Characteristic.CurrentPosition, + value.rangeValue, + ); } } setTargetPostion(value: CharacteristicValue): void { const tmpValue = value as number; - this.platform.log.debug('[setTargetPostion()]: device:', this.accessory.displayName, ', value=', tmpValue); + this.platform.log.debug( + '[setTargetPostion()]: device:', + this.accessory.displayName, + ', value=', + tmpValue, + ); if (this.states.targetPosition !== tmpValue) { this.setRangeValue(value); @@ -79,8 +117,12 @@ export class SinricProBlind extends AccessoryController implements SinricProAcce } getCurrentPostion(): CharacteristicValue { - this.platform.log.debug('[getCurrentPostion()]: device:', this.accessory.displayName, ', currentPosition=', this.states.currentPosition); + this.platform.log.debug( + '[getCurrentPostion()]: device:', + this.accessory.displayName, + ', currentPosition=', + this.states.currentPosition, + ); return this.states.currentPosition; } - -} \ No newline at end of file +} diff --git a/src/accessory/contact-sensor.ts b/src/accessory/contact-sensor.ts index 616d588..eaf9085 100644 --- a/src/accessory/contact-sensor.ts +++ b/src/accessory/contact-sensor.ts @@ -15,11 +15,15 @@ import { ActionConstants, ModelConstants } from '../constants'; * Sinric Pro - Contact Sensor * https://developers.homebridge.io/#/service/ContactSensor */ -export class SinricProContactSensor extends AccessoryController implements SinricProAccessory { +export class SinricProContactSensor + extends AccessoryController + implements SinricProAccessory +{ private readonly service: Service; private states = { - contactState: this.platform.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED, + contactState: + this.platform.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED, }; constructor( @@ -28,17 +32,35 @@ export class SinricProContactSensor extends AccessoryController implements Sinri ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.CONTACT_SENSOR_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.CONTACT_SENSOR_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); - this.platform.log.debug('[SinricProContactSensor()]:', this.accessory.displayName, accessory.context.device); + this.platform.log.debug( + '[SinricProContactSensor()]:', + this.accessory.displayName, + accessory.context.device, + ); - this.service = this.accessory.getService(this.platform.Service.ContactSensor) - ?? this.accessory.addService(this.platform.Service.ContactSensor); + this.service = + this.accessory.getService(this.platform.Service.ContactSensor) ?? + this.accessory.addService(this.platform.Service.ContactSensor); - this.service.setCharacteristic(this.platform.Characteristic.Name, `${accessory.context.device.name} Contact Sensor`); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + `${accessory.context.device.name} Contact Sensor`, + ); // register handlers for Characteristic this.service @@ -46,21 +68,30 @@ export class SinricProContactSensor extends AccessoryController implements Sinri .onGet(this.getContactState.bind(this)); // restore present device state - this.states.contactState = this.toContactSensorState(accessory.context.device.contactState); - this.service.getCharacteristic(this.platform.Characteristic.ContactSensorState).updateValue(this.states.contactState); + this.states.contactState = this.toContactSensorState( + accessory.context.device.contactState, + ); + this.service + .getCharacteristic(this.platform.Characteristic.ContactSensorState) + .updateValue(this.states.contactState); } /** * Convert SinricPro contact state to Homebridge contact state */ toContactSensorState(contactState: string) { - return contactState !== 'open' ? - this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED + return contactState !== 'open' + ? this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED : this.platform.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; } getContactState(): CharacteristicValue { - this.platform.log.debug('getContactState:', this.accessory.displayName, '=', this.states.contactState); + this.platform.log.debug( + 'getContactState:', + this.accessory.displayName, + '=', + this.states.contactState, + ); return this.states.contactState; } @@ -70,11 +101,21 @@ export class SinricProContactSensor extends AccessoryController implements Sinri * @param value - "state": "open" or "state": "closed" */ updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); - if(action === ActionConstants.SET_CONTACT_STATE) { + if (action === ActionConstants.SET_CONTACT_STATE) { this.states.contactState = this.toContactSensorState(value.state); - this.service.updateCharacteristic(this.platform.Characteristic.ContactSensorState, this.states.contactState); + this.service.updateCharacteristic( + this.platform.Characteristic.ContactSensorState, + this.states.contactState, + ); } } -} \ No newline at end of file +} diff --git a/src/accessory/dimmable-switch.ts b/src/accessory/dimmable-switch.ts index fc61f54..7574bdb 100644 --- a/src/accessory/dimmable-switch.ts +++ b/src/accessory/dimmable-switch.ts @@ -11,12 +11,14 @@ import { SinricProAccessory } from './accessory'; import { AccessoryController } from './accessory-controller'; import { ActionConstants, ModelConstants } from '../constants'; - /** * Sinric Pro - Dimmer Switch * https://developers.homebridge.io/#/service/Lightbulb */ -export class SinricProDimmableSwitch extends AccessoryController implements SinricProAccessory { +export class SinricProDimmableSwitch + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private dimmerStates = { @@ -30,26 +32,46 @@ export class SinricProDimmableSwitch extends AccessoryController implements Sinr ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.DIMMABLE_SWIRCH_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); - - this.platform.log.debug('[SinricProDimmableSwitch()]: Adding device:', this.accessory.displayName, accessory.context.device); - - this.service = this.accessory.getService(this.platform.Service.Lightbulb) - ?? this.accessory.addService(this.platform.Service.Lightbulb); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.DIMMABLE_SWIRCH_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); + + this.platform.log.debug( + '[SinricProDimmableSwitch()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); + + this.service = + this.accessory.getService(this.platform.Service.Lightbulb) ?? + this.accessory.addService(this.platform.Service.Lightbulb); this.service.setPrimaryService(true); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); // register handlers for the On/Off Characteristic - this.service.getCharacteristic(this.platform.Characteristic.On) + this.service + .getCharacteristic(this.platform.Characteristic.On) .onSet(this.setPowerState.bind(this)) .onGet(this.getPowerState.bind(this)); // register handlers for the Brightness Characteristic - this.service.getCharacteristic(this.platform.Characteristic.Brightness) + this.service + .getCharacteristic(this.platform.Characteristic.Brightness) .setProps({ minValue: 1, maxValue: 100, @@ -60,29 +82,53 @@ export class SinricProDimmableSwitch extends AccessoryController implements Sinr // restore present device state. this.dimmerStates.powerLevel = accessory.context.device.powerLevel ?? 100; - this.dimmerStates.on = ('ON' === accessory.context.device.powerState?.toUpperCase()); - + this.dimmerStates.on = + 'ON' === accessory.context.device.powerState?.toUpperCase(); } public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); - - if(action === ActionConstants.SET_POWER_STATE) { - this.dimmerStates.on = ('ON' === value.state.toUpperCase()); - this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.dimmerStates.on); - } else if(action === ActionConstants.SET_POWER_LEVEL || ActionConstants.ADJUST_POWER_LEVEL) { + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); + + if (action === ActionConstants.SET_POWER_STATE) { + this.dimmerStates.on = 'ON' === value.state.toUpperCase(); + this.service + .getCharacteristic(this.platform.Characteristic.On) + .updateValue(this.dimmerStates.on); + } else if ( + action === ActionConstants.SET_POWER_LEVEL || + ActionConstants.ADJUST_POWER_LEVEL + ) { this.dimmerStates.powerLevel = value.powerLevel; - this.service.getCharacteristic(this.platform.Characteristic.Brightness).updateValue(this.dimmerStates.powerLevel); + this.service + .getCharacteristic(this.platform.Characteristic.Brightness) + .updateValue(this.dimmerStates.powerLevel); } } getPowerState(): CharacteristicValue { - this.platform.log.debug('[getPowerState()]:', this.accessory.displayName, '=', this.dimmerStates.on); + this.platform.log.debug( + '[getPowerState()]:', + this.accessory.displayName, + '=', + this.dimmerStates.on, + ); return this.dimmerStates.on; } getPowerLevel(): CharacteristicValue { - this.platform.log.debug('[getPowerLevel()]:', this.accessory.displayName, '=', this.dimmerStates.powerLevel); + this.platform.log.debug( + '[getPowerLevel()]:', + this.accessory.displayName, + '=', + this.dimmerStates.powerLevel, + ); return this.dimmerStates.powerLevel; } -} \ No newline at end of file +} diff --git a/src/accessory/doorbell.ts b/src/accessory/doorbell.ts index 62f41ab..365a86e 100644 --- a/src/accessory/doorbell.ts +++ b/src/accessory/doorbell.ts @@ -17,7 +17,10 @@ import { ModelConstants } from '../constants'; * * AppleHome app shows device is not supported. */ -export class SinricProDoorbell extends AccessoryController implements SinricProAccessory { +export class SinricProDoorbell + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private states = { @@ -30,17 +33,35 @@ export class SinricProDoorbell extends AccessoryController implements SinricProA ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.DOORBELL_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.DOORBELL_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); - this.platform.log.debug('[SinricProDoorbell()]: Adding device:', this.accessory.displayName, accessory.context.device); + this.platform.log.debug( + '[SinricProDoorbell()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); - this.service = this.accessory.getService(this.platform.Service.Doorbell) - ?? this.accessory.addService(this.platform.Service.Doorbell); + this.service = + this.accessory.getService(this.platform.Service.Doorbell) ?? + this.accessory.addService(this.platform.Service.Doorbell); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); this.service.setPrimaryService(true); // register handlers for characteristics @@ -57,7 +78,8 @@ export class SinricProDoorbell extends AccessoryController implements SinricProA handleProgrammableSwitchEventGet() { //this.setDoorbellPress(); - const currentValue = this.platform.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS; + const currentValue = + this.platform.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS; return currentValue; } -} \ No newline at end of file +} diff --git a/src/accessory/fan.ts b/src/accessory/fan.ts index 7db4372..b27c5f1 100644 --- a/src/accessory/fan.ts +++ b/src/accessory/fan.ts @@ -15,7 +15,10 @@ import { ActionConstants, ModelConstants } from '../constants'; * Sinric Pro - Fan * https://developers.homebridge.io/#/service/Fanv2 */ -export class SinricProFan extends AccessoryController implements SinricProAccessory { +export class SinricProFan + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private fanState = { @@ -29,34 +32,57 @@ export class SinricProFan extends AccessoryController implements SinricProAccess ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.FAN_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); - - this.platform.log.debug('[SinricProFan()]: Adding device:', this.accessory.displayName, accessory.context.device); - - this.service = this.accessory.getService(this.platform.Service.Fan) - ?? this.accessory.addService(this.platform.Service.Fan); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.FAN_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); + + this.platform.log.debug( + '[SinricProFan()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); + + this.service = + this.accessory.getService(this.platform.Service.Fan) ?? + this.accessory.addService(this.platform.Service.Fan); this.service.setPrimaryService(true); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); - this.fanState.on = ('ON' === accessory.context.device.powerState?.toUpperCase()); + this.fanState.on = + 'ON' === accessory.context.device.powerState?.toUpperCase(); // register handlers for the On/Off Characteristic - this.service.getCharacteristic(this.platform.Characteristic.On) + this.service + .getCharacteristic(this.platform.Characteristic.On) .onSet(this.setPowerState.bind(this)) .onGet(this.getPowerState.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.RotationSpeed) + this.service + .getCharacteristic(this.platform.Characteristic.RotationSpeed) .onGet(this.getRotationSpeed.bind(this)) .onSet(this.setRotationSpeed.bind(this)); // restore present device state. - this.fanState.on = ('ON' === accessory.context.device.powerState?.toUpperCase()); - if(this.fanState.on) { - this.fanState.rotationSpeed = this.accessory.context.device.rangeValue ?? 1; + this.fanState.on = + 'ON' === accessory.context.device.powerState?.toUpperCase(); + if (this.fanState.on) { + this.fanState.rotationSpeed = + this.accessory.context.device.rangeValue ?? 1; } } @@ -66,28 +92,49 @@ export class SinricProFan extends AccessoryController implements SinricProAccess * @param value - {"state":"Off"}, "{"value" : 1}" */ public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); - - if(action === ActionConstants.SET_POWER_STATE) { + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); + + if (action === ActionConstants.SET_POWER_STATE) { this.fanState.on = 'ON' === value.state.toUpperCase(); - this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.fanState.on); - } else if(action === ActionConstants.SET_RANGE_VALUE) { + this.service + .getCharacteristic(this.platform.Characteristic.On) + .updateValue(this.fanState.on); + } else if (action === ActionConstants.SET_RANGE_VALUE) { this.fanState.rotationSpeed = value.rangeValue; - this.service.getCharacteristic(this.platform.Characteristic.RotationSpeed).updateValue(this.fanState.rotationSpeed); + this.service + .getCharacteristic(this.platform.Characteristic.RotationSpeed) + .updateValue(this.fanState.rotationSpeed); } } private getPowerState(): CharacteristicValue { - this.platform.log.debug('getPowerState:', this.accessory.displayName, '=', this.fanState.on); + this.platform.log.debug( + 'getPowerState:', + this.accessory.displayName, + '=', + this.fanState.on, + ); return this.fanState.on; } private getRotationSpeed(): CharacteristicValue { - this.platform.log.info('getRotationSpeed:', this.accessory.displayName, 'is currently', this.fanState.rotationSpeed); + this.platform.log.info( + 'getRotationSpeed:', + this.accessory.displayName, + 'is currently', + this.fanState.rotationSpeed, + ); return this.fanState.rotationSpeed; } private setRotationSpeed(value: CharacteristicValue): void { super.setRangeValue(value); } -} \ No newline at end of file +} diff --git a/src/accessory/garage-door.ts b/src/accessory/garage-door.ts index 402ac35..b2c734d 100644 --- a/src/accessory/garage-door.ts +++ b/src/accessory/garage-door.ts @@ -15,7 +15,10 @@ import { ModelConstants, ActionConstants } from '../constants'; * Sinric Pro - Garage Door * https://developers.homebridge.io/#/service/GarageDoorOpener */ -export class SinricProGarageDoor extends AccessoryController implements SinricProAccessory { +export class SinricProGarageDoor + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private states = { @@ -29,32 +32,56 @@ export class SinricProGarageDoor extends AccessoryController implements SinricPr ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.GARAGE_DOOR_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); - - this.platform.log.debug('[SinricProGarageDoor()]: Adding device:', this.accessory.displayName, accessory.context.device); - - this.service = this.accessory.getService(this.platform.Service.GarageDoorOpener) - ?? this.accessory.addService(this.platform.Service.GarageDoorOpener); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.GARAGE_DOOR_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); + + this.platform.log.debug( + '[SinricProGarageDoor()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); + + this.service = + this.accessory.getService(this.platform.Service.GarageDoorOpener) ?? + this.accessory.addService(this.platform.Service.GarageDoorOpener); this.service.setPrimaryService(true); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); // register handlers for the characteristic - this.service.getCharacteristic(this.platform.Characteristic.CurrentDoorState) + this.service + .getCharacteristic(this.platform.Characteristic.CurrentDoorState) .onGet(this.getCurrentDoorState.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.TargetDoorState) + this.service + .getCharacteristic(this.platform.Characteristic.TargetDoorState) .onSet(this.setTargetState.bind(this)) .onGet(this.getTargetState.bind(this)); // restore present device state. - if('OPEN' === this.accessory.context.device.garageDoorState?.toUpperCase()) { - this.states.currentState = this.platform.Characteristic.TargetDoorState.OPEN; + if ( + 'OPEN' === this.accessory.context.device.garageDoorState?.toUpperCase() + ) { + this.states.currentState = + this.platform.Characteristic.TargetDoorState.OPEN; } else { - this.states.currentState = this.platform.Characteristic.TargetDoorState.CLOSED; + this.states.currentState = + this.platform.Characteristic.TargetDoorState.CLOSED; } } @@ -64,22 +91,38 @@ export class SinricProGarageDoor extends AccessoryController implements SinricPr * @param value - {"mode":"Open"} or {"mode":"Close"} */ public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); - - if(action === ActionConstants.SET_MODE) { - if('OPEN' === value.mode?.toUpperCase()) { - this.states.currentState = this.platform.Characteristic.TargetDoorState.OPEN; + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); + + if (action === ActionConstants.SET_MODE) { + if ('OPEN' === value.mode?.toUpperCase()) { + this.states.currentState = + this.platform.Characteristic.TargetDoorState.OPEN; } else { - this.states.currentState = this.platform.Characteristic.TargetDoorState.CLOSED; + this.states.currentState = + this.platform.Characteristic.TargetDoorState.CLOSED; } - this.service.getCharacteristic(this.platform.Characteristic.TargetDoorState).updateValue(this.states.currentState); + this.service + .getCharacteristic(this.platform.Characteristic.TargetDoorState) + .updateValue(this.states.currentState); this.accessory.context.device.garageDoorState = value.mode; } } setTargetState(value: CharacteristicValue) { const tmpValue = value as number; - this.platform.log.debug('[setTargetState()]:', this.accessory.displayName, '=', tmpValue); + this.platform.log.debug( + '[setTargetState()]:', + this.accessory.displayName, + '=', + tmpValue, + ); if (this.states.targetState !== tmpValue) { this.states.targetState = tmpValue; @@ -87,14 +130,24 @@ export class SinricProGarageDoor extends AccessoryController implements SinricPr } } - getTargetState() : CharacteristicValue { + getTargetState(): CharacteristicValue { const State = this.states.targetState; - this.platform.log.info('[getTargetState()]:', this.accessory.displayName, 'targetState', State); + this.platform.log.info( + '[getTargetState()]:', + this.accessory.displayName, + 'targetState', + State, + ); return State; } getCurrentDoorState(): CharacteristicValue { - this.platform.log.info('[getCurrentDoorState()]:', this.accessory.displayName, 'currentState', this.states.currentState); + this.platform.log.info( + '[getCurrentDoorState()]:', + this.accessory.displayName, + 'currentState', + this.states.currentState, + ); return this.states.currentState; } -} \ No newline at end of file +} diff --git a/src/accessory/light.ts b/src/accessory/light.ts index cecfe93..c460d6c 100644 --- a/src/accessory/light.ts +++ b/src/accessory/light.ts @@ -2,7 +2,7 @@ * Copyright (c) 2019-2023 Sinric. All rights reserved. * Licensed under Creative Commons Attribution-Share Alike (CC BY-SA) * - * This file is part of the Sinric Pro - Homebridge Plugin (https://github.com/sinricpro/homebridge-sinricpro) + * This file is part of the Sinric Pro - Homebridge Plugin */ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; @@ -10,73 +10,215 @@ import { SinricProPlatform } from '../platform'; import { SinricProAccessory } from './accessory'; import { AccessoryController } from './accessory-controller'; import { ModelConstants, ActionConstants } from '../constants'; +import { ColorConverter } from '../utils/color-converter'; /** * Sinric Pro - Light * https://developers.homebridge.io/#/service/Lightbulb */ -export class SinricProLight extends AccessoryController implements SinricProAccessory { +export class SinricProLight + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private lightStates = { on: false, brightness: 100, + hue: 0, + saturation: 0, + colorTemperature: 2700, }; constructor( private readonly platform: SinricProPlatform, private readonly accessory: PlatformAccessory, ) { - super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.LIGHT_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); - - this.platform.log.debug('[SinricProLight()]: Adding device:', this.accessory.displayName, accessory.context.device); - - this.service = this.accessory.getService(this.platform.Service.Lightbulb) - ?? this.accessory.addService(this.platform.Service.Lightbulb); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.LIGHT_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); + + this.platform.log.debug( + '[SinricProLight()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); + + this.service = + this.accessory.getService(this.platform.Service.Lightbulb) ?? + this.accessory.addService(this.platform.Service.Lightbulb); this.service.setPrimaryService(true); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); - - // register handlers for the On/Off Characteristic - this.service.getCharacteristic(this.platform.Characteristic.On) + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); + + // Register handlers for the On/Off Characteristic + this.service + .getCharacteristic(this.platform.Characteristic.On) .onSet(this.setPowerState.bind(this)) .onGet(this.getPowerState.bind(this)); - // register handlers for the Brightness Characteristic - this.service.getCharacteristic(this.platform.Characteristic.Brightness) - .onGet(this.getBrightness.bind(this)) - .onSet(this.setBrightness.bind(this)); - - // restore present device state. + // Register handlers for the Brightness Characteristic + this.service + .getCharacteristic(this.platform.Characteristic.Brightness) + .onSet(this.setBrightness.bind(this)) + .onGet(this.getBrightness.bind(this)); + + // Register handlers for Hue and Saturation Characteristics + this.service + .getCharacteristic(this.platform.Characteristic.Hue) + .onSet(this.setHue.bind(this)) + .onGet(this.getHue.bind(this)); + + this.service + .getCharacteristic(this.platform.Characteristic.Saturation) + .onSet(this.setSaturation.bind(this)) + .onGet(this.getSaturation.bind(this)); + + // Register handler for Color Temperature Characteristic + this.service + .getCharacteristic(this.platform.Characteristic.ColorTemperature) + .onSet(this.setColorTemperature.bind(this)) + .onGet(this.getColorTemperature.bind(this)); + + // Restore present device state. this.lightStates.brightness = accessory.context.device.brightness ?? 100; - this.lightStates.on = ('ON' === accessory.context.device.powerState?.toUpperCase()); + this.lightStates.on = + 'ON' === accessory.context.device.powerState?.toUpperCase(); + this.lightStates.hue = accessory.context.device.hue ?? 0; + this.lightStates.saturation = accessory.context.device.saturation ?? 0; + this.lightStates.colorTemperature = ColorConverter.miredsToKelvin( + accessory.context.device.colorTemperature ?? 140, + ); } public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); - - if(action === ActionConstants.SET_POWER_STATE) { - this.lightStates.on = ('ON' === value.state.toUpperCase()); - this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.lightStates.on); - } else if(action === ActionConstants.SET_BRIGHTNESS) { - this.lightStates.brightness =value.brightness; - this.service.getCharacteristic(this.platform.Characteristic.Brightness).updateValue(this.lightStates.brightness); + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); + + if (action === ActionConstants.SET_POWER_STATE) { + this.lightStates.on = 'ON' === value.state.toUpperCase(); + this.service + .getCharacteristic(this.platform.Characteristic.On) + .updateValue(this.lightStates.on); + } else if (action === ActionConstants.SET_BRIGHTNESS) { + this.lightStates.brightness = value.brightness; + this.service + .getCharacteristic(this.platform.Characteristic.Brightness) + .updateValue(this.lightStates.brightness); + } else if (action === ActionConstants.SET_COLOR) { + const { r, g, b } = value.color; + const hsv = ColorConverter.rgbToHsv(r, g, b); + this.lightStates.hue = hsv.hue; + this.lightStates.saturation = hsv.saturation; + this.service + .getCharacteristic(this.platform.Characteristic.Hue) + .updateValue(this.lightStates.hue); + this.service + .getCharacteristic(this.platform.Characteristic.Saturation) + .updateValue(this.lightStates.saturation); + } else if (action === ActionConstants.SET_COLOR_TEMPERATURE) { + this.lightStates.colorTemperature = value.colorTemperature; + this.service + .getCharacteristic(this.platform.Characteristic.ColorTemperature) + .updateValue( + ColorConverter.kelvinToMireds(this.lightStates.colorTemperature), + ); } } getPowerState(): CharacteristicValue { - this.platform.log.debug('[getPowerState()]:', this.accessory.displayName, '=', this.lightStates.on); + this.platform.log.debug( + '[getPowerState()]:', + this.accessory.displayName, + '=', + this.lightStates.on, + ); return this.lightStates.on; } getBrightness(): CharacteristicValue { - this.platform.log.debug('[getBrightness()]:', this.accessory.displayName, '=', this.lightStates.brightness); + this.platform.log.debug( + '[getBrightness()]:', + this.accessory.displayName, + '=', + this.lightStates.brightness, + ); return this.lightStates.brightness; } -} \ No newline at end of file + + getHue(): CharacteristicValue { + this.platform.log.debug( + '[getHue()]:', + this.accessory.displayName, + '=', + this.lightStates.hue, + ); + return this.lightStates.hue; + } + + getSaturation(): CharacteristicValue { + this.platform.log.debug( + '[getSaturation()]:', + this.accessory.displayName, + '=', + this.lightStates.saturation, + ); + return this.lightStates.saturation; + } + + getColorTemperature(): CharacteristicValue { + this.platform.log.debug( + '[getColorTemperature()]:', + this.accessory.displayName, + '=', + this.lightStates.colorTemperature, + ); + return ColorConverter.kelvinToMireds(this.lightStates.colorTemperature); + } + + // Add setHue method + async setHue(value: CharacteristicValue) { + const hue = value as number; + + // Update the local state + this.lightStates.hue = hue; + + // Retrieve current saturation value + const saturation = this.lightStates.saturation; + + // Convert HSV to RGB + const rgb = ColorConverter.hsvToRgb(hue, saturation, 100); + + await this.setColor(rgb); + } + + // Add setSaturation method + async setSaturation(value: CharacteristicValue) { + const saturation = value as number; + + // Update the local state + this.lightStates.saturation = saturation; + } +} diff --git a/src/accessory/lock.ts b/src/accessory/lock.ts index 6d7caf1..bd4bb61 100644 --- a/src/accessory/lock.ts +++ b/src/accessory/lock.ts @@ -15,7 +15,10 @@ import { ModelConstants, ActionConstants } from '../constants'; * Sinric Pro - Lock * https://developers.homebridge.io/#/service/LockMechanism */ -export class SinricProLock extends AccessoryController implements SinricProAccessory { +export class SinricProLock + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private states = { @@ -29,31 +32,54 @@ export class SinricProLock extends AccessoryController implements SinricProAcces ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.LOCK_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); - - this.platform.log.debug('[SinricProLock()]: Adding device:', this.accessory.displayName, accessory.context.device); - - this.service = this.accessory.getService(this.platform.Service.LockMechanism) - ?? this.accessory.addService(this.platform.Service.LockMechanism); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.LOCK_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); + + this.platform.log.debug( + '[SinricProLock()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); + + this.service = + this.accessory.getService(this.platform.Service.LockMechanism) ?? + this.accessory.addService(this.platform.Service.LockMechanism); this.service.setPrimaryService(true); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); // register handlers for the characteristic - this.service.getCharacteristic(this.platform.Characteristic.LockCurrentState); + this.service.getCharacteristic( + this.platform.Characteristic.LockCurrentState, + ); - this.service.getCharacteristic(this.platform.Characteristic.LockTargetState) + this.service + .getCharacteristic(this.platform.Characteristic.LockTargetState) .onSet(this.setLockTargetState.bind(this)) .onGet(this.getLockTargetState.bind(this)); // restore present device state. - if('LOCKED' === this.accessory.context.device.lockState?.toUpperCase()) { - this.states.lockCurrentState = this.platform.Characteristic.LockCurrentState.SECURED; + if ('LOCKED' === this.accessory.context.device.lockState?.toUpperCase()) { + this.states.lockCurrentState = + this.platform.Characteristic.LockCurrentState.SECURED; } else { - this.states.lockCurrentState = this.platform.Characteristic.LockCurrentState.UNSECURED; + this.states.lockCurrentState = + this.platform.Characteristic.LockCurrentState.UNSECURED; } } @@ -63,23 +89,39 @@ export class SinricProLock extends AccessoryController implements SinricProAcces * @param value - { mode: 'lock' } */ public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); - - if(action === ActionConstants.SET_LOCK_STATE) { - if(('LOCKED' === value.state?.toUpperCase())) { - this.states.lockCurrentState = this.platform.Characteristic.LockCurrentState.SECURED; + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); + + if (action === ActionConstants.SET_LOCK_STATE) { + if ('LOCKED' === value.state?.toUpperCase()) { + this.states.lockCurrentState = + this.platform.Characteristic.LockCurrentState.SECURED; } else { - this.states.lockCurrentState = this.platform.Characteristic.LockCurrentState.UNSECURED; + this.states.lockCurrentState = + this.platform.Characteristic.LockCurrentState.UNSECURED; } - this.service.getCharacteristic(this.platform.Characteristic.LockCurrentState).updateValue(this.states.lockCurrentState); + this.service + .getCharacteristic(this.platform.Characteristic.LockCurrentState) + .updateValue(this.states.lockCurrentState); this.accessory.context.device.lockState = value.state; } } async setLockTargetState(value: CharacteristicValue) { const tmpValue = value as number; - this.platform.log.debug('[setLockTargetState()]:', this.accessory.displayName, '=', tmpValue); + this.platform.log.debug( + '[setLockTargetState()]:', + this.accessory.displayName, + '=', + tmpValue, + ); if (this.states.lockTargetState !== tmpValue) { this.states.lockTargetState = tmpValue; @@ -89,7 +131,12 @@ export class SinricProLock extends AccessoryController implements SinricProAcces getLockTargetState(): CharacteristicValue { const isLock = this.states.lockCurrentState; - this.platform.log.debug('[getLockTargetState()]:', this.accessory.displayName, '=', isLock); + this.platform.log.debug( + '[getLockTargetState()]:', + this.accessory.displayName, + '=', + isLock, + ); return isLock; } -} \ No newline at end of file +} diff --git a/src/accessory/motion-sensor.ts b/src/accessory/motion-sensor.ts index b0db507..bc632e8 100644 --- a/src/accessory/motion-sensor.ts +++ b/src/accessory/motion-sensor.ts @@ -15,7 +15,10 @@ import { ActionConstants, ModelConstants } from '../constants'; * Sinric Pro - Motion Sensor * https://developers.homebridge.io/#/service/MotionSensor */ -export class SinricProMotionSensor extends AccessoryController implements SinricProAccessory { +export class SinricProMotionSensor + extends AccessoryController + implements SinricProAccessory +{ private readonly service: Service; private states = { @@ -28,17 +31,35 @@ export class SinricProMotionSensor extends AccessoryController implements Sinric ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.MOTION_SENSOR_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.MOTION_SENSOR_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); - this.platform.log.debug('[SinricProMotionSensor()]: Adding device:', this.accessory.displayName, accessory.context.device); + this.platform.log.debug( + '[SinricProMotionSensor()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); - this.service = this.accessory.getService(this.platform.Service.MotionSensor) - ?? this.accessory.addService(this.platform.Service.MotionSensor); + this.service = + this.accessory.getService(this.platform.Service.MotionSensor) ?? + this.accessory.addService(this.platform.Service.MotionSensor); - this.service.setCharacteristic(this.platform.Characteristic.Name, `${accessory.context.device.name} Motion Sensor`); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + `${accessory.context.device.name} Motion Sensor`, + ); // register handlers for Characteristic this.service @@ -46,11 +67,17 @@ export class SinricProMotionSensor extends AccessoryController implements Sinric .onGet(this.getMotionDetected.bind(this)); // restore present device state. - this.states.motionDetected = (accessory.context.device.lastMotionState === 'detected' ? 1 : 0); + this.states.motionDetected = + accessory.context.device.lastMotionState === 'detected' ? 1 : 0; } getMotionDetected(): CharacteristicValue { - this.platform.log.debug('getMotionDetected:', this.accessory.displayName, '=', this.states.motionDetected); + this.platform.log.debug( + 'getMotionDetected:', + this.accessory.displayName, + '=', + this.states.motionDetected, + ); return this.states.motionDetected; } @@ -60,12 +87,22 @@ export class SinricProMotionSensor extends AccessoryController implements Sinric * @param value - "state": "detected" or "state": "notDetected" */ updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); - if(action === ActionConstants.MOTION) { - this.states.motionDetected = (value.state === 'detected' ? 1 : 0); + if (action === ActionConstants.MOTION) { + this.states.motionDetected = value.state === 'detected' ? 1 : 0; } - this.service.updateCharacteristic(this.platform.Characteristic.MotionDetected, this.states.motionDetected); + this.service.updateCharacteristic( + this.platform.Characteristic.MotionDetected, + this.states.motionDetected, + ); } -} \ No newline at end of file +} diff --git a/src/accessory/switch.ts b/src/accessory/switch.ts index d8ed183..c4b4032 100644 --- a/src/accessory/switch.ts +++ b/src/accessory/switch.ts @@ -15,7 +15,10 @@ import { ActionConstants, ModelConstants } from '../constants'; * Sinric Pro - Switch * https://developers.homebridge.io/#/service/Switch */ -export class SinricProSwitch extends AccessoryController implements SinricProAccessory { +export class SinricProSwitch + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private switchStates = { @@ -28,26 +31,46 @@ export class SinricProSwitch extends AccessoryController implements SinricProAcc ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.SWITCH_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.SWITCH_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); - this.platform.log.debug('[SinricProSwitch()]: Adding device:', this.accessory.displayName, accessory.context.device); + this.platform.log.debug( + '[SinricProSwitch()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); - this.service = this.accessory.getService(this.platform.Service.Switch) - ?? this.accessory.addService(this.platform.Service.Switch); + this.service = + this.accessory.getService(this.platform.Service.Switch) ?? + this.accessory.addService(this.platform.Service.Switch); this.service.setPrimaryService(true); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); // register handlers for the On/Off Characteristic - this.service.getCharacteristic(this.platform.Characteristic.On) + this.service + .getCharacteristic(this.platform.Characteristic.On) .onSet(this.setPowerState.bind(this)) .onGet(this.getPowerState.bind(this)); // restore present device state. - this.switchStates.on = ('ON' === accessory.context.device.powerState?.toUpperCase()); + this.switchStates.on = + 'ON' === accessory.context.device.powerState?.toUpperCase(); } /** @@ -56,16 +79,30 @@ export class SinricProSwitch extends AccessoryController implements SinricProAcc * @param value - The message containing the new value. eg: {"state":"Off"} */ public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); - if(action === ActionConstants.SET_POWER_STATE) { + if (action === ActionConstants.SET_POWER_STATE) { this.switchStates.on = 'ON' === value.state.toUpperCase(); - this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.switchStates.on); + this.service + .getCharacteristic(this.platform.Characteristic.On) + .updateValue(this.switchStates.on); } } private getPowerState(): CharacteristicValue { - this.platform.log.debug('[getPowerState()]: device:', this.accessory.displayName, ', on=', this.switchStates.on); + this.platform.log.debug( + '[getPowerState()]: device:', + this.accessory.displayName, + ', on=', + this.switchStates.on, + ); return this.switchStates.on; } -} \ No newline at end of file +} diff --git a/src/accessory/temperature-sensor.ts b/src/accessory/temperature-sensor.ts index 0a2c9e3..c575ecd 100644 --- a/src/accessory/temperature-sensor.ts +++ b/src/accessory/temperature-sensor.ts @@ -15,7 +15,10 @@ import { ModelConstants, ActionConstants } from '../constants'; * Sinric Pro - Temperature Sensor * https://developers.homebridge.io/#/service/TemperatureSensor and https://developers.homebridge.io/#/service/HumiditySensor */ -export class SinricProTemperatureSensor extends AccessoryController implements SinricProAccessory { +export class SinricProTemperatureSensor + extends AccessoryController + implements SinricProAccessory +{ private readonly temperatureService: Service; private readonly humidityService: Service; @@ -30,21 +33,43 @@ export class SinricProTemperatureSensor extends AccessoryController implements S ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.TEMPERATURE_SENSOR_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); - - this.platform.log.debug('[SinricProTemperatureSensor()]: Adding device:', this.accessory.displayName, accessory.context.device); - - this.temperatureService = this.accessory.getService(this.platform.Service.TemperatureSensor) - ?? this.accessory.addService(this.platform.Service.TemperatureSensor); - - this.humidityService = this.accessory.getService(this.platform.Service.HumiditySensor) - ?? this.accessory.addService(this.platform.Service.HumiditySensor); - - this.temperatureService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.context.device.name} Temperature Sensor`); - this.humidityService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.context.device.name} Humidity Sensor`); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.TEMPERATURE_SENSOR_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); + + this.platform.log.debug( + '[SinricProTemperatureSensor()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); + + this.temperatureService = + this.accessory.getService(this.platform.Service.TemperatureSensor) ?? + this.accessory.addService(this.platform.Service.TemperatureSensor); + + this.humidityService = + this.accessory.getService(this.platform.Service.HumiditySensor) ?? + this.accessory.addService(this.platform.Service.HumiditySensor); + + this.temperatureService.setCharacteristic( + this.platform.Characteristic.Name, + `${accessory.context.device.name} Temperature Sensor`, + ); + this.humidityService.setCharacteristic( + this.platform.Characteristic.Name, + `${accessory.context.device.name} Humidity Sensor`, + ); // register handlers for Characteristic this.temperatureService @@ -63,12 +88,22 @@ export class SinricProTemperatureSensor extends AccessoryController implements S } getCurrentRelativeHumidity(): CharacteristicValue { - this.platform.log.debug('getCurrentRelativeHumidity:', this.accessory.displayName, '=', this.states.humidity); + this.platform.log.debug( + 'getCurrentRelativeHumidity:', + this.accessory.displayName, + '=', + this.states.humidity, + ); return this.states.humidity; } getCurrentTemperature(): CharacteristicValue { - this.platform.log.debug('getCurrentTemperature:', this.accessory.displayName, '=', this.states.temperature); + this.platform.log.debug( + 'getCurrentTemperature:', + this.accessory.displayName, + '=', + this.states.temperature, + ); return this.states.temperature; } @@ -78,13 +113,24 @@ export class SinricProTemperatureSensor extends AccessoryController implements S * @param value - The new temperature/humidity value. */ updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); - - if(action === ActionConstants.CURRENT_TEMPERATURE) { + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); + + if (action === ActionConstants.CURRENT_TEMPERATURE) { this.states.temperature = value.temperature ?? 0; this.states.humidity = value.humidity ?? 0; - this.temperatureService.getCharacteristic(this.platform.Characteristic.CurrentTemperature).updateValue(this.states.temperature); - this.temperatureService.getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity).updateValue(this.states.humidity); + this.temperatureService + .getCharacteristic(this.platform.Characteristic.CurrentTemperature) + .updateValue(this.states.temperature); + this.temperatureService + .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity) + .updateValue(this.states.humidity); } } -} \ No newline at end of file +} diff --git a/src/accessory/thermostat.ts b/src/accessory/thermostat.ts index 6bd8e81..f85df57 100644 --- a/src/accessory/thermostat.ts +++ b/src/accessory/thermostat.ts @@ -16,16 +16,22 @@ import { ActionConstants, ModelConstants } from '../constants'; * Sinric Pro - Thermostat * https://developers.homebridge.io/#/service/Thermostat */ -export class SinricProThermostat extends AccessoryController implements SinricProAccessory { +export class SinricProThermostat + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private thermostatStates = { on: false, currentTemperature: 10, - targetTemperature : 10, - targetTemperatureDisplayUnit: this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS, - currentHeatingCoolingState: this.platform.Characteristic.CurrentHeatingCoolingState.OFF, - targetHeatingCoolingState: this.platform.Characteristic.TargetHeatingCoolingState.AUTO, + targetTemperature: 10, + targetTemperatureDisplayUnit: + this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS, + currentHeatingCoolingState: + this.platform.Characteristic.CurrentHeatingCoolingState.OFF, + targetHeatingCoolingState: + this.platform.Characteristic.TargetHeatingCoolingState.AUTO, }; constructor( @@ -34,47 +40,79 @@ export class SinricProThermostat extends AccessoryController implements SinricPr ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.THERMOSTAT_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); - - this.platform.log.debug('[SinricProThermostat()]: Adding device:', this.accessory.displayName, accessory.context.device); - - this.service = this.accessory.getService(this.platform.Service.Thermostat) - ?? this.accessory.addService(this.platform.Service.Thermostat); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.THERMOSTAT_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); + + this.platform.log.debug( + '[SinricProThermostat()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); + + this.service = + this.accessory.getService(this.platform.Service.Thermostat) ?? + this.accessory.addService(this.platform.Service.Thermostat); this.service.setPrimaryService(true); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); // register handlers for the Characteristic - this.service.getCharacteristic(this.platform.Characteristic.On) + this.service + .getCharacteristic(this.platform.Characteristic.On) .onSet(this.setPowerState.bind(this)) .onGet(this.getPowerState.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature) + this.service + .getCharacteristic(this.platform.Characteristic.CurrentTemperature) .onGet(this.getCurrentTemperature.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.TargetTemperature) + this.service + .getCharacteristic(this.platform.Characteristic.TargetTemperature) .onGet(this.onGetTargetTemperature.bind(this)) .onSet(this.onSetTargetTemperature.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits) + this.service + .getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits) .onGet(this.getTemperatureDisplayUnits.bind(this)) .onSet(this.setTemperatureDisplayUnits.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState) + this.service + .getCharacteristic( + this.platform.Characteristic.CurrentHeatingCoolingState, + ) .onGet(this.getCurrentHeatingCoolingState.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState) + this.service + .getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState) .onGet(this.getTargetHeatingCoolingState.bind(this)) .onSet(this.setTargetHeatingCoolingState.bind(this)); // restore present device state. - this.thermostatStates.currentTemperature = accessory.context.device.temperature ?? 10; - this.thermostatStates.targetTemperature = accessory.context.device.targetTemperature ?? 10; - this.thermostatStates.currentHeatingCoolingState = this.toCurrentHeatingCoolingState(accessory.context.device.thermostatMode); - this.thermostatStates.targetHeatingCoolingState = this.toTargetHeatingCoolingState(accessory.context.device.thermostatMode); + this.thermostatStates.currentTemperature = + accessory.context.device.temperature ?? 10; + this.thermostatStates.targetTemperature = + accessory.context.device.targetTemperature ?? 10; + this.thermostatStates.currentHeatingCoolingState = + this.toCurrentHeatingCoolingState( + accessory.context.device.thermostatMode, + ); + this.thermostatStates.targetHeatingCoolingState = + this.toTargetHeatingCoolingState(accessory.context.device.thermostatMode); } /** @@ -83,19 +121,40 @@ export class SinricProThermostat extends AccessoryController implements SinricPr * @param value - {"state":"Off"}, { temperature: 17.5 }, { thermostatMode: 'HEAT' } */ public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); - - if(action === ActionConstants.SET_POWER_STATE) { + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); + + if (action === ActionConstants.SET_POWER_STATE) { this.thermostatStates.on = 'ON' === value.state.toUpperCase(); - this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.thermostatStates.on); - } else if(action === ActionConstants.TARGET_TEMPERATURE) { + this.service + .getCharacteristic(this.platform.Characteristic.On) + .updateValue(this.thermostatStates.on); + } else if (action === ActionConstants.TARGET_TEMPERATURE) { this.thermostatStates.targetTemperature = value.temperature; - this.service.getCharacteristic(this.platform.Characteristic.TargetTemperature).updateValue(this.thermostatStates.targetTemperature); - } else if(action === ActionConstants.SET_THERMOSTAT_MODE) { - this.thermostatStates.currentHeatingCoolingState = this.toCurrentHeatingCoolingState(value.thermostatMode); - this.thermostatStates.targetHeatingCoolingState = this.toTargetHeatingCoolingState(value.thermostatMode); - this.service.getCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState).updateValue(this.thermostatStates.currentHeatingCoolingState); - this.service.getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState).updateValue(this.thermostatStates.targetHeatingCoolingState); + this.service + .getCharacteristic(this.platform.Characteristic.TargetTemperature) + .updateValue(this.thermostatStates.targetTemperature); + } else if (action === ActionConstants.SET_THERMOSTAT_MODE) { + this.thermostatStates.currentHeatingCoolingState = + this.toCurrentHeatingCoolingState(value.thermostatMode); + this.thermostatStates.targetHeatingCoolingState = + this.toTargetHeatingCoolingState(value.thermostatMode); + this.service + .getCharacteristic( + this.platform.Characteristic.CurrentHeatingCoolingState, + ) + .updateValue(this.thermostatStates.currentHeatingCoolingState); + this.service + .getCharacteristic( + this.platform.Characteristic.TargetHeatingCoolingState, + ) + .updateValue(this.thermostatStates.targetHeatingCoolingState); } } @@ -105,11 +164,11 @@ export class SinricProThermostat extends AccessoryController implements SinricPr private toTargetHeatingCoolingState(thermostatMode: string) { let state = this.platform.Characteristic.TargetHeatingCoolingState.COOL; - if('OFF' === thermostatMode) { + if ('OFF' === thermostatMode) { state = this.platform.Characteristic.TargetHeatingCoolingState.OFF; - } else if('HEAT' === thermostatMode) { + } else if ('HEAT' === thermostatMode) { state = this.platform.Characteristic.TargetHeatingCoolingState.HEAT; - } else if('COOL' === thermostatMode) { + } else if ('COOL' === thermostatMode) { state = this.platform.Characteristic.TargetHeatingCoolingState.COOL; } @@ -122,11 +181,11 @@ export class SinricProThermostat extends AccessoryController implements SinricPr private toCurrentHeatingCoolingState(thermostatMode: string) { let state = this.platform.Characteristic.CurrentHeatingCoolingState.COOL; - if('OFF' === thermostatMode) { + if ('OFF' === thermostatMode) { state = this.platform.Characteristic.CurrentHeatingCoolingState.OFF; - } else if('HEAT' === thermostatMode) { + } else if ('HEAT' === thermostatMode) { state = this.platform.Characteristic.CurrentHeatingCoolingState.HEAT; - } else if('COOL' === thermostatMode) { + } else if ('COOL' === thermostatMode) { state = this.platform.Characteristic.CurrentHeatingCoolingState.COOL; } @@ -134,62 +193,118 @@ export class SinricProThermostat extends AccessoryController implements SinricPr } private getCurrentTemperature(): CharacteristicValue { - this.platform.log.debug('getCurrentTemperature:', this.accessory.displayName, '=', this.thermostatStates.currentTemperature); + this.platform.log.debug( + 'getCurrentTemperature:', + this.accessory.displayName, + '=', + this.thermostatStates.currentTemperature, + ); return this.thermostatStates.currentTemperature; } private onGetTargetTemperature(): CharacteristicValue { - this.platform.log.debug('onGetTargetTemperature:', this.accessory.displayName, '=', this.thermostatStates.targetTemperature); + this.platform.log.debug( + 'onGetTargetTemperature:', + this.accessory.displayName, + '=', + this.thermostatStates.targetTemperature, + ); return this.thermostatStates.targetTemperature; } private onSetTargetTemperature(value: CharacteristicValue) { - this.platform.log.debug('onSetTargetTemperature:', this.accessory.displayName, 'to', value); + this.platform.log.debug( + 'onSetTargetTemperature:', + this.accessory.displayName, + 'to', + value, + ); this.thermostatStates.targetTemperature = value as number; super.targetTemperature(value); } getTemperatureDisplayUnits(): CharacteristicValue { - this.platform.log.debug('getTemperatureDisplayUnits:', this.accessory.displayName, '=', this.thermostatStates.targetTemperatureDisplayUnit); + this.platform.log.debug( + 'getTemperatureDisplayUnits:', + this.accessory.displayName, + '=', + this.thermostatStates.targetTemperatureDisplayUnit, + ); return this.thermostatStates.targetTemperatureDisplayUnit; } private setTemperatureDisplayUnits(value: CharacteristicValue) { - this.platform.log.debug('setTemperatureDisplayUnits:', this.accessory.displayName, 'to', value); + this.platform.log.debug( + 'setTemperatureDisplayUnits:', + this.accessory.displayName, + 'to', + value, + ); this.thermostatStates.targetTemperatureDisplayUnit = value as number; } private getCurrentHeatingCoolingState(): CharacteristicValue { - this.platform.log.debug('getCurrentHeatingCoolingState:', this.accessory.displayName, '=', this.thermostatStates.currentHeatingCoolingState); + this.platform.log.debug( + 'getCurrentHeatingCoolingState:', + this.accessory.displayName, + '=', + this.thermostatStates.currentHeatingCoolingState, + ); return this.thermostatStates.currentHeatingCoolingState; } private getTargetHeatingCoolingState(): CharacteristicValue { - this.platform.log.debug('getTargetHeatingCoolingState:', this.accessory.displayName, '=', this.thermostatStates.targetHeatingCoolingState); + this.platform.log.debug( + 'getTargetHeatingCoolingState:', + this.accessory.displayName, + '=', + this.thermostatStates.targetHeatingCoolingState, + ); return this.thermostatStates.targetHeatingCoolingState; } - private setTargetHeatingCoolingState(value: CharacteristicValue) : void { - this.platform.log.debug('setTargetHeatingCoolingState:', this.accessory.displayName, 'to', value); + private setTargetHeatingCoolingState(value: CharacteristicValue): void { + this.platform.log.debug( + 'setTargetHeatingCoolingState:', + this.accessory.displayName, + 'to', + value, + ); this.thermostatStates.targetHeatingCoolingState = value as number; let thermostatMode = 'AUTO'; - if(this.platform.Characteristic.TargetHeatingCoolingState.OFF === this.thermostatStates.targetHeatingCoolingState) { + if ( + this.platform.Characteristic.TargetHeatingCoolingState.OFF === + this.thermostatStates.targetHeatingCoolingState + ) { thermostatMode = 'OFF'; - } else if(this.platform.Characteristic.TargetHeatingCoolingState.HEAT === this.thermostatStates.targetHeatingCoolingState) { + } else if ( + this.platform.Characteristic.TargetHeatingCoolingState.HEAT === + this.thermostatStates.targetHeatingCoolingState + ) { thermostatMode = 'HEAT'; - } else if(this.platform.Characteristic.TargetHeatingCoolingState.COOL === this.thermostatStates.targetHeatingCoolingState) { + } else if ( + this.platform.Characteristic.TargetHeatingCoolingState.COOL === + this.thermostatStates.targetHeatingCoolingState + ) { thermostatMode = 'COOL'; - } else if(this.platform.Characteristic.TargetHeatingCoolingState.AUTO === this.thermostatStates.targetHeatingCoolingState) { + } else if ( + this.platform.Characteristic.TargetHeatingCoolingState.AUTO === + this.thermostatStates.targetHeatingCoolingState + ) { thermostatMode = 'AUTO'; } super.setThermostatMode(thermostatMode); - } private getPowerState(): CharacteristicValue { - this.platform.log.debug('getPowerState:', this.accessory.displayName, '=', this.thermostatStates.on); + this.platform.log.debug( + 'getPowerState:', + this.accessory.displayName, + '=', + this.thermostatStates.on, + ); return this.thermostatStates.on; } -} \ No newline at end of file +} diff --git a/src/accessory/tv.ts b/src/accessory/tv.ts index 98dac03..d1e4b89 100644 --- a/src/accessory/tv.ts +++ b/src/accessory/tv.ts @@ -15,7 +15,10 @@ import { ActionConstants, ModelConstants } from '../constants'; * Sinric Pro - TV * https://developers.homebridge.io/#/service/Television */ -export class SinricProTV extends AccessoryController implements SinricProAccessory { +export class SinricProTV + extends AccessoryController + implements SinricProAccessory +{ private tvService: Service; private tvStates = { on: false, @@ -27,24 +30,41 @@ export class SinricProTV extends AccessoryController implements SinricProAccesso ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.TV_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.TV_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); - this.platform.log.debug('[SinricProTV()]: Adding device:', this.accessory.displayName, accessory.context.device); + this.platform.log.debug( + '[SinricProTV()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); - this.tvService = this.accessory.getService(this.platform.Service.Television) - ?? this.accessory.addService(this.platform.Service.Television); + this.tvService = + this.accessory.getService(this.platform.Service.Television) ?? + this.accessory.addService(this.platform.Service.Television); this.tvService.setPrimaryService(true); // register handlers for the characteristic - this.tvService.getCharacteristic(this.platform.Characteristic.On) + this.tvService + .getCharacteristic(this.platform.Characteristic.On) .onSet(this.setPowerState.bind(this)) .onGet(this.getPowerState.bind(this)); // restore present device state. - this.tvStates.on = ('ON' === accessory.context.device.powerState?.toUpperCase()); + this.tvStates.on = + 'ON' === accessory.context.device.powerState?.toUpperCase(); } /** @@ -53,16 +73,30 @@ export class SinricProTV extends AccessoryController implements SinricProAccesso * @param value - The message containing the new value. eg: {"state":"Off"} */ public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); - if(action === ActionConstants.SET_POWER_STATE) { + if (action === ActionConstants.SET_POWER_STATE) { this.tvStates.on = 'ON' === value.state.toUpperCase(); - this.tvService.getCharacteristic(this.platform.Characteristic.On).updateValue(this.tvStates.on); + this.tvService + .getCharacteristic(this.platform.Characteristic.On) + .updateValue(this.tvStates.on); } } getPowerState(): CharacteristicValue { - this.platform.log.debug('[getPowerState()]: device:', this.accessory.displayName, ', on=', this.tvStates.on); + this.platform.log.debug( + '[getPowerState()]: device:', + this.accessory.displayName, + ', on=', + this.tvStates.on, + ); return this.tvStates.on; } -} \ No newline at end of file +} diff --git a/src/accessory/window-ac-unit.ts b/src/accessory/window-ac-unit.ts index 62630be..88bac81 100644 --- a/src/accessory/window-ac-unit.ts +++ b/src/accessory/window-ac-unit.ts @@ -16,16 +16,22 @@ import { ModelConstants, ActionConstants } from '../constants'; * Sinric Pro - WindowACUnit * https://developers.homebridge.io/#/service/Thermostat */ -export class SinricProWindowACUnit extends AccessoryController implements SinricProAccessory { +export class SinricProWindowACUnit + extends AccessoryController + implements SinricProAccessory +{ private service: Service; private thermostatStates = { on: false, currentTemperature: 10, - targetTemperature : 10, - targetTemperatureDisplayUnit: this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS, - currentHeatingCoolingState: this.platform.Characteristic.CurrentHeatingCoolingState.OFF, - targetHeatingCoolingState: this.platform.Characteristic.TargetHeatingCoolingState.AUTO, + targetTemperature: 10, + targetTemperatureDisplayUnit: + this.platform.Characteristic.TemperatureDisplayUnits.CELSIUS, + currentHeatingCoolingState: + this.platform.Characteristic.CurrentHeatingCoolingState.OFF, + targetHeatingCoolingState: + this.platform.Characteristic.TargetHeatingCoolingState.AUTO, }; constructor( @@ -34,47 +40,79 @@ export class SinricProWindowACUnit extends AccessoryController implements Sinric ) { super(platform, accessory); - this.accessory.getService(this.platform.Service.AccessoryInformation)! - .setCharacteristic(this.platform.Characteristic.Manufacturer, ModelConstants.MANUFACTURER) - .setCharacteristic(this.platform.Characteristic.Model, ModelConstants.THERMOSTAT_MODEL) - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.sinricProDeviceId); - - this.platform.log.debug('[SinricProThermostat()]: Adding device:', this.accessory.displayName, accessory.context.device); - - this.service = this.accessory.getService(this.platform.Service.Thermostat) - ?? this.accessory.addService(this.platform.Service.Thermostat); + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic( + this.platform.Characteristic.Manufacturer, + ModelConstants.MANUFACTURER, + ) + .setCharacteristic( + this.platform.Characteristic.Model, + ModelConstants.THERMOSTAT_MODEL, + ) + .setCharacteristic( + this.platform.Characteristic.SerialNumber, + this.sinricProDeviceId, + ); + + this.platform.log.debug( + '[SinricProThermostat()]: Adding device:', + this.accessory.displayName, + accessory.context.device, + ); + + this.service = + this.accessory.getService(this.platform.Service.Thermostat) ?? + this.accessory.addService(this.platform.Service.Thermostat); this.service.setPrimaryService(true); - this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name); + this.service.setCharacteristic( + this.platform.Characteristic.Name, + accessory.context.device.name, + ); // register handlers for the Characteristic - this.service.getCharacteristic(this.platform.Characteristic.On) + this.service + .getCharacteristic(this.platform.Characteristic.On) .onSet(this.setPowerState.bind(this)) .onGet(this.getPowerState.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature) + this.service + .getCharacteristic(this.platform.Characteristic.CurrentTemperature) .onGet(this.getCurrentTemperature.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.TargetTemperature) + this.service + .getCharacteristic(this.platform.Characteristic.TargetTemperature) .onGet(this.onGetTargetTemperature.bind(this)) .onSet(this.onSetTargetTemperature.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits) + this.service + .getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits) .onGet(this.getTemperatureDisplayUnits.bind(this)) .onSet(this.setTemperatureDisplayUnits.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState) + this.service + .getCharacteristic( + this.platform.Characteristic.CurrentHeatingCoolingState, + ) .onGet(this.getCurrentHeatingCoolingState.bind(this)); - this.service.getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState) + this.service + .getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState) .onGet(this.getTargetHeatingCoolingState.bind(this)) .onSet(this.setTargetHeatingCoolingState.bind(this)); // restore present device state. - this.thermostatStates.currentTemperature = accessory.context.device.temperature ?? 10; - this.thermostatStates.targetTemperature = accessory.context.device.targetTemperature ?? 10; - this.thermostatStates.currentHeatingCoolingState = this.toCurrentHeatingCoolingState(accessory.context.device.thermostatMode); - this.thermostatStates.targetHeatingCoolingState = this.toTargetHeatingCoolingState(accessory.context.device.thermostatMode); + this.thermostatStates.currentTemperature = + accessory.context.device.temperature ?? 10; + this.thermostatStates.targetTemperature = + accessory.context.device.targetTemperature ?? 10; + this.thermostatStates.currentHeatingCoolingState = + this.toCurrentHeatingCoolingState( + accessory.context.device.thermostatMode, + ); + this.thermostatStates.targetHeatingCoolingState = + this.toTargetHeatingCoolingState(accessory.context.device.thermostatMode); } /** @@ -83,30 +121,51 @@ export class SinricProWindowACUnit extends AccessoryController implements Sinric * @param value - {"state":"Off"}, { temperature: 17.5 }, { thermostatMode: 'HEAT' } */ public updateState(action: string, value: any): void { - this.platform.log.debug('[updateState()]:', this.accessory.displayName, 'action=', action, 'value=', value); - - if(action === ActionConstants.SET_POWER_STATE) { + this.platform.log.debug( + '[updateState()]:', + this.accessory.displayName, + 'action=', + action, + 'value=', + value, + ); + + if (action === ActionConstants.SET_POWER_STATE) { this.thermostatStates.on = 'ON' === value.state.toUpperCase(); - this.service.getCharacteristic(this.platform.Characteristic.On).updateValue(this.thermostatStates.on); - } else if(action === ActionConstants.TARGET_TEMPERATURE) { + this.service + .getCharacteristic(this.platform.Characteristic.On) + .updateValue(this.thermostatStates.on); + } else if (action === ActionConstants.TARGET_TEMPERATURE) { this.thermostatStates.targetTemperature = value.temperature; - this.service.getCharacteristic(this.platform.Characteristic.TargetTemperature).updateValue(this.thermostatStates.targetTemperature); - } else if(action === ActionConstants.SET_THERMOSTAT_MODE) { - this.thermostatStates.currentHeatingCoolingState = this.toCurrentHeatingCoolingState(value.thermostatMode); - this.thermostatStates.targetHeatingCoolingState = this.toTargetHeatingCoolingState(value.thermostatMode); - this.service.getCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState).updateValue(this.thermostatStates.currentHeatingCoolingState); - this.service.getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState).updateValue(this.thermostatStates.targetHeatingCoolingState); + this.service + .getCharacteristic(this.platform.Characteristic.TargetTemperature) + .updateValue(this.thermostatStates.targetTemperature); + } else if (action === ActionConstants.SET_THERMOSTAT_MODE) { + this.thermostatStates.currentHeatingCoolingState = + this.toCurrentHeatingCoolingState(value.thermostatMode); + this.thermostatStates.targetHeatingCoolingState = + this.toTargetHeatingCoolingState(value.thermostatMode); + this.service + .getCharacteristic( + this.platform.Characteristic.CurrentHeatingCoolingState, + ) + .updateValue(this.thermostatStates.currentHeatingCoolingState); + this.service + .getCharacteristic( + this.platform.Characteristic.TargetHeatingCoolingState, + ) + .updateValue(this.thermostatStates.targetHeatingCoolingState); } } toTargetHeatingCoolingState(thermostatMode: string) { let state = this.platform.Characteristic.TargetHeatingCoolingState.COOL; - if('OFF' === thermostatMode) { + if ('OFF' === thermostatMode) { state = this.platform.Characteristic.TargetHeatingCoolingState.OFF; - } else if('HEAT' === thermostatMode) { + } else if ('HEAT' === thermostatMode) { state = this.platform.Characteristic.TargetHeatingCoolingState.HEAT; - } else if('COOL' === thermostatMode) { + } else if ('COOL' === thermostatMode) { state = this.platform.Characteristic.TargetHeatingCoolingState.COOL; } @@ -116,11 +175,11 @@ export class SinricProWindowACUnit extends AccessoryController implements Sinric toCurrentHeatingCoolingState(thermostatMode: string) { let state = this.platform.Characteristic.CurrentHeatingCoolingState.COOL; - if('OFF' === thermostatMode) { + if ('OFF' === thermostatMode) { state = this.platform.Characteristic.CurrentHeatingCoolingState.OFF; - } else if('HEAT' === thermostatMode) { + } else if ('HEAT' === thermostatMode) { state = this.platform.Characteristic.CurrentHeatingCoolingState.HEAT; - } else if('COOL' === thermostatMode) { + } else if ('COOL' === thermostatMode) { state = this.platform.Characteristic.CurrentHeatingCoolingState.COOL; } @@ -128,62 +187,118 @@ export class SinricProWindowACUnit extends AccessoryController implements Sinric } getCurrentTemperature(): CharacteristicValue { - this.platform.log.debug('getCurrentTemperature:', this.accessory.displayName, '=', this.thermostatStates.currentTemperature); + this.platform.log.debug( + 'getCurrentTemperature:', + this.accessory.displayName, + '=', + this.thermostatStates.currentTemperature, + ); return this.thermostatStates.currentTemperature; } onGetTargetTemperature(): CharacteristicValue { - this.platform.log.debug('onGetTargetTemperature:', this.accessory.displayName, '=', this.thermostatStates.targetTemperature); + this.platform.log.debug( + 'onGetTargetTemperature:', + this.accessory.displayName, + '=', + this.thermostatStates.targetTemperature, + ); return this.thermostatStates.targetTemperature; } onSetTargetTemperature(value: CharacteristicValue) { - this.platform.log.debug('onSetTargetTemperature:', this.accessory.displayName, 'to', value); + this.platform.log.debug( + 'onSetTargetTemperature:', + this.accessory.displayName, + 'to', + value, + ); this.thermostatStates.targetTemperature = value as number; super.targetTemperature(value); } getTemperatureDisplayUnits(): CharacteristicValue { - this.platform.log.debug('getTemperatureDisplayUnits:', this.accessory.displayName, '=', this.thermostatStates.targetTemperatureDisplayUnit); + this.platform.log.debug( + 'getTemperatureDisplayUnits:', + this.accessory.displayName, + '=', + this.thermostatStates.targetTemperatureDisplayUnit, + ); return this.thermostatStates.targetTemperatureDisplayUnit; } setTemperatureDisplayUnits(value: CharacteristicValue) { - this.platform.log.debug('setTemperatureDisplayUnits:', this.accessory.displayName, 'to', value); + this.platform.log.debug( + 'setTemperatureDisplayUnits:', + this.accessory.displayName, + 'to', + value, + ); this.thermostatStates.targetTemperatureDisplayUnit = value as number; } getCurrentHeatingCoolingState(): CharacteristicValue { - this.platform.log.debug('getCurrentHeatingCoolingState:', this.accessory.displayName, '=', this.thermostatStates.currentHeatingCoolingState); + this.platform.log.debug( + 'getCurrentHeatingCoolingState:', + this.accessory.displayName, + '=', + this.thermostatStates.currentHeatingCoolingState, + ); return this.thermostatStates.currentHeatingCoolingState; } getTargetHeatingCoolingState(): CharacteristicValue { - this.platform.log.debug('getTargetHeatingCoolingState:', this.accessory.displayName, '=', this.thermostatStates.targetHeatingCoolingState); + this.platform.log.debug( + 'getTargetHeatingCoolingState:', + this.accessory.displayName, + '=', + this.thermostatStates.targetHeatingCoolingState, + ); return this.thermostatStates.targetHeatingCoolingState; } setTargetHeatingCoolingState(value: CharacteristicValue) { - this.platform.log.debug('setTargetHeatingCoolingState:', this.accessory.displayName, 'to', value); + this.platform.log.debug( + 'setTargetHeatingCoolingState:', + this.accessory.displayName, + 'to', + value, + ); this.thermostatStates.targetHeatingCoolingState = value as number; let thermostatMode = 'AUTO'; - if(this.platform.Characteristic.TargetHeatingCoolingState.OFF === this.thermostatStates.targetHeatingCoolingState) { + if ( + this.platform.Characteristic.TargetHeatingCoolingState.OFF === + this.thermostatStates.targetHeatingCoolingState + ) { thermostatMode = 'OFF'; - } else if(this.platform.Characteristic.TargetHeatingCoolingState.HEAT === this.thermostatStates.targetHeatingCoolingState) { + } else if ( + this.platform.Characteristic.TargetHeatingCoolingState.HEAT === + this.thermostatStates.targetHeatingCoolingState + ) { thermostatMode = 'HEAT'; - } else if(this.platform.Characteristic.TargetHeatingCoolingState.COOL === this.thermostatStates.targetHeatingCoolingState) { + } else if ( + this.platform.Characteristic.TargetHeatingCoolingState.COOL === + this.thermostatStates.targetHeatingCoolingState + ) { thermostatMode = 'COOL'; - } else if(this.platform.Characteristic.TargetHeatingCoolingState.AUTO === this.thermostatStates.targetHeatingCoolingState) { + } else if ( + this.platform.Characteristic.TargetHeatingCoolingState.AUTO === + this.thermostatStates.targetHeatingCoolingState + ) { thermostatMode = 'AUTO'; } super.setThermostatMode(thermostatMode); - } getPowerState(): CharacteristicValue { - this.platform.log.debug('getPowerState:', this.accessory.displayName, '=', this.thermostatStates.on); + this.platform.log.debug( + 'getPowerState:', + this.accessory.displayName, + '=', + this.thermostatStates.on, + ); return this.thermostatStates.on; } -} \ No newline at end of file +} diff --git a/src/api-client.ts b/src/api-client.ts index 5344f84..62379d5 100644 --- a/src/api-client.ts +++ b/src/api-client.ts @@ -10,7 +10,11 @@ import https from 'https'; import * as util from 'util'; import { SinricProDevice } from './model/sinricpro-device'; -import { ActionConstants, SINRICPRO_API_ENDPOINT_BASE_URL, SINRICPRO_HOMEBRIDGE_CLIENT_ID } from './constants'; +import { + ActionConstants, + SINRICPRO_API_ENDPOINT_BASE_URL, + SINRICPRO_HOMEBRIDGE_CLIENT_ID, +} from './constants'; import { Guid } from './utils/guid'; export class SinricProApiClient { @@ -28,7 +32,8 @@ export class SinricProApiClient { constructor( apiKey: string, - public readonly log: Logger) { + public readonly log: Logger, + ) { this.apiKey = apiKey; } @@ -42,11 +47,11 @@ export class SinricProApiClient { const devices: SinricProDevice[] = []; if (await this.authenticate()) { - const initData = await Promise.all([ - this.axiosClient.get('/devices'), - ]); + const initData = await Promise.all([this.axiosClient.get('/devices')]); - this.log.debug(`[getDevices()]: ${initData[0].data.devices.length} device(s) found!`); + this.log.debug( + `[getDevices()]: ${initData[0].data.devices.length} device(s) found!`, + ); for (const device of initData[0].data.devices) { const sinricproDevice: SinricProDevice = { @@ -76,7 +81,10 @@ export class SinricProApiClient { this.log.debug('[authenticate()]: Login to SinricPro...'); // eslint-disable-next-line eqeqeq - if (this.expiresAt != null && (new Date().getTime() - this.expiresAt.getTime() < this.TWO_MINUTES_MILLIS)) { + if ( + this.expiresAt && + new Date().getTime() - this.expiresAt.getTime() < this.TWO_MINUTES_MILLIS + ) { // Has a valid auth token. do nothing... return true; } @@ -84,18 +92,26 @@ export class SinricProApiClient { this.log.info('[authenticate()]: Getting a new auth token...'); try { - const response = await axios.post(this.authEndpointUri, {}, { - httpsAgent: this.httpsAgent, - headers: { - 'Content-Type': 'application/json', - 'x-sinric-api-key': this.apiKey, + const response = await axios.post( + this.authEndpointUri, + {}, + { + httpsAgent: this.httpsAgent, + headers: { + 'Content-Type': 'application/json', + 'x-sinric-api-key': this.apiKey, + }, }, - }); + ); if (response.data.success) { this.accessToken = response.data.accessToken; - this.expiresAt = new Date(new Date().getTime() + (1000 * response.data.expiresIn)); - this.log.info('[authenticate()]: New auth token expires at: ' + this.expiresAt); + this.expiresAt = new Date( + new Date().getTime() + 1000 * response.data.expiresIn, + ); + this.log.info( + '[authenticate()]: New auth token expires at: ' + this.expiresAt, + ); const config: AxiosRequestConfig = { timeout: 10000, @@ -107,9 +123,7 @@ export class SinricProApiClient { }, }; - this.axiosClient = axios.create( - config, - ); + this.axiosClient = axios.create(config); this.log.info('[authenticate()]: Success!'); return true; @@ -134,12 +148,12 @@ export class SinricProApiClient { private getCommand(action, value) { return { - 'clientId': SINRICPRO_HOMEBRIDGE_CLIENT_ID, - 'messageId': Guid.newGuid(), - 'type': 'request', - 'action': action, - 'createdAt': this.getSecondsSinceEpich(), - 'value': JSON.stringify(value), + clientId: SINRICPRO_HOMEBRIDGE_CLIENT_ID, + messageId: Guid.newGuid(), + type: 'request', + action: action, + createdAt: this.getSecondsSinceEpich(), + value: JSON.stringify(value), }; } @@ -148,16 +162,18 @@ export class SinricProApiClient { if (await this.authenticate()) { try { - const response = await this.axiosClient.post( - deviceActionUrl, - data, - ); + const response = await this.axiosClient.post(deviceActionUrl, data); if (response.status === 200) { - this.log.debug('[execAction()]: request has been queued for processing!'); + this.log.debug( + '[execAction()]: request has been queued for processing!', + ); return true; } else { - this.log.error('[execAction()]: server returned an error. status: ', response.status); + this.log.error( + '[execAction()]: server returned an error. status: ', + response.status, + ); return false; } } catch (error) { @@ -170,50 +186,100 @@ export class SinricProApiClient { return false; } - public async setPowerState(deviceId: string, powerState: string): Promise { - const data = this.getCommand(ActionConstants.SET_POWER_STATE, { 'state': powerState }); + public async setPowerState( + deviceId: string, + powerState: string, + ): Promise { + const data = this.getCommand(ActionConstants.SET_POWER_STATE, { + state: powerState, + }); return this.execAction(deviceId, data); } - public async setBrightness(deviceId: string, brightness: number): Promise { - const data = this.getCommand(ActionConstants.SET_BRIGHTNESS, { 'brightness': brightness }); + public async setBrightness( + deviceId: string, + brightness: number, + ): Promise { + const data = this.getCommand(ActionConstants.SET_BRIGHTNESS, { + brightness: brightness, + }); return this.execAction(deviceId, data); } - public async setRangeValue(deviceId: string, rangeValue: number): Promise { - const data = this.getCommand(ActionConstants.SET_RANGE_VALUE, { 'rangeValue': rangeValue }); + public async setColor( + deviceId: string, + color: { r: number; g: number; b: number }, + ): Promise { + const data = this.getCommand(ActionConstants.SET_COLOR, { color }); return this.execAction(deviceId, data); } - public async setTargetTemperature(deviceId: string, toTemperature: number): Promise { - const data = this.getCommand(ActionConstants.TARGET_TEMPERATURE, { 'temperature': toTemperature }); + public async setColorTemperature( + deviceId: string, + colorTemperature: number, + ): Promise { + const data = this.getCommand(ActionConstants.SET_COLOR_TEMPERATURE, { + colorTemperature, + }); + return this.execAction(deviceId, data); + } + + public async setRangeValue( + deviceId: string, + rangeValue: number, + ): Promise { + const data = this.getCommand(ActionConstants.SET_RANGE_VALUE, { + rangeValue: rangeValue, + }); + return this.execAction(deviceId, data); + } + + public async setTargetTemperature( + deviceId: string, + toTemperature: number, + ): Promise { + const data = this.getCommand(ActionConstants.TARGET_TEMPERATURE, { + temperature: toTemperature, + }); return this.execAction(deviceId, data); } public async setMode(deviceId: string, mode: string): Promise { - const data = this.getCommand(ActionConstants.SET_MODE, { 'mode': mode }); + const data = this.getCommand(ActionConstants.SET_MODE, { mode: mode }); return this.execAction(deviceId, data); } public async setLockState(deviceId: string, state: string): Promise { - const data = this.getCommand(ActionConstants.SET_LOCK_STATE, { 'state': state }); + const data = this.getCommand(ActionConstants.SET_LOCK_STATE, { + state: state, + }); return this.execAction(deviceId, data); } public async setDoorbellPress(deviceId: string): Promise { - const data = this.getCommand(ActionConstants.DOORBELL_PRESS, { 'state': 'pressed' }); + const data = this.getCommand(ActionConstants.DOORBELL_PRESS, { + state: 'pressed', + }); return this.execAction(deviceId, data); } - public async setPowerLevel(deviceId: string, powerLevel: number): Promise { - const data = this.getCommand(ActionConstants.SET_POWER_LEVEL, { 'powerLevel': powerLevel }); + public async setPowerLevel( + deviceId: string, + powerLevel: number, + ): Promise { + const data = this.getCommand(ActionConstants.SET_POWER_LEVEL, { + powerLevel: powerLevel, + }); return this.execAction(deviceId, data); } - public async setThermostatMode(deviceId: string, thermostatMode: string): Promise { - const data = this.getCommand(ActionConstants.SET_THERMOSTAT_MODE, { 'thermostatMode': thermostatMode }); + public async setThermostatMode( + deviceId: string, + thermostatMode: string, + ): Promise { + const data = this.getCommand(ActionConstants.SET_THERMOSTAT_MODE, { + thermostatMode: thermostatMode, + }); return this.execAction(deviceId, data); } - - -} \ No newline at end of file +} diff --git a/src/constants.ts b/src/constants.ts index 3d3d9d8..e2c7c16 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -7,7 +7,8 @@ export const SINRICPRO_HOMEBRIDGE_CLIENT_ID = 'homebridge'; export const SINRICPRO_API_ENDPOINT_BASE_URL = 'https://api.sinric.pro/api/v1'; -export const SINRICPRO_SSE_ENDPOINT_BASE_URL = 'https://portal.sinric.pro/sse/stream?accessToken=%s'; +export const SINRICPRO_SSE_ENDPOINT_BASE_URL = + 'https://portal.sinric.pro/sse/stream?accessToken=%s'; export abstract class ModelConstants { static readonly MANUFACTURER = 'SinricPro'; @@ -40,6 +41,8 @@ export abstract class ActionConstants { static readonly SET_THERMOSTAT_MODE = 'setThermostatMode'; static readonly SET_MODE = 'setMode'; static readonly DOORBELL_PRESS = 'DoorbellPress'; + static readonly SET_COLOR_TEMPERATURE = 'setColorTemperature'; + static readonly SET_COLOR = 'setColor'; } export abstract class DeviceTypeConstants { @@ -59,7 +62,9 @@ export abstract class DeviceTypeConstants { static readonly GARAGE_DOOR = 'sinric.devices.types.GARAGE_DOOR'; static readonly BLIND = 'sinric.devices.types.BLIND'; static readonly CAMERA = 'sinric.devices.types.CAMERA'; - static readonly AIR_QUALITY_SENSOR = 'sinric.devices.types.AIR_QUALITY_SENSOR'; + static readonly AIR_QUALITY_SENSOR = + 'sinric.devices.types.AIR_QUALITY_SENSOR'; + static readonly ENERGY_SENSOR = 'sinric.devices.types.ENERGY_SENSOR'; static readonly CUSTOM = 'sinric.devices.types.CUSTOM'; -} \ No newline at end of file +} diff --git a/src/model/sinricpro-device.ts b/src/model/sinricpro-device.ts index cb0c798..a67b242 100644 --- a/src/model/sinricpro-device.ts +++ b/src/model/sinricpro-device.ts @@ -5,21 +5,21 @@ * This file is part of the Sinric Pro - Homebridge Plugin (https://github.com/sinricpro/homebridge-sinricpro) */ export interface SinricProDeviceType { - code: string; + code: string; } export interface SinricProDevice { - id: number; - name: string; - deviceType: SinricProDeviceType; - powerState?: string; - rangeValue?: number; - garageDoorState?: number; - brightness?: number; - powerLevel?: number; - temperature?: number; - thermostatMode?: string; - contactState?: string; - humidity?: number; - lastMotionState?: string; -} \ No newline at end of file + id: number; + name: string; + deviceType: SinricProDeviceType; + powerState?: string; + rangeValue?: number; + garageDoorState?: number; + brightness?: number; + powerLevel?: number; + temperature?: number; + thermostatMode?: string; + contactState?: string; + humidity?: number; + lastMotionState?: string; +} diff --git a/src/model/sse.ts b/src/model/sse.ts index 5b78987..baeb944 100644 --- a/src/model/sse.ts +++ b/src/model/sse.ts @@ -5,34 +5,34 @@ * This file is part of the Sinric Pro - Homebridge Plugin (https://github.com/sinricpro/homebridge-sinricpro) */ export interface SSEMessage { - event: string; - device: Device; - message: Message; + event: string; + device: Device; + message: Message; } export interface Device { - name: string; - powerState: string; - isOnline: boolean; - id: string; + name: string; + powerState: string; + isOnline: boolean; + id: string; } export interface Message { - name: string; - channel: string; - userId: string; - deviceId: string; - payload: Payload; + name: string; + channel: string; + userId: string; + deviceId: string; + payload: Payload; } export interface Payload { - action: string; - clientId: string; - createdAt: number; - deviceId: string; - message: string; - replyToken: string; - success: boolean; - type: string; - value: any; -} \ No newline at end of file + action: string; + clientId: string; + createdAt: number; + deviceId: string; + message: string; + replyToken: string; + success: boolean; + type: string; + value: any; +} diff --git a/src/platform.ts b/src/platform.ts index 556cd79..a4ee8b6 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -4,7 +4,15 @@ * * This file is part of the Sinric Pro - Homebridge Plugin (https://github.com/sinricpro/homebridge-sinricpro) */ -import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service, Characteristic } from 'homebridge'; +import { + API, + DynamicPlatformPlugin, + Logger, + PlatformAccessory, + PlatformConfig, + Service, + Characteristic, +} from 'homebridge'; import { PLATFORM_NAME, PLUGIN_NAME } from './settings'; import { SinricProApiClient } from './api-client'; @@ -33,7 +41,8 @@ import { SinricProWindowACUnit } from './accessory/window-ac-unit'; */ export class SinricProPlatform implements DynamicPlatformPlugin { public readonly Service: typeof Service = this.api.hap.Service; - public readonly Characteristic: typeof Characteristic = this.api.hap.Characteristic; + public readonly Characteristic: typeof Characteristic = + this.api.hap.Characteristic; public sinricProApiClient!: SinricProApiClient; public sinricProSseClient!: SinricProSseClient; @@ -47,8 +56,7 @@ export class SinricProPlatform implements DynamicPlatformPlugin { public readonly config: PlatformConfig, public readonly api: API, ) { - - if(!config.token) { + if (!config.token) { this.log.error('API Token is empty. Cannot continue!'); return; } @@ -62,8 +70,7 @@ export class SinricProPlatform implements DynamicPlatformPlugin { // to start discovery of new accessories. this.api.on('didFinishLaunching', () => { this.didFinishLaunching(); - }, - ); + }); } /** @@ -86,22 +93,35 @@ export class SinricProPlatform implements DynamicPlatformPlugin { this.log.info('[didFinishLaunching()]: authenticate..!'); const authenticated = await this.sinricProApiClient.authenticate(); - if(authenticated) { + if (authenticated) { this.log.info('[didFinishLaunching()]: init SSE Client..'); - this.sinricProSseClient = new SinricProSseClient(this.log, this.sinricProApiClient.authToken); + this.sinricProSseClient = new SinricProSseClient( + this.log, + this.sinricProApiClient.authToken, + ); // listen to device state changes - this.sinricProSseClient.onDeviceStateChange = (deviceId: string, action: string, value: any) => { - this.sinricproDevices.filter(spd => spd.sinricProDeviceId === deviceId).map((device) => { - this.log.info('[onDeviceStateChange()]: Update device id: %s with %s', device.sinricProDeviceId, value); - device.updateState(action, value); - }); + this.sinricProSseClient.onDeviceStateChange = ( + deviceId: string, + action: string, + value: any, + ) => { + this.sinricproDevices + .filter((spd) => spd.sinricProDeviceId === deviceId) + .map((device) => { + this.log.info( + '[onDeviceStateChange()]: Update device id: %s with %s', + device.sinricProDeviceId, + value, + ); + device.updateState(action, value); + }); }; this.sinricProSseClient.listen(); } this.log.info('[didFinishLaunching()]: start device discovery..!'); - const devices = await this.sinricProApiClient.getDevices() || []; + const devices = (await this.sinricProApiClient.getDevices()) || []; for (const device of devices) { // generate a unique id for the accessory this should be generated from @@ -111,11 +131,16 @@ export class SinricProPlatform implements DynamicPlatformPlugin { // see if an accessory with the same uuid has already been registered and restored from // the cached devices we stored in the `configureAccessory` method above - const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); + const existingAccessory = this.accessories.find( + (accessory) => accessory.UUID === uuid, + ); if (existingAccessory) { // the accessory already exists - this.log.info('[didFinishLaunching()]: Restoring existing accessory from cache:', existingAccessory.displayName); + this.log.info( + '[didFinishLaunching()]: Restoring existing accessory from cache:', + existingAccessory.displayName, + ); // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: existingAccessory.context.device = device; @@ -142,9 +167,11 @@ export class SinricProPlatform implements DynamicPlatformPlugin { // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` // new CrestronHomePlatformAccessory(this, accessory); - if(this.createSinricProAccessory(accessory)){ + if (this.createSinricProAccessory(accessory)) { // link the accessory to your platform - this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); + this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [ + accessory, + ]); this.accessories.push(accessory); } } @@ -154,7 +181,10 @@ export class SinricProPlatform implements DynamicPlatformPlugin { createSinricProAccessory(accessory: PlatformAccessory): boolean { const deviceTypeCode = accessory.context.device.deviceType.code; - this.log.info('[createSinricProAccessory()]: Adding new accessory:', accessory.displayName); + this.log.info( + '[createSinricProAccessory()]: Adding new accessory:', + accessory.displayName, + ); switch (deviceTypeCode) { case DeviceTypeConstants.SWITCH: @@ -164,13 +194,17 @@ export class SinricProPlatform implements DynamicPlatformPlugin { this.sinricproDevices.push(new SinricProLight(this, accessory)); break; case DeviceTypeConstants.DIMMABLE_SWITCH: - this.sinricproDevices.push(new SinricProDimmableSwitch(this, accessory)); + this.sinricproDevices.push( + new SinricProDimmableSwitch(this, accessory), + ); break; case DeviceTypeConstants.DOORBELL: this.sinricproDevices.push(new SinricProDoorbell(this, accessory)); break; case DeviceTypeConstants.TEMPERATURE_SENSOR: - this.sinricproDevices.push(new SinricProTemperatureSensor(this, accessory)); + this.sinricproDevices.push( + new SinricProTemperatureSensor(this, accessory), + ); break; case DeviceTypeConstants.FAN: this.sinricproDevices.push(new SinricProFan(this, accessory)); @@ -200,11 +234,13 @@ export class SinricProPlatform implements DynamicPlatformPlugin { this.sinricproDevices.push(new SinricProWindowACUnit(this, accessory)); break; default: - this.log.warn('[createSinricProAccessory()]: Unsupported accessory type:', accessory.context.device.type); + this.log.warn( + '[createSinricProAccessory()]: Unsupported accessory type:', + accessory.context.device.type, + ); break; } return true; - } -} \ No newline at end of file +} diff --git a/src/settings.ts b/src/settings.ts index c7d1cea..a4279b3 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -12,4 +12,4 @@ export const PLATFORM_NAME = 'SinricPro'; /** * This must match the name of your plugin as defined the package.json */ -export const PLUGIN_NAME = 'homebridge-sinricpro'; \ No newline at end of file +export const PLUGIN_NAME = 'homebridge-sinricpro'; diff --git a/src/sse-client.ts b/src/sse-client.ts index 33897e1..129dbf9 100644 --- a/src/sse-client.ts +++ b/src/sse-client.ts @@ -15,26 +15,41 @@ import { SINRICPRO_SSE_ENDPOINT_BASE_URL } from './constants'; export class SinricProSseClient { private eventSource!: EventSource; - public onDeviceStateChange?: (deviceId: string, action: string, value: any) => void; + public onDeviceStateChange?: ( + deviceId: string, + action: string, + value: any, + ) => void; constructor( - public readonly log: Logger, - public readonly authToken: string) { - } + public readonly log: Logger, + public readonly authToken: string, + ) {} public listen() { if (this.eventSource) { return; } else { - const url: string = util.format(SINRICPRO_SSE_ENDPOINT_BASE_URL, this.authToken); + const url: string = util.format( + SINRICPRO_SSE_ENDPOINT_BASE_URL, + this.authToken, + ); this.eventSource = new EventSource(url); this.eventSource.onmessage = (e) => { if (this.onDeviceStateChange === null) { return; } const sseMessage: SSEMessage = JSON.parse(e.data); - if(sseMessage.event === 'deviceMessageArrived' && (sseMessage.message.payload.type === 'response' || sseMessage.message.payload.type === 'event')) { - this.onDeviceStateChange!(sseMessage.message.payload.deviceId, sseMessage.message.payload.action, sseMessage.message.payload.value); + if ( + sseMessage.event === 'deviceMessageArrived' && + (sseMessage.message.payload.type === 'response' || + sseMessage.message.payload.type === 'event') + ) { + this.onDeviceStateChange!( + sseMessage.message.payload.deviceId, + sseMessage.message.payload.action, + sseMessage.message.payload.value, + ); } }; @@ -43,4 +58,4 @@ export class SinricProSseClient { }; } } -} \ No newline at end of file +} diff --git a/src/utils/color-converter.ts b/src/utils/color-converter.ts new file mode 100644 index 0000000..6886963 --- /dev/null +++ b/src/utils/color-converter.ts @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2019-2023 Sinric. All rights reserved. + * Licensed under Creative Commons Attribution-Share Alike (CC BY-SA) + * + * This file is part of the Sinric Pro - Homebridge Plugin (https://github.com/sinricpro/homebridge-sinricpro) + */ + +export class ColorConverter { + /** + * Converts RGB color values to HSV. + * @param r Red component (0-255) + * @param g Green component (0-255) + * @param b Blue component (0-255) + * @returns An object containing hue (0-360), saturation (0-100), and value (0-100) + */ + static rgbToHsv(r: number, g: number, b: number) { + r /= 255; + g /= 255; + b /= 255; + + const max = Math.max(r, g, b), + min = Math.min(r, g, b); + let h = 0; + const v = max; + + const d = max - min; + const s = max === 0 ? 0 : d / max; + + if (max !== min) { + switch (max) { + case r: + h = ((g - b) / d + (g < b ? 6 : 0)) % 6; + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h *= 60; + } + + return { + hue: h, + saturation: s * 100, + value: v * 100, + }; + } + + /** + * Converts HSV color values to RGB. + * @param h Hue (0-360) + * @param s Saturation (0-100) + * @param v Value (0-100) + * @returns An object containing red, green, and blue components (0-255) + */ + static hsvToRgb(h: number, s: number, v: number) { + h = ((h % 360) + 360) % 360; // Ensure h is between 0 and 360 + s /= 100; + v /= 100; + + const c = v * s; + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)); + const m = v - c; + + let r = 0, + g = 0, + b = 0; + + if (h < 60) { + r = c; + g = x; + } else if (h < 120) { + r = x; + g = c; + } else if (h < 180) { + g = c; + b = x; + } else if (h < 240) { + g = x; + b = c; + } else if (h < 300) { + r = x; + b = c; + } else { + r = c; + b = x; + } + + return { + r: Math.round((r + m) * 255), + g: Math.round((g + m) * 255), + b: Math.round((b + m) * 255), + }; + } + + static miredsToKelvin(mireds: number) { + if (mireds <= 0) { + throw new Error('Mireds must be a positive value.'); + } + return 1000000 / mireds; + } + + static kelvinToMireds(kelvin) { + if (kelvin <= 0) { + throw new Error('Kelvin must be a positive value.'); + } + return 1000000 / kelvin; + } +} diff --git a/src/utils/guid.ts b/src/utils/guid.ts index 8df838b..a43b0ee 100644 --- a/src/utils/guid.ts +++ b/src/utils/guid.ts @@ -7,9 +7,9 @@ export class Guid { static newGuid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { - const r = Math.random() * 16 | 0, - v = c == 'x' ? r : (r & 0x3 | 0x8); + const r = (Math.random() * 16) | 0, + v = c == 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index bf07099..97b06b2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,12 +2,7 @@ "compilerOptions": { "target": "ES2018", // ~node10 "module": "commonjs", - "lib": [ - "es2015", - "es2016", - "es2017", - "es2018" - ], + "lib": ["es2015", "es2016", "es2017", "es2018"], "declaration": true, "declarationMap": true, "sourceMap": true, @@ -17,10 +12,6 @@ "esModuleInterop": true, "noImplicitAny": false }, - "include": [ - "src/" - ], - "exclude": [ - "**/*.spec.ts" - ] + "include": ["src/"], + "exclude": ["**/*.spec.ts"] }