From 9128ec36525372934ecf2b20c55dcfae40ed124a Mon Sep 17 00:00:00 2001 From: Actardnes Date: Mon, 9 Nov 2020 17:03:58 +0100 Subject: [PATCH] Saunabox (#8) * saunaBox support * refactor and cleanup --- blebox/abstractBox.js | 244 +++++++++++++------ blebox/dimmerBox.js | 250 +++++++++---------- blebox/gateBox.js | 257 ++++++++++--------- blebox/index.js | 22 ++ blebox/saunaBox.js | 230 +++++++++++++++++ blebox/shutterBox.js | 308 +++++++++++------------ blebox/smartWindowBox.js | 414 +++++++++++-------------------- blebox/switchBox.js | 186 +++++++------- blebox/switchBoxD.js | 237 +++++++----------- blebox/wLightBox.js | 488 ++++++++++++++++++++----------------- blebox/wLightBoxS.js | 245 +++++++++---------- common/bleboxCommands.js | 17 +- common/bleboxConst.js | 3 +- common/colorHelper.js | 2 +- package.json | 10 +- platform/bleboxPlatform.js | 429 ++++++++++++++------------------ 16 files changed, 1706 insertions(+), 1636 deletions(-) create mode 100644 blebox/index.js create mode 100644 blebox/saunaBox.js diff --git a/blebox/abstractBox.js b/blebox/abstractBox.js index 95a34fc..3794002 100644 --- a/blebox/abstractBox.js +++ b/blebox/abstractBox.js @@ -1,91 +1,181 @@ -var communication = require("../common/communication"); -var bleboxCommands = require("../common/bleboxCommands"); -var NON_ALPHANUMERIC_REGEX = /[^a-zA-Z0-9\u00C0-\u024F]/g; +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); + +const NON_ALPHANUMERIC_REGEX = /[^a-zA-Z0-9\u00C0-\u024F]/g; +const maxBadRequestsCount = 4; +const deviceInfoIntervalInMs = 30000; +const specificStateIntervalInMs = 10000; + +class AbstractAccessoryWrapper { + constructor(accessory, log, api) { + this._log = log; + this.badRequestsCounter = 0; + this.servicesDefList = []; + this.servicesSubTypes = []; + + this.nameCharacteristic = api.hap.Characteristic.Name; + this.accessory = accessory; + } -module.exports = AbstractAccessoryWrapper; + init(deviceInfo, stateInfo) { + if (deviceInfo && stateInfo) { + this.createBleboxContext(); + this.updateDeviceInfo(deviceInfo); + for (let serviceNumber = 0; serviceNumber < this.servicesDefList.length; serviceNumber++) { + const serviceSubType = this.servicesSubTypes[serviceNumber]; + this.accessory.addService(this.servicesDefList[serviceNumber], this.getServiceName(serviceNumber), serviceSubType); + } + this.updateDeviceInfo(deviceInfo); + this.updateStateInfo(stateInfo); + } + } -function AbstractAccessoryWrapper(accessory, log, deviceInfo) { - this.log = log; - this.deviceId = deviceInfo.id; + log(message) { + const device = this.getDevice(); + const params = Array.prototype.slice.call(arguments, 1) || []; + params.unshift("%s ( %s ): " + message, this.type.toUpperCase(), device.deviceName); + this._log.apply(this._log, params) + } - this.deviceName = deviceInfo.deviceName ? deviceInfo.deviceName.replace(NON_ALPHANUMERIC_REGEX, ' ') : ""; - this.deviceIp = deviceInfo.ip; - this.badRequestsCounter = 0; - this.accessory = accessory; -} + getAccessory() { + return this.accessory; + }; -AbstractAccessoryWrapper.prototype.maxBadRequestsCount = 4; + createBleboxContext() { + this.accessory.context.blebox = this.accessory.context.blebox || {}; + } -AbstractAccessoryWrapper.prototype.deviceStateIntervalInMs = 30000; + getService(serviceNumber) { + const serviceDef = this.servicesDefList[serviceNumber]; + const serviceSubType = this.servicesSubTypes[serviceNumber]; + if (serviceSubType) { + return this.accessory.getServiceById(serviceDef, serviceSubType); + } else { + return this.accessory.getService(serviceDef); + } + } + + assignCharacteristics() { + for (let serviceNumber = 0; serviceNumber < this.servicesDefList.length; serviceNumber++) { + const service = this.getService(serviceNumber); + service.getCharacteristic(this.nameCharacteristic) + .on('get', this.onGetServiceName.bind(this, serviceNumber)); + } + } + + updateDeviceInfoCharacteristics() { + for (let serviceNumber = 0; serviceNumber < this.servicesDefList.length; serviceNumber++) { + const service = this.getService(serviceNumber); + service.updateCharacteristic(this.nameCharacteristic, this.getServiceName(serviceNumber)) + } + } -AbstractAccessoryWrapper.prototype.specificStateIntervalInMs = 10000; + updateStateInfoCharacteristics() { + this.log("Not supported method: updateStateInfo") + }; -AbstractAccessoryWrapper.prototype.getAccessory = function () { - return this.accessory; -}; + getServiceName(serviceNumber) { + const device = this.getDevice(); + return device.deviceName; + } -AbstractAccessoryWrapper.prototype.setDeviceIp = function (deviceInfo) { - this.deviceIp = deviceInfo.ip || this.deviceIp; - this.startListening(); -}; + onGetServiceName(serviceNumber, callback) { + const serviceName = this.getServiceName(serviceNumber); + this.log("Getting 'name' characteristic ..."); + if (this.isResponding() && serviceName) { + this.log("Current 'name' characteristic is %s", serviceName); + callback(null, serviceName); + } else { + this.log("Error getting 'name' characteristic. Name: %s", serviceName); + callback(new Error("Error getting 'name'.")); + } + }; -AbstractAccessoryWrapper.prototype.isResponding = function () { - return this.badRequestsCounter <= this.maxBadRequestsCount; -}; + parseDeviceInfo(deviceInfo) { + if (deviceInfo) { + const device = deviceInfo.device || deviceInfo; + if (!device.ip && deviceInfo.network) { + device.ip = deviceInfo.network.ip; + } + device.deviceName = device.deviceName ? device.deviceName.replace(NON_ALPHANUMERIC_REGEX, ' ') : ""; + return device; + } + }; + + updateDeviceInfo(deviceInfo) { + const newDevice = this.parseDeviceInfo(deviceInfo); + if (newDevice) { + const device = this.getDevice(); + this.accessory.context.blebox.device = newDevice; + if (device && device.deviceName !== newDevice.deviceName) { + this.updateDeviceInfoCharacteristics(); + } + } + }; -AbstractAccessoryWrapper.prototype.onDeviceNameChange = function () { - this.log("Abstract: Device name change: %s", this.deviceName); -}; + updateStateInfo(stateInfo) { + this.log("Not supported method: updateStateInfo") + } -AbstractAccessoryWrapper.prototype.checkDeviceState = function () { - var self = this; - communication.send(bleboxCommands.getDeviceState, self.deviceIp, { - onSuccess: function (deviceInfo) { - if (deviceInfo) { - deviceInfo = deviceInfo.device || deviceInfo; - if (self.deviceId == deviceInfo.id) { - self.badRequestsCounter = 0; - if (self.deviceName != deviceInfo.deviceName) { - self.deviceName = deviceInfo.deviceName ? deviceInfo.deviceName.replace(NON_ALPHANUMERIC_REGEX, ' ') : ""; - self.onDeviceNameChange(); + getDevice() { + return this.accessory.context.blebox.device; + }; + + isResponding() { + return this.badRequestsCounter <= maxBadRequestsCount; + }; + + checkDeviceState() { + const device = this.getDevice(); + const self = this; + communication.send(bleboxCommands.getDeviceState, device.ip, { + onSuccess: function (deviceInfo) { + const newDevice = self.parseDeviceInfo(deviceInfo) + if (newDevice) { + if (device.id === newDevice.id) { + self.badRequestsCounter = 0; + self.updateDeviceInfo(deviceInfo); } - } else { - self.badRequestsCounter = self.maxBadRequestsCount + 1; - self.stopListening(); + // else { + // self.badRequestsCounter = maxBadRequestsCount + 1; + // self.stopListening(); + // } } + }, onError: function () { + self.badRequestsCounter++; } - }, onError: function () { - self.badRequestsCounter++; - } - }); -}; - -AbstractAccessoryWrapper.prototype.checkSpecificState = function () { - throw new Error("Method 'checkSpecificState' not implemented!"); -}; - -AbstractAccessoryWrapper.prototype.startListening = function () { - this.stopListening(); - var randomDelay = Math.floor(Math.random() * 5000); - this.checkDeviceState(); - this.deviceStateInterval = setInterval(this.checkDeviceState.bind(this), this.deviceStateIntervalInMs + randomDelay); - this.checkSpecificState(); - this.specificStateInterval = setInterval(this.checkSpecificState.bind(this), this.specificStateIntervalInMs + randomDelay); -}; - -AbstractAccessoryWrapper.prototype.stopListening = function () { - clearInterval(this.deviceStateInterval); - clearInterval(this.specificStateInterval); -}; - - -AbstractAccessoryWrapper.prototype.getName = function (callback) { - this.log("( %s ): Getting 'name' characteristic ...", this.deviceName); - if (this.isResponding() && this.deviceName) { - this.log("( %s ): Current 'name' characteristic is %s", this.deviceName, this.deviceName); - callback(null, this.deviceName); - } else { - this.log("( %s ): Error getting 'name' characteristic. Name: %s", this.deviceName, this.deviceName); - callback(new Error("Error getting 'name'.")); - } -}; \ No newline at end of file + }); + }; + + checkSpecificState() { + const device = this.getDevice(); + const self = this; + communication.send(this.checkStateCommand, device.ip, { + onSuccess: function (stateInfo) { + if (stateInfo) { + self.badRequestsCounter = 0; + self.updateStateInfo(stateInfo); + self.updateStateInfoCharacteristics(); + } + }, onError: function () { + self.badRequestsCounter++; + } + }); + }; + + startListening() { + this.stopListening(); + const randomDelay = Math.floor(Math.random() * 5000); + this.checkDeviceState(); + this.deviceInfoInterval = setInterval(this.checkDeviceState.bind(this), deviceInfoIntervalInMs + randomDelay); + this.checkSpecificState(); + this.specificStateInterval = setInterval(this.checkSpecificState.bind(this), specificStateIntervalInMs + randomDelay); + }; + + stopListening() { + clearInterval(this.deviceInfoInterval); + clearInterval(this.specificStateInterval); + }; +} + +module.exports = AbstractAccessoryWrapper; \ No newline at end of file diff --git a/blebox/dimmerBox.js b/blebox/dimmerBox.js index cf4d571..54244c4 100644 --- a/blebox/dimmerBox.js +++ b/blebox/dimmerBox.js @@ -1,151 +1,141 @@ -var communication = require("../common/communication"); -var bleboxCommands = require("../common/bleboxCommands"); -var DIMMERBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.DIMMERBOX; -var AbstractBoxWrapper = require("./abstractBox"); +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); +const DIMMERBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.DIMMERBOX; +const AbstractBoxWrapper = require("./abstractBox"); -module.exports = { - create: function (homebridge, log, api, deviceInfo, dimmerInfo) { - return new DimmerBoxAccessoryWrapper(homebridge, null, log, api, deviceInfo, dimmerInfo); - }, restore: function (accessory, log, api, deviceInfo) { - return new DimmerBoxAccessoryWrapper(null, accessory, log, api, deviceInfo.device, deviceInfo.dimmer); +class DimmerBoxAccessoryWrapper extends AbstractBoxWrapper { + constructor(accessory, log, api, deviceInfo, stateInfo) { + super(accessory, log, api); + + this.type = DIMMERBOX_TYPE; + this.checkStateCommand = bleboxCommands.getDimmerState; + + this.servicesDefList = [api.hap.Service.Lightbulb]; + + this.onCharacteristic = api.hap.Characteristic.On; + this.brightnessCharacteristic = api.hap.Characteristic.Brightness; + + this.init(deviceInfo, stateInfo); + + this.assignCharacteristics(); + + this.startListening(); } -}; -function DimmerBoxAccessoryWrapper(homebridge, accessory, log, api, deviceInfo, dimmerInfo) { - AbstractBoxWrapper.call(this, accessory, log, deviceInfo); - this.dimmer = dimmerInfo ? (dimmerInfo.dimmer || dimmerInfo) : null; + assignCharacteristics() { + super.assignCharacteristics(); + const serviceNumber = 0; + const service = this.getService(serviceNumber); - this.nameCharacteristic = api.hap.Characteristic.Name; - this.onCharacteristic = api.hap.Characteristic.On; - this.brightnessCharacteristic = api.hap.Characteristic.Brightness; - this.lightBulbService = api.hap.Service.Lightbulb; + service.getCharacteristic(this.onCharacteristic) + .on('get', this.onGetOnState.bind(this)) + .on('set', this.onSetOnState.bind(this)); - if (!this.accessory) { - var uuid = homebridge.hap.uuid.generate(this.deviceName + DIMMERBOX_TYPE + this.deviceIp); - this.accessory = new homebridge.platformAccessory(this.deviceName, uuid); - this.accessory.addService(this.lightBulbService, this.deviceName); + service.getCharacteristic(this.brightnessCharacteristic) + .on('get', this.onGetBrightness.bind(this)) + .on('set', this.onSetBrightness.bind(this)); } - this.accessory.getService(this.lightBulbService) - .getCharacteristic(this.onCharacteristic) - .on('get', this.getOnState.bind(this)) - .on('set', this.setOnState.bind(this)); - - this.accessory.getService(this.lightBulbService) - .getCharacteristic(this.brightnessCharacteristic) - .on('get', this.getBrightness.bind(this)) - .on('set', this.setBrightness.bind(this)); - - this.accessory.getService(this.lightBulbService) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this)); - - //for restore purpose - this.accessory.context.blebox = { - "type": DIMMERBOX_TYPE, - "device": { - "id": this.deviceId, - "ip": this.deviceIp, - "deviceName": this.deviceName - }, "dimmer": this.dimmer + updateStateInfoCharacteristics() { + const dimmer = this.getDimmer(); + if (dimmer) { + //update characteristics + const serviceNumber = 0; + const service = this.getService(serviceNumber); + service.updateCharacteristic(this.onCharacteristic, this.getOnState()); + service.updateCharacteristic(this.brightnessCharacteristic, this.getBrightness()); + } }; - this.updateCharacteristics(); - this.startListening(); -} - -DimmerBoxAccessoryWrapper.prototype = Object.create(AbstractBoxWrapper.prototype); - -DimmerBoxAccessoryWrapper.prototype.checkSpecificState = function () { - var self = this; - communication.send(bleboxCommands.getDimmerState, self.deviceIp, { - onSuccess: function (dimmerState) { - if (dimmerState) { - self.badRequestsCounter = 0; - dimmerState = dimmerState.dimmer || dimmerState; - self.dimmer = dimmerState; - self.updateCharacteristics(); - } - }, onError: function () { - self.badRequestsCounter++; + updateStateInfo(stateInfo) { + if (stateInfo) { + this.accessory.context.blebox.dimmer = stateInfo.dimmer || stateInfo; + this.updateStateInfoCharacteristics(); } - }); -}; + } + + getDimmer() { + return this.accessory.context.blebox.dimmer; + } -DimmerBoxAccessoryWrapper.prototype.updateCharacteristics = function () { - if (this.dimmer && this.dimmer.desiredBrightness) { - var currentBrightness = Number((this.dimmer.desiredBrightness / 255 * 100).toFixed(0)) || 0; - var currentOnValue = currentBrightness !== 0; + getOnState() { + const {desiredBrightness = 0} = this.getDimmer() || {}; + return desiredBrightness !== 0; + }; - this.accessory.getService(this.lightBulbService) - .updateCharacteristic(this.onCharacteristic, currentOnValue); + getBrightness() { + const {desiredBrightness = 0} = this.getDimmer() || {}; + return Number((desiredBrightness / 255 * 100).toFixed(0)) || 0; - this.accessory.getService(this.lightBulbService) - .updateCharacteristic(this.brightnessCharacteristic, currentBrightness); - } -}; + }; -DimmerBoxAccessoryWrapper.prototype.sendSetSimpleDimmerStateCommand = function (value, callback, errorMsg) { - var self = this; - communication.send(bleboxCommands.setSimpleDimmerState, this.deviceIp, { - params: [value.toString(16)], - onSuccess: function (dimmerState) { - if (dimmerState) { - self.dimmer = dimmerState.dimmer || dimmerState; - self.updateCharacteristics(); - callback(null); // success - } else { - callback(new Error(errorMsg)); - } - }, - onError: function () { - callback(new Error(errorMsg)); + onGetOnState(callback) { + this.log("Getting 'On' characteristic ..."); + const dimmer = this.getDimmer(); + if (this.isResponding() && dimmer) { + const currentOnValue = this.getOnState(); + this.log("Current 'On' characteristic is %s", currentOnValue); + callback(null, currentOnValue); + } else { + this.log("Error getting 'On' characteristic. Dimmer: %s", dimmer); + callback(new Error("Error getting 'On'.")); } - }); -}; + }; -DimmerBoxAccessoryWrapper.prototype.onDeviceNameChange = function () { - this.accessory.getService(this.lightBulbService) - .updateCharacteristic(this.nameCharacteristic, this.deviceName); -}; + onSetOnState(turnOn, callback) { + // We should handle only turn OFF + if (!turnOn) { + this.log("Setting 'On' characteristic to %s ...", turnOn); + const brightness = 0; + this.sendSetSimpleDimmerStateCommand(brightness, callback); + } else { + callback(null); + } + }; -DimmerBoxAccessoryWrapper.prototype.getOnState = function (callback) { - this.log("DIMMERBOX ( %s ): Getting 'On' characteristic ...", this.deviceName); - if (this.isResponding() && this.dimmer) { - var currentOnValue = this.dimmer.currentBrightness !== 0; - this.log("DIMMERBOX ( %s ): Current 'On' characteristic is %s", this.deviceName, currentOnValue); - callback(null, currentOnValue); - } else { - this.log("DIMMERBOX ( %s ): Error getting 'On' characteristic. Dimmer: %s", this.deviceName, this.dimmer); - callback(new Error("Error getting 'On'.")); - } -}; + onGetBrightness(callback) { + this.log("Getting 'Brightness' characteristic ..."); + const dimmer = this.getDimmer(); + if (this.isResponding() && dimmer) { + const currentBrightness = this.getBrightness(); + this.log("Current 'Brightness' characteristic is %s", currentBrightness); + callback(null, currentBrightness); + } else { + this.log("Error getting 'Brightness' characteristic. Dimmer: %s", dimmer); + callback(new Error("Error getting 'On'.")); + } + }; -DimmerBoxAccessoryWrapper.prototype.setOnState = function (turnOn, callback) { - var currentOnValue = this.dimmer.currentBrightness !== 0; - if (!turnOn || !currentOnValue) { - this.log("DIMMERBOX ( %s ): Setting 'On' characteristic to %s ...", this.deviceName, turnOn); - var brightness = turnOn ? 255 : 0; - this.sendSetSimpleDimmerStateCommand(brightness, callback, "Error setting 'On'."); - } else { - callback(null); - } -}; + onSetBrightness(brightness, callback) { + this.log("Setting 'Brightness' characteristic to %s ...", brightness); + const parsedBrightness = Number((brightness / 100 * 255).toFixed(0)); + this.sendSetSimpleDimmerStateCommand(parsedBrightness, callback) + }; + + sendSetSimpleDimmerStateCommand(brightness, callback) { + const device = this.getDevice(); + const self = this; + communication.send(bleboxCommands.setSimpleDimmerState, device.ip, { + params: [brightness.toString(16)], + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); + callback(null); + }, + onError: function () { + callback(new Error("Error setting 'Target brightness'.")); + } + }); + }; -DimmerBoxAccessoryWrapper.prototype.getBrightness = function (callback) { - this.log("DIMMERBOX ( %s ): Getting 'setBrightness' characteristic ...", this.deviceName); - if (this.isResponding() && this.dimmer) { - var currentBrightness = Number((this.dimmer.currentBrightness / 255 * 100).toFixed(0)); - this.log("DIMMERBOX ( %s ): Current 'setBrightness' characteristic is %s", this.deviceName, currentBrightness); - callback(null, currentBrightness); - } else { - this.log("DIMMERBOX ( %s ): Error getting 'setBrightness' characteristic. Dimmer: %s", this.deviceName, this.dimmer); - callback(new Error("Error getting 'On'.")); +} + + +module.exports = { + type: DIMMERBOX_TYPE, + checkStateCommand: bleboxCommands.getDimmerState, + create: function (accessory, log, api, deviceInfo, stateInfo) { + return new DimmerBoxAccessoryWrapper(accessory, log, api, deviceInfo, stateInfo); + }, restore: function (accessory, log, api) { + return new DimmerBoxAccessoryWrapper(accessory, log, api); } }; - -DimmerBoxAccessoryWrapper.prototype.setBrightness = function (brightness, callback) { - this.log("DIMMERBOX ( %s ): Setting 'Brightness' characteristic to %s ...", this.deviceName, brightness); - brightness = Number((brightness / 100 * 255).toFixed(0)); - this.sendSetSimpleDimmerStateCommand(brightness, callback, "Error setting 'setBrightness'.") -}; \ No newline at end of file diff --git a/blebox/gateBox.js b/blebox/gateBox.js index 3ade956..24106d7 100644 --- a/blebox/gateBox.js +++ b/blebox/gateBox.js @@ -1,155 +1,152 @@ -var communication = require("../common/communication"); -var bleboxCommands = require("../common/bleboxCommands"); -var GATEBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.GATEBOX; -var AbstractBoxWrapper = require("./abstractBox"); +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); +const GATEBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.GATEBOX; +const AbstractBoxWrapper = require("./abstractBox"); -module.exports = { - create: function (homebridge, log, api, deviceInfo, gateInfo) { - return new GateBoxAccessoryWrapper(homebridge, null, log, api, deviceInfo, gateInfo); - }, restore: function (accessory, log, api, deviceInfo) { - return new GateBoxAccessoryWrapper(null, accessory, log, api, deviceInfo.device, deviceInfo.gate); - } -}; +class GateBoxAccessoryWrapper extends AbstractBoxWrapper { + constructor(accessory, log, api, deviceInfo, stateInfo) { + super(accessory, log, api); + + this.type = GATEBOX_TYPE; + this.checkStateCommand = bleboxCommands.getGateState; + + this.servicesDefList = [api.hap.Service.GarageDoorOpener]; -function GateBoxAccessoryWrapper(homebridge, accessory, log, api, deviceInfo, gateInfo) { - AbstractBoxWrapper.call(this, accessory, log, deviceInfo); - this.gateInfo = gateInfo ? (gateInfo.gate || gateInfo) : null; + this.currentDoorStateCharacteristic = api.hap.Characteristic.CurrentDoorState; + this.targetDoorStateCharacteristic = api.hap.Characteristic.TargetDoorState; + this.obstructionDetectedCharacteristic = api.hap.Characteristic.ObstructionDetected; - this.nameCharacteristic = api.hap.Characteristic.Name; - this.currentDoorStateCharacteristic = api.hap.Characteristic.CurrentDoorState; - this.targetDoorStateCharacteristic = api.hap.Characteristic.TargetDoorState; - this.obstructionDetectedCharacteristic = api.hap.Characteristic.ObstructionDetected; - this.garageDoorOpenerService = api.hap.Service.GarageDoorOpener; + this.init(deviceInfo, stateInfo); - if (!this.accessory) { - var uuid = homebridge.hap.uuid.generate(this.deviceName + GATEBOX_TYPE + this.deviceIp); - this.accessory = new homebridge.platformAccessory(this.deviceName, uuid); - this.accessory.addService(this.garageDoorOpenerService, this.deviceName); + this.assignCharacteristics(); + + this.startListening(); } - this.accessory.getService(this.garageDoorOpenerService) - .getCharacteristic(this.currentDoorStateCharacteristic) - .on('get', this.getCurrentDoorState.bind(this)); - - this.accessory.getService(this.garageDoorOpenerService) - .getCharacteristic(this.targetDoorStateCharacteristic) - .on('get', this.getTargetDoorState.bind(this)) - .on('set', this.setTargetDoorState.bind(this)); - - this.accessory.getService(this.garageDoorOpenerService) - .getCharacteristic(this.obstructionDetectedCharacteristic) - .on('get', this.getObstructionDetected.bind(this)); - - this.accessory.getService(this.garageDoorOpenerService) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this)); - - //for restore purpose - this.accessory.context.blebox = { - "type": GATEBOX_TYPE, - "device": { - "id": this.deviceId, - "ip": this.deviceIp, - "deviceName": this.deviceName - }, "gate": this.gateInfo - }; + assignCharacteristics() { + super.assignCharacteristics(); + const serviceNumber = 0; + const service = this.getService(serviceNumber); - this.updateCharacteristics(); - this.startListening(); -} + service.getCharacteristic(this.currentDoorStateCharacteristic) + .on('get', this.onGetCurrentDoorState.bind(this)); -GateBoxAccessoryWrapper.prototype = Object.create(AbstractBoxWrapper.prototype); - -GateBoxAccessoryWrapper.prototype.checkSpecificState = function () { - var self = this; - communication.send(bleboxCommands.getGateState, self.deviceIp, { - onSuccess: function (gateInfo) { - if (gateInfo) { - self.badRequestsCounter = 0; - gateInfo = gateInfo.gate || gateInfo; - self.gateInfo = gateInfo; - self.updateCharacteristics(); - } - }, onError: function () { - self.badRequestsCounter++; - } - }); -}; + service.getCharacteristic(this.targetDoorStateCharacteristic) + .on('get', this.onGetTargetDoorState.bind(this)) + .on('set', this.onSetTargetDoorState.bind(this)); + + service.getCharacteristic(this.obstructionDetectedCharacteristic) + .on('get', this.onGetObstructionDetected.bind(this)); -GateBoxAccessoryWrapper.prototype.updateCharacteristics = function () { - if (this.gateInfo) { - //update characteristics - this.accessory.getService(this.garageDoorOpenerService) - .updateCharacteristic(this.currentDoorStateCharacteristic, this.getCurrentDoorStateValue()); } -}; -GateBoxAccessoryWrapper.prototype.onDeviceNameChange = function () { - this.accessory.getService(this.garageDoorOpenerService) - .updateCharacteristic(this.nameCharacteristic, this.deviceName); -}; + updateStateInfoCharacteristics() { + const gate = this.getGate(); + if (gate) { + //update characteristics + const serviceNumber = 0; + const service = this.getService(serviceNumber); + service.updateCharacteristic(this.currentDoorStateCharacteristic, this.getCurrentDoorStateValue()); + + service.updateCharacteristic(this.targetDoorStateCharacteristic, this.getTargetDoorStateValue()); + } + }; -GateBoxAccessoryWrapper.prototype.getCurrentDoorStateValue = function () { - var state = this.currentDoorStateCharacteristic.OPEN; //default value - if (this.gateInfo) { - var currentPosition = Number(this.gateInfo.currentPos) || 0; - if (currentPosition === 0) { - state = this.currentDoorStateCharacteristic.CLOSED; + updateStateInfo(stateInfo) { + if (stateInfo) { + this.accessory.context.blebox.gate = stateInfo.gate || stateInfo; + this.updateStateInfoCharacteristics(); } } - return state; -}; -GateBoxAccessoryWrapper.prototype.getCurrentDoorState = function (callback) { - this.log("GATEBOX ( %s ): Getting 'current door' characteristic ...", this.deviceName); - if (this.isResponding() && this.gateInfo) { - var currentState = this.getCurrentDoorStateValue(); - this.log("GATEBOX ( %s ): Current 'current door' characteristic is %s", this.deviceName, currentState); - callback(null, currentState); - } else { - this.log("GATEBOX ( %s ): Error getting 'current door' characteristic. GateInfo: %s", this.deviceName, this.gateInfo); - callback(new Error("Error getting 'current door'.")); + getGate() { + return this.accessory.context.blebox.gate; } -}; + getCurrentDoorStateValue() { + let state = this.currentDoorStateCharacteristic.OPEN; + const gate = this.getGate(); + if (gate) { + const currentPosition = Number(gate.currentPos) || 0; + if (currentPosition === 0) { + state = this.currentDoorStateCharacteristic.CLOSED; + } + } + return state; + }; + + getTargetDoorStateValue() { + let state = this.targetDoorStateCharacteristic.OPEN; + const gate = this.getGate(); + if (gate) { + const currentPosition = Number(gate.desiredPos) || 0; + if (currentPosition === 0) { + state = this.targetDoorStateCharacteristic.CLOSED; + } + } + return state; + }; -GateBoxAccessoryWrapper.prototype.getTargetDoorState = function (callback) { - this.log("GATEBOX ( %s ): Getting 'target door' characteristic ...", this.deviceName); - if (this.isResponding() && this.gateInfo) { - var currentState = this.getCurrentDoorStateValue(); - this.log("GATEBOX ( %s ): Current 'target door' characteristic is %s", this.deviceName, currentState); - callback(null, currentState); - } else { - this.log("GATEBOX ( %s ): Error getting 'target door' characteristic. GateInfo: %s", this.deviceName, this.gateInfo); - callback(new Error("Error getting 'target door'.")); - } -}; + onGetCurrentDoorState(callback) { + this.log("Getting 'current door' characteristic ..."); + const gate = this.getGate(); + if (this.isResponding() && gate) { + const currentState = this.getCurrentDoorStateValue(); + this.log("Current 'current door' characteristic is %s", currentState); + callback(null, currentState); + } else { + this.log("Error getting 'current door' characteristic. GateInfo: %s", gate); + callback(new Error("Error getting 'current door'.")); + } + }; -GateBoxAccessoryWrapper.prototype.setTargetDoorState = function (state, callback) { - this.log("GATEBOX ( %s ): Setting 'target door' characteristic to %s", this.deviceName, state); - var self = this; - communication.send(bleboxCommands.setSimpleGateState, this.deviceIp, { - onSuccess: function (gateInfo) { - if (gateInfo) { - self.gateInfo = gateInfo.gate || gateInfo; - self.updateCharacteristics(); - callback(null); // success - } else { + onGetTargetDoorState(callback) { + this.log("Getting 'target door' characteristic ..."); + const gate = this.getGate(); + if (this.isResponding() && gate) { + const targetState = this.getTargetDoorStateValue(); + this.log("Current 'target door' characteristic is %s", targetState); + callback(null, targetState); + } else { + this.log("Error getting 'target door' characteristic. GateInfo: %s", gate); + callback(new Error("Error getting 'current door'.")); + } + }; + + onSetTargetDoorState(state, callback) { + this.log("Setting 'target door' characteristic to %s", state); + const device = this.getDevice(); + const self = this; + communication.send(bleboxCommands.setSimpleGateState, device.ip, { + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); + callback(null); + }, onError: function () { callback(new Error("Error setting 'target door'.")); } - }, onError: function () { - callback(new Error("Error setting 'target door'.")); + }); + }; + + onGetObstructionDetected(callback) { + this.log("Getting 'obstruction detected' characteristic ..."); + if (this.isResponding()) { + const isObstructionDetected = false; + this.log("Current 'obstruction detected' characteristic is %s", isObstructionDetected); + callback(null, isObstructionDetected); + } else { + this.log("Error getting 'obstruction detected' characteristic."); + callback(new Error("Error getting 'obstruction detected'.")); } - }); -}; + }; +} -GateBoxAccessoryWrapper.prototype.getObstructionDetected = function (callback) { - this.log("GATEBOX ( %s ): Getting 'obstruction detected' characteristic ...", this.deviceName); - if (this.isResponding()) { - this.log("GATEBOX ( %s ): Current 'obstruction detected' characteristic is %s", this.deviceName, false); - callback(null, false); - } else { - this.log("GATEBOX ( %s ): Error getting 'obstruction detected' characteristic.", this.deviceName); - callback(new Error("Error getting 'obstruction detected'.")); + +module.exports = { + type: GATEBOX_TYPE, + checkStateCommand: bleboxCommands.getGateState, + create: function (accessory, log, api, deviceInfo, stateInfo) { + return new GateBoxAccessoryWrapper(accessory, log, api, deviceInfo, stateInfo); + }, restore: function (accessory, log, api) { + return new GateBoxAccessoryWrapper(accessory, log, api); } }; diff --git a/blebox/index.js b/blebox/index.js new file mode 100644 index 0000000..e3d4678 --- /dev/null +++ b/blebox/index.js @@ -0,0 +1,22 @@ +const dimmerBox = require("./dimmerBox"); +const gateBox = require("./gateBox"); +const saunaBox = require("./saunaBox"); +const shutterBox = require("./shutterBox"); +const smartWindowBox = require("./smartWindowBox"); +const switchBox = require("./switchBox"); +const switchBoxD = require("./switchBoxD"); +const wLightBox = require("./wLightBox"); +const wLightBoxS = require("./wLightBoxS"); + +const wrappers = {}; +wrappers[dimmerBox.type] = dimmerBox; +wrappers[gateBox.type] = gateBox; +wrappers[saunaBox.type] = saunaBox; +wrappers[shutterBox.type] = shutterBox; +wrappers[smartWindowBox.type] = smartWindowBox; +wrappers[switchBox.type] = switchBox; +wrappers[switchBoxD.type] = switchBoxD; +wrappers[wLightBox.type] = wLightBox; +wrappers[wLightBoxS.type] = wLightBoxS; + +module.exports = wrappers; \ No newline at end of file diff --git a/blebox/saunaBox.js b/blebox/saunaBox.js new file mode 100644 index 0000000..f8d9fa1 --- /dev/null +++ b/blebox/saunaBox.js @@ -0,0 +1,230 @@ +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); +const SAUNABOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.SAUNABOX; +const AbstractBoxWrapper = require("./abstractBox"); + +class SaunaBoxAccessoryWrapper extends AbstractBoxWrapper { + constructor(accessory, log, api, deviceInfo, stateInfo) { + super(accessory, log, api); + + this.type = SAUNABOX_TYPE; + this.maxTemperature = 125; + this.checkStateCommand = bleboxCommands.getHeatState; + + this.servicesDefList = [api.hap.Service.Thermostat]; + + this.currentHeatingCoolingStateCharacteristic = api.hap.Characteristic.CurrentHeatingCoolingState; + this.targetHeatingCoolingStateCharacteristic = api.hap.Characteristic.TargetHeatingCoolingState; + this.currentTemperatureCharacteristic = api.hap.Characteristic.CurrentTemperature; + this.targetTemperatureCharacteristic = api.hap.Characteristic.TargetTemperature; + this.temperatureDisplayUnitsCharacteristic = api.hap.Characteristic.TemperatureDisplayUnits; + + this.init(deviceInfo, stateInfo); + + this.assignCharacteristics(); + + this.startListening(); + } + + assignCharacteristics() { + super.assignCharacteristics(); + const serviceNumber = 0; + const service = this.getService(serviceNumber); + service.getCharacteristic(this.currentHeatingCoolingStateCharacteristic) + .on('get', this.onGetCurrentHeatingCoolingState.bind(this)); + + service.getCharacteristic(this.targetHeatingCoolingStateCharacteristic) + .on('get', this.onGetTargetHeatingCoolingState.bind(this)) + .on('set', this.onSetTargetHeatingCoolingState.bind(this)); + + service.getCharacteristic(this.currentTemperatureCharacteristic) + .setProps({ + maxValue: this.maxTemperature, + minValue: -30, + }) + .on('get', this.onGetCurrentTemperature.bind(this)); + + service.getCharacteristic(this.targetTemperatureCharacteristic) + .setProps({ + maxValue: this.maxTemperature, + minValue: 0 + }) + .on('get', this.onGetTargetTemperature.bind(this)) + .on('set', this.onSetTargetTemperature.bind(this)); + + service.getCharacteristic(this.temperatureDisplayUnitsCharacteristic) + .on('get', this.onGetTemperatureDisplayUnits.bind(this)) + .on('set', this.onSetTemperatureDisplayUnits.bind(this)); + } + + updateStateInfoCharacteristics() { + const heat = this.getHeat(); + if (heat) { + //update characteristics + const serviceNumber = 0; + const service = this.getService(serviceNumber); + service.updateCharacteristic(this.currentHeatingCoolingStateCharacteristic, this.getCurrentHeatingCoolingStateValue()); + + service.updateCharacteristic(this.targetHeatingCoolingStateCharacteristic, this.getTargetHeatingCoolingStateValue()); + + service.updateCharacteristic(this.currentTemperatureCharacteristic, this.getCurrentTemperatureValue()); + + service.updateCharacteristic(this.targetTemperatureCharacteristic, this.getTargetTemperatureValue()); + + service.updateCharacteristic(this.temperatureDisplayUnitsCharacteristic, this.getTemperatureDisplayUnitsValue()); + } + }; + + updateStateInfo(stateInfo) { + if (stateInfo) { + this.accessory.context.blebox.heat = stateInfo.heat || stateInfo; + this.updateStateInfoCharacteristics(); + } + } + + getHeat() { + return this.accessory.context.blebox.heat; + } + + getCurrentHeatingCoolingStateValue() { + const heat = this.getHeat(); + return heat && heat.state ? this.currentHeatingCoolingStateCharacteristic.HEAT : this.currentHeatingCoolingStateCharacteristic.OFF; + }; + + getTargetHeatingCoolingStateValue() { + const heat = this.getHeat(); + return heat && heat.state ? this.targetHeatingCoolingStateCharacteristic.HEAT : this.targetHeatingCoolingStateCharacteristic.OFF; + }; + + getCurrentTemperatureValue() { + const {sensors: [{value: currentTemperature = 0} = {}] = []} = this.getHeat() || {}; + return Number((currentTemperature / 100).toFixed(1)) || 0; + }; + + getTargetTemperatureValue() { + const {desiredTemp = 0} = this.getHeat() || {}; + return Number((desiredTemp / 100).toFixed(1)) || 0; + }; + + getTemperatureDisplayUnitsValue() { + return this.accessory.context.blebox.temperatureDisplayUnits || this.temperatureDisplayUnitsCharacteristic.CELSIUS; + }; + + onGetCurrentHeatingCoolingState(callback) { + this.log("Getting 'Current heating cooling state' characteristic ..."); + const heat = this.getHeat(); + if (this.isResponding() && heat) { + const currentHeatingCoolingStateValue = this.getCurrentHeatingCoolingStateValue(); + this.log("Current 'Current heating cooling state' characteristic is %s", currentHeatingCoolingStateValue); + + callback(null, currentHeatingCoolingStateValue); + } else { + this.log("Error getting 'Current heating cooling state' characteristic. Heat: %s", heat); + callback(new Error("Error getting 'Current heating cooling state'.")); + } + }; + + onGetTargetHeatingCoolingState(callback) { + this.log("Getting 'Target heating cooling state' characteristic ..."); + const heat = this.getHeat(); + if (this.isResponding() && heat) { + const targetHeatingCoolingStateValue = this.getTargetHeatingCoolingStateValue(); + this.log("Current 'Target heating cooling state' characteristic is %s", targetHeatingCoolingStateValue); + + callback(null, targetHeatingCoolingStateValue); + } else { + this.log("Error getting 'Target heating cooling state' characteristic. Heat: %s", heat); + callback(new Error("Error getting 'Target heating cooling state'.")); + } + }; + + onSetTargetHeatingCoolingState(targetHeatingCoolingState, callback) { + this.log("SAUNABOX: Setting 'Target heating cooling' characteristic to %s ...", targetHeatingCoolingState); + //if not OFF always send HEAT (saunaBox support only 0/1 values) + if (targetHeatingCoolingState !== this.targetHeatingCoolingStateCharacteristic.OFF) { + targetHeatingCoolingState = this.targetHeatingCoolingStateCharacteristic.HEAT; + } + const device = this.getDevice(); + const self = this; + communication.send(bleboxCommands.setSimpleHeatState, device.ip, { + params: [targetHeatingCoolingState], + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); + callback(null); + }, + onError: function () { + callback(new Error("Error setting 'Target heating cooling'.")); + } + }); + }; + + onGetCurrentTemperature(callback) { + this.log("Getting 'Current temperature' characteristic ..."); + const heat = this.getHeat(); + if (this.isResponding() && heat) { + const currentTemperatureValue = this.getCurrentTemperatureValue(); + this.log("Current 'Current temperature' characteristic is %s", currentTemperatureValue); + callback(null, currentTemperatureValue); + } else { + this.log("Error getting 'Current temperature' characteristic. Heat: %s", heat); + callback(new Error("Error getting 'Current temperature'.")); + } + }; + + onGetTargetTemperature(callback) { + this.log("Getting 'Target temperature' characteristic ..."); + const heat = this.getHeat(); + if (this.isResponding() && heat) { + const targetTemperatureValue = this.getTargetTemperatureValue(); + this.log("Current 'Target temperature' characteristic is %s", targetTemperatureValue); + callback(null, targetTemperatureValue); + } else { + this.log("Error getting 'Target temperature' characteristic. Heat: %s", heat); + callback(new Error("Error getting 'Target temperature'.")); + } + }; + + onSetTargetTemperature(targetTemperature, callback) { + this.log("Setting 'Target temperature' characteristic to %s ...", targetTemperature); + const device = this.getDevice(); + const self = this; + communication.send(bleboxCommands.setSimpleHeatDesiredTemperature, device.ip, { + params: [targetTemperature * 100], + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); + callback(null); + }, + onError: function () { + callback(new Error("Error setting 'Target temperature'.")); + } + }); + }; + + onGetTemperatureDisplayUnits(callback) { + this.log("Getting 'Temperature display units' characteristic ..."); + if (this.isResponding()) { + const temperatureDisplayUnitsValue = this.getTemperatureDisplayUnitsValue(); + this.log("Current 'Temperature display units' characteristic is %s", temperatureDisplayUnitsValue); + callback(null, temperatureDisplayUnitsValue); + } else { + const heat = this.getHeat(); + this.log("Error getting 'Temperature display units' characteristic. Heat: %s", heat); + callback(new Error("Error getting 'Temperature display units'.")); + } + }; + + onSetTemperatureDisplayUnits(temperatureDisplayUnits, callback) { + this.accessory.context.blebox.temperatureDisplayUnits = temperatureDisplayUnits; + callback(null); + }; +} + +module.exports = { + type: SAUNABOX_TYPE, + checkStateCommand: bleboxCommands.getHeatState, + create: function (accessory, log, api, deviceInfo, stateInfo) { + return new SaunaBoxAccessoryWrapper(accessory, log, api, deviceInfo, stateInfo); + }, restore: function (accessory, log, api) { + return new SaunaBoxAccessoryWrapper(accessory, log, api); + } +}; \ No newline at end of file diff --git a/blebox/shutterBox.js b/blebox/shutterBox.js index c1373e2..636e0f3 100644 --- a/blebox/shutterBox.js +++ b/blebox/shutterBox.js @@ -1,192 +1,174 @@ -var communication = require("../common/communication"); -var bleboxCommands = require("../common/bleboxCommands"); -var SHUTTERBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.SHUTTERBOX; -var AbstractBoxWrapper = require("./abstractBox"); +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); +const SHUTTERBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.SHUTTERBOX; +const AbstractBoxWrapper = require("./abstractBox"); -module.exports = { - create: function (homebridge, log, api, deviceInfo, shutterInfo) { - return new ShutterBoxAccessoryWrapper(homebridge, null, log, api, deviceInfo, shutterInfo); - }, restore: function (accessory, log, api, deviceInfo) { - return new ShutterBoxAccessoryWrapper(null, accessory, log, api, deviceInfo.device, deviceInfo.shutter); - } -}; -function ShutterBoxAccessoryWrapper(homebridge, accessory, log, api, deviceInfo, shutterInfo) { - AbstractBoxWrapper.call(this, accessory, log, deviceInfo); - this.shutter = shutterInfo ? (shutterInfo.shutter || shutterInfo) : null; +class ShutterBoxAccessoryWrapper extends AbstractBoxWrapper { + constructor(accessory, log, api, deviceInfo, stateInfo) { + super(accessory, log, api); - this.nameCharacteristic = api.hap.Characteristic.Name; - this.currentPositionCharacteristic = api.hap.Characteristic.CurrentPosition; - this.targetPositionCharacteristic = api.hap.Characteristic.TargetPosition; - this.postitionStateCharacteristic = api.hap.Characteristic.PositionState; - this.windowCoveringService = api.hap.Service.WindowCovering; + this.type = SHUTTERBOX_TYPE; + this.checkStateCommand = bleboxCommands.getShutterState; - if (!this.accessory) { - var uuid = homebridge.hap.uuid.generate(this.deviceName + SHUTTERBOX_TYPE + this.deviceIp); - this.accessory = new homebridge.platformAccessory(this.deviceName, uuid); - this.accessory.addService(this.windowCoveringService, this.deviceName); - } + this.servicesDefList = [api.hap.Service.WindowCovering]; - this.accessory.getService(this.windowCoveringService) - .getCharacteristic(this.postitionStateCharacteristic) - .on('get', this.getPositionState.bind(this)); - - this.accessory.getService(this.windowCoveringService) - .getCharacteristic(this.currentPositionCharacteristic) - .on('get', this.getCurrentPosition.bind(this)); - - this.accessory.getService(this.windowCoveringService) - .getCharacteristic(this.targetPositionCharacteristic) - .on('get', this.getTargetPosition.bind(this)) - .on('set', this.setTargetPosition.bind(this)); - - this.accessory.getService(this.windowCoveringService) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this)); - - //for restore purpose - this.accessory.context.blebox = { - "type": SHUTTERBOX_TYPE, - "device": { - "id": this.deviceId, - "ip": this.deviceIp, - "deviceName": this.deviceName - }, "shutter": this.shutter - }; + this.currentPositionCharacteristic = api.hap.Characteristic.CurrentPosition; + this.targetPositionCharacteristic = api.hap.Characteristic.TargetPosition; + this.postitionStateCharacteristic = api.hap.Characteristic.PositionState; - this.updateCharacteristics(); - this.startListening(); -} + this.init(deviceInfo, stateInfo); -ShutterBoxAccessoryWrapper.prototype = Object.create(AbstractBoxWrapper.prototype); - -ShutterBoxAccessoryWrapper.prototype.checkSpecificState = function () { - var self = this; - communication.send(bleboxCommands.getShutterState, this.deviceIp, { - onSuccess: function (shutterState) { - if (shutterState) { - self.badRequestsCounter = 0; - shutterState = shutterState.shutter || shutterState; - self.shutter = shutterState; - self.updateCharacteristics(); - } - }, onError: function () { - self.badRequestsCounter++; - } - }); -}; + this.assignCharacteristics(); + + this.startListening(); + } -ShutterBoxAccessoryWrapper.prototype.updateCharacteristics = function () { - if (this.shutter) { - //update characteristics - this.accessory.getService(this.windowCoveringService) - .updateCharacteristic(this.currentPositionCharacteristic, this.getCurrentPositionValue()); + assignCharacteristics() { + this.log("Services: "+this.getAccessory().services.length); + super.assignCharacteristics(); + const serviceNumber = 0; + const service = this.getService(serviceNumber); + service.getCharacteristic(this.postitionStateCharacteristic) + .on('get', this.onGetPositionState.bind(this)); - this.accessory.getService(this.windowCoveringService) - .updateCharacteristic(this.targetPositionCharacteristic, this.getTargetPositionValue()); + service.getCharacteristic(this.targetPositionCharacteristic) + .on('get', this.onGetTargetPosition.bind(this)) + .on('set', this.onSetTargetPosition.bind(this)); - this.accessory.getService(this.windowCoveringService) - .updateCharacteristic(this.postitionStateCharacteristic, this.getPositionStateValue()); + service.getCharacteristic(this.currentPositionCharacteristic) + .on('get', this.onGetCurrentPosition.bind(this)); } -}; -ShutterBoxAccessoryWrapper.prototype.getCurrentPositionValue = function () { - var currentPosition = 0; - if (this.shutter && this.shutter.currentPos) { - currentPosition = (Number(this.shutter.currentPos.position) || ( Number(this.shutter.currentPos) || 0)); - currentPosition = 100 - Math.min(Math.max(currentPosition, 0), 100); + updateStateInfoCharacteristics() { + const shutter = this.getShutter(); + if (shutter) { + //update characteristics + const serviceNumber = 0; + const service = this.getService(serviceNumber); + service.updateCharacteristic(this.currentPositionCharacteristic, this.getCurrentPositionValue()); + + service.updateCharacteristic(this.targetPositionCharacteristic, this.getTargetPositionValue()); + + service.updateCharacteristic(this.postitionStateCharacteristic, this.getPositionStateValue()); + + } + }; + + updateStateInfo(stateInfo) { + if (stateInfo) { + this.accessory.context.blebox.shutter = stateInfo.shutter || stateInfo; + this.updateStateInfoCharacteristics(); + } } - return currentPosition; -}; -ShutterBoxAccessoryWrapper.prototype.getTargetPositionValue = function () { - var currentPosition = 0; - if (this.shutter && this.shutter.desiredPos) { - currentPosition = (Number(this.shutter.desiredPos.position) || ( Number(this.shutter.desiredPos) || 0)); - currentPosition = 100 - Math.min(Math.max(currentPosition, 0), 100); + getShutter() { + return this.accessory.context.blebox.shutter; } - return currentPosition; -}; -ShutterBoxAccessoryWrapper.prototype.getPositionStateValue = function () { - var positionState = this.postitionStateCharacteristic.STOPPED; - if (this.shutter) { - switch (this.shutter.state) { - case 0: //down - positionState = this.postitionStateCharacteristic.DECREASING; - break; - case 1: // up - positionState = this.postitionStateCharacteristic.INCREASING; - break; - default: - positionState = this.postitionStateCharacteristic.STOPPED; - break; + _getPositionValue(positionType) { + let position = 0; + const shutter = this.getShutter(); + if (shutter) { + const shutterPosition = shutter[positionType]; + position = !isNaN(Number(shutterPosition.position)) ? shutterPosition.position : shutterPosition; + position = 100 - (Math.min(Math.max(position, 0), 100) || 0); } + return position; } - return positionState; -}; -ShutterBoxAccessoryWrapper.prototype.onDeviceNameChange = function () { - this.accessory.getService(this.windowCoveringService) - .updateCharacteristic(this.nameCharacteristic, this.deviceName); -}; + getCurrentPositionValue() { + return this._getPositionValue('currentPos'); + }; -ShutterBoxAccessoryWrapper.prototype.getCurrentPosition = function (callback) { - this.log("SHUTTERBOX ( %s ): Getting 'Current position' characteristic ...", this.deviceName); - if (this.isResponding() && this.shutter) { - var currentPosition = this.getCurrentPositionValue(); - this.log("SHUTTERBOX ( %s ): Current 'Current position' characteristic is %s", this.deviceName, currentPosition); + getTargetPositionValue() { + return this._getPositionValue('desiredPos'); + }; - callback(null, currentPosition); - } else { - this.log("SHUTTERBOX ( %s ): Error getting 'Current position' characteristic. Shutter: %s", this.deviceName, this.shutter); - callback(new Error("Error getting 'Current position'.")); - } -}; + getPositionStateValue() { + let positionState = this.postitionStateCharacteristic.STOPPED; + const shutter = this.getShutter(); + if (shutter) { + switch (shutter.state) { + case 0: //down + positionState = this.postitionStateCharacteristic.DECREASING; + break; + case 1: // up + positionState = this.postitionStateCharacteristic.INCREASING; + break; + default: + positionState = this.postitionStateCharacteristic.STOPPED; + break; + } + } + return positionState; + }; -ShutterBoxAccessoryWrapper.prototype.getTargetPosition = function (callback) { - this.log("SHUTTERBOX ( %s ): Getting 'Target position' characteristic ...", this.deviceName); - if (this.isResponding() && this.shutter) { - var currentPosition = this.getTargetPositionValue(); - this.log("SHUTTERBOX ( %s ): Current 'Target position' characteristic is %s", this.deviceName, currentPosition); + onGetCurrentPosition(callback) { + this.log("Getting 'Current position' characteristic ..."); + const shutter = this.getShutter(); + if (this.isResponding() && shutter) { + const currentPosition = this.getCurrentPositionValue(); + this.log("Current 'Current position' characteristic is %s", currentPosition); + callback(null, currentPosition); + } else { + this.log("Error getting 'Current position' characteristic. Shutter: %s", shutter); + callback(new Error("Error getting 'Current position'.")); + } + }; - callback(null, currentPosition); - } else { - this.log("SHUTTERBOX ( %s ): Error getting 'Target position' characteristic. Shutter: %s", this.deviceName, this.shutter); - callback(new Error("Error getting 'Target position'.")); - } -}; + onGetTargetPosition(callback) { + this.log("Getting 'Target position' characteristic ..."); + const shutter = this.getShutter(); + if (this.isResponding() && shutter) { + const currentPosition = this.getTargetPositionValue(); + this.log("Current 'Target position' characteristic is %s", currentPosition); + callback(null, currentPosition); + } else { + this.log("Error getting 'Target position' characteristic. Shutter: %s", shutter); + callback(new Error("Error getting 'Target position'.")); + } + }; -ShutterBoxAccessoryWrapper.prototype.setTargetPosition = function (position, callback) { - this.log("SHUTTERBOX: Setting 'target position' characteristic to %s ...", position); - position = 100 - position; - var self = this; - var command = this.shutter.desiredPos.position != undefined ? bleboxCommands.setSimplePositionShutterState : bleboxCommands.setSimpleShutterState; - communication.send(command, this.deviceIp, { - params: [position], - onSuccess: function (shutterState) { - if (shutterState) { - self.shutter = shutterState.shutter || shutterState; - self.updateCharacteristics(); - callback(null); // success - } else { + onSetTargetPosition(position, callback) { + this.log("Setting 'target position' characteristic to %s ...", position); + position = 100 - position; + const self = this; + const shutter = this.getShutter(); + const command = shutter && shutter.desiredPos.position !== undefined ? bleboxCommands.setSimplePositionShutterState : bleboxCommands.setSimpleShutterState; + const device = this.getDevice(); + communication.send(command, device.ip, { + params: [position], + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); + callback(null); + }, + onError: function () { callback(new Error("Error setting 'target position'.")); } - }, - onError: function () { - callback(new Error("Error setting 'target position'.")); + }); + }; + + onGetPositionState(callback) { + this.log("Getting 'Position state' characteristic ..."); + const shutter = this.getShutter(); + if (this.isResponding() && shutter) { + const positionState = this.getPositionStateValue(); + this.log("Current 'Position state' characteristic is %s", positionState); + callback(null, positionState); // success + } else { + this.log("Error getting 'Position state' characteristic. Shutter: %s", shutter); + callback(new Error("Error getting 'Position state'.")); } - }); -}; + }; +} -ShutterBoxAccessoryWrapper.prototype.getPositionState = function (callback) { - this.log("SHUTTERBOX ( %s ): Getting 'Position state' characteristic ...", this.deviceName); - if (this.isResponding() && this.shutter) { - var positionState = this.getPositionStateValue(); - this.log("SHUTTERBOX ( %s ): Current 'Position state' characteristic is %s", this.deviceName, positionState); - callback(null, positionState); // success - } else { - this.log("SHUTTERBOX ( %s ): Error getting 'Position state' characteristic. Shutter: %s", this.deviceName, this.shutter); - callback(new Error("Error getting 'Position state'.")); +module.exports = { + type: SHUTTERBOX_TYPE, + checkStateCommand: bleboxCommands.getShutterState, + create: function (accessory, log, api, deviceInfo, stateInfo) { + return new ShutterBoxAccessoryWrapper(accessory, log, api, deviceInfo, stateInfo); + }, restore: function (accessory, log, api) { + return new ShutterBoxAccessoryWrapper(accessory, log, api); } -}; \ No newline at end of file +}; diff --git a/blebox/smartWindowBox.js b/blebox/smartWindowBox.js index 4815563..d967f9a 100644 --- a/blebox/smartWindowBox.js +++ b/blebox/smartWindowBox.js @@ -1,303 +1,181 @@ -var communication = require("../common/communication"); -var bleboxCommands = require("../common/bleboxCommands"); -var SMARTWINDOWBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.SMARTWINDOWBOX; -var AbstractBoxWrapper = require("./abstractBox"); - -module.exports = { - create: function (homebridge, log, api, deviceInfo, windowInfo) { - return new SmartWindowBoxAccessoryWrapper(homebridge, null, log, api, deviceInfo, windowInfo); - }, restore: function (accessory, log, api, deviceInfo) { - return new SmartWindowBoxAccessoryWrapper(null, accessory, log, api, deviceInfo.device, deviceInfo.window); +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); +const SMARTWINDOWBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.SMARTWINDOWBOX; +const AbstractBoxWrapper = require("./abstractBox"); + +class SmartWindowBoxAccessoryWrapper extends AbstractBoxWrapper { + constructor(accessory, log, api, deviceInfo, stateInfo) { + super(accessory, log, api); + + this.type = SMARTWINDOWBOX_TYPE; + this.checkStateCommand = bleboxCommands.getRelayState; + + this.servicesDefList = [ + api.hap.Service.WindowCovering, + api.hap.Service.WindowCovering, + api.hap.Service.WindowCovering + ]; + this.servicesSubTypes = [ + 'Motor 1', + 'Motor 2', + 'Motor 3' + ]; + + this.currentPositionCharacteristic = api.hap.Characteristic.CurrentPosition; + this.targetPositionCharacteristic = api.hap.Characteristic.TargetPosition; + this.postitionStateCharacteristic = api.hap.Characteristic.PositionState; + + this.init(deviceInfo, stateInfo); + + this.assignCharacteristics(); + + this.startListening(); } -}; - -function SmartWindowBoxAccessoryWrapper(homebridge, accessory, log, api, deviceInfo, windowInfo) { - AbstractBoxWrapper.call(this, accessory, log, deviceInfo); - this.window = windowInfo ? (windowInfo.window || windowInfo) : null; - - this.firstServiceSubtype = "Motor 1"; - this.secondServiceSubtype = "Motor 2"; - this.thirdServiceSubtype = "Motor 3"; - this.nameCharacteristic = api.hap.Characteristic.Name; - this.currentPositionCharacteristic = api.hap.Characteristic.CurrentPosition; - this.targetPositionCharacteristic = api.hap.Characteristic.TargetPosition; - this.postitionStateCharacteristic = api.hap.Characteristic.PositionState; - this.windowCoveringService = api.hap.Service.WindowCovering; + assignCharacteristics() { + super.assignCharacteristics(); + for (let serviceNumber = 0; serviceNumber < this.servicesDefList.length; serviceNumber++) { + const service = this.getService(serviceNumber); + service.getCharacteristic(this.postitionStateCharacteristic) + .on('get', this.onGetPositionState.bind(this, serviceNumber)); - if (!this.accessory) { - var uuid = homebridge.hap.uuid.generate(this.deviceName + SMARTWINDOWBOX_TYPE + this.deviceIp); - this.accessory = new homebridge.platformAccessory(this.deviceName, uuid); + service.getCharacteristic(this.currentPositionCharacteristic) + .on('get', this.onGetCurrentPosition.bind(this, serviceNumber)); - this.firstServiceName = this.getServiceName(0); - this.accessory.addService(this.windowCoveringService, this.firstServiceName, this.firstServiceSubtype); - - this.secondServiceName = this.getServiceName(1); - this.accessory.addService(this.windowCoveringService, this.secondServiceName, this.secondServiceSubtype); - - this.thirdServiceName = this.getServiceName(2); - this.accessory.addService(this.windowCoveringService, this.thirdServiceName, this.thirdServiceSubtype); + service.getCharacteristic(this.targetPositionCharacteristic) + .on('get', this.onGetTargetPosition.bind(this, serviceNumber)) + .on('set', this.onSetTargetPosition.bind(this, serviceNumber)); + } } - // first service - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.firstServiceSubtype) - .getCharacteristic(this.postitionStateCharacteristic) - .on('get', this.getPositionState.bind(this, 0)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.firstServiceSubtype) - .getCharacteristic(this.currentPositionCharacteristic) - .on('get', this.getCurrentPosition.bind(this, 0)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.firstServiceSubtype) - .getCharacteristic(this.targetPositionCharacteristic) - .on('get', this.getTargetPosition.bind(this, 0)) - .on('set', this.setTargetPosition.bind(this, 0)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.firstServiceSubtype) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this, 0)); - - // second service - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.secondServiceSubtype) - .getCharacteristic(this.postitionStateCharacteristic) - .on('get', this.getPositionState.bind(this, 1)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.secondServiceSubtype) - .getCharacteristic(this.currentPositionCharacteristic) - .on('get', this.getCurrentPosition.bind(this, 1)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.secondServiceSubtype) - .getCharacteristic(this.targetPositionCharacteristic) - .on('get', this.getTargetPosition.bind(this, 1)) - .on('set', this.setTargetPosition.bind(this, 1)); + updateStateInfoCharacteristics() { + const window = this.getWindow(); + if (window) { + for (let serviceNumber = 0; serviceNumber < this.servicesDefList.length; serviceNumber++) { + const service = this.getService(serviceNumber); + service.updateCharacteristic(this.currentPositionCharacteristic, this.getCurrentPositionValue(serviceNumber)); - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.secondServiceSubtype) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this, 1)); + service.updateCharacteristic(this.targetPositionCharacteristic, this.getTargetPositionValue(serviceNumber)); - // third service - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.thirdServiceSubtype) - .getCharacteristic(this.postitionStateCharacteristic) - .on('get', this.getPositionState.bind(this, 2)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.thirdServiceSubtype) - .getCharacteristic(this.currentPositionCharacteristic) - .on('get', this.getCurrentPosition.bind(this, 2)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.thirdServiceSubtype) - .getCharacteristic(this.targetPositionCharacteristic) - .on('get', this.getTargetPosition.bind(this, 2)) - .on('set', this.setTargetPosition.bind(this, 2)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.thirdServiceSubtype) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this, 2)); - - //for restore purpose - this.accessory.context.blebox = { - "type": SMARTWINDOWBOX_TYPE, - "device": { - "id": this.deviceId, - "ip": this.deviceIp, - "deviceName": this.deviceName - }, "window": this.window - }; - - this.updateCharacteristics(); - this.startListening(); -} - -SmartWindowBoxAccessoryWrapper.prototype = Object.create(AbstractBoxWrapper.prototype); - -SmartWindowBoxAccessoryWrapper.prototype.checkSpecificState = function () { - var self = this; - communication.send(bleboxCommands.getWindowState, this.deviceIp, { - onSuccess: function (windowState) { - if (windowState) { - self.badRequestsCounter = 0; - windowState = windowState.window || windowState; - self.window = windowState; - self.updateCharacteristics(); + service.updateCharacteristic(this.postitionStateCharacteristic, this.getPositionStateValue(serviceNumber)); } - }, onError: function () { - self.badRequestsCounter++; } - }); -}; - -SmartWindowBoxAccessoryWrapper.prototype.updateCharacteristics = function () { - if (this.window) { - //update characteristics - - // first service - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.firstServiceSubtype) - .updateCharacteristic(this.currentPositionCharacteristic, this.getCurrentPositionValue(0)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.firstServiceSubtype) - .updateCharacteristic(this.targetPositionCharacteristic, this.getTargetPositionValue(0)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.firstServiceSubtype) - .updateCharacteristic(this.postitionStateCharacteristic, this.getPositionStateValue(0)); - - // second service - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.secondServiceSubtype) - .updateCharacteristic(this.currentPositionCharacteristic, this.getCurrentPositionValue(1)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.secondServiceSubtype) - .updateCharacteristic(this.targetPositionCharacteristic, this.getTargetPositionValue(1)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.secondServiceSubtype) - .updateCharacteristic(this.postitionStateCharacteristic, this.getPositionStateValue(1)); - - // third service - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.thirdServiceSubtype) - .updateCharacteristic(this.currentPositionCharacteristic, this.getCurrentPositionValue(2)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.thirdServiceSubtype) - .updateCharacteristic(this.targetPositionCharacteristic, this.getTargetPositionValue(2)); - - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.thirdServiceSubtype) - .updateCharacteristic(this.postitionStateCharacteristic, this.getPositionStateValue(2)); } -}; -SmartWindowBoxAccessoryWrapper.prototype.getServiceName = function (motorNumber) { - var name_postfix = ""; - if (this.window && this.window.motors && this.window.motors.length > motorNumber && this.window.motors[motorNumber].name) { - name_postfix = this.window.motors[motorNumber].name; - } else { - switch (motorNumber) { - case 0: - name_postfix = this.firstServiceSubtype; - break; - case 1: - name_postfix = this.secondServiceSubtype; - break; - case 2: - default: - name_postfix = this.thirdServiceSubtype; - break; + updateStateInfo(stateInfo) { + if (stateInfo) { + this.accessory.context.blebox.window = stateInfo.window || stateInfo; + this.updateStateInfoCharacteristics(); } } - return this.deviceName + " " + name_postfix; -}; - -SmartWindowBoxAccessoryWrapper.prototype.getCurrentPositionValue = function (motorNumber) { - var currentPosition = 0; - if (this.window && this.window.motors && this.window.motors[motorNumber] && this.window.motors[motorNumber].currentPos) { - currentPosition = Number(this.window.motors[motorNumber].currentPos.position) || 0; - currentPosition = 100 - Math.min(Math.max(currentPosition, 0), 100); - } - return currentPosition; -}; - -SmartWindowBoxAccessoryWrapper.prototype.getTargetPositionValue = function (motorNumber) { - var targetPosition = 0; - if (this.window && this.window.motors && this.window.motors[motorNumber] && this.window.motors[motorNumber].desiredPos) { - targetPosition = Number(this.window.motors[motorNumber].desiredPos.position) || 0; - targetPosition = 100 - Math.min(Math.max(targetPosition, 0), 100); + getWindow() { + return this.accessory.context.blebox.window; } - return targetPosition; -}; -SmartWindowBoxAccessoryWrapper.prototype.getPositionStateValue = function (motorNumber) { - var positionState = this.postitionStateCharacteristic.STOPPED; - if (this.window && this.window.motors && this.window.motors[motorNumber]) { - switch (this.window.motors[motorNumber].state) { - case 0: //down - positionState = this.postitionStateCharacteristic.DECREASING; - break; - case 1: // up - positionState = this.postitionStateCharacteristic.INCREASING; - break; - default: - positionState = this.postitionStateCharacteristic.STOPPED; - break; - } + getServiceName(serviceNumber) { + const serviceName = super.getServiceName(serviceNumber); + const suffix = this.getServiceNameSuffix(serviceNumber); + return `${serviceName} ${suffix}`; } - return positionState; -}; -SmartWindowBoxAccessoryWrapper.prototype.onDeviceNameChange = function () { - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.firstServiceSubtype) - .updateCharacteristic(this.nameCharacteristic, this.getServiceName(0)); + getServiceNameSuffix(serviceNumber) { + const {motors = []} = this.getWindow() || {}; + const {name = this.servicesSubTypes[serviceNumber]} = motors[serviceNumber] || {}; + return name; + }; - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.secondServiceSubtype) - .updateCharacteristic(this.nameCharacteristic, this.getServiceName(1)); + _getPositionValue(serviceNumber, positionType) { + const {motors = []} = this.getWindow() || {}; + const {[positionType]: {position = 0} = {}} = motors[serviceNumber] || {}; + return 100 - Math.min(Math.max(position, 0), 100); + } - this.accessory.getServiceByUUIDAndSubType(this.windowCoveringService, this.thirdServiceSubtype) - .updateCharacteristic(this.nameCharacteristic, this.getServiceName(2)); -}; + getCurrentPositionValue(serviceNumber) { + return this._getPositionValue(serviceNumber, 'currentPos'); + } -SmartWindowBoxAccessoryWrapper.prototype.getCurrentPosition = function (motorNumber, callback) { - this.log("SMARTWINDOWBOX ( %s ): Getting 'Current position' characteristic for motor %s ...", this.deviceName, motorNumber); - if (this.isResponding() && this.window) { - var currentPosition = this.getCurrentPositionValue(motorNumber); - this.log("SMARTWINDOWBOX ( %s ): Current 'Current position' characteristic for motor %s is %s", this.deviceName, motorNumber, currentPosition); + getTargetPositionValue(serviceNumber) { + return this._getPositionValue(serviceNumber, 'desiredPos'); + } - callback(null, currentPosition); - } else { - this.log("SMARTWINDOWBOX ( %s ): Error getting 'Current position' characteristic for motor %s. Window: %s", this.deviceName, motorNumber, this.window); - callback(new Error("Error getting 'Current position'.")); + getPositionStateValue(serviceNumber) { + let positionState = this.postitionStateCharacteristic.STOPPED; + const {motors = []} = this.getWindow() || {}; + const {state} = motors[serviceNumber] || {}; + if (state === 0) { //down + positionState = this.postitionStateCharacteristic.DECREASING; + } else if (state === 1) { //up + positionState = this.postitionStateCharacteristic.INCREASING; + } + return positionState; } -}; -SmartWindowBoxAccessoryWrapper.prototype.getTargetPosition = function (motorNumber, callback) { - this.log("SMARTWINDOWBOX ( %s ): Getting 'Target position' characteristic for motor %s ...", this.deviceName, motorNumber); - if (this.isResponding() && this.window) { - var currentPosition = this.getTargetPositionValue(motorNumber); - this.log("SMARTWINDOWBOX ( %s ): Current 'Target position' characteristic for motor %s is %s", this.deviceName, motorNumber, currentPosition); + onGetCurrentPosition(serviceNumber, callback) { + this.log("Getting 'Current position' characteristic for motor %s ...", serviceNumber); + const window = this.getWindow(); + if (this.isResponding() && window) { + const currentPosition = this.getCurrentPositionValue(serviceNumber); + this.log("Current 'Current position' characteristic for motor %s is %s", serviceNumber, currentPosition); + + callback(null, currentPosition); + } else { + this.log("Error getting 'Current position' characteristic for motor %s. Window: %s", serviceNumber, window); + callback(new Error("Error getting 'Current position'.")); + } + } - callback(null, currentPosition); - } else { - this.log("SMARTWINDOWBOX ( %s ): Error getting 'Target position' characteristic for motor %s. Window: %s", this.deviceName, motorNumber, this.window); - callback(new Error("Error getting 'Target position'.")); + onGetTargetPosition(serviceNumber, callback) { + this.log("Getting 'Target position' characteristic for motor %s ...", serviceNumber); + const window = this.getWindow(); + if (this.isResponding() && window) { + const currentPosition = this.getTargetPositionValue(serviceNumber); + this.log("Current 'Target position' characteristic for motor %s is %s", serviceNumber, currentPosition); + + callback(null, currentPosition); + } else { + this.log("Error getting 'Target position' characteristic for motor %s. Window: %s", serviceNumber, window); + callback(new Error("Error getting 'Target position'.")); + } } -}; -SmartWindowBoxAccessoryWrapper.prototype.setTargetPosition = function (motorNumber, position, callback) { - this.log("SMARTWINDOWBOX ( %s ): Setting 'target position' characteristic for motor %s to %s ...", this.deviceName, motorNumber, position); - position = 100 - position; - var self = this; - communication.send(bleboxCommands.setWindowPositionPercentage, this.deviceIp, { - params: [motorNumber, position], - onSuccess: function (windowState) { - if (windowState) { - self.window = windowState.window || windowState; - self.updateCharacteristics(); + onSetTargetPosition(serviceNumber, position, callback) { + this.log("Setting 'target position' characteristic for motor %s to %s ...", serviceNumber, position); + position = 100 - position; + const self = this; + const device = this.getDevice(); + communication.send(bleboxCommands.setWindowPositionPercentage, device.ip, { + params: [serviceNumber, position], + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); callback(null); // success - } else { + }, + onError: function () { callback(new Error("Error setting 'target position'.")); } - }, - onError: function () { - callback(new Error("Error setting 'target position'.")); - } - }); -}; - -SmartWindowBoxAccessoryWrapper.prototype.getPositionState = function (motorNumber, callback) { - this.log("SMARTWINDOWBOX ( %s ): Getting 'Position state' characteristic for motor %s ...", this.deviceName, motorNumber); - if (this.isResponding() && this.window) { - var positionState = this.getPositionStateValue(); - this.log("SMARTWINDOWBOX ( %s ): Current 'Position state' characteristic for motor %s is %s", this.deviceName, motorNumber, positionState); - callback(null, positionState); // success - } else { - this.log("SMARTWINDOWBOX ( %s ): Error getting 'Position state' characteristic for motor. Window: %s", this.deviceName, motorNumber, this.window); - callback(new Error("Error getting 'Position state'.")); + }); } -}; + onGetPositionState(serviceNumber, callback) { + this.log("Getting 'Position state' characteristic for motor %s ...", serviceNumber); + const window = this.getWindow(); + if (this.isResponding() && window) { + const positionState = this.getPositionStateValue(); + this.log("Current 'Position state' characteristic for motor %s is %s", serviceNumber, positionState); + callback(null, positionState); // success + } else { + this.log("Error getting 'Position state' characteristic for motor. Window: %s", serviceNumber, window); + callback(new Error("Error getting 'Position state'.")); + } + } +} -SmartWindowBoxAccessoryWrapper.prototype.getName = function (motorNumber, callback) { - this.log("SMARTWINDOWBOX ( %s ): Getting 'name' characteristic ...", this.deviceName); - if (this.isResponding() && this.deviceName) { - var currentName = this.getServiceName(motorNumber); - this.log("SMARTWINDOWBOX ( %s ): Current 'name' characteristic is %s", this.deviceName, currentName); - callback(null, currentName); - } else { - this.log("SMARTWINDOWBOX ( %s ): Error getting 'name' characteristic. Name: %s", this.deviceName, this.deviceName); - callback(new Error("Error getting 'name'.")); +module.exports = { + type: SMARTWINDOWBOX_TYPE, + checkStateCommand: bleboxCommands.getWindowState, + create: function (accessory, log, api, deviceInfo, stateInfo) { + return new SmartWindowBoxAccessoryWrapper(accessory, log, api, deviceInfo, stateInfo); + }, restore: function (accessory, log, api) { + return new SmartWindowBoxAccessoryWrapper(accessory, log, api); } -}; +}; \ No newline at end of file diff --git a/blebox/switchBox.js b/blebox/switchBox.js index e85f46c..3a3d519 100644 --- a/blebox/switchBox.js +++ b/blebox/switchBox.js @@ -1,121 +1,101 @@ -var communication = require("../common/communication"); -var bleboxCommands = require("../common/bleboxCommands"); -var SWITCHBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.SWITCHBOX; -var AbstractBoxWrapper = require("./abstractBox"); +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); +const SWITCHBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.SWITCHBOX; +const AbstractBoxWrapper = require("./abstractBox"); -module.exports = { - create: function (homebridge, log, api, deviceInfo, relayInfo) { - return new SwitchBoxAccessoryWrapper(homebridge, null, log, api, deviceInfo, relayInfo); - }, restore: function (accessory, log, api, deviceInfo) { - return new SwitchBoxAccessoryWrapper(null, accessory, log, api, deviceInfo.device, deviceInfo.relay); - } -}; -function SwitchBoxAccessoryWrapper(homebridge, accessory, log, api, deviceInfo, relayInfo) { - AbstractBoxWrapper.call(this, accessory, log, deviceInfo); - this.relay = relayInfo ? (relayInfo.relays || relayInfo) : null; +class SwitchBoxAccessoryWrapper extends AbstractBoxWrapper { + constructor(accessory, log, api, deviceInfo, stateInfo) { + super(accessory, log, api); - this.nameCharacteristic = api.hap.Characteristic.Name; - this.onCharacteristic = api.hap.Characteristic.On; - this.switchService = api.hap.Service.Switch; + this.type = SWITCHBOX_TYPE; + this.checkStateCommand = bleboxCommands.getRelayState; - if (!this.accessory) { - var uuid = homebridge.hap.uuid.generate(this.deviceName + SWITCHBOX_TYPE + this.deviceIp); - this.accessory = new homebridge.platformAccessory(this.deviceName, uuid); - this.accessory.addService(this.switchService, this.deviceName); - } + this.servicesDefList = [api.hap.Service.Switch]; - this.accessory.getService(this.switchService) - .getCharacteristic(this.onCharacteristic) - .on('get', this.getOnState.bind(this)) - .on('set', this.setOnState.bind(this)); - - this.accessory.getService(this.switchService) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this)); - - //for restore purpose - this.accessory.context.blebox = { - "type" : SWITCHBOX_TYPE, - "device": { - "id": this.deviceId, - "ip": this.deviceIp, - "deviceName": this.deviceName - }, "relay": this.relay - }; + this.onCharacteristic = api.hap.Characteristic.On; - this.updateCharacteristics(); - this.startListening(); -} + this.init(deviceInfo, stateInfo); -SwitchBoxAccessoryWrapper.prototype = Object.create(AbstractBoxWrapper.prototype); - -SwitchBoxAccessoryWrapper.prototype.checkSpecificState = function () { - var self = this; - communication.send(bleboxCommands.getRelayState, self.deviceIp, { - onSuccess: function (relayState) { - if (relayState) { - self.badRequestsCounter = 0; - relayState = relayState.relays || relayState; - self.relay = relayState; - self.updateCharacteristics(); - } - }, onError: function () { - self.badRequestsCounter++; - } - }); -}; + this.assignCharacteristics(); -SwitchBoxAccessoryWrapper.prototype.updateCharacteristics = function () { - if (this.relay) { - //update characteristics - this.accessory.getService(this.switchService) - .updateCharacteristic(this.onCharacteristic, this.getCurrentRelayValue()); + this.startListening(); } -}; -SwitchBoxAccessoryWrapper.prototype.onDeviceNameChange = function () { - this.accessory.getService(this.switchService) - .updateCharacteristic(this.nameCharacteristic, this.deviceName); -}; + assignCharacteristics() { + super.assignCharacteristics(); + const serviceNumber = 0; + const service = this.getService(serviceNumber); + service.getCharacteristic(this.onCharacteristic) + .on('get', this.onGetOnState.bind(this)) + .on('set', this.onSetOnState.bind(this)); + } + + updateStateInfoCharacteristics() { + const relays = this.getRelays(); + if (relays) { + //update characteristics + const serviceNumber = 0; + const service = this.getService(serviceNumber); + service.updateCharacteristic(this.onCharacteristic, this.getCurrentRelayValue()); + } + }; -SwitchBoxAccessoryWrapper.prototype.getCurrentRelayValue = function () { - var result = false; - if (this.relay && this.relay.length > 0) { - result = !!this.relay[0].state + updateStateInfo(stateInfo) { + if (stateInfo) { + this.accessory.context.blebox.relays = stateInfo.relays || stateInfo; + this.updateStateInfoCharacteristics(); + } } - return result; -}; -SwitchBoxAccessoryWrapper.prototype.getOnState = function (callback) { - this.log("SWITCHBOX ( %s ): Getting 'On' characteristic ...", this.deviceName); - if (this.isResponding()) { - var value = this.getCurrentRelayValue(); - this.log("SWITCHBOX ( %s ): Current 'On' characteristic is %s", this.deviceName, value); - callback(null, value); - } else { - this.log("SWITCHBOX ( %s ): Error getting 'On' characteristic. Relay: %s", this.deviceName, this.relay); - callback(new Error("Error getting 'On'.")); + getRelays() { + return this.accessory.context.blebox.relays; } -}; -SwitchBoxAccessoryWrapper.prototype.setOnState = function (turnOn, callback) { - this.log("SWITCHBOX ( %s ): Setting 'On' characteristic to %s ...", this.deviceName, turnOn); - var onOffParam = (turnOn ? "1" : "0"); - var self = this; - communication.send(bleboxCommands.setSimpleRelayState, this.deviceIp, { - params: [onOffParam], - onSuccess: function (relayState) { - if (relayState) { - self.relay = relayState.relays || relayState; - self.updateCharacteristics(); - callback(null); // success - } else { + getCurrentRelayValue() { + const relays = this.getRelays() || []; + const {state = false} = relays[0] || {}; + return !!state; + }; + + onGetOnState(callback) { + this.log("Getting 'On' characteristic ..."); + const relays = this.getRelays(); + if (this.isResponding() && relays) { + const value = this.getCurrentRelayValue(); + this.log("Current 'On' characteristic is %s", value); + callback(null, value); + } else { + this.log("Error getting 'On' characteristic. Relay: %s", relays); + callback(new Error("Error getting 'On'.")); + } + }; + + onSetOnState(turnOn, callback) { + this.log("Setting 'On' characteristic to %s ...", turnOn); + const onOffParam = (turnOn ? "1" : "0"); + const self = this; + const device = this.getDevice(); + communication.send(bleboxCommands.setSimpleRelayState, device.ip, { + params: [onOffParam], + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); + callback(null); + }, + onError: function () { callback(new Error("Error setting 'On'.")); } - }, - onError: function () { - callback(new Error("Error setting 'On'.")); - } - }); + }); + }; +} + + +module.exports = { + type: SWITCHBOX_TYPE, + checkStateCommand: bleboxCommands.getRelayState, + create: function (accessory, log, api, deviceInfo, stateInfo) { + return new SwitchBoxAccessoryWrapper(accessory, log, api, deviceInfo, stateInfo); + }, restore: function (accessory, log, api) { + return new SwitchBoxAccessoryWrapper(accessory, log, api); + } }; diff --git a/blebox/switchBoxD.js b/blebox/switchBoxD.js index d3cba05..138d376 100644 --- a/blebox/switchBoxD.js +++ b/blebox/switchBoxD.js @@ -1,174 +1,115 @@ -var communication = require("../common/communication"); -var bleboxCommands = require("../common/bleboxCommands"); -var SWITCHBOXD_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.SWITCHBOXD; -var AbstractBoxWrapper = require("./abstractBox"); +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); +const SWITCHBOXD_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.SWITCHBOXD; +const AbstractBoxWrapper = require("./abstractBox"); -module.exports = { - create: function (homebridge, log, api, deviceInfo, relaysInfo) { - return new SwitchBoxDAccessoryWrapper(homebridge, null, log, api, deviceInfo, relaysInfo); - }, restore: function (accessory, log, api, deviceInfo) { - return new SwitchBoxDAccessoryWrapper(null, accessory, log, api, deviceInfo.device, deviceInfo.relays); - } -}; +class SwitchBoxDAccessoryWrapper extends AbstractBoxWrapper { + constructor(accessory, log, api, deviceInfo, stateInfo) { + super(accessory, log, api); -function SwitchBoxDAccessoryWrapper(homebridge, accessory, log, api, deviceInfo, relaysInfo) { - AbstractBoxWrapper.call(this, accessory, log, deviceInfo); - this.relays = relaysInfo ? (relaysInfo.relays || relaysInfo) : null; + this.type = SWITCHBOXD_TYPE; + this.checkStateCommand = bleboxCommands.getRelayState; - this.firstServiceSubtype = "Output 1"; - this.secondServiceSubtype = "Output 2"; + this.servicesDefList = [api.hap.Service.Switch, api.hap.Service.Switch]; + this.servicesSubTypes = ['Output 1', 'Output 2']; - this.nameCharacteristic = api.hap.Characteristic.Name; - this.onCharacteristic = api.hap.Characteristic.On; - this.switchService = api.hap.Service.Switch; + this.onCharacteristic = api.hap.Characteristic.On; - if (!this.accessory) { - var uuid = homebridge.hap.uuid.generate(this.deviceName + SWITCHBOXD_TYPE + this.deviceIp); - this.accessory = new homebridge.platformAccessory(this.deviceName, uuid); - this.firstServiceName = this.getServiceName(0); - this.accessory.addService(this.switchService, this.firstServiceName, this.firstServiceSubtype); + this.init(deviceInfo, stateInfo); - this.secondServiceName = this.getServiceName(1); - this.accessory.addService(this.switchService, this.secondServiceName, this.secondServiceSubtype); - } + this.assignCharacteristics(); - this.accessory.getServiceByUUIDAndSubType(this.switchService, this.firstServiceSubtype) - .getCharacteristic(this.onCharacteristic) - .on('get', this.getOnState.bind(this, 0)) - .on('set', this.setOnState.bind(this, 0)); - - this.accessory.getServiceByUUIDAndSubType(this.switchService, this.firstServiceSubtype) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this, 0)); - - this.accessory.getServiceByUUIDAndSubType(this.switchService, this.secondServiceSubtype) - .getCharacteristic(this.onCharacteristic) - .on('get', this.getOnState.bind(this, 1)) - .on('set', this.setOnState.bind(this, 1)); - - this.accessory.getServiceByUUIDAndSubType(this.switchService, this.secondServiceSubtype) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this, 1)); - - //for restore purpose - this.accessory.context.blebox = { - "type": SWITCHBOXD_TYPE, - "device": { - "id": this.deviceId, - "ip": this.deviceIp, - "deviceName": this.deviceName - }, "relays": this.relays - }; - - this.updateCharacteristics(); - this.startListening(); -} + this.startListening(); + } -SwitchBoxDAccessoryWrapper.prototype = Object.create(AbstractBoxWrapper.prototype); - -SwitchBoxDAccessoryWrapper.prototype.checkSpecificState = function () { - var self = this; - communication.send(bleboxCommands.getRelayState, self.deviceIp, { - onSuccess: function (relayState) { - if (relayState) { - self.badRequestsCounter = 0; - relayState = relayState.relays || relayState; - self.relays = relayState; - self.updateCharacteristics(); - } - }, onError: function () { - self.badRequestsCounter++; + assignCharacteristics() { + super.assignCharacteristics(); + for (let serviceNumber = 0; serviceNumber < this.servicesDefList.length; serviceNumber++) { + const service = this.getService(serviceNumber); + service.getCharacteristic(this.onCharacteristic) + .on('get', this.onGetOnState.bind(this, serviceNumber)) + .on('set', this.onSetOnState.bind(this, serviceNumber)); } - }); -}; - -SwitchBoxDAccessoryWrapper.prototype.updateCharacteristics = function () { - if (this.relays) { - //update characteristics - this.accessory.getServiceByUUIDAndSubType(this.switchService, this.firstServiceSubtype) - .updateCharacteristic(this.onCharacteristic, this.getCurrentRelayValue(0)); - - this.accessory.getServiceByUUIDAndSubType(this.switchService, this.secondServiceSubtype) - .updateCharacteristic(this.onCharacteristic, this.getCurrentRelayValue(1)); } -}; -SwitchBoxDAccessoryWrapper.prototype.onDeviceNameChange = function () { - this.accessory.getServiceByUUIDAndSubType(this.switchService, this.firstServiceSubtype) - .updateCharacteristic(this.nameCharacteristic, this.getServiceName(0)); - - this.accessory.getServiceByUUIDAndSubType(this.switchService, this.secondServiceSubtype) - .updateCharacteristic(this.nameCharacteristic, this.getServiceName(1)); -}; + updateStateInfoCharacteristics() { + const relays = this.getRelays(); + if (relays) { + for (let serviceNumber = 0; serviceNumber < this.servicesDefList.length; serviceNumber++) { + const service = this.getService(serviceNumber); + service.updateCharacteristic(this.onCharacteristic, this.getCurrentRelayValue(serviceNumber)); + } + } + }; -SwitchBoxDAccessoryWrapper.prototype.getServiceName = function (relayNumber) { - var name_postfix = ""; - if (this.relays && this.relays.length > relayNumber && this.relays[relayNumber].name) { - name_postfix = this.relays[relayNumber].name; - } else { - switch (relayNumber) { - case 0: - name_postfix = this.firstServiceSubtype; - break; - case 1: - default: - name_postfix = this.secondServiceSubtype; - break; + updateStateInfo(stateInfo) { + if (stateInfo) { + this.accessory.context.blebox.relays = stateInfo.relays || stateInfo; + this.updateStateInfoCharacteristics(); } } - return this.deviceName + " " + name_postfix; -}; + getRelays() { + return this.accessory.context.blebox.relays; + } -SwitchBoxDAccessoryWrapper.prototype.getCurrentRelayValue = function (relayNumber) { - var result = false; - if (this.relays && this.relays.length > relayNumber) { - result = !!this.relays[relayNumber].state + getServiceName(serviceNumber) { + const serviceName = super.getServiceName(serviceNumber); + const suffix = this.getServiceNameSuffix(serviceNumber); + return `${serviceName} ${suffix}`; } - return result; -}; -SwitchBoxDAccessoryWrapper.prototype.getOnState = function (relayNumber, callback) { - this.log("SWITCHBOXD ( %s ): Getting 'On' characteristic ...", this.deviceName); - if (this.isResponding()) { - var value = this.getCurrentRelayValue(relayNumber); - this.log("SWITCHBOXD ( %s ): Current 'On' characteristic is %s", this.deviceName, value); - callback(null, value); - } else { - this.log("SWITCHBOXD ( %s ): Error getting 'On' characteristic. Relays: %s", this.deviceName, this.relays); - callback(new Error("Error getting 'On'.")); + getServiceNameSuffix(serviceNumber) { + const relays = this.getRelays() || []; + const {name = this.servicesSubTypes[serviceNumber]} = relays[serviceNumber] || {}; + return name; } -}; -SwitchBoxDAccessoryWrapper.prototype.setOnState = function (relayNumber, turnOn, callback) { - this.log("SWITCHBOXD ( %s ): Setting 'On' characteristic to %s ...", this.deviceName, turnOn); - var onOffParam = (turnOn ? "1" : "0"); - var self = this; - communication.send(bleboxCommands.setSimpleRelaysState, this.deviceIp, { - params: [relayNumber, onOffParam], - onSuccess: function (relayState) { - if (relayState) { - self.relays = relayState.relays || relayState; - self.updateCharacteristics(); + getCurrentRelayValue(serviceNumber) { + const relays = this.getRelays() || []; + const {state = false} = relays[serviceNumber] || {}; + return !!state; + }; + + onGetOnState(serviceNumber, callback) { + this.log("Getting 'On' characteristic ..."); + const relays = this.getRelays(); + if (this.isResponding() && relays) { + const value = this.getCurrentRelayValue(serviceNumber); + this.log("Current 'On' characteristic is %s", value); + callback(null, value); + } else { + this.log("Error getting 'On' characteristic. Relays: %s", relays); + callback(new Error("Error getting 'On'.")); + } + }; + + onSetOnState(serviceNumber, turnOn, callback) { + this.log("Setting 'On' characteristic to %s ...", turnOn); + const onOffParam = (turnOn ? "1" : "0"); + const device = this.getDevice(); + const self = this; + communication.send(bleboxCommands.setSimpleRelaysState, device.ip, { + params: [serviceNumber, onOffParam], + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); callback(null); // success - } else { + }, + onError: function () { callback(new Error("Error setting 'On'.")); } - }, - onError: function () { - callback(new Error("Error setting 'On'.")); - } - }); -}; + }); + }; -SwitchBoxDAccessoryWrapper.prototype.getName = function (relayNumber, callback) { - this.log("SWITCHBOXD( %s ): Getting 'name' characteristic ...", this.deviceName); - if (this.isResponding() && this.deviceName) { - var currentName = this.getServiceName(relayNumber); - this.log("SWITCHBOXD( %s ): Current 'name' characteristic is %s", this.deviceName, currentName); - callback(null, currentName); - } else { - this.log("SWITCHBOXD( %s ): Error getting 'name' characteristic. Name: %s", this.deviceName, this.deviceName); - callback(new Error("Error getting 'name'.")); +} + + +module.exports = { + type: SWITCHBOXD_TYPE, + checkStateCommand: bleboxCommands.getRelayState, + create: function (accessory, log, api, deviceInfo, stateInfo) { + return new SwitchBoxDAccessoryWrapper(accessory, log, api, deviceInfo, stateInfo); + }, restore: function (accessory, log, api) { + return new SwitchBoxDAccessoryWrapper(accessory, log, api); } }; diff --git a/blebox/wLightBox.js b/blebox/wLightBox.js index 021c1a0..7d97bee 100644 --- a/blebox/wLightBox.js +++ b/blebox/wLightBox.js @@ -1,275 +1,303 @@ -var communication = require("../common/communication"); -var bleboxCommands = require("../common/bleboxCommands"); -var WLIGHTBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.WLIGHTBOX; -var AbstractBoxWrapper = require("./abstractBox"); -var colorHelper = require("../common/colorHelper"); +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); +const WLIGHTBOX_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.WLIGHTBOX; +const AbstractBoxWrapper = require("./abstractBox"); +const colorHelper = require("../common/colorHelper"); -module.exports = { - create: function (homebridge, log, api, deviceInfo, rgbwInfo) { - return new WLightBoxAccessoryWrapper(homebridge, null, log, api, deviceInfo, rgbwInfo); - }, restore: function (accessory, log, api, deviceInfo) { - return new WLightBoxAccessoryWrapper(null, accessory, log, api, deviceInfo.device, deviceInfo.rgbw); - } -}; -function WLightBoxAccessoryWrapper(homebridge, accessory, log, api, deviceInfo, rgbwInfo) { - AbstractBoxWrapper.call(this, accessory, log, deviceInfo); - this.rgbw = rgbwInfo ? (rgbwInfo.rgbw || rgbwInfo) : null; - this.desiredHSVColor = {h: 0, s: 0, v: 0}; - this.desiredWhite = 0; +class WLightBoxAccessoryWrapper extends AbstractBoxWrapper { + constructor(accessory, log, api, deviceInfo, stateInfo) { + super(accessory, log, api); + + this.type = WLIGHTBOX_TYPE; + this.checkStateCommand = bleboxCommands.getRgbwState; + + this.servicesDefList = [api.hap.Service.Lightbulb, api.hap.Service.Lightbulb]; + this.servicesSubTypes = ['RGB', 'WHITE']; - this.rgbServiceSubtype = "RGB"; - this.whiteServiceSubtype = "WHITE"; + this.onCharacteristic = api.hap.Characteristic.On; + this.brightnessCharacteristic = api.hap.Characteristic.Brightness; + this.hueCharacteristic = api.hap.Characteristic.Hue; + this.saturationCharacteristic = api.hap.Characteristic.Saturation; - this.nameCharacteristic = api.hap.Characteristic.Name; - this.onCharacteristic = api.hap.Characteristic.On; - this.brightnessCharacteristic = api.hap.Characteristic.Brightness; - this.hueCharacteristic = api.hap.Characteristic.Hue; - this.saturationCharacteristic = api.hap.Characteristic.Saturation; - this.lightBulbService = api.hap.Service.Lightbulb; + this.desiredHsv = {h: 0, s: 0, v: 0}; - if (!this.accessory) { - var uuid = homebridge.hap.uuid.generate(this.deviceName + WLIGHTBOX_TYPE + this.deviceIp); - this.accessory = new homebridge.platformAccessory(this.deviceName, uuid); + this.init(deviceInfo, stateInfo); - this.firstServiceName = this.rgbServiceSubtype + " " + this.deviceName; - this.accessory.addService(this.lightBulbService, this.firstServiceName, this.rgbServiceSubtype); + this.assignCharacteristics(); - this.secondServiceName = this.whiteServiceSubtype + " " + this.deviceName; - this.accessory.addService(this.lightBulbService, this.secondServiceName, this.whiteServiceSubtype); + this.startListening(); } - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.rgbServiceSubtype) - .getCharacteristic(this.onCharacteristic) - .on('get', this.getRgbOnState.bind(this)) - .on('set', this.setRgbOnState.bind(this)); - - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.rgbServiceSubtype) - .getCharacteristic(this.brightnessCharacteristic) - .on('get', this.getRgbBrightness.bind(this)) - .on('set', this.setRgbBrightness.bind(this)); - - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.rgbServiceSubtype) - .getCharacteristic(this.hueCharacteristic) - .on('get', this.getRgbHue.bind(this)) - .on('set', this.setRgbHue.bind(this)); - - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.rgbServiceSubtype) - .getCharacteristic(this.saturationCharacteristic) - .on('get', this.getRgbSaturation.bind(this)) - .on('set', this.setRgbSaturation.bind(this)); - - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.whiteServiceSubtype) - .getCharacteristic(this.onCharacteristic) - .on('get', this.getWhiteOnState.bind(this)) - .on('set', this.setWhiteOnState.bind(this)); - - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.whiteServiceSubtype) - .getCharacteristic(this.brightnessCharacteristic) - .on('get', this.getWhiteBrightness.bind(this)) - .on('set', this.setWhiteBrightness.bind(this)); - - //for restore purpose - this.accessory.context.blebox = { - "type": WLIGHTBOX_TYPE, - "device": { - "id": this.deviceId, - "ip": this.deviceIp, - "deviceName": this.deviceName - }, "rgbw": this.rgbw - }; + assignCharacteristics() { + super.assignCharacteristics(); + const rgbServiceNumber = 0; + const rgbService = this.getService(rgbServiceNumber); + rgbService.getCharacteristic(this.onCharacteristic) + .on('get', this.onGetRgbOnState.bind(this)) + .on('set', this.onSetRgbOnState.bind(this)); + + rgbService.getCharacteristic(this.brightnessCharacteristic) + .on('get', this.onGetRgbBrightness.bind(this)) + .on('set', this.onSetRgbBrightness.bind(this)); + + rgbService.getCharacteristic(this.hueCharacteristic) + .on('get', this.onGetRgbHue.bind(this)) + .on('set', this.onSetRgbHue.bind(this)); + + rgbService.getCharacteristic(this.saturationCharacteristic) + .on('get', this.onGetRgbSaturation.bind(this)) + .on('set', this.onSetRgbSaturation.bind(this)); + + const whiteServiceNumber = 1; + const whiteService = this.getService(whiteServiceNumber); + whiteService.getCharacteristic(this.onCharacteristic) + .on('get', this.onGetWhiteOnState.bind(this)) + .on('set', this.onSetWhiteOnState.bind(this)); + + whiteService.getCharacteristic(this.brightnessCharacteristic) + .on('get', this.onGetWhiteBrightness.bind(this)) + .on('set', this.onSetWhiteBrightness.bind(this)); + } - this.setDesiredColorsAndUpdateCharacteristics(); - this.startListening(); -} + updateStateInfoCharacteristics() { + const rgbw = this.getRgbw(); + if (rgbw) { + //update characteristics + const rgbServiceNumber = 0; + const rgbService = this.getService(rgbServiceNumber); + rgbService.updateCharacteristic(this.onCharacteristic, this.getRgbOnValue()); -WLightBoxAccessoryWrapper.prototype = Object.create(AbstractBoxWrapper.prototype); - -WLightBoxAccessoryWrapper.prototype.checkSpecificState = function () { - var self = this; - communication.send(bleboxCommands.getRgbwState, self.deviceIp, { - onSuccess: function (rgbwState) { - if (rgbwState) { - self.badRequestsCounter = 0; - rgbwState = rgbwState.rgbw || rgbwState; - self.rgbw = rgbwState; - self.setDesiredColorsAndUpdateCharacteristics(); - } - }, onError: function () { - self.badRequestsCounter++; - } - }); -}; + rgbService.updateCharacteristic(this.hueCharacteristic, this.getRgbHueValue()); -WLightBoxAccessoryWrapper.prototype.onDeviceNameChange = function () { - this.accessory.getService(this.lightBulbService) - .updateCharacteristic(this.nameCharacteristic, this.deviceName); -}; + rgbService.updateCharacteristic(this.saturationCharacteristic, this.getRgbSaturationValue()); + rgbService.updateCharacteristic(this.brightnessCharacteristic, this.getRgbBrightnessValue()); -WLightBoxAccessoryWrapper.prototype.sendSetSimpleRgbStateCommand = function (callback, errorMsg) { - var newValue = colorHelper.rgbToHex(colorHelper.hsvToRgb(this.desiredHSVColor)).substring(1, 7) + colorHelper.toHex(this.desiredWhite / 100 * 255); - var self = this; - communication.send(bleboxCommands.setSimpleRgbwState, this.deviceIp, { - params: [newValue], - onSuccess: function (rgbwState) { - if (rgbwState) { - self.rgbw = rgbwState.rgbw || rgbwState; - self.setDesiredColorsAndUpdateCharacteristics(); - callback(null); // success - } else { - callback(new Error(errorMsg)); - } - }, - onError: function () { - callback(new Error(errorMsg)); + const whiteServiceNumber = 1; + const whiteService = this.getService(whiteServiceNumber); + whiteService.updateCharacteristic(this.onCharacteristic, this.getWhiteOnValue()); + + whiteService.updateCharacteristic(this.brightnessCharacteristic, this.getWhiteBrightnessValue()); } - }); -}; + } -WLightBoxAccessoryWrapper.prototype.setDesiredColorsAndUpdateCharacteristics = function () { - if (this.rgbw && this.rgbw.desiredColor) { - this.desiredHSVColor = colorHelper.rgbToHsv(colorHelper.hexToRgb(this.rgbw.desiredColor.substring(0, 6))); - this.desiredWhite = Number((parseInt(this.rgbw.desiredColor.substring(6, 8), 16) / 255 * 100).toFixed(0)); + updateStateInfo(stateInfo) { + if (stateInfo) { + this.accessory.context.blebox.rgbw = stateInfo.rgbw || stateInfo; + this.desiredHsv = this._getRgbAsHsv(); + this.updateStateInfoCharacteristics(); + } + } - //update characteristics - var isRgbOn = this.desiredHSVColor.v !== 0; - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.rgbServiceSubtype) - .updateCharacteristic(this.onCharacteristic, isRgbOn); + getRgbw() { + return this.accessory.context.blebox.rgbw; + } - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.rgbServiceSubtype) - .updateCharacteristic(this.hueCharacteristic, this.desiredHSVColor.h); + getServiceName(serviceNumber) { + const serviceName = super.getServiceName(serviceNumber); + const suffix = this.getServiceNameSuffix(serviceNumber); + return `${serviceName} ${suffix}`; + } - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.rgbServiceSubtype) - .updateCharacteristic(this.saturationCharacteristic, this.desiredHSVColor.s); + getServiceNameSuffix(serviceNumber) { + return this.servicesSubTypes[serviceNumber]; + } - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.rgbServiceSubtype) - .updateCharacteristic(this.brightnessCharacteristic, this.desiredHSVColor.v); - var isWhiteOn = this.desiredWhite !== 0; - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.whiteServiceSubtype) - .updateCharacteristic(this.onCharacteristic, isWhiteOn); + _getRgbAsHsv() { + const {desiredColor = ""} = this.getRgbw() || {}; + const desiredRgbHex = desiredColor.substring(0, 6) || "00000000"; + return colorHelper.rgbToHsv(colorHelper.hexToRgb(desiredRgbHex)); + } - this.accessory.getServiceByUUIDAndSubType(this.lightBulbService, this.whiteServiceSubtype) - .updateCharacteristic(this.brightnessCharacteristic, this.desiredWhite); + getRgbOnValue() { + const rgbAsHsv = this._getRgbAsHsv(); + return rgbAsHsv.v !== 0; } -}; -WLightBoxAccessoryWrapper.prototype.getRgbOnState = function (callback) { - this.log("WLIGHTBOX ( %s ): Getting 'On' characteristic ...", this.deviceName); - if (this.isResponding() && this.desiredHSVColor) { - var currentOnValue = this.desiredHSVColor.v !== 0; - this.log("WLIGHTBOX ( %s ): Current 'On' characteristic is %s", this.deviceName, currentOnValue); - callback(null, currentOnValue); - } else { - this.log("WLIGHTBOX ( %s ): Error getting 'On' characteristic. Rgbw: %s", this.deviceName, this.rgbw); - callback(new Error("Error getting 'On'.")); + getRgbHueValue() { + const rgbAsHsv = this._getRgbAsHsv(); + return rgbAsHsv.h; } -}; -WLightBoxAccessoryWrapper.prototype.setRgbOnState = function (turnOn, callback) { - if (!turnOn) { - this.log("WLIGHTBOX ( %s ): Setting 'On' characteristic to %s ...", this.deviceName, turnOn); - this.desiredHSVColor.h = 0; - this.desiredHSVColor.s = 0; - this.desiredHSVColor.v = 0; - this.sendSetSimpleRgbStateCommand(callback, "Error setting 'On'."); - } else { - callback(null) + getRgbSaturationValue() { + const rgbAsHsv = this._getRgbAsHsv(); + return rgbAsHsv.s; } -}; + getRgbBrightnessValue() { + const rgbAsHsv = this._getRgbAsHsv(); + return rgbAsHsv.v; + } -WLightBoxAccessoryWrapper.prototype.getRgbBrightness = function (callback) { - this.log("WLIGHTBOX ( %s ): Getting 'Brightness' characteristic ...", this.deviceName); - if (this.isResponding() && this.desiredHSVColor) { - this.log("WLIGHTBOX ( %s ): Current 'Brightness' characteristic is %s", this.deviceName, this.desiredHSVColor.v); - callback(null, this.desiredHSVColor.v); - } else { - this.log("WLIGHTBOX ( %s ): Error getting 'Brightness' characteristic. Rgbw: %s", this.deviceName, this.rgbw); - callback(new Error("Error getting 'Brightness'.")); + _getWhiteValue() { + const {desiredColor = ""} = this.getRgbw() || {}; + const desiredWhiteHex = desiredColor.substring(6, 8) || "00"; + return Number((parseInt(desiredWhiteHex, 16) / 255 * 100).toFixed(0)); } -}; -WLightBoxAccessoryWrapper.prototype.setRgbBrightness = function (brightness, callback) { - this.log("WLIGHTBOX ( %s ): Setting 'Brightness' characteristic to %s ...", this.deviceName, brightness); - this.desiredHSVColor.v = brightness; - this.sendSetSimpleRgbStateCommand(callback, "Error setting 'Brightness'."); -}; + getWhiteOnValue() { + const whiteValue = this._getWhiteValue(); + return whiteValue !== 0; + } -WLightBoxAccessoryWrapper.prototype.getRgbHue = function (callback) { - this.log("WLIGHTBOX ( %s ): Getting 'Hue' characteristic ...", this.deviceName); - if (this.isResponding() && this.desiredHSVColor) { - this.log("WLIGHTBOX ( %s ): Current 'Hue' characteristic is %s", this.deviceName, this.desiredHSVColor.h); - callback(null, this.desiredHSVColor.h); - } else { - this.log("WLIGHTBOX ( %s ): Error getting 'Hue' characteristic. Rgbw: %s", this.deviceName, this.rgbw); - callback(new Error("Error getting 'Hue'.")); + getWhiteBrightnessValue() { + return this._getWhiteValue(); } -}; -WLightBoxAccessoryWrapper.prototype.setRgbHue = function (hue, callback) { - this.log("WLIGHTBOX ( %s ): Setting 'Hue' characteristic to %s ...", this.deviceName, hue); - this.desiredHSVColor.h = hue; - this.desiredHSVColor.v = this.desiredHSVColor.v || 100; - this.sendSetSimpleRgbStateCommand(callback, "Error setting 'Hue'."); -}; + sendSetSimpleRgbStateCommand(hsv, white, callback) { + const newRgbwColorHex = colorHelper.rgbToHex(colorHelper.hsvToRgb(hsv)) + colorHelper.toHex(white / 100 * 255); + const self = this; + const device = this.getDevice(); + communication.send(bleboxCommands.setSimpleRgbwState, device.ip, { + params: [newRgbwColorHex], + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); + callback(null); + }, + onError: function () { + callback(new Error("Error setting new color: " + newRgbwColorHex)); + } + }); + }; + + onGetRgbOnState(callback) { + this.log("Getting 'On' characteristic ..."); + const rgbw = this.getRgbw(); + if (this.isResponding() && rgbw) { + const currentRgbOnValue = this.getRgbOnValue(); + this.log("Current 'On' characteristic is %s", currentRgbOnValue); + callback(null, currentRgbOnValue); + } else { + this.log("Error getting 'On' characteristic. Rgbw: %s", rgbw); + callback(new Error("Error getting 'On'.")); + } + }; -WLightBoxAccessoryWrapper.prototype.getRgbSaturation = function (callback) { - this.log("WLIGHTBOX ( %s ): Getting 'Saturation' characteristic ...", this.deviceName); - if (this.isResponding() && this.desiredHSVColor) { - this.log("WLIGHTBOX ( %s ): Current 'Saturation' characteristic is %s", this.deviceName, this.desiredHSVColor.s); - callback(null, this.desiredHSVColor.s); - } else { - this.log("WLIGHTBOX ( %s ): Error getting 'Saturation' characteristic. Rgbw: %s", this.deviceName, this.rgbw); - callback(new Error("Error getting 'Saturation'.")); + onSetRgbOnState(turnOn, callback) { + // We should only handle turn OFF + if (!turnOn) { + this.log("Setting 'On' characteristic to %s ...", turnOn); + this.desiredHsv = {h: 0, s: 0, v: 0}; + this.sendSetSimpleRgbStateCommand(this.desiredHsv, this._getWhiteValue(), callback); + } else { + callback(null); + } } -}; -WLightBoxAccessoryWrapper.prototype.setRgbSaturation = function (saturation, callback) { - this.log("WLIGHTBOX ( %s ): Setting 'Saturation' characteristic to %s ...", this.deviceName, saturation); - this.desiredHSVColor.s = saturation; - this.desiredHSVColor.v = this.desiredHSVColor.v || 100; - this.sendSetSimpleRgbStateCommand(callback, "Error setting 'Saturation'."); -}; + onGetRgbBrightness(callback) { + this.log("Getting 'Brightness' characteristic ..."); + const rgbw = this.getRgbw(); + if (this.isResponding() && rgbw) { + const currentRgbBrightnessValue = this.getRgbBrightnessValue(); + this.log("Current 'Brightness' characteristic is %s", currentRgbBrightnessValue); + callback(null, currentRgbBrightnessValue); + } else { + this.log("Error getting 'Brightness' characteristic. Rgbw: %s", rgbw); + callback(new Error("Error getting 'Brightness'.")); + } + } -WLightBoxAccessoryWrapper.prototype.getWhiteOnState = function (callback) { - this.log("WLIGHTBOX ( %s ): Getting 'On white' characteristic ...", this.deviceName); - if (this.isResponding()) { - var isOn = this.desiredWhite !== 0; - this.log("WLIGHTBOX ( %s ): Current 'On white' characteristic is %s", this.deviceName, isOn); - callback(null, isOn); - } else { - this.log("WLIGHTBOX ( %s ): Error getting 'On white' characteristic. Rgbw: %s", this.deviceName, this.rgbw); - callback(new Error("Error getting 'On white'.")); + onSetRgbBrightness(brightness, callback) { + this.log("Setting 'Brightness' characteristic to %s ...", brightness); + this.desiredHsv.v = brightness + this.sendSetSimpleRgbStateCommand(this.desiredHsv, this._getWhiteValue(), callback); } -}; -WLightBoxAccessoryWrapper.prototype.setWhiteOnState = function (turnOn, callback) { - // Turning on option is handled by @setWhiteBrightness - if (!turnOn) { - this.log("WLIGHTBOX ( %s ): Setting 'On white' characteristic to %s ...", this.deviceName, turnOn); - this.desiredWhite = turnOn ? 100 : 0; - this.sendSetSimpleRgbStateCommand(callback, "Error setting 'On white'."); - } else { - callback(null); // success + onGetRgbHue(callback) { + this.log("Getting 'Hue' characteristic ..."); + const rgbw = this.getRgbw(); + if (this.isResponding() && rgbw) { + const currentRgbHueValue = this.getRgbHueValue() + this.log("Current 'Hue' characteristic is %s", currentRgbHueValue); + callback(null, currentRgbHueValue); + } else { + this.log("Error getting 'Hue' characteristic. Rgbw: %s", rgbw); + callback(new Error("Error getting 'Hue'.")); + } } -}; -WLightBoxAccessoryWrapper.prototype.getWhiteBrightness = function (callback) { - this.log("WLIGHTBOX ( %s ): Getting 'Brightness white' characteristic ...", this.deviceName); - if (this.isResponding()) { - var currentBrightness = this.desiredWhite || 0; - this.log("WLIGHTBOX ( %s ): Current 'Brightness white' characteristic is %s", this.deviceName, currentBrightness); - callback(null, currentBrightness); - } else { - this.log("WLIGHTBOX ( %s ): Error getting 'Brightness white' characteristic. Rgbw: %s", this.deviceName, this.rgbw); - callback(new Error("Error getting 'Brightness white'.")); + + onSetRgbHue(hue, callback) { + this.log("Setting 'Hue' characteristic to %s ...", hue); + this.desiredHsv.h = hue; + this.desiredHsv.v = this.desiredHsv.v || 100; + this.sendSetSimpleRgbStateCommand(this.desiredHsv, this._getWhiteValue(), callback); + } + + onGetRgbSaturation(callback) { + this.log("Getting 'Saturation' characteristic ..."); + const rgbw = this.getRgbw(); + if (this.isResponding() && rgbw) { + const currentRgbSaturationValue = this.getRgbSaturationValue() + this.log("Current 'Saturation' characteristic is %s", currentRgbSaturationValue); + callback(null, currentRgbSaturationValue); + } else { + this.log("Error getting 'Saturation' characteristic. Rgbw: %s", rgbw); + callback(new Error("Error getting 'Saturation'.")); + } + } + + onSetRgbSaturation(saturation, callback) { + this.log("Setting 'Saturation' characteristic to %s ...", saturation); + this.desiredHsv.s = saturation; + this.desiredHsv.v = this.desiredHsv.v || 100; + this.sendSetSimpleRgbStateCommand(this.desiredHsv, this._getWhiteValue(), callback); } -}; -WLightBoxAccessoryWrapper.prototype.setWhiteBrightness = function (brightness, callback) { - this.log("WLIGHTBOX ( %s ): Setting 'Brightness white' characteristic to %s ...", this.deviceName, brightness); - this.desiredWhite = brightness; - this.sendSetSimpleRgbStateCommand(callback, "Error setting 'Brightness white'."); + onGetWhiteOnState(callback) { + this.log("Getting 'On white' characteristic ..."); + const rgbw = this.getRgbw(); + if (this.isResponding() && rgbw) { + const currentWhiteOnValue = this.getWhiteOnValue(); + this.log("Current 'On white' characteristic is %s", currentWhiteOnValue); + callback(null, currentWhiteOnValue); + } else { + this.log("Error getting 'On white' characteristic. Rgbw: %s", rgbw); + callback(new Error("Error getting 'On white'.")); + } + } + + + onSetWhiteOnState(turnOn, callback) { + // We should only handle turn OFF + if (!turnOn) { + this.log("Setting 'On white' characteristic to %s ...", turnOn); + const newWhite = 0; + this.sendSetSimpleRgbStateCommand(this._getRgbAsHsv(), newWhite, callback); + } else { + callback(null); // success + } + } + + onGetWhiteBrightness(callback) { + this.log("Getting 'Brightness white' characteristic ..."); + const rgbw = this.getRgbw(); + if (this.isResponding() && rgbw) { + const currentWhiteBrightnessValue = this.getWhiteBrightnessValue(); + this.log("Current 'Brightness white' characteristic is %s", currentWhiteBrightnessValue); + callback(null, currentWhiteBrightnessValue); + } else { + this.log("Error getting 'Brightness white' characteristic. Rgbw: %s", rgbw); + callback(new Error("Error getting 'Brightness white'.")); + } + } + + onSetWhiteBrightness(brightness, callback) { + this.log("Setting 'Brightness white' characteristic to %s ...", brightness); + this.sendSetSimpleRgbStateCommand(this._getRgbAsHsv(), brightness, callback); + } + +} + + +module.exports = { + type: WLIGHTBOX_TYPE, + checkStateCommand: bleboxCommands.getRgbwState, + create: function (accessory, log, api, deviceInfo, stateInfo) { + return new WLightBoxAccessoryWrapper(accessory, log, api, deviceInfo, stateInfo); + }, restore: function (accessory, log, api) { + return new WLightBoxAccessoryWrapper(accessory, log, api); + } }; diff --git a/blebox/wLightBoxS.js b/blebox/wLightBoxS.js index aa849fd..b7d9c1f 100644 --- a/blebox/wLightBoxS.js +++ b/blebox/wLightBoxS.js @@ -1,154 +1,141 @@ -var communication = require("../common/communication"); -var bleboxCommands = require("../common/bleboxCommands"); -var WLIGHTBOXS_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.WLIGHTBOXS; -var AbstractBoxWrapper = require("./abstractBox"); -var colorHelper = require("../common/colorHelper"); +const communication = require("../common/communication"); +const bleboxCommands = require("../common/bleboxCommands"); +const WLIGHTBOXS_TYPE = require("../common/bleboxConst").BLEBOX_TYPE.WLIGHTBOXS; +const AbstractBoxWrapper = require("./abstractBox"); +const colorHelper = require("../common/colorHelper"); -module.exports = { - create: function (homebridge, log, api, deviceInfo, lightInfo) { - return new WLightBoxSAccessoryWrapper(homebridge, null, log, api, deviceInfo, lightInfo); - }, restore: function (accessory, log, api, deviceInfo) { - return new WLightBoxSAccessoryWrapper(null, accessory, log, api, deviceInfo.device, deviceInfo.light); +class WLightBoxSAccessoryWrapper extends AbstractBoxWrapper { + constructor(accessory, log, api, deviceInfo, stateInfo) { + super(accessory, log, api); + + this.type = WLIGHTBOXS_TYPE; + this.checkStateCommand = bleboxCommands.Lightbulb; + + this.servicesDefList = [api.hap.Service.Thermostat]; + + this.nameCharacteristic = api.hap.Characteristic.Name; + this.onCharacteristic = api.hap.Characteristic.On; + this.brightnessCharacteristic = api.hap.Characteristic.Brightness; + + this.init(deviceInfo, stateInfo); + + this.assignCharacteristics(); + + this.startListening(); } -}; -function WLightBoxSAccessoryWrapper(homebridge, accessory, log, api, deviceInfo, lightInfo) { - AbstractBoxWrapper.call(this, accessory, log, deviceInfo); - this.light = lightInfo ? (lightInfo.light || lightInfo) : null; + assignCharacteristics() { + super.assignCharacteristics(); + const serviceNumber = 0; + const service = this.getService(serviceNumber); - this.nameCharacteristic = api.hap.Characteristic.Name; - this.onCharacteristic = api.hap.Characteristic.On; - this.brightnessCharacteristic = api.hap.Characteristic.Brightness; - this.lightBulbService = api.hap.Service.Lightbulb; + service.getCharacteristic(this.onCharacteristic) + .on('get', this.onGetOnState.bind(this)) + .on('set', this.onSetOnState.bind(this)); - if (!this.accessory) { - var uuid = homebridge.hap.uuid.generate(this.deviceName + WLIGHTBOXS_TYPE + this.deviceIp); - this.accessory = new homebridge.platformAccessory(this.deviceName, uuid); - this.accessory.addService(this.lightBulbService, this.deviceName); + service.getCharacteristic(this.brightnessCharacteristic) + .on('get', this.onGetBrightness.bind(this)) + .on('set', this.onSetBrightness.bind(this)); } - this.accessory.getService(this.lightBulbService) - .getCharacteristic(this.onCharacteristic) - .on('get', this.getOnState.bind(this)) - .on('set', this.setOnState.bind(this)); - - this.accessory.getService(this.lightBulbService) - .getCharacteristic(this.brightnessCharacteristic) - .on('get', this.getBrightness.bind(this)) - .on('set', this.setBrightness.bind(this)); - - this.accessory.getService(this.lightBulbService) - .getCharacteristic(this.nameCharacteristic) - .on('get', this.getName.bind(this)); - - //for restore purpose - this.accessory.context.blebox = { - "type": WLIGHTBOXS_TYPE, - "device": { - "id": this.deviceId, - "ip": this.deviceIp, - "deviceName": this.deviceName - }, "light": this.light - }; + updateStateInfoCharacteristics() { + const light = this.getLight(); + if (light) { + //update characteristics + const serviceNumber = 0; + const service = this.getService(serviceNumber); + service.updateCharacteristic(this.onCharacteristic, this.getOnStateValue()); - this.updateCharacteristics(); - this.startListening(); -} + service.updateCharacteristic(this.brightnessCharacteristic, this.getBrightnessValue()); + } + } -WLightBoxSAccessoryWrapper.prototype = Object.create(AbstractBoxWrapper.prototype); - -WLightBoxSAccessoryWrapper.prototype.checkSpecificState = function () { - var self = this; - communication.send(bleboxCommands.getLightState, self.deviceIp, { - onSuccess: function (lightState) { - if (lightState) { - self.badRequestsCounter = 0; - lightState = lightState.light || lightState; - self.light = lightState; - self.updateCharacteristics(); - } - }, onError: function () { - self.badRequestsCounter++; + updateStateInfo(stateInfo) { + if (stateInfo) { + this.accessory.context.blebox.light = stateInfo.light || stateInfo; + this.updateStateInfoCharacteristics(); } - }); -}; + } -WLightBoxSAccessoryWrapper.prototype.updateCharacteristics = function () { - if (this.light && this.light.desiredColor) { - var currentBrightness = Number((parseInt(this.light.desiredColor, 16) / 255 * 100).toFixed(0)) || 0; - var currentOnValue = currentBrightness !== 0; + getLight() { + return this.accessory.context.blebox.light; + } - this.accessory.getService(this.lightBulbService) - .updateCharacteristic(this.onCharacteristic, currentOnValue); + getBrightnessValue() { + const {desiredColor = "00"} = this.getLight() || {}; + return Number((parseInt(desiredColor, 16) / 255 * 100).toFixed(0)) || 0; + } - this.accessory.getService(this.lightBulbService) - .updateCharacteristic(this.brightnessCharacteristic, currentBrightness); + getOnStateValue() { + return this.getBrightnessValue() !== 0; } -}; -WLightBoxSAccessoryWrapper.prototype.sendSetSimpleLightStateCommand = function (value, callback, errorMsg) { - var self = this; - communication.send(bleboxCommands.setSimpleLightState, this.deviceIp, { - params: [value], - onSuccess: function (lightState) { - if (lightState) { - self.light = lightState.light || lightState; - self.updateCharacteristics(); - - callback(null); // success - } else { - callback(new Error(errorMsg)); + sendSetSimpleLightStateCommand(value, callback) { + const self = this; + const device = this.getDevice(); + communication.send(bleboxCommands.setSimpleLightState, device.ip, { + params: [value], + onSuccess: function (stateInfo) { + self.updateStateInfo(stateInfo); + callback(null); + }, + onError: function () { + callback(new Error("Error setting new light value: " + value)); } - }, - onError: function () { - callback(new Error(errorMsg)); - } - }); -}; + }); + }; -WLightBoxSAccessoryWrapper.prototype.onDeviceNameChange = function () { - this.accessory.getService(this.lightBulbService) - .updateCharacteristic(this.nameCharacteristic, this.deviceName); -}; + onGetOnState(callback) { + this.log("Getting 'On' characteristic ..."); + const light = this.getLight(); + if (this.isResponding() && light) { + const currentOnValue = this.getOnStateValue(); + this.log("Current 'On' characteristic is %s", currentOnValue); + callback(null, currentOnValue); + } else { + this.log("Error getting 'On' characteristic. Light: %s", light); + callback(new Error("Error getting 'On'.")); + } + } -WLightBoxSAccessoryWrapper.prototype.getOnState = function (callback) { - this.log("WLIGHTBOXS ( %s ): Getting 'On' characteristic ...", this.deviceName); - if (this.isResponding() && this.light) { - var currentOnValue = (parseInt(this.light.desiredColor, 16) / 255 * 100).toFixed(0) != 0; - this.log("WLIGHTBOXS ( %s ): Current 'On' characteristic is %s", this.deviceName, currentOnValue); - callback(null, currentOnValue); - } else { - this.log("WLIGHTBOXS ( %s ): Error getting 'On' characteristic. Light: %s", this.deviceName, this.light); - callback(new Error("Error getting 'On'.")); + onSetOnState(turnOn, callback) { + // We should only handle turn OFF + if (!turnOn) { + this.log("Setting 'On white' characteristic to %s ...", turnOn); + const newValue = colorHelper.toHex(0); + this.sendSetSimpleLightStateCommand(newValue, callback); + } else { + callback(null); // success + } } -}; -WLightBoxSAccessoryWrapper.prototype.setOnState = function (turnOn, callback) { - // Turning on option is handled by @setBrightness - if (!turnOn) { - this.log("WLIGHTBOXS: Setting 'On white' characteristic to %s ...", turnOn); - var newValue = colorHelper.toHex(0); - this.sendSetSimpleLightStateCommand(newValue, callback, "Error setting 'On'."); - } else { - callback(null); // success + onGetBrightness(callback) { + this.log("Getting 'Brightness' characteristic ..."); + const light = this.getLight(); + if (this.isResponding() && light) { + const currentBrightnessValue = this.getBrightnessValue() + this.log("Current 'Brightness' characteristic is %s", currentBrightnessValue); + callback(null, currentBrightnessValue); + } else { + this.log("Error getting 'Brightness' characteristic. Light: %s", light); + callback(new Error("Error getting 'Brightness'.")); + } + } + + onSetBrightness(brightness, callback) { + this.log("WLIGHTBOXS: Setting 'Brightness' characteristic to %s ...", brightness); + const newValue = colorHelper.toHex(brightness / 100 * 255); + this.sendSetSimpleLightStateCommand(newValue, callback); } +} -}; -WLightBoxSAccessoryWrapper.prototype.getBrightness = function (callback) { - this.log("WLIGHTBOXS ( %s ): Getting 'Brightness' characteristic ...", this.deviceName); - if (this.isResponding() && this.light) { - var currentBrightness = Number((parseInt(this.light.desiredColor, 16) / 255 * 100).toFixed(0)) || 0; - this.log("WLIGHTBOXS ( %s ): Current 'Brightness' characteristic is %s", this.deviceName, currentBrightness); - callback(null, currentBrightness); - } else { - this.log("WLIGHTBOXS ( %s ): Error getting 'Brightness' characteristic. Light: %s", this.deviceName, this.light); - callback(new Error("Error getting 'Brightness'.")); +module.exports = { + type: WLIGHTBOXS_TYPE, + checkStateCommand: bleboxCommands.getLightState, + create: function (accessory, log, api, deviceInfo, stateInfo) { + return new WLightBoxSAccessoryWrapper(accessory, log, api, deviceInfo, stateInfo); + }, restore: function (accessory, log, api) { + return new WLightBoxSAccessoryWrapper(accessory, log, api); } }; - -WLightBoxSAccessoryWrapper.prototype.setBrightness = function (brightness, callback) { - this.log("WLIGHTBOXS: Setting 'Brightness' characteristic to %s ...", brightness); - var newValue = colorHelper.toHex(brightness / 100 * 255); - this.sendSetSimpleLightStateCommand(newValue, callback, "Error setting 'Brightness'."); -}; \ No newline at end of file diff --git a/common/bleboxCommands.js b/common/bleboxCommands.js index c0521b6..03a2ebb 100644 --- a/common/bleboxCommands.js +++ b/common/bleboxCommands.js @@ -83,5 +83,20 @@ module.exports = { "name": "setWindowPositionPercentage", "method": "GET", "url": "/s/{0}/p/{1}" - } + }, + "getHeatState": { + "name": "getHeatState", + "method": "GET", + "url": "/api/heat/state" + }, + "setSimpleHeatState": { + "name": "setSimpleHeatState", + "method": "GET", + "url": "/s/{0}" + }, + "setSimpleHeatDesiredTemperature": { + "name": "setSimpleHeatDesiredTemperature", + "method": "GET", + "url": "/s/t/{0}" + }, }; \ No newline at end of file diff --git a/common/bleboxConst.js b/common/bleboxConst.js index 09e3b7c..06a6592 100644 --- a/common/bleboxConst.js +++ b/common/bleboxConst.js @@ -7,6 +7,7 @@ module.exports= { DIMMERBOX : "dimmerbox", WLIGHTBOX : "wlightbox", WLIGHTBOXS : "wlightboxs", - SMARTWINDOWBOX : "smartwindowbox" + SMARTWINDOWBOX : "smartwindowbox", + SAUNABOX : "saunabox" } }; \ No newline at end of file diff --git a/common/colorHelper.js b/common/colorHelper.js index 83756af..4ebb4b1 100644 --- a/common/colorHelper.js +++ b/common/colorHelper.js @@ -126,7 +126,7 @@ module.exports = { rgb.b = Number(rgb.b); if (rgb.r > 255 || rgb.g > 255 || rgb.b > 255) throw "Invalid color component"; - return "#" + ((1 << 24) + (rgb.r << 16) + (rgb.g << 8) + rgb.b).toString(16).slice(1); + return "" + ((1 << 24) + (rgb.r << 16) + (rgb.g << 8) + rgb.b).toString(16).slice(1); }, hexToRgb: function (hex) { diff --git a/package.json b/package.json index 0516224..017d002 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,13 @@ ], "engines": { "node": ">=0.12.0", - "homebridge": ">=0.2.0" + "homebridge": ">=1.1.6" }, "dependencies": { - "request": "^2.65.0" + "request": "^2.65.0", + "lodash": "^4.17.20" + }, + "devDependencies": { + "homebridge": "^1.1.6" } -} \ No newline at end of file +} diff --git a/platform/bleboxPlatform.js b/platform/bleboxPlatform.js index 7f7a617..ee14096 100644 --- a/platform/bleboxPlatform.js +++ b/platform/bleboxPlatform.js @@ -1,272 +1,197 @@ var os = require('os'); var communication = require("./../common/communication"); var bleboxCommands = require("./../common/bleboxCommands"); -var BLEBOX_TYPE = require("./../common/bleboxConst").BLEBOX_TYPE; -var GateBoxAccessoryWrapperFactory = require("./../blebox/gateBox"); -var DimmerBoxAccessoryWrapperFactory = require("./../blebox/dimmerBox"); -var ShutterBoxAccessoryWrapperFactory = require("./../blebox/shutterBox"); -var SwitchBoxAccessoryWrapperFactory = require("./../blebox/switchBox"); -var SwitchBoxDAccessoryWrapperFactory = require("./../blebox/switchBoxD"); -var WLightBoxAccessoryWrapperFactory = require("./../blebox/wLightBox"); -var WLightBoxSAccessoryWrapperFactory = require("./../blebox/wLightBoxS"); -var SmartWindowBoxAccessoryWrapperFactory = require("./../blebox/smartWindowBox"); - -module.exports = BleBoxPlatform; - -function BleBoxPlatform(homebridge, log, config, api) { - this.log = log; - this.config = config; - this.startNextScanDelayInMin = Number(this.config["NEXT_SCAN_DELAY_IN_MIN"]) || 0; - this.ipList = []; - this.scanNextIpDelayInMs = 100; - this.accessoriesWrapperList = []; - this.accessoriesWrapperObj = {}; - this.homebridge = homebridge; - this.api = api; - - this.prepareIpListToScan(); - - this.api.on('didFinishLaunching', function () { - this.startSearching(); - }.bind(this)); -} - -BleBoxPlatform.prototype.prepareIpListToScan = function () { - var interfaces = os.networkInterfaces(); - for (var i in interfaces) { - for (var j in interfaces[i]) { - var netInterface = interfaces[i][j]; - if (netInterface.family === 'IPv4' && !netInterface.internal) { - this.addIpsFromInterface(netInterface); - break; - } - } +var bleBoxAccessoryWrapperFactories = require("./../blebox") + +class BleBoxPlatform { + constructor(homebridge, log, config, api) { + this.log = log; + this.config = config; + this.startNextScanDelayInMin = Number(this.config["NEXT_SCAN_DELAY_IN_MIN"]) || 0; + this.ipList = []; + this.scanNextIpDelayInMs = 100; + this.accessoriesWrapperList = []; + this.accessoriesWrapperObj = {}; + this.homebridge = homebridge; + this.api = api; + + this.prepareIpListToScan(); + + this.api.on('didFinishLaunching', function () { + this.startSearching(); + }.bind(this)); } -}; -BleBoxPlatform.prototype.addIpsFromInterface = function (netInterface) { - var maskArray = netInterface.netmask.split('.', 4); - var maskBitsCount = 0; - for (var i in maskArray) { - var maskNode = Number(maskArray[i]); - maskBitsCount += (((maskNode >>> 0).toString(2)).match(/1/g) || []).length; - } - if (maskBitsCount > 16 && maskBitsCount < 32) { - var ipNumber = ipStringToNumber(netInterface.address); - if (ipNumber) { - var firstPossibleIpNumber = ipNumber & ((-1 << (32 - maskBitsCount))); //network address - var lastPossibleIpNumber = firstPossibleIpNumber + Math.pow(2, (32 - maskBitsCount)) - 1; // broadcast address + // Implement for DynamicPlatformPlugin + configureAccessory(accessory) { + const deviceType = accessory.context.blebox.device.type || accessory.context.blebox.type || ""; + const accessoryWrapperFactory = bleBoxAccessoryWrapperFactories[deviceType.toLowerCase()]; + if (accessoryWrapperFactory) { + const accessoryWrapper = accessoryWrapperFactory.restore(accessory, this.log, this.api); + this.addAccessoryWrapper(accessoryWrapper, false); + }else{ + this.log(JSON.stringify(accessory.context)) + this.api.unregisterPlatformAccessories("homebridge-blebox", "BleBoxPlatform", [accessory]); + } + }; - var possibleIpAddressesCount = lastPossibleIpNumber - firstPossibleIpNumber; - // add all addresses except network and broadcast - for (var j = 1; j < possibleIpAddressesCount; j++) { - var currentIpNumber = firstPossibleIpNumber + j; - var currentIpString = ipNumberToString(currentIpNumber); - if (this.ipList.indexOf(currentIpString) === -1) { - this.ipList.push(currentIpString); + // Implement for StaticPlatformPlugin + accessories(callback) { + var accessoriesList = []; + for (var i = 0; i < this.accessoriesWrapperList.length; i++) { + accessoriesList.push(this.accessoriesWrapperList[i].getAccessory()); + } + callback(accessoriesList); + }; + + prepareIpListToScan() { + var interfaces = os.networkInterfaces(); + for (var i in interfaces) { + for (var j in interfaces[i]) { + var netInterface = interfaces[i][j]; + if (netInterface.family === 'IPv4' && !netInterface.internal) { + this.addIpsFromInterface(netInterface); + break; } } } - } - - function ipNumberToString(ipNumber) { - var byte1 = ( ipNumber >>> 24 ); - var byte2 = ( ipNumber >>> 16 ) & 255; - var byte3 = ( ipNumber >>> 8 ) & 255; - var byte4 = ipNumber & 255; - return ( byte1 + '.' + byte2 + '.' + byte3 + '.' + byte4 ); - } - - function ipStringToNumber(ipString) { - var split = ipString.split('.', 4); - if (split.length === 4) { - var myInt = ( - parseFloat(split[0] * 16777216) /* 2^24 */ - + parseFloat(split[1] * 65536) /* 2^16 */ - + parseFloat(split[2] * 256) /* 2^8 */ - + parseFloat(split[3]) - ); - return myInt; + }; + + addIpsFromInterface(netInterface) { + var maskArray = netInterface.netmask.split('.', 4); + var maskBitsCount = 0; + for (var i in maskArray) { + var maskNode = Number(maskArray[i]); + maskBitsCount += (((maskNode >>> 0).toString(2)).match(/1/g) || []).length; } - return null; - } -}; - -BleBoxPlatform.prototype.configureAccessory = function (accessory) { - var accessoryWrapperFactory; - var accessoryWrapper; - switch (accessory.context.blebox.type) { - case BLEBOX_TYPE.WLIGHTBOXS: - accessoryWrapperFactory = WLightBoxSAccessoryWrapperFactory; - break; - case BLEBOX_TYPE.WLIGHTBOX: - accessoryWrapperFactory = WLightBoxAccessoryWrapperFactory; - break; - case BLEBOX_TYPE.DIMMERBOX: - accessoryWrapperFactory = DimmerBoxAccessoryWrapperFactory; - break; - case BLEBOX_TYPE.SHUTTERBOX: - accessoryWrapperFactory = ShutterBoxAccessoryWrapperFactory; - break; - case BLEBOX_TYPE.GATEBOX: - accessoryWrapperFactory = GateBoxAccessoryWrapperFactory; - break; - case BLEBOX_TYPE.SWITCHBOXD: - accessoryWrapperFactory = SwitchBoxDAccessoryWrapperFactory; - break; - case BLEBOX_TYPE.SWITCHBOX: - accessoryWrapperFactory = SwitchBoxAccessoryWrapperFactory; - break; - case BLEBOX_TYPE.SMARTWINDOWBOX: - accessoryWrapperFactory = SmartWindowBoxAccessoryWrapperFactory; - break; - default : - console.log("Wrong accessory!", accessory); - this.api.unregisterPlatformAccessories("homebridge-blebox", "BleBoxPlatform", [accessory]); - return; - } - - accessoryWrapper = accessoryWrapperFactory.restore(accessory, this.log, this.api, accessory.context.blebox); - this.addAccessoryWrapper(accessoryWrapper, false); -}; - -BleBoxPlatform.prototype.startSearching = function () { - this.log("Searching blebox devices started!"); - this.sendSearchRequest(0); -}; - -BleBoxPlatform.prototype.sendSearchRequest = function (index) { - (function (index) { - var ipAddress = this.ipList[index]; - if (ipAddress) { - var self = this; - console.log("Checking ip: %s", ipAddress); - communication.send(bleboxCommands.getDeviceState, ipAddress, { - onSuccess: function (deviceInfo) { - if (deviceInfo) { - deviceInfo = deviceInfo.device || deviceInfo; - deviceInfo.ip = ipAddress; - var accessoryWrapperObj = self.accessoriesWrapperObj[deviceInfo.id]; - if (!accessoryWrapperObj) { - self.checkSpecificStateAndAddAccessory(deviceInfo) - } else { - accessoryWrapperObj.setDeviceIp(deviceInfo); - } + if (maskBitsCount > 16 && maskBitsCount < 32) { + var ipNumber = ipStringToNumber(netInterface.address); + if (ipNumber) { + var firstPossibleIpNumber = ipNumber & ((-1 << (32 - maskBitsCount))); //network address + var lastPossibleIpNumber = firstPossibleIpNumber + Math.pow(2, (32 - maskBitsCount)) - 1; // broadcast address + + var possibleIpAddressesCount = lastPossibleIpNumber - firstPossibleIpNumber; + // add all addresses except network and broadcast + for (var j = 1; j < possibleIpAddressesCount; j++) { + var currentIpNumber = firstPossibleIpNumber + j; + var currentIpString = ipNumberToString(currentIpNumber); + if (this.ipList.indexOf(currentIpString) === -1) { + this.ipList.push(currentIpString); } - self.checkIfSearchIsFinishedAndScheduleNext(index); - }, onError: function () { - self.checkIfSearchIsFinishedAndScheduleNext(index); } - }); + } } - }).call(this, index); - - index++; - if (index < this.ipList.length) { - setTimeout(this.sendSearchRequest.bind(this, index), this.scanNextIpDelayInMs); - } -}; -BleBoxPlatform.prototype.checkIfSearchIsFinishedAndScheduleNext = function (index) { - if (index >= this.ipList.length - 1) { - this.log("Searching blebox devices finished!"); - if (this.startNextScanDelayInMin) { //if defined and different than 0 - var startNextScanDelayInMs = this.startNextScanDelayInMin * 60 * 1000; - setTimeout(this.startSearching.bind(this), startNextScanDelayInMs); + function ipNumberToString(ipNumber) { + var byte1 = (ipNumber >>> 24); + var byte2 = (ipNumber >>> 16) & 255; + var byte3 = (ipNumber >>> 8) & 255; + var byte4 = ipNumber & 255; + return (byte1 + '.' + byte2 + '.' + byte3 + '.' + byte4); } - } -}; -BleBoxPlatform.prototype.createAndAddAccessoryWrapper = function (accessoryWrapperFactory, deviceInfo, stateInfo) { - if (accessoryWrapperFactory && deviceInfo && stateInfo) { - var accessoryWrapper = accessoryWrapperFactory.create(this.homebridge, this.log, this.api, deviceInfo, stateInfo); - this.addAccessoryWrapper(accessoryWrapper, true); - } -}; + function ipStringToNumber(ipString) { + var split = ipString.split('.', 4); + if (split.length === 4) { + var myInt = ( + parseFloat(split[0] * 16777216) /* 2^24 */ + + parseFloat(split[1] * 65536) /* 2^16 */ + + parseFloat(split[2] * 256) /* 2^8 */ + + parseFloat(split[3]) + ); + return myInt; + } + return null; + } + }; + + startSearching() { + this.log("Searching blebox devices started!"); + this.sendSearchRequest(0); + }; + + sendSearchRequest(index) { + (function (index) { + var ipAddress = this.ipList[index]; + if (ipAddress) { + var self = this; + console.log("Checking ip: %s", ipAddress); + communication.send(bleboxCommands.getDeviceState, ipAddress, { + onSuccess: function (deviceInfo) { + if (deviceInfo) { + deviceInfo = deviceInfo.device || deviceInfo; + if (deviceInfo.id && deviceInfo.type) { + deviceInfo.ip = ipAddress; + var accessoryWrapperObj = self.accessoriesWrapperObj[deviceInfo.id]; + if (!accessoryWrapperObj) { + self.checkSpecificStateAndAddAccessory(deviceInfo) + } else { + accessoryWrapperObj.updateDeviceInfo(deviceInfo); + } + } + } + self.checkIfSearchIsFinishedAndScheduleNext(index); + }, onError: function () { + self.checkIfSearchIsFinishedAndScheduleNext(index); + } + }); + } + }).call(this, index); -BleBoxPlatform.prototype.checkSpecificStateAndAddAccessory = function (deviceInfo) { - var accessoryWrapper = null; - var ipAddress = deviceInfo.ip; - var self = this; - switch (typeof deviceInfo.type === 'string' && deviceInfo.type.toLowerCase()) { - case BLEBOX_TYPE.WLIGHTBOXS: - communication.send(bleboxCommands.getLightState, ipAddress, { - onSuccess: function (lightInfo) { - self.createAndAddAccessoryWrapper(WLightBoxSAccessoryWrapperFactory, deviceInfo, lightInfo); - } - }); - break; - case BLEBOX_TYPE.WLIGHTBOX: - communication.send(bleboxCommands.getRgbwState, ipAddress, { - onSuccess: function (rgbState) { - self.createAndAddAccessoryWrapper(WLightBoxAccessoryWrapperFactory, deviceInfo, rgbState); - } - }); - break; - case BLEBOX_TYPE.DIMMERBOX: - communication.send(bleboxCommands.getDimmerState, ipAddress, { - onSuccess: function (dimmerInfo) { - self.createAndAddAccessoryWrapper(DimmerBoxAccessoryWrapperFactory, deviceInfo, dimmerInfo); - } - }); - break; - case BLEBOX_TYPE.SHUTTERBOX: - communication.send(bleboxCommands.getShutterState, ipAddress, { - onSuccess: function (shutterInfo) { - self.createAndAddAccessoryWrapper(ShutterBoxAccessoryWrapperFactory, deviceInfo, shutterInfo); - } - }); - break; - case BLEBOX_TYPE.GATEBOX: - communication.send(bleboxCommands.getGateState, ipAddress, { - onSuccess: function (gateInfo) { - self.createAndAddAccessoryWrapper(GateBoxAccessoryWrapperFactory, deviceInfo, gateInfo); - } - }); - break; - case BLEBOX_TYPE.SWITCHBOXD: - communication.send(bleboxCommands.getRelayState, ipAddress, { - onSuccess: function (relayInfo) { - self.createAndAddAccessoryWrapper(SwitchBoxDAccessoryWrapperFactory, deviceInfo, relayInfo); - } - }); - break; - case BLEBOX_TYPE.SWITCHBOX: - communication.send(bleboxCommands.getRelayState, ipAddress, { - onSuccess: function (relayInfo) { - self.createAndAddAccessoryWrapper(SwitchBoxAccessoryWrapperFactory, deviceInfo, relayInfo); - } - }); - break; - case BLEBOX_TYPE.SMARTWINDOWBOX: - communication.send(bleboxCommands.getWindowState, ipAddress, { - onSuccess: function (windowInfo) { - self.createAndAddAccessoryWrapper(SmartWindowBoxAccessoryWrapperFactory, deviceInfo, windowInfo); + index++; + if (index < this.ipList.length) { + setTimeout(this.sendSearchRequest.bind(this, index), this.scanNextIpDelayInMs); + } + }; + + checkIfSearchIsFinishedAndScheduleNext(index) { + if (index >= this.ipList.length - 1) { + this.log("Searching blebox devices finished!"); + if (this.startNextScanDelayInMin) { //if defined and different than 0 + var startNextScanDelayInMs = this.startNextScanDelayInMin * 60 * 1000; + setTimeout(this.startSearching.bind(this), startNextScanDelayInMs); + } + } + }; + + createAndAddAccessoryWrapper(accessoryWrapperFactory, deviceInfo, stateInfo) { + if (deviceInfo && stateInfo) { + var uuid = this.homebridge.hap.uuid.generate(accessoryWrapperFactory.type + deviceInfo.ip); + var accessory = new this.homebridge.platformAccessory(deviceInfo.deviceName, uuid); + var accessoryWrapper = accessoryWrapperFactory.create(accessory, this.log, this.api, deviceInfo, stateInfo); + this.addAccessoryWrapper(accessoryWrapper, true); + } + }; + + checkSpecificStateAndAddAccessory(deviceInfo) { + var ipAddress = deviceInfo.ip; + var self = this; + var deviceType = deviceInfo.type.toLowerCase(); + var accessoryWrapperFactory = bleBoxAccessoryWrapperFactories[deviceType]; + if (accessoryWrapperFactory) { + communication.send(accessoryWrapperFactory.checkStateCommand, ipAddress, { + onSuccess: function (stateInfo) { + self.createAndAddAccessoryWrapper(accessoryWrapperFactory, deviceInfo, stateInfo); } - }); - break; - default: - this.log("Unknown device type: %s", deviceInfo.type); - break; - } - -}; - -BleBoxPlatform.prototype.addAccessoryWrapper = function (accessoryWrapper, register) { - if (accessoryWrapper && this.accessoriesWrapperList.length < 98) { // we cannot have more than 98 devices - this.accessoriesWrapperList.push(accessoryWrapper); - this.accessoriesWrapperObj[accessoryWrapper.deviceId] = accessoryWrapper; - if (register) { - this.api.registerPlatformAccessories("homebridge-blebox", "BleBoxPlatform", [accessoryWrapper.getAccessory()]); + }) + } else { + this.log("Unknown device type: %s", deviceType); } - this.log("Added accessory! Now we have: %s", this.accessoriesWrapperList.length); - } -}; + }; + + addAccessoryWrapper(accessoryWrapper, register) { + if (accessoryWrapper && this.accessoriesWrapperList.length < 98) { // we cannot have more than 98 devices + this.accessoriesWrapperList.push(accessoryWrapper); + this.accessoriesWrapperObj[accessoryWrapper.getAccessory().context.blebox.device.id] = accessoryWrapper; + if (register) { + this.api.registerPlatformAccessories("homebridge-blebox", "BleBoxPlatform", [accessoryWrapper.getAccessory()]); + this.log("Added new accessory %s!", accessoryWrapper.type); + } else { + this.log("Restored accessory %s!", accessoryWrapper.type); + } + this.log("Now we have: %s", this.accessoriesWrapperList.length); + } + }; +} -BleBoxPlatform.prototype.accessories = function (callback) { - var accessoriesList = []; - for (var i = 0; i < this.accessoriesWrapperList.length; i++) { - accessoriesList.push(this.accessoriesWrapperList[i].getAccessory()); - } - callback(accessoriesList); -}; +module.exports = BleBoxPlatform; \ No newline at end of file