From 69f82ceb94e026cff0a92a2ad91f69c55afe397c Mon Sep 17 00:00:00 2001 From: Max Nowack Date: Mon, 9 Oct 2017 22:01:37 +0200 Subject: [PATCH 1/4] update dependencies --- package.json | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index def2488..080369d 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,7 @@ }, "babel": { "presets": [ - "es2015", - "stage-0" + "env" ], "plugins": [ "transform-class-properties" @@ -59,24 +58,23 @@ } }, "dependencies": { - "babel-core": "6.18.2", - "dot-prop": "^4.1.0", - "eq3ble": "1.0.0", "is-mac": "1.0.1", - "mqtt": "1.12.0", "p-timeout": "1.0.0" + "babel-core": "6.26.0", + "dot-prop": "^4.2.0", + "eq3ble": "2.0.0-beta.0", + "mqtt": "2.13.0" }, "devDependencies": { - "babel-cli": "6.18.0", - "babel-eslint": "7.1.0", - "babel-plugin-transform-class-properties": "6.18.0", - "babel-preset-es2015": "6.18.0", - "babel-preset-stage-0": "6.16.0", - "babel-watch": "2.0.3", - "eslint": "3.9.1", - "eslint-config-airbnb-base": "10.0.1", - "eslint-plugin-babel": "3.3.0", - "eslint-plugin-import": "2.2.0", - "homebridge": "0.4.8" + "babel-cli": "6.26.0", + "babel-eslint": "8.0.1", + "babel-plugin-transform-class-properties": "6.24.1", + "babel-preset-env": "^1.6.0", + "babel-watch": "2.0.7", + "eslint": "4.8.0", + "eslint-config-airbnb-base": "12.0.2", + "eslint-plugin-babel": "4.1.2", + "eslint-plugin-import": "2.7.0", + "homebridge": "0.4.28" } } From 4b585a465070786a054cf11d0c6a42f9da7d34b6 Mon Sep 17 00:00:00 2001 From: Max Nowack Date: Mon, 9 Oct 2017 22:02:08 +0200 Subject: [PATCH 2/4] add test script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 080369d..3856be6 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "start": "NODE=production node ./dist/index", "dev": "npm run build && ./node_modules/.bin/homebridge -D -U ./homebridge-config -P ./", "lint": "./node_modules/.bin/eslint src", + "test": "npm run lint", "build": "./node_modules/.bin/babel src -d dist" }, "author": "Max Nowack ", From 256b27d53554b44cd37bdcf44baa6a49ee15431d Mon Sep 17 00:00:00 2001 From: Max Nowack Date: Tue, 10 Oct 2017 00:55:35 +0200 Subject: [PATCH 3/4] refactoring --- .travis.yml | 4 ++ README.md | 53 +--------------- package.json | 19 +----- src/Accessory.js | 154 +++++++++++++++++++++++++++++++++++++++++++++ src/MQTTClient.js | 37 ----------- src/Platform.js | 41 ++++++++++++ src/Thermostat.js | 118 ---------------------------------- src/callbackify.js | 9 --- src/constants.js | 8 +-- src/index.js | 127 ++----------------------------------- src/parseInfo.js | 33 +++++----- 11 files changed, 232 insertions(+), 371 deletions(-) create mode 100644 src/Accessory.js delete mode 100644 src/MQTTClient.js create mode 100644 src/Platform.js delete mode 100644 src/Thermostat.js delete mode 100644 src/callbackify.js diff --git a/.travis.yml b/.travis.yml index 2197832..ee7148b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,7 @@ language: node_js node_js: - "node" +addons: + apt: + packages: + - libavahi-compat-libdnssd-dev diff --git a/README.md b/README.md index 4764ede..4987488 100644 --- a/README.md +++ b/README.md @@ -13,60 +13,13 @@ It's possible to report MQTT topic's messages as current temperature of this the "pin": "031-45-154" }, - "accessories": [{ - "accessory": "EQ3-Thermostat", - "name": "Thermostat", - "address": "00:1a:22:07:48:77" + "platforms": [{ + "platform": "EQ3BLE", + "disableBoostSwitch": false }] } ```` -## Options - -You can configure the homebridge integration for the thermostat with the following options - -| Option | Default value | Description | -| --- | --- | --- | -| `address` *(required)* | | Address of the thermostat | -| `discoverTimeout` | `60000` | time in milliseconds before a timeout error will be triggered | -| `connectionTimeout` | `10000` | time in milliseconds before homebridge will disconnect from the device after last action | -| `disableBoostSwitch` | `false` | if set to true, the boost switch won't be published from homebridge | -| `currentTemperature` | | MQTT configuration for current temperature | - -### External current temperature configuration options - -| Option | Description | -| --- | --- | -| `url` *(required)* | MQTT URL | -| `topic` *(required)* | MQTT Topic name | -| `username` | Username for accessing MQTT server | -| `password` | Password for accessing MQTT server | - -## Usage with external current temperature sensor -````json -{ - "bridge": { - "name": "Homebridge", - "username": "CC:22:3D:E3:CE:30", - "port": 51826, - "pin": "031-45-154" - }, - - "accessories": [{ - "accessory": "EQ3-Thermostat", - "name": "Bedroom Thermostat", - "address": "00:1a:22:07:48:77", - "currentTemperature": { - "url": "mqtt://localhost", - "topic": "/home/sensors/bedroom/temperature", - "username": "sensors", - "password": "Sensors!" - } - }] -} -```` - - ## License Licensed under GPLv3 license. Copyright (c) 2015 Max Nowack diff --git a/package.json b/package.json index 3856be6..ef27ede 100644 --- a/package.json +++ b/package.json @@ -44,27 +44,12 @@ "extends": [ "airbnb-base" ], - "parser": "babel-eslint", - "rules": { - "semi": [ - 2, - "never" - ], - "no-param-reassign": [ - 2, - { - "props": false - } - ] - } + "parser": "babel-eslint" }, "dependencies": { - "is-mac": "1.0.1", - "p-timeout": "1.0.0" "babel-core": "6.26.0", "dot-prop": "^4.2.0", - "eq3ble": "2.0.0-beta.0", - "mqtt": "2.13.0" + "eq3ble": "^2.0.0-beta.1" }, "devDependencies": { "babel-cli": "6.26.0", diff --git a/src/Accessory.js b/src/Accessory.js new file mode 100644 index 0000000..e8e8201 --- /dev/null +++ b/src/Accessory.js @@ -0,0 +1,154 @@ +import parseInfo from './parseInfo'; + +let Characteristic; +let Service; + +export function setup(homebridge) { + Characteristic = homebridge.hap.Characteristic; // eslint-disable-line prefer-destructuring + Service = homebridge.hap.Service; // eslint-disable-line prefer-destructuring +} + +export default class EQ3BLEAccessory { + constructor(log, device, disableBoostSwitch) { + this.state = {}; + this.log = log; + this.name = device.address; + this.address = device.address; + this.disableBoostSwitch = disableBoostSwitch; + this.temperatureDisplayUnits = Characteristic.TemperatureDisplayUnits.CELSIUS; + + this.thermostat = device; + + this.thermostatService = new Service.Thermostat(this.name); + this.informationService = new Service.AccessoryInformation(); + this.boostService = new Service.Switch(`${this.name} boost mode`); + + this.currentTemperature = null; + + this.setupServices(); + } + + getServices() { + const services = [ + this.informationService, + this.thermostatService, + ]; + if (!this.disableBoostSwitch) services.push(this.boostService); + return services; + } + + setBoost(boost, callback) { + if (boost === this.state.boost) { + callback(null); + return; + } + this.state.boost = boost; + this.log('set boost', this.name, boost); + this.thermostat.setBoost(boost) + .then(() => { + this.thermostat.getInfo(); + callback(null); + }) + .catch(err => callback(err)); + } + setThermostatState(state) { + switch (state) { + case Characteristic.TargetHeatingCoolingState.OFF: return this.thermostat.turnOff(); + case Characteristic.TargetHeatingCoolingState.HEAT: return this.thermostat.turnOn(); + case Characteristic.TargetHeatingCoolingState.AUTO: return this.thermostat.automaticMode(); + case Characteristic.TargetHeatingCoolingState.COOL: return this.thermostat.manualMode(); + default: throw new Error('Unsupported mode'); + } + } + setTargetHeatingCoolingState(state, callback) { + if (state === this.state.targetHeatingCoolingState) { + callback(null); + return; + } + this.state.targetHeatingCoolingState = state; + this.log('set state', this.name, state); + this.setThermostatState(state) + .then(() => { + this.thermostat.getInfo(); + callback(null); + }) + .catch(err => callback(err)); + } + setTargetTemperature(temperature, callback) { + if (temperature === this.state.targetTemperature) { + callback(null); + return; + } + this.state.targetTemperature = temperature; + this.log('set temperature', this.name, temperature); + this.thermostat.setTemperature(temperature) + .then(() => { + this.thermostat.getInfo(); + callback(null); + }) + .catch(err => callback(err)); + } + + setupServices() { + this.boostService.setCharacteristic(Characteristic.Name, `${this.name} Boost Mode`); + + this.informationService + .setCharacteristic(Characteristic.Manufacturer, 'eq-3') + .setCharacteristic(Characteristic.Model, 'CC-RT-BLE'); + + const boostOn = this.boostService.getCharacteristic(Characteristic.On); + const currHeatingCoolingState = this.thermostatService + .getCharacteristic(Characteristic.CurrentHeatingCoolingState); + const targetHeatingCoolingState = this.thermostatService + .getCharacteristic(Characteristic.TargetHeatingCoolingState); + const currentTemperature = this.thermostatService + .getCharacteristic(Characteristic.CurrentTemperature) + .setProps({ + minValue: -100, + maxValue: 100, + }); + const targetTemperature = this.thermostatService + .getCharacteristic(Characteristic.TargetTemperature) + .setProps({ + minValue: 4.5, + maxValue: 30, + }); + const heatingThresholdTemperature = this.thermostatService + .getCharacteristic(Characteristic.HeatingThresholdTemperature); + + boostOn + .on('get', callback => callback(null, this.state.boost)) + .on('set', this.setBoost.bind(this)); + + currHeatingCoolingState + .on('get', callback => callback(null, this.state.currentHeatingCoolingState)); + + targetHeatingCoolingState + .on('get', callback => callback(null, this.state.targetHeatingCoolingState)) + .on('set', this.setTargetHeatingCoolingState.bind(this)); + + currentTemperature + .on('get', callback => callback(null, this.state.targetTemperature)); + + targetTemperature + .on('get', callback => callback(null, this.state.targetTemperature)) + .on('set', this.setTargetTemperature.bind(this)); + + heatingThresholdTemperature + .on('get', callback => callback(null, this.state.targetTemperature)); + + this.thermostat.on('update', (data) => { + this.log('update', this.thermostat.address, data); + this.state = parseInfo(data, Characteristic); + boostOn.setValue(this.state.boost); + currHeatingCoolingState.setValue(this.state.currentHeatingCoolingState); + targetHeatingCoolingState.setValue(this.state.targetHeatingCoolingState); + targetTemperature.setValue(this.state.targetTemperature); + heatingThresholdTemperature.setValue(this.state.targetTemperature); + currentTemperature.setValue(this.state.targetTemperature); + }); + + this.thermostat.getInfo(); + setInterval(() => this.thermostat.getInfo(), 10 * 60 * 1000); + } +} diff --git a/src/MQTTClient.js b/src/MQTTClient.js deleted file mode 100644 index f6af120..0000000 --- a/src/MQTTClient.js +++ /dev/null @@ -1,37 +0,0 @@ -import EventEmitter from 'events' -import mqtt from 'mqtt' - -export default class MQTTClient extends EventEmitter { - constructor(config) { - super() - this.mqtt = mqtt.connect(config.url, { - keepalive: 10, - clientId: 'mqttjs_'.concat(Math.random().toString(16).substr(2, 8)), - protocolId: 'MQTT', - protocolVersion: 4, - clean: true, - reconnectPeriod: 1000, - connectTimeout: 30 * 1000, - will: { - topic: 'WillMsg', - payload: 'Connection Closed abnormally..!', - qos: 0, - retain: false, - }, - username: config.username, - password: config.password, - rejectUnauthorized: false, - }) - - this.mqtt.on('connect', () => { - this.emit('connected') - this.mqtt.subscribe(config.topic) - }) - - this.mqttClient.on('message', (topic, message) => { - const mqttData = JSON.parse(message) - if (mqttData === null) return - this.emit('change', parseFloat(mqttData)) - }) - } -} diff --git a/src/Platform.js b/src/Platform.js new file mode 100644 index 0000000..e353def --- /dev/null +++ b/src/Platform.js @@ -0,0 +1,41 @@ +import { discoverAll } from 'eq3ble'; +import EQ3BLEAccessory from './Accessory'; + +export { setup } from './Accessory'; + +export default class EQ3BLEPlatform { + constructor(log, config, api) { + this.log = log; + this.config = config; + this.disableBoostSwitch = config.disableBoostSwitch; + this.api = api; + + // if (!this.config) { + // this.log.warn('Ignoring EQ3BLE Platform setup because it is not configured'); + // this.disabled = true; + // } + } + + accessories(callback) { + const devices = []; + + if (this.disabled) { + callback(devices); + return; + } + + this.log('discovery started'); + const helper = discoverAll((device) => { + devices.push(device); + this.log(`discovered eq3ble device with address ${device.address}`); + }, 30 * 1000); + helper.on('stopScanning', () => { + this.log(`found ${devices.length} devices`); + callback(devices.map(device => this.createAccessory(device))); + }); + } + + createAccessory(device) { + return new EQ3BLEAccessory(this.log, device, this.disableBoostSwitch); + } +} diff --git a/src/Thermostat.js b/src/Thermostat.js deleted file mode 100644 index 7f6e540..0000000 --- a/src/Thermostat.js +++ /dev/null @@ -1,118 +0,0 @@ -import EQ3BLE from 'eq3ble' -import pTimeout from 'p-timeout' -import EventEmitter from 'events' -import validateAddress from 'is-mac' -import { TargetHeatingCoolingState } from './constants' -import parseInfo from './parseInfo' - -export default class Thermostat extends EventEmitter { - constructor({ address, discoverTimeout, connectionTimeout }) { - super() - this.address = address.toLowerCase() - this.discoverTimeout = discoverTimeout - this.connectionTimeout = connectionTimeout - - if (!validateAddress(this.address)) { - throw new Error(`invalid address "${this.address}"`) - } - } - - discover() { - return pTimeout(new Promise((resolve, reject) => { - this.emit('discovering') - EQ3BLE.discoverByAddress(this.address, (device) => { - this.device = device - this.emit('discovered', device) - resolve() - }, (err) => { - this.discoverPromise = null - this.emit('undiscoverable', err) - reject(err) - }) - }), this.discoverTimeout) - } - - connect() { - this.discoverPromise = this.discoverPromise || this.discover() - return this.discoverPromise.then(() => { - this.connectionPromise = this.connectionPromise || pTimeout(new Promise((resolve, reject) => { - clearTimeout(this.timeout) - if (this.isConnected) { - this.connectionPromise = null - resolve() - return - } - this.emit('connecting') - this.device.connectAndSetup().then(() => { - this.emit('connected', this.device) - this.connectionPromise = null - this.isConnected = true - this.startDisconnectTimeout() - resolve() - }, (err) => { - this.emit('connectionError', err) - this.connectionPromise = null - this.isConnected = false - this.discoverPromise = null - reject(err) - }) - }), this.connectionTimeout) - return this.connectionPromise - }) - } - - disconnect() { - if (this.device) this.device.disconnect() - this.isConnected = false - this.connectionPromise = null - this.emit('disconnected') - } - startDisconnectTimeout() { - clearTimeout(this.timeout) - this.timeout = setTimeout(this.disconnect.bind(this), this.connectionTimeout) - } - getInfo() { - this.emit('gettingInfo') - return this.connect() - .then(() => this.device.getInfo()) - .then((raw) => { - const info = parseInfo(raw) - this.emit('info', info) - return info - }) - } - getCachedInfo() { - return new Promise((resolve, reject) => { - if (this.info) { - resolve(this.info) - return - } - this.infoPromise = this.infoPromise || this.getInfo() - this.infoPromise.then((info) => { - this.info = info - this.infoPromise = null - resolve(this.info) - setTimeout(() => { - this.info = null - }, 250) - }, reject) - }) - } - setBoost(boost) { - return this.connect().then(() => this.device.setBoost(boost)) - } - setTargetTemperature(temperature) { - return this.connect().then(() => this.device.setTemperature(temperature)) - } - setTargetHeatingCoolingState(state) { - return this.connect().then(() => { - switch (state) { - case TargetHeatingCoolingState.OFF: return this.device.turnOff() - case TargetHeatingCoolingState.HEAT: return this.device.turnOn() - case TargetHeatingCoolingState.AUTO: return this.device.automaticMode() - case TargetHeatingCoolingState.COOL: return this.device.manualMode() - default: throw new Error('Unsupported mode') - } - }) - } -} diff --git a/src/callbackify.js b/src/callbackify.js deleted file mode 100644 index ac1b491..0000000 --- a/src/callbackify.js +++ /dev/null @@ -1,9 +0,0 @@ -export default function callbackify(fn) { - return (callback, ...args) => { - fn(...args).then((...results) => { - callback(null, ...results) - }, (err) => { - callback(err) - }) - } -} diff --git a/src/constants.js b/src/constants.js index 842517f..b095209 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,17 +1,17 @@ -import dotProp from 'dot-prop' +import dotProp from 'dot-prop'; export const TargetHeatingCoolingState = { OFF: 'TargetHeatingCoolingState.OFF', HEAT: 'TargetHeatingCoolingState.HEAT', AUTO: 'TargetHeatingCoolingState.AUTO', COOL: 'TargetHeatingCoolingState.COOL', -} +}; export const CurrentHeatingCoolingState = { OFF: 'CurrentHeatingCoolingState.OFF', HEAT: 'CurrentHeatingCoolingState.HEAT', -} +}; export function map(Characteristic, value) { - return dotProp.get(Characteristic, value) + return dotProp.get(Characteristic, value); } diff --git a/src/index.js b/src/index.js index e92e2ce..5d6e47a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,123 +1,6 @@ -import Thermostat from './Thermostat' -import MQTTClient from './MQTTClient' -import callbackify from './callbackify' -import { map as mapValue } from './constants' +import EQ3BLEPlatform, { setup } from './Platform'; -function thermostatFactory({ Service, Characteristic }) { - return class EQ3Thermostat { - constructor(log, config) { - this.log = log - this.name = config.name - this.address = config.address.toLowerCase() - this.discoverTimeout = config.discoverTimeout || (60 * 1000) // 1 minute - this.connectionTimeout = config.connectionTimeout || (10 * 1000) // 10 seconds - this.disableBoostSwitch = false - this.temperatureDisplayUnits = Characteristic.TemperatureDisplayUnits.CELSIUS - - this.thermostat = new Thermostat({ - address: this.address, - discoverTimeout: this.discoverTimeout, - connectionTimeout: this.connectionTimeout, - }) - - this.thermostatService = new Service.Thermostat(this.name) - this.informationService = new Service.AccessoryInformation() - this.boostService = new Service.Switch(`${this.name} boost mode`) - - this.currentTemperature = null - if (config.currentTemperature) { - const mqttTemperature = new MQTTClient(config.currentTemperature) - mqttTemperature.on('change', (temperature) => { - this.thermostatService.setCharacteristic(Characteristic.CurrentTemperature, temperature) - this.currentTemperature = temperature - }) - } - - this.setupServices() - } - getServices() { - return [ - this.informationService, - this.thermostatService, - !this.disableBoostSwitch && this.boostService, - ] - } - setupServices() { - this.boostService.setCharacteristic(Characteristic.Name, 'Boost Mode') - - this.informationService - .setCharacteristic(Characteristic.Manufacturer, 'eq-3') - .setCharacteristic(Characteristic.Model, 'CC-RT-BLE') - - const boostOn = this.boostService.getCharacteristic(Characteristic.On) - const currentHeatingCoolingState = this.thermostatService - .getCharacteristic(Characteristic.CurrentHeatingCoolingState) - const targetHeatingCoolingState = this.thermostatService - .getCharacteristic(Characteristic.TargetHeatingCoolingState) - const currentTemperature = this.thermostatService - .getCharacteristic(Characteristic.CurrentTemperature) - .setProps({ - minValue: -100, - maxValue: 100, - }) - const targetTemperature = this.thermostatService - .getCharacteristic(Characteristic.TargetTemperature) - .setProps({ - minValue: 4.5, - maxValue: 30, - }) - const heatingThresholdTemperature = this.thermostatService - .getCharacteristic(Characteristic.HeatingThresholdTemperature) - - boostOn - .on('get', callbackify(() => this.getInfoValue('boost'))) - .on('set', callbackify(boost => this.thermostat.setBoost(boost))) - - currentHeatingCoolingState - .on('get', callbackify(() => this.getInfoValue('currentHeatingCoolingState'))) - - targetHeatingCoolingState - .on('get', callbackify(() => this.getInfoValue('targetHeatingCoolingState'))) - .on('set', callbackify(state => this.thermostat.setTargetHeatingCoolingState(state))) - - currentTemperature - .on('get', this.currentTemperature == null - ? callbackify(() => this.getInfoValue('targetTemperature')) - : callback => callback(null, this.currentTemperature)) - - targetTemperature - .on('get', callbackify(() => this.getInfoValue('targetTemperature'))) - .on('set', callbackify(temperature => this.thermostat.setTargetTemperature(temperature))) - - heatingThresholdTemperature - .on('get', callbackify(() => this.getInfoValue('targetTemperature'))) - - this.thermostat.on('info', (info) => { - boostOn.setValue(info.boost) - currentHeatingCoolingState.setValue(mapValue(Characteristic, - info.currentHeatingCoolingState)) - targetHeatingCoolingState.setValue(mapValue(Characteristic, - info.targetHeatingCoolingState)) - targetTemperature.setValue(info.targetTemperature) - heatingThresholdTemperature.setValue(info.targetTemperature) - - if (this.currentTemperature == null) { - currentTemperature.setValue(info.targetTemperature) - } - }) - } - getInfoValue(what) { - return this.thermostat.getCachedInfo().then(info => info[what]) - } - } -} - -module.exports = function register(homebridge) { - const Service = homebridge.hap.Service - const Characteristic = homebridge.hap.Characteristic - - homebridge.registerAccessory('homebridge-eq3ble', 'EQ3-Thermostat', thermostatFactory({ - Service, - Characteristic, - })) -} +module.exports = function setupEQ3BLEPlatform(homebridge) { + setup(homebridge); + homebridge.registerPlatform('homebridge-platform-eq3ble', 'EQ3BLE', EQ3BLEPlatform); +}; diff --git a/src/parseInfo.js b/src/parseInfo.js index c7ddf4e..d3242ab 100644 --- a/src/parseInfo.js +++ b/src/parseInfo.js @@ -1,20 +1,25 @@ -import { TargetHeatingCoolingState, CurrentHeatingCoolingState } from './constants' +import { TargetHeatingCoolingState, CurrentHeatingCoolingState, map } from './constants'; -export default function parseInfo({ status: { manual, boost }, valvePosition, targetTemperature }) { - const info = { boost } +function parseTargetState({ targetTemperature, manualMode, boost }) { + if (targetTemperature <= 4.5) return TargetHeatingCoolingState.OFF; + if (targetTemperature >= 30 || boost) return TargetHeatingCoolingState.HEAT; + if (manualMode) return TargetHeatingCoolingState.COOL; + return TargetHeatingCoolingState.AUTO; +} - info.currentHeatingCoolingState = valvePosition - ? CurrentHeatingCoolingState.HEAT - : CurrentHeatingCoolingState.OFF +export default function parseInfo(data, Characteristic) { + const { + boost, + valvePosition, + targetTemperature, + } = data; + const info = { boost, targetTemperature }; - info.targetHeatingCoolingState = (() => { - if (targetTemperature <= 4.5) return TargetHeatingCoolingState.OFF - if (targetTemperature >= 30 || boost) return TargetHeatingCoolingState.HEAT - if (manual) return TargetHeatingCoolingState.COOL - return TargetHeatingCoolingState.AUTO - })() + info.currentHeatingCoolingState = map(Characteristic, valvePosition + ? CurrentHeatingCoolingState.HEAT + : CurrentHeatingCoolingState.OFF); - info.targetTemperature = targetTemperature < 10 ? 10 : targetTemperature + info.targetHeatingCoolingState = map(Characteristic, parseTargetState(data)); - return info + return info; } From 178fb74d7d3e2f1f4b0b85c915c31c424d91dfb3 Mon Sep 17 00:00:00 2001 From: Max Nowack Date: Tue, 10 Oct 2017 11:17:15 +0200 Subject: [PATCH 4/4] v2.0.0-beta.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ef27ede..bd58876 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "homebridge-eq3ble", - "version": "1.3.2", - "description": "Homebridge plugin to control EQ3 bluetooth thermostats", + "version": "2.0.0-beta.0", + "description": "Homebridge platform to control EQ3 bluetooth thermostats", "main": "dist/index.js", "scripts": { "start": "NODE=production node ./dist/index",