diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e360ea6..d2d6d73f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) +## [Version 1.7.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v1.7.0) (2022-01-05) + +## What's Changed +* Added option to display Bot a Fan. +* Added option to display Bot a Door. [#179](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/179) +* Added option to display Bot a Lock. [#179](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/179) +* Added option to display Bot a Faucet. +* Added option to display Bot a Window. +* Added option to display Bot a WindowCovering. +* Added option to display Bot a Garage Door Opener. [#179](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/179) + +**Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v1.6.3...v1.7.0 + ## [Version 1.6.3](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v1.6.3) (2022-01-03) ## What's Changed diff --git a/config.schema.json b/config.schema.json index 566e5d98..7488ac7b 100644 --- a/config.schema.json +++ b/config.schema.json @@ -182,6 +182,48 @@ "outlet" ] }, + { + "title": "Garage Door", + "enum": [ + "garagedoor" + ] + }, + { + "title": "Door", + "enum": [ + "door" + ] + }, + { + "title": "Lock", + "enum": [ + "lock" + ] + }, + { + "title": "Faucet", + "enum": [ + "faucet" + ] + }, + { + "title": "Fan", + "enum": [ + "fan" + ] + }, + { + "title": "Window", + "enum": [ + "window" + ] + }, + { + "title": "Window Covering", + "enum": [ + "windowcovering" + ] + }, { "title": "Switch", "enum": [ diff --git a/package-lock.json b/package-lock.json index e8f51f66..792585d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@switchbot/homebridge-switchbot", - "version": "1.6.3", + "version": "1.7.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@switchbot/homebridge-switchbot", - "version": "1.6.3", + "version": "1.7.0", "funding": [ { "type": "Paypal", @@ -24,7 +24,7 @@ "rxjs": "^7.5.1" }, "devDependencies": { - "@types/node": "^17.0.7", + "@types/node": "^17.0.8", "@typescript-eslint/eslint-plugin": "^5.9.0", "@typescript-eslint/parser": "^5.9.0", "eslint": "^8.6.0", @@ -415,9 +415,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.7.tgz", - "integrity": "sha512-1QUk+WAUD4t8iR+Oj+UgI8oJa6yyxaB8a8pHaC8uqM6RrS1qbL7bf3Pwl5rHv0psm2CuDErgho6v5N+G+5fwtQ==", + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", + "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -2186,9 +2186,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "node_modules/hap-nodejs": { @@ -5532,9 +5532,9 @@ "dev": true }, "@types/node": { - "version": "17.0.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.7.tgz", - "integrity": "sha512-1QUk+WAUD4t8iR+Oj+UgI8oJa6yyxaB8a8pHaC8uqM6RrS1qbL7bf3Pwl5rHv0psm2CuDErgho6v5N+G+5fwtQ==", + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", + "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", "dev": true }, "@typescript-eslint/eslint-plugin": { @@ -6844,9 +6844,9 @@ } }, "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "hap-nodejs": { diff --git a/package.json b/package.json index 52f41bf8..940c4fe8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "Homebridge SwitchBot", "name": "@switchbot/homebridge-switchbot", - "version": "1.6.3", + "version": "1.7.0", "description": "The [Homebridge](https://homebridge.io) SwitchBot plugin allows you to access your [SwitchBot](https://www.switch-bot.com) device(s) from HomeKit.", "author": "SwitchBot (https://github.com/SwitchBot)", "license": "ISC", @@ -56,7 +56,7 @@ "rxjs": "^7.5.1" }, "devDependencies": { - "@types/node": "^17.0.7", + "@types/node": "^17.0.8", "@typescript-eslint/eslint-plugin": "^5.9.0", "@typescript-eslint/parser": "^5.9.0", "eslint": "^8.6.0", diff --git a/src/devices/bots.ts b/src/devices/bots.ts index 378a6d53..39ab0de6 100644 --- a/src/devices/bots.ts +++ b/src/devices/bots.ts @@ -13,9 +13,17 @@ import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceSta */ export class Bot { // Services - private outletService?: Service; + private fanService?: Service; + private doorService?: Service; + private lockService?: Service; + private faucetService?: Service; + private windowService?: Service; private switchService?: Service; + private outletService?: Service; private batteryService?: Service; + private garageDoorService?: Service; + private windowCoveringService?: Service; + // Characteristic Values On!: CharacteristicValue; @@ -94,29 +102,180 @@ export class Bot { // get the LightBulb service if it exists, otherwise create a new LightBulb service // you can create multiple services for each accessory if (device.bot?.deviceType === 'switch') { - // If outletService still pressent, then remove first - if (this.outletService) { - this.debugLog(`Bot: ${accessory.displayName} Removing Leftover outletService first`); - } - this.outletService = this.accessory.getService(this.platform.Service.Outlet); - accessory.removeService(this.outletService!); + this.removeFanService(accessory); + this.removeLockService(accessory); + this.removeDoorService(accessory); + this.removeFaucetService(accessory); + this.removeOutletService(accessory); + this.removeWindowService(accessory); + this.removeGarageDoorService(accessory); + this.removeWindowCoveringService(accessory); // Add switchService (this.switchService = accessory.getService(this.platform.Service.Switch) || accessory.addService(this.platform.Service.Switch)), `${accessory.displayName} Switch`; - if (this.deviceLogging !== 'none') { - this.infoLog(`Bot: ${accessory.displayName} Displaying as Switch`); - } + this.infoLog(`Bot: ${accessory.displayName} Displaying as Switch`); + + this.switchService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.switchService.getCharacteristic(this.platform.Characteristic.On).onSet(this.handleOnSet.bind(this)); + } else if (device.bot?.deviceType === 'garagedoor') { + this.removeFanService(accessory); + this.removeLockService(accessory); + this.removeDoorService(accessory); + this.removeFaucetService(accessory); + this.removeOutletService(accessory); + this.removeSwitchService(accessory); + this.removeWindowService(accessory); + this.removeWindowCoveringService(accessory); + + // Add switchService + (this.garageDoorService = + accessory.getService(this.platform.Service.GarageDoorOpener) || + accessory.addService(this.platform.Service.GarageDoorOpener)), `${accessory.displayName} Garage Door Opener`; + this.infoLog(`Bot: ${accessory.displayName} Displaying as Garage Door Opener`); + + this.garageDoorService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.garageDoorService.getCharacteristic(this.platform.Characteristic.TargetDoorState).onSet(this.handleOnSet.bind(this)); + this.garageDoorService.setCharacteristic(this.platform.Characteristic.ObstructionDetected, false); + } else if (device.bot?.deviceType === 'door') { + this.removeFanService(accessory); + this.removeLockService(accessory); + this.removeOutletService(accessory); + this.removeFaucetService(accessory); + this.removeSwitchService(accessory); + this.removeWindowService(accessory); + this.removeGarageDoorService(accessory); + this.removeWindowCoveringService(accessory); + + // Add switchService + (this.doorService = + accessory.getService(this.platform.Service.Door) || + accessory.addService(this.platform.Service.Door)), `${accessory.displayName} Door`; + this.infoLog(`Bot: ${accessory.displayName} Displaying as Door`); - this.switchService?.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.doorService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.doorService.getCharacteristic(this.platform.Characteristic.TargetPosition) + .setProps({ + validValues: [0, 100], + minValue: 0, + maxValue: 100, + minStep: 100, + }).onSet(this.handleOnSet.bind(this)); + this.doorService.setCharacteristic(this.platform.Characteristic.PositionState, this.platform.Characteristic.PositionState.STOPPED); + } else if (device.bot?.deviceType === 'window') { + this.removeFanService(accessory); + this.removeLockService(accessory); + this.removeDoorService(accessory); + this.removeOutletService(accessory); + this.removeFaucetService(accessory); + this.removeSwitchService(accessory); + this.removeGarageDoorService(accessory); + this.removeWindowCoveringService(accessory); + + // Add switchService + (this.windowService = + accessory.getService(this.platform.Service.Window) || + accessory.addService(this.platform.Service.Window)), `${accessory.displayName} Window`; + this.infoLog(`Bot: ${accessory.displayName} Displaying as Window`); + + this.windowService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.windowService.getCharacteristic(this.platform.Characteristic.TargetPosition) + .setProps({ + validValues: [0, 100], + minValue: 0, + maxValue: 100, + minStep: 100, + }).onSet(this.handleOnSet.bind(this)); + this.windowService.setCharacteristic(this.platform.Characteristic.PositionState, this.platform.Characteristic.PositionState.STOPPED); + } else if (device.bot?.deviceType === 'windowcovering') { + this.removeFanService(accessory); + this.removeLockService(accessory); + this.removeDoorService(accessory); + this.removeOutletService(accessory); + this.removeFaucetService(accessory); + this.removeSwitchService(accessory); + this.removeWindowService(accessory); + this.removeGarageDoorService(accessory); + + // Add switchService + (this.windowCoveringService = + accessory.getService(this.platform.Service.WindowCovering) || + accessory.addService(this.platform.Service.WindowCovering)), `${accessory.displayName} Window Covering`; + this.infoLog(`Bot: ${accessory.displayName} Displaying as Window Covering`); + + this.windowCoveringService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.windowCoveringService.getCharacteristic(this.platform.Characteristic.TargetPosition) + .setProps({ + validValues: [0, 100], + minValue: 0, + maxValue: 100, + minStep: 100, + }).onSet(this.handleOnSet.bind(this)); + this.windowCoveringService.setCharacteristic(this.platform.Characteristic.PositionState, this.platform.Characteristic.PositionState.STOPPED); + } else if (device.bot?.deviceType === 'lock') { + this.removeFanService(accessory); + this.removeDoorService(accessory); + this.removeOutletService(accessory); + this.removeSwitchService(accessory); + this.removeFaucetService(accessory); + this.removeWindowService(accessory); + this.removeGarageDoorService(accessory); + this.removeWindowCoveringService(accessory); + + // Add switchService + (this.lockService = + accessory.getService(this.platform.Service.LockMechanism) || + accessory.addService(this.platform.Service.LockMechanism)), `${accessory.displayName} Lock`; + this.infoLog(`Bot: ${accessory.displayName} Displaying as Lock`); + + this.lockService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.lockService.getCharacteristic(this.platform.Characteristic.LockTargetState).onSet(this.handleOnSet.bind(this)); + } else if (device.bot?.deviceType === 'faucet') { + this.removeFanService(accessory); + this.removeLockService(accessory); + this.removeDoorService(accessory); + this.removeOutletService(accessory); + this.removeSwitchService(accessory); + this.removeWindowService(accessory); + this.removeGarageDoorService(accessory); + this.removeWindowCoveringService(accessory); + + // Add switchService + (this.faucetService = + accessory.getService(this.platform.Service.Faucet) || + accessory.addService(this.platform.Service.Faucet)), `${accessory.displayName} Faucet`; + this.infoLog(`Bot: ${accessory.displayName} Displaying as Faucet`); + + this.faucetService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.faucetService.getCharacteristic(this.platform.Characteristic.Active).onSet(this.handleOnSet.bind(this)); + } else if (device.bot?.deviceType === 'fan') { + this.removeLockService(accessory); + this.removeDoorService(accessory); + this.removeFaucetService(accessory); + this.removeOutletService(accessory); + this.removeSwitchService(accessory); + this.removeWindowService(accessory); + this.removeGarageDoorService(accessory); + this.removeWindowCoveringService(accessory); + + // Add switchService + (this.fanService = + accessory.getService(this.platform.Service.Fan) || + accessory.addService(this.platform.Service.Fan)), `${accessory.displayName} Fan`; + this.infoLog(`Bot: ${accessory.displayName} Displaying as Fan`); + + this.fanService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.fanService.getCharacteristic(this.platform.Characteristic.On).onSet(this.handleOnSet.bind(this)); } else { - // If switchService still pressent, then remove first - if (this.switchService) { - this.debugLog(`Bot: ${accessory.displayName} Removing Leftover switchService first`); - } - this.switchService = this.accessory.getService(this.platform.Service.Switch); - accessory.removeService(this.switchService!); + this.removeFanService(accessory); + this.removeLockService(accessory); + this.removeDoorService(accessory); + this.removeFaucetService(accessory); + this.removeSwitchService(accessory); + this.removeWindowService(accessory); + this.removeGarageDoorService(accessory); + this.removeWindowCoveringService(accessory); // Add outletService (this.outletService = @@ -124,7 +283,8 @@ export class Bot { accessory.addService(this.platform.Service.Outlet)), `${accessory.displayName} Outlet`; this.infoLog(`Bot: ${accessory.displayName} Displaying as Outlet`); - this.outletService?.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.outletService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.outletService.getCharacteristic(this.platform.Characteristic.On).onSet(this.handleOnSet.bind(this)); } if (device.ble) { @@ -135,15 +295,6 @@ export class Bot { this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`); } - // each service must implement at-minimum the "required characteristics" for the given service type - // see https://developers.homebridge.io/#/service/Outlet - - if (device.bot?.deviceType === 'switch') { - this.switchService!.getCharacteristic(this.platform.Characteristic.On).onSet(this.handleOnSet.bind(this)); - } else { - this.outletService!.getCharacteristic(this.platform.Characteristic.On).onSet(this.handleOnSet.bind(this)); - } - // Retrieve initial values and updateHomekit this.updateHomeKitCharacteristics(); @@ -185,6 +336,87 @@ export class Bot { }); } + public removeOutletService(accessory: PlatformAccessory) { + // If outletService still pressent, then remove first + this.outletService = this.accessory.getService(this.platform.Service.Outlet); + if (this.outletService) { + this.warnLog(`Bot: ${accessory.displayName} Removing Leftover Outlet Service`); + } + accessory.removeService(this.outletService!); + } + + public removeGarageDoorService(accessory: PlatformAccessory) { + // If garageDoorService still pressent, then remove first + this.garageDoorService = this.accessory.getService(this.platform.Service.GarageDoorOpener); + if (this.garageDoorService) { + this.warnLog(`Bot: ${accessory.displayName} Removing Leftover Garage Door Service`); + } + accessory.removeService(this.garageDoorService!); + } + + public removeDoorService(accessory: PlatformAccessory) { + // If doorService still pressent, then remove first + this.doorService = this.accessory.getService(this.platform.Service.Door); + if (this.doorService) { + this.warnLog(`Bot: ${accessory.displayName} Removing Leftover Door Service`); + } + accessory.removeService(this.doorService!); + } + + public removeLockService(accessory: PlatformAccessory) { + // If lockService still pressent, then remove first + this.lockService = this.accessory.getService(this.platform.Service.LockMechanism); + if (this.lockService) { + this.warnLog(`Bot: ${accessory.displayName} Removing Leftover Lock Service`); + } + accessory.removeService(this.lockService!); + } + + public removeFaucetService(accessory: PlatformAccessory) { + // If faucetService still pressent, then remove first + this.faucetService = this.accessory.getService(this.platform.Service.Faucet); + if (this.faucetService) { + this.warnLog(`Bot: ${accessory.displayName} Removing Leftover Faucet Service`); + } + accessory.removeService(this.faucetService!); + } + + public removeFanService(accessory: PlatformAccessory) { + // If fanService still pressent, then remove first + this.fanService = this.accessory.getService(this.platform.Service.Fan); + if (this.fanService) { + this.warnLog(`Bot: ${accessory.displayName} Removing Leftover Fan Service`); + } + accessory.removeService(this.fanService!); + } + + public removeWindowService(accessory: PlatformAccessory) { + // If windowService still pressent, then remove first + this.windowService = this.accessory.getService(this.platform.Service.Window); + if (this.windowService) { + this.warnLog(`Bot: ${accessory.displayName} Removing Leftover Window Service`); + } + accessory.removeService(this.windowService!); + } + + public removeWindowCoveringService(accessory: PlatformAccessory) { + // If windowCoveringService still pressent, then remove first + this.windowCoveringService = this.accessory.getService(this.platform.Service.WindowCovering); + if (this.windowCoveringService) { + this.warnLog(`Bot: ${accessory.displayName} Removing Leftover Window Covering Service`); + } + accessory.removeService(this.windowCoveringService!); + } + + public removeSwitchService(accessory: PlatformAccessory) { + // If switchService still pressent, then remove first + this.switchService = this.accessory.getService(this.platform.Service.Switch); + if (this.switchService) { + this.warnLog(`Bot: ${accessory.displayName} Removing Leftover Switch Service`); + } + accessory.removeService(this.switchService!); + } + refreshRate() { if (this.device.refreshRate) { this.deviceRefreshRate = this.accessory.context.refreshRate = this.device.refreshRate; @@ -321,11 +553,27 @@ export class Bot { } } - private connectBLE() { - // Convert to BLE Address - this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); - this.debugLog(`Bot: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); - const switchbot = this.platform.connectBLE(); + public async connectBLE() { + let switchbot: any; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const Switchbot = require('node-switchbot'); + switchbot = new Switchbot(); + // Convert to BLE Address + this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); + this.debugLog(`Bot: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); + } catch (e: any) { + switchbot = false; + this.errorLog(`Bot: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`); + if (this.deviceLogging === 'debug') { + this.errorLog(`Bot: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.platform.debugMode) { + this.errorLog(`Bot: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error: ${JSON.stringify(e)}`); + } + } return switchbot; } @@ -333,69 +581,64 @@ export class Bot { this.debugLog(`Bot: ${this.accessory.displayName} BLE refreshStatus`); // Convert to BLE Address - const switchbot = this.connectBLE(); + const switchbot = await this.connectBLE(); // Start to monitor advertisement packets - if (switchbot !== false) { - switchbot.startScan({ - model: 'H', - id: this.device.bleMac, - }).then(() => { - // Set an event hander - switchbot.onadvertisement = (ad: ad) => { - this.serviceData = ad.serviceData; - this.mode = ad.serviceData.mode; - this.state = ad.serviceData.state; - this.battery = ad.serviceData.battery; - this.debugLog(`Bot: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); - this.debugLog(`Bot: ${this.accessory.displayName}, model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName},` - + ` mode: ${ad.serviceData.mode}, state: ${ad.serviceData.state}, battery: ${ad.serviceData.battery}`); - - if (this.serviceData) { - this.connected = true; - this.debugLog(`Bot: ${this.accessory.displayName} connected: ${this.connected}`); - } else { - this.connected = false; - this.debugLog(`Bot: ${this.accessory.displayName} connected: ${this.connected}`); - } - }; - // Wait 2 seconds - return switchbot.wait(this.scanDuration * 1000); - }).then(async () => { - // Stop to monitor - switchbot.stopScan(); - if (this.connected) { - this.parseStatus(); - this.updateHomeKitCharacteristics(); + if (switchbot === false) { + this.errorLog(`Bot: ${this.accessory.displayName} wasn't able to establish BLE Connection: ${switchbot}`); + } + switchbot.startScan({ + model: 'H', + id: this.device.bleMac, + }).then(() => { + // Set an event hander + switchbot.onadvertisement = (ad: ad) => { + this.serviceData = ad.serviceData; + this.mode = ad.serviceData.mode; + this.state = ad.serviceData.state; + this.battery = ad.serviceData.battery; + this.debugLog(`Bot: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + this.debugLog(`Bot: ${this.accessory.displayName}, model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName},` + + ` mode: ${ad.serviceData.mode}, state: ${ad.serviceData.state}, battery: ${ad.serviceData.battery}`); + + if (this.serviceData) { + this.connected = true; + this.debugLog(`Bot: ${this.accessory.displayName} connected: ${this.connected}`); } else { - await this.BLEconnection(); - } - }).catch(async (e: any) => { - this.errorLog(`Bot: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); - if (this.deviceLogging === 'debug') { - this.errorLog(`Bot: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` - + ` Error Message: ${JSON.stringify(e.message)}`); - } - if (this.platform.debugMode) { - this.errorLog(`Bot: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` - + ` Error: ${JSON.stringify(e)}`); + this.connected = false; + this.debugLog(`Bot: ${this.accessory.displayName} connected: ${this.connected}`); } + }; + // Wait 2 seconds + return switchbot.wait(this.scanDuration * 1000); + }).then(async () => { + // Stop to monitor + switchbot.stopScan(); + if (this.connected) { + this.parseStatus(); + this.updateHomeKitCharacteristics(); + } else { + this.errorLog(`Bot: ${this.accessory.displayName} wasn't able to establish BLE Connection`); if (this.platform.config.credentials?.openToken) { this.warnLog(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); await this.openAPIRefreshStatus(); } - this.apiError(e); - }); - } else { - await this.BLEconnection(); - } - } - - private async BLEconnection() { - this.errorLog(`Bot: ${this.accessory.displayName} wasn't able to establish BLE Connection`); - if (this.platform.config.credentials?.openToken) { - this.warnLog(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); - await this.openAPIRefreshStatus(); - } + } + }).catch(async (e: any) => { + this.errorLog(`Bot: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); + if (this.deviceLogging === 'debug') { + this.errorLog(`Bot: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.platform.debugMode) { + this.errorLog(`Bot: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.warnLog(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIRefreshStatus(); + } + this.apiError(e); + }); } private async openAPIRefreshStatus() { @@ -443,54 +686,30 @@ export class Bot { } private async BLEpushChanges() { + this.debugLog(`Bot: ${this.accessory.displayName} BLE pushChanges On: ${this.On} OnCached: ${this.OnCached}`); if (this.On !== this.OnCached) { this.debugLog(`Bot: ${this.accessory.displayName} BLE pushChanges`); - const switchbot = this.connectBLE(); - if (switchbot !== false) { - if (this.botMode === 'press') { - this.debugLog(`Bot: ${this.accessory.displayName} Bot Mode: ${this.botMode}`); - switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }) - .then((device_list: { press: (arg0: { id: string | undefined; }) => any; }[]) => { - this.infoLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); - return device_list[0].press({ id: this.device.bleMac }); - }).then(() => { - this.debugLog(`Bot: ${this.accessory.displayName} Done.`); - this.On = false; - this.OnCached = this.On; - this.accessory.context.On = this.OnCached; - setTimeout(() => { - if (this.device.bot?.deviceType === 'switch') { - this.switchService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On); - } else { - this.outletService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On); - } - this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}, Switch Timeout`); - }, 500); - }).catch(async (e: any) => { - this.errorLog(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection`); - if (this.deviceLogging === 'debug') { - this.errorLog(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` - + ` Error Message: ${JSON.stringify(e.message)}`); - } - if (this.platform.debugMode) { - this.errorLog(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` - + ` Error: ${JSON.stringify(e)}`); - } - if (this.platform.config.credentials?.openToken) { - this.warnLog(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); - await this.openAPIpushChanges(); - } - this.apiError(e); - }); - } else if (this.botMode === 'switch') { - this.debugLog(`Bot: ${this.accessory.displayName} Press Mode: ${this.botMode}`); - switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list: any) => { + const switchbot = await this.connectBLE(); + //if (switchbot !== false) { + if (this.botMode === 'press') { + this.debugLog(`Bot: ${this.accessory.displayName} Bot Mode: ${this.botMode}`); + switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }) + .then((device_list: { press: (arg0: { id: string | undefined; }) => any; }[]) => { this.infoLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); - return this.turnOnOff(device_list); + return device_list[0].press({ id: this.device.bleMac }); }).then(() => { this.debugLog(`Bot: ${this.accessory.displayName} Done.`); + this.On = false; this.OnCached = this.On; this.accessory.context.On = this.OnCached; + setTimeout(() => { + if (this.device.bot?.deviceType === 'switch') { + this.switchService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On); + } else { + this.outletService?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.On); + } + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}, Switch Timeout`); + }, 500); }).catch(async (e: any) => { this.errorLog(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection`); if (this.deviceLogging === 'debug') { @@ -507,16 +726,41 @@ export class Bot { } this.apiError(e); }); - } else { - throw new Error(`Bot: ${this.accessory.displayName} Bot Mode: ${this.botMode}`); - } + } else if (this.botMode === 'switch') { + this.debugLog(`Bot: ${this.accessory.displayName} Press Mode: ${this.botMode}`); + switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list: any) => { + this.infoLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); + return this.turnOnOff(device_list); + }).then(() => { + this.debugLog(`Bot: ${this.accessory.displayName} Done.`); + this.OnCached = this.On; + this.accessory.context.On = this.OnCached; + }).catch(async (e: any) => { + this.errorLog(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection`); + if (this.deviceLogging === 'debug') { + this.errorLog(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.platform.debugMode) { + this.errorLog(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.warnLog(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIpushChanges(); + } + this.apiError(e); + }); } else { - this.errorLog(`Bot: ${this.accessory.displayName} wasn't able to establish BLE Connection`); - if (this.platform.config.credentials?.openToken) { - this.warnLog(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); - await this.openAPIpushChanges(); - } + throw new Error(`Bot: ${this.accessory.displayName} Bot Mode: ${this.botMode}`); } + /* } else { + this.errorLog(`Bot: ${this.accessory.displayName} wasn't able to establish BLE Connection`); + if (this.platform.config.credentials?.openToken) { + this.warnLog(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIpushChanges(); + } + }*/ } this.OnCached = this.On; this.accessory.context.On = this.OnCached; @@ -585,15 +829,143 @@ export class Bot { * Updates the status for each of the HomeKit Characteristics */ updateHomeKitCharacteristics() { - if (this.On === undefined) { - this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); - } else { - if (this.device.bot?.deviceType === 'switch') { + if (this.device.bot?.deviceType === 'garagedoor') { + if (this.On === undefined) { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); + } else { + if (this.On) { + this.garageDoorService?.updateCharacteristic(this.platform.Characteristic.TargetDoorState, + this.platform.Characteristic.TargetDoorState.OPEN); + this.garageDoorService?.updateCharacteristic(this.platform.Characteristic.CurrentDoorState, + this.platform.Characteristic.CurrentDoorState.OPEN); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic TargetDoorState: Open, CurrentDoorState: Open`); + } else { + this.garageDoorService?.updateCharacteristic(this.platform.Characteristic.TargetDoorState, + this.platform.Characteristic.TargetDoorState.CLOSED); + this.garageDoorService?.updateCharacteristic(this.platform.Characteristic.CurrentDoorState, + this.platform.Characteristic.CurrentDoorState.CLOSED); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic TargetDoorState: Open, CurrentDoorState: Open`); + } + } + this.debugLog(`Bot: ${this.accessory.displayName} Garage Door On: ${this.On}`); + } else if (this.device.bot?.deviceType === 'door') { + if (this.On === undefined) { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); + } else { + if (this.On) { + this.doorService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, 100); + this.doorService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, 100); + this.doorService?.updateCharacteristic(this.platform.Characteristic.PositionState, + this.platform.Characteristic.PositionState.STOPPED); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`); + } else { + this.doorService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, 0); + this.doorService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, 0); + this.doorService?.updateCharacteristic(this.platform.Characteristic.PositionState, + this.platform.Characteristic.PositionState.STOPPED); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`); + } + } + this.debugLog(`Bot: ${this.accessory.displayName} Door On: ${this.On}`); + } else if (this.device.bot?.deviceType === 'window') { + if (this.On === undefined) { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); + } else { + if (this.On) { + this.windowService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, 100); + this.windowService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, 100); + this.windowService?.updateCharacteristic(this.platform.Characteristic.PositionState, + this.platform.Characteristic.PositionState.STOPPED); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`); + } else { + this.windowService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, 0); + this.windowService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, 0); + this.windowService?.updateCharacteristic(this.platform.Characteristic.PositionState, + this.platform.Characteristic.PositionState.STOPPED); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`); + } + } + this.debugLog(`Bot: ${this.accessory.displayName} Window On: ${this.On}`); + } else if (this.device.bot?.deviceType === 'windowcovering') { + if (this.On === undefined) { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); + } else { + if (this.On) { + this.windowCoveringService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, 100); + this.windowCoveringService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, 100); + this.windowCoveringService?.updateCharacteristic(this.platform.Characteristic.PositionState, + this.platform.Characteristic.PositionState.STOPPED); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`); + } else { + this.windowCoveringService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, 0); + this.windowCoveringService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, 0); + this.windowCoveringService?.updateCharacteristic(this.platform.Characteristic.PositionState, + this.platform.Characteristic.PositionState.STOPPED); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`); + } + } + this.debugLog(`Bot: ${this.accessory.displayName} Window Covering On: ${this.On}`); + } else if (this.device.bot?.deviceType === 'lock') { + if (this.On === undefined) { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); + } else { + if (this.On) { + this.lockService?.updateCharacteristic(this.platform.Characteristic.LockTargetState, + this.platform.Characteristic.LockTargetState.UNSECURED); + this.lockService?.updateCharacteristic(this.platform.Characteristic.LockCurrentState, + this.platform.Characteristic.LockCurrentState.UNSECURED); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic LockTargetState: UNSECURED, LockCurrentState: UNSECURED`); + } else { + this.lockService?.updateCharacteristic(this.platform.Characteristic.LockTargetState, + this.platform.Characteristic.LockTargetState.SECURED); + this.lockService?.updateCharacteristic(this.platform.Characteristic.LockCurrentState, + this.platform.Characteristic.LockCurrentState.SECURED); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic LockTargetState: SECURED, LockCurrentState: SECURED`); + } + } + this.debugLog(`Bot: ${this.accessory.displayName} Lock On: ${this.On}`); + } else if (this.device.bot?.deviceType === 'faucet') { + if (this.On === undefined) { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); + } else { + if (this.On) { + this.faucetService?.updateCharacteristic(this.platform.Characteristic.Active, + this.platform.Characteristic.Active.ACTIVE); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic Active: ${this.On}`); + } else { + this.faucetService?.updateCharacteristic(this.platform.Characteristic.Active, + this.platform.Characteristic.Active.INACTIVE); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic Active: ${this.On}`); + } + } + this.debugLog(`Bot: ${this.accessory.displayName} Faucet On: ${this.On}`); + } else if (this.device.bot?.deviceType === 'fan') { + if (this.On === undefined) { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); + } else { + if (this.On) { + this.fanService?.updateCharacteristic(this.platform.Characteristic.On, this.On); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + } else { + this.fanService?.updateCharacteristic(this.platform.Characteristic.On, this.On); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + } + } + this.debugLog(`Bot: ${this.accessory.displayName} Fan On: ${this.On}`); + } else if (this.device.bot?.deviceType === 'switch') { + if (this.On === undefined) { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); + } else { this.switchService?.updateCharacteristic(this.platform.Characteristic.On, this.On); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + } + } else { + if (this.On === undefined) { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${this.On}`); } else { this.outletService?.updateCharacteristic(this.platform.Characteristic.On, this.On); + this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); } - this.debugLog(`Bot: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); } if (this.device.ble) { if (this.BatteryLevel === undefined) { @@ -612,7 +984,30 @@ export class Bot { } public apiError(e: any) { - if (this.device.bot?.deviceType === 'switch') { + if (this.device.bot?.deviceType === 'garagedoor') { + this.garageDoorService?.updateCharacteristic(this.platform.Characteristic.TargetDoorState, e); + this.garageDoorService?.updateCharacteristic(this.platform.Characteristic.CurrentDoorState, e); + this.garageDoorService?.updateCharacteristic(this.platform.Characteristic.ObstructionDetected, e); + } else if (this.device.bot?.deviceType === 'door') { + this.doorService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, e); + this.doorService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, e); + this.doorService?.updateCharacteristic(this.platform.Characteristic.PositionState, e); + } else if (this.device.bot?.deviceType === 'window') { + this.windowService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, e); + this.windowService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, e); + this.windowService?.updateCharacteristic(this.platform.Characteristic.PositionState, e); + } else if (this.device.bot?.deviceType === 'windowcovering') { + this.windowCoveringService?.updateCharacteristic(this.platform.Characteristic.TargetPosition, e); + this.windowCoveringService?.updateCharacteristic(this.platform.Characteristic.CurrentPosition, e); + this.windowCoveringService?.updateCharacteristic(this.platform.Characteristic.PositionState, e); + } else if (this.device.bot?.deviceType === 'lock') { + this.doorService?.updateCharacteristic(this.platform.Characteristic.LockTargetState, e); + this.doorService?.updateCharacteristic(this.platform.Characteristic.LockCurrentState, e); + } else if (this.device.bot?.deviceType === 'faucet') { + this.faucetService?.updateCharacteristic(this.platform.Characteristic.Active, e); + } else if (this.device.bot?.deviceType === 'fan') { + this.fanService?.updateCharacteristic(this.platform.Characteristic.On, e); + } else if (this.device.bot?.deviceType === 'switch') { this.switchService?.updateCharacteristic(this.platform.Characteristic.On, e); } else { this.outletService?.updateCharacteristic(this.platform.Characteristic.On, e); @@ -670,8 +1065,40 @@ export class Bot { * Handle requests to set the "On" characteristic */ private handleOnSet(value: CharacteristicValue) { - this.debugLog(`Bot: ${this.accessory.displayName} On: ${value}`); - this.On = value; + if (this.device.bot?.deviceType === 'garagedoor') { + this.debugLog(`Bot: ${this.accessory.displayName} TargetDoorState: ${value}`); + if (value === this.platform.Characteristic.TargetDoorState.CLOSED) { + this.On = false; + } else { + this.On = true; + } + } else if (this.device.bot?.deviceType === 'door' + || this.device.bot?.deviceType === 'window' + || this.device.bot?.deviceType === 'windowcovering') { + this.debugLog(`Bot: ${this.accessory.displayName} TargetPosition: ${value}`); + if (value === 0) { + this.On = false; + } else { + this.On = true; + } + } else if (this.device.bot?.deviceType === 'lock') { + this.debugLog(`Bot: ${this.accessory.displayName} LockTargetState: ${value}`); + if (value === this.platform.Characteristic.LockTargetState.SECURED) { + this.On = false; + } else { + this.On = true; + } + } else if (this.device.bot?.deviceType === 'faucet') { + this.debugLog(`Bot: ${this.accessory.displayName} Active: ${value}`); + if (value === this.platform.Characteristic.Active.INACTIVE) { + this.On = false; + } else { + this.On = true; + } + } else { + this.debugLog(`Bot: ${this.accessory.displayName} On: ${value}`); + this.On = value; + } this.doBotUpdate.next(); } diff --git a/src/devices/contact.ts b/src/devices/contact.ts index 5f6b5f26..9100e178 100644 --- a/src/devices/contact.ts +++ b/src/devices/contact.ts @@ -273,18 +273,34 @@ export class Contact { } } - private connectBLE() { - // Convert to BLE Address - this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); - this.debugLog(`Contact Sensor: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); - const switchbot = this.platform.connectBLE(); + public async connectBLE() { + let switchbot: any; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const Switchbot = require('node-switchbot'); + switchbot = new Switchbot(); + // Convert to BLE Address + this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); + this.debugLog(`Contact Sensor: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); + } catch (e: any) { + switchbot = false; + this.errorLog(`Contact Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`); + if (this.deviceLogging === 'debug') { + this.errorLog(`Contact Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.platform.debugMode) { + this.errorLog(`Contact Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error: ${JSON.stringify(e)}`); + } + } return switchbot; } private async BLERefreshStatus() { this.debugLog(`Contact Sensor: ${this.accessory.displayName} BLE refreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires - const switchbot = this.connectBLE(); + const switchbot = await this.connectBLE(); // Start to monitor advertisement packets if (switchbot !== false) { switchbot.startScan({ @@ -342,12 +358,12 @@ export class Contact { this.apiError(e); }); } else { - await this.BLEconnection(); + await this.BLEconnection(switchbot); } } - private async BLEconnection() { - this.errorLog(`Contact Sensor: ${this.accessory.displayName} wasn't able to establish BLE Connection`); + public async BLEconnection(switchbot: any) { + this.errorLog(`Contact Sensor: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`); if (this.platform.config.credentials?.openToken) { this.warnLog(`Contact Sensor: ${this.accessory.displayName} Using OpenAPI Connection`); await this.openAPIRefreshStatus(); diff --git a/src/devices/curtains.ts b/src/devices/curtains.ts index 663ac38e..64130f23 100644 --- a/src/devices/curtains.ts +++ b/src/devices/curtains.ts @@ -253,11 +253,27 @@ export class Curtain { } } - private connectBLE() { - // Convert to BLE Address - this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); - this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); - const switchbot = this.platform.connectBLE(); + public async connectBLE() { + let switchbot: any; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const Switchbot = require('node-switchbot'); + switchbot = new Switchbot(); + // Convert to BLE Address + this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); + this.debugLog(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); + } catch (e: any) { + switchbot = false; + this.errorLog(`Curtain: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`); + if (this.deviceLogging === 'debug') { + this.errorLog(`Curtain: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.platform.debugMode) { + this.errorLog(`Curtain: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error: ${JSON.stringify(e)}`); + } + } return switchbot; } @@ -452,7 +468,7 @@ export class Curtain { private async BLERefreshStatus() { this.debugLog(`Curtain: ${this.accessory.displayName} BLE refreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires - const switchbot = this.connectBLE(); + const switchbot = await this.connectBLE(); // Start to monitor advertisement packets if (switchbot !== false) { switchbot.startScan({ @@ -510,12 +526,12 @@ export class Curtain { this.apiError(e); }); } else { - await this.BLEconnection(); + await this.BLEconnection(switchbot); } } - private async BLEconnection() { - this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection`); + public async BLEconnection(switchbot: any) { + this.errorLog(`Curtain: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`); if (this.platform.config.credentials?.openToken) { this.warnLog(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`); await this.openAPIRefreshStatus(); @@ -562,7 +578,7 @@ export class Curtain { private async BLEpushChanges() { if (this.TargetPosition !== this.CurrentPosition) { this.debugLog(`Curtain: ${this.accessory.displayName} BLE pushChanges`); - const switchbot = this.connectBLE(); + const switchbot = await this.connectBLE(); if (switchbot !== false) { switchbot.discover({ model: 'c', quick: true, id: this.device.bleMac }).then((device_list) => { this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`); diff --git a/src/devices/humidifiers.ts b/src/devices/humidifiers.ts index d10f32ba..2ab0ca6f 100644 --- a/src/devices/humidifiers.ts +++ b/src/devices/humidifiers.ts @@ -339,18 +339,34 @@ export class Humidifier { } } - private connectBLE() { - // Convert to BLE Address - this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); - this.debugLog(`Humidifier: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); - const switchbot = this.platform.connectBLE(); + public async connectBLE() { + let switchbot: any; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const Switchbot = require('node-switchbot'); + switchbot = new Switchbot(); + // Convert to BLE Address + this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); + this.debugLog(`Humidifier: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); + } catch (e: any) { + switchbot = false; + this.errorLog(`Humidifier: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`); + if (this.deviceLogging === 'debug') { + this.errorLog(`Humidifier: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.platform.debugMode) { + this.errorLog(`Humidifier: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error: ${JSON.stringify(e)}`); + } + } return switchbot; } private async BLERefreshStatus() { this.debugLog(`Humidifier: ${this.accessory.displayName} BLE refreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires - const switchbot = this.connectBLE(); + const switchbot = await this.connectBLE(); // Start to monitor advertisement packets if (switchbot !== false) { switchbot.startScan({ @@ -384,7 +400,7 @@ export class Humidifier { this.parseStatus(); this.updateHomeKitCharacteristics(); } else { - await this.BLEconnection(); + await this.BLEconnection(switchbot); } }).catch(async (e: any) => { this.errorLog(`Humidifier: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); @@ -403,12 +419,12 @@ export class Humidifier { this.apiError(e); }); } else { - await this.BLEconnection(); + await this.BLEconnection(switchbot); } } - private async BLEconnection() { - this.errorLog(`Humidifier: ${this.accessory.displayName} wasn't able to establish BLE Connection`); + public async BLEconnection(switchbot: any) { + this.errorLog(`Humidifier: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`); if (this.platform.config.credentials?.openToken) { this.warnLog(`Humidifier: ${this.accessory.displayName} Using OpenAPI Connection`); await this.openAPIRefreshStatus(); @@ -461,7 +477,7 @@ export class Humidifier { private async BLEpushChanges() { this.debugLog(`Humidifier: ${this.accessory.displayName} BLE pushChanges`); - const switchbot = this.connectBLE(); + const switchbot = await this.connectBLE(); if (switchbot !== false) { switchbot.discover({ model: 'e', quick: true, id: this.device.bleMac }).then((device_list) => { this.infoLog(`${this.accessory.displayName} Target Position: ${this.Active}`); diff --git a/src/devices/meters.ts b/src/devices/meters.ts index cd349604..87e39b49 100644 --- a/src/devices/meters.ts +++ b/src/devices/meters.ts @@ -303,19 +303,34 @@ export class Meter { } } - private connectBLE() { - // Convert to BLE Address - this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); - this.debugLog(`Meter: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); - const switchbot = this.platform.connectBLE(); + private async connectBLE() { + let switchbot: any; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const Switchbot = require('node-switchbot'); + switchbot = new Switchbot(); + // Convert to BLE Address + this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); + this.debugLog(`Meter: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); + } catch (e: any) { + switchbot = false; + this.errorLog(`Meter: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`); + if (this.deviceLogging === 'debug') { + this.errorLog(`Meter: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.platform.debugMode) { + this.errorLog(`Meter: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error: ${JSON.stringify(e)}`); + } + } return switchbot; } - private async BLErefreshStatus() { this.debugLog(`Meter: ${this.accessory.displayName} BLE RefreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires - const switchbot = this.connectBLE(); + const switchbot = await this.connectBLE(); // Start to monitor advertisement packets if (switchbot !== false) { switchbot.startScan({ @@ -356,7 +371,7 @@ export class Meter { this.parseStatus(); this.updateHomeKitCharacteristics(); } else { - await this.BLEconnection(); + await this.BLEconnection(switchbot); } }).catch(async (e: any) => { this.errorLog(`Meter: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); @@ -375,12 +390,12 @@ export class Meter { this.apiError(e); }); } else { - await this.BLEconnection(); + await this.BLEconnection(switchbot); } } - private async BLEconnection() { - this.errorLog(`Meter: ${this.accessory.displayName} wasn't able to establish BLE Connection`); + private async BLEconnection(switchbot: any) { + this.errorLog(`Meter: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`); if (this.platform.config.credentials?.openToken) { this.warnLog(`Meter: ${this.accessory.displayName} Using OpenAPI Connection`); await this.openAPIRefreshStatus(); diff --git a/src/devices/motion.ts b/src/devices/motion.ts index 25ab3754..696e878e 100644 --- a/src/devices/motion.ts +++ b/src/devices/motion.ts @@ -226,18 +226,34 @@ export class Motion { } } - private connectBLE() { - // Convert to BLE Address - this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); - this.debugLog(`Motion Sensor: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); - const switchbot = this.platform.connectBLE(); + public async connectBLE() { + let switchbot: any; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const Switchbot = require('node-switchbot'); + switchbot = new Switchbot(); + // Convert to BLE Address + this.device.bleMac = ((this.device.deviceId!.match(/.{1,2}/g))!.join(':')).toLowerCase(); + this.debugLog(`Motion Sensor: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); + } catch (e: any) { + switchbot = false; + this.errorLog(`Motion Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot}`); + if (this.deviceLogging === 'debug') { + this.errorLog(`Motion Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.platform.debugMode) { + this.errorLog(`Motion Sensor: ${this.accessory.displayName} 'node-switchbot' found: ${switchbot},` + + ` Error: ${JSON.stringify(e)}`); + } + } return switchbot; } private async BLERefreshStatus() { this.debugLog(`Motion Sensor: ${this.accessory.displayName} BLE RefreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires - const switchbot = this.connectBLE(); + const switchbot = await this.connectBLE(); // Start to monitor advertisement packets if (switchbot !== false) { switchbot.startScan({ @@ -271,7 +287,7 @@ export class Motion { this.parseStatus(); this.updateHomeKitCharacteristics(); } else { - await this.BLEconnection(); + await this.BLEconnection(switchbot); } }).catch(async (e: any) => { this.errorLog(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); @@ -290,12 +306,12 @@ export class Motion { this.apiError(e); }); } else { - await this.BLEconnection(); + await this.BLEconnection(switchbot); } } - private async BLEconnection() { - this.errorLog(`Motion Sensor: ${this.accessory.displayName} wasn't able to establish BLE Connection`); + public async BLEconnection(switchbot: any) { + this.errorLog(`Motion Sensor: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`); if (this.platform.config.credentials?.openToken) { this.warnLog(`Motion Sensor: ${this.accessory.displayName} Using OpenAPI Connection`); await this.openAPIRefreshStatus(); diff --git a/src/platform.ts b/src/platform.ts index b935e82f..2a9b4a26 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -41,7 +41,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { registeringDevice!: boolean; debugMode!: boolean; platformLogging?: string; - switchbot: any; constructor(public readonly log: Logger, public readonly config: SwitchBotPlatformConfig, public readonly api: API) { this.logs(); @@ -193,18 +192,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - connectBLE() { - try { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const Switchbot = require('node-switchbot'); - this.switchbot = new Switchbot(); - } catch (e) { - this.switchbot = false; - this.errorLog(`Was 'node-switchbot' found: ${this.switchbot}`); - } - return this.switchbot; - } - /** * this method discovers the Locations */