diff --git a/APPSTORE.md b/APPSTORE.md index 09a27d8..a663427 100644 --- a/APPSTORE.md +++ b/APPSTORE.md @@ -12,6 +12,7 @@ Below is a list of supported devices and devices. Post a comment in the [support * Xiamomi Humidifier (tested) * Xiaomi Single Power Plug and Power Strip WiFi version (tested) * Xiaomi PM2.5 Air Monitor (tested) +* Xiaomi Gateway Light - alarm not supported yet and subdevices are supported directly with the Xiaomi ZigBee app (tested) ## Support topic For support please use the official support topic on the forum [here](https://forum.athom.com/discussion/3295/). @@ -46,6 +47,11 @@ For Homey to be able to communicate with devices over the miIO protocol a unique * Default flow cards for on/off, measure power and meter power capabilities class ## Changelog +### 2018-03-15 -- v2.6.2 +* NEW: add support for the Xiaomi Gateway Light - alarm not supported yet and subdevices are supported directly with the Xiaomi ZigBee app +* FIX: fix for pairing wizard Philips Light Bulbs +* FIX: small fix in Yeelights driver + ### 2018-03-04 -- v2.6.1 * CHANGE: updated device class of Mi Power Plug and Mi Power Strip to "socket" (this require re-pairing of these devices) * FIX: solved small device naming issue in pair wizard diff --git a/app.json b/app.json index 89f9a0d..dab47f3 100644 --- a/app.json +++ b/app.json @@ -6,10 +6,10 @@ "nl": "Xiaomi Mi Home" }, "tags": { - "en": [ "Xiaomi", "Mi", "Mi Home", "miio", "vacuumcleaner", "robot", "yeelight", "yeelights", "purifier", "humidifier", "philips", "eyecare", "powerplug" ], - "nl": [ "Xiaomi", "Mi", "Mi home", "miio", "stofzuiger", "robot", "yeelight", "yeelights", "luchtreiniger", "luchtbevochtiger", "philips", "eyecare", "powerplug" ] + "en": [ "Xiaomi", "Mi", "Mi Home", "miio", "vacuumcleaner", "robot", "yeelight", "yeelights", "purifier", "humidifier", "philips", "eyecare", "powerplug", "gateway" ], + "nl": [ "Xiaomi", "Mi", "Mi home", "miio", "stofzuiger", "robot", "yeelight", "yeelights", "luchtreiniger", "luchtbevochtiger", "philips", "eyecare", "powerplug", "gateway" ] }, - "version": "2.6.1", + "version": "2.6.2", "compatibility": "1.x >=1.5.0", "author": { "name": "Jelger Haanstra", @@ -663,6 +663,92 @@ ] } ] + }, + { + "id": "gateway", + "name": { + "en": "Gateway", + "nl": "Gateway" + }, + "images": { + "large": "drivers/gateway/assets/images/large.jpg", + "small": "drivers/gateway/assets/images/small.jpg" + }, + "class": "other", + "capabilities": [ + "onoff", + "dim", + "light_hue", + "light_saturation", + "measure_luminance" + ], + "mobile": { + "components": [ + { + "id": "icon", + "capabilities": [ "onoff" ] + }, + { + "id": "sensor", + "capabilities": [ "measure_luminance" ] + }, + { + "id": "slider", + "capabilities": [ "dim" ] + }, + { + "id": "color", + "capabilities": [ "light_hue", "light_saturation" ] + } + ] + }, + "pair": [ + { + "id": "start" + } + ], + "settings": [ + { + "type": "group", + "label": { + "en": "Gateway Settings", + "nl": "Gateway Instellingen" + }, + "children": [ + { + "id": "address", + "type": "text", + "value": "0.0.0.0", + "label": { + "en": "IP Address", + "nl": "IP Adres" + } + }, + { + "id": "token", + "type": "text", + "value": "", + "label": { + "en": "Gateway Token", + "nl": "Gateway Token" + } + }, + { + "id": "polling", + "type": "number", + "value": 60, + "attr": { + "min": 5, + "max": 3600 + }, + "label": { + "en": "Gateway Polling", + "nl": "Gateway Polling" + } + } + ] + } + ] } ], "flow": { diff --git a/drivers/air-monitor/pair/start.html b/drivers/air-monitor/pair/start.html index b9a98c7..9146770 100644 --- a/drivers/air-monitor/pair/start.html +++ b/drivers/air-monitor/pair/start.html @@ -11,7 +11,7 @@ var inputaddress = $('#address').val(); var inputtoken = $('#token').val(); - if( inputaddress != '' && inputtoken != '') { + if (inputaddress != '' && inputtoken != '') { var device_data = { address: inputaddress, token: inputtoken @@ -132,7 +132,7 @@
- +
diff --git a/drivers/gateway/assets/icon.svg b/drivers/gateway/assets/icon.svg new file mode 100644 index 0000000..75b8176 --- /dev/null +++ b/drivers/gateway/assets/icon.svg @@ -0,0 +1,1618 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/gateway/assets/images/large.jpg b/drivers/gateway/assets/images/large.jpg new file mode 100644 index 0000000..abe26bc Binary files /dev/null and b/drivers/gateway/assets/images/large.jpg differ diff --git a/drivers/gateway/assets/images/small.jpg b/drivers/gateway/assets/images/small.jpg new file mode 100644 index 0000000..93fe9d2 Binary files /dev/null and b/drivers/gateway/assets/images/small.jpg differ diff --git a/drivers/gateway/device.js b/drivers/gateway/device.js new file mode 100644 index 0000000..ca6b810 --- /dev/null +++ b/drivers/gateway/device.js @@ -0,0 +1,125 @@ +'use strict'; + +const Homey = require('homey'); +const util = require('/lib/util.js'); +const miio = require('miio'); +const tinycolor = require("tinycolor2"); + +class GatewayDevice extends Homey.Device { + + onInit() { + this.createDevice(); + + this.registerCapabilityListener('onoff', this.onCapabilityOnoff.bind(this)); + this.registerCapabilityListener('dim', this.onCapabilityDim.bind(this)); + this.registerMultipleCapabilityListener(['light_hue', 'light_saturation'], this.onCapabilityHueSaturation.bind(this), 500); + } + + onDeleted() { + clearInterval(this.pollingInterval); + this.miio.destroy(); + } + + // LISTENERS FOR UPDATING CAPABILITIES + onCapabilityOnoff(value, opts, callback) { + this.miio.light.setPower(value) + .then(result => { callback(null, value) }) + .catch(error => { callback(error, false) }); + } + + onCapabilityDim(value, opts, callback) { + var brightness = value * 100; + this.miio.light.setBrightness(brightness) + .then(result => { callback(null, value) }) + .catch(error => { callback(error, false) }); + } + + onCapabilityHueSaturation(valueObj, optsObj) { + if (typeof valueObj.light_hue !== 'undefined') { + var hue_value = valueObj.light_hue; + } else { + var hue_value = this.getCapabilityValue('light_hue'); + } + + if (typeof valueObj.light_saturation !== 'undefined') { + var saturation_value = valueObj.light_saturation; + } else { + var saturation_value = this.getCapabilityValue('light_saturation'); + } + + var hue = hue_value * 359; + var saturation = saturation_value * 100; + var dim = this.getCapabilityValue('dim') * 100 + var colorUpdate = tinycolor({ h: Math.round(hue), s: Math.round(saturation), v: dim }); + this.miio.light.color(colorUpdate.toRgbString()) + + return Promise.resolve(); + } + + // HELPER FUNCTIONS + createDevice() { + miio.device({ + address: this.getSetting('address'), + token: this.getSetting('token') + }).then(miiodevice => { + this.miio = miiodevice; + this.miio.light = miiodevice.child('light'); + + miiodevice.on('illuminanceChanged', illuminance => { + if (this.getCapabilityValue('measure_luminance') != illuminance.value) { + this.setCapabilityValue('measure_luminance', illuminance.value); + } + }); + + this.miio.light.on('colorChanged', c => { + var colorChanged = tinycolor({r: c.rgb.red, g: c.rgb.green, b: c.rgb.blue}); + var hsv = colorChanged.toHsv(); + var hue = Math.round(hsv.h) / 359; + var saturation = Math.round(hsv.s); + + if (this.getCapabilityValue('light_hue') != hue) { + this.setCapabilityValue('light_hue', hue); + } + + if (this.getCapabilityValue('light_saturation') != saturation) { + this.setCapabilityValue('light_saturation', saturation); + } + }); + + var interval = this.getSetting('polling') || 60; + this.pollDevice(interval); + }).catch(function (error) { + return reject(error); + }); + } + + pollDevice(interval) { + clearInterval(this.pollingInterval); + + this.pollingInterval = setInterval(() => { + const getData = async () => { + try { + const power = await this.miio.light.power(); + const brightness = await this.miio.light.brightness() + + if (this.getCapabilityValue('onoff') != power) { + this.setCapabilityValue('onoff', power); + } + var dim = brightness / 100; + if (this.getCapabilityValue('dim') != dim) { + this.setCapabilityValue('dim', dim); + } + if (!this.getAvailable()) { + this.setAvailable(); + } + } catch (error) { + this.setUnavailable(Homey.__('unreachable')); + this.log(error); + } + } + getData(); + }, 1000 * interval); + } +} + +module.exports = GatewayDevice; diff --git a/drivers/gateway/driver.js b/drivers/gateway/driver.js new file mode 100644 index 0000000..14af5df --- /dev/null +++ b/drivers/gateway/driver.js @@ -0,0 +1,36 @@ +"use strict"; + +const Homey = require('homey'); +const miio = require('miio'); + +class GatewayDriver extends Homey.Driver { + + onPair(socket) { + socket.on('testConnection', function(data, callback) { + miio.device({ + address: data.address, + token: data.token + }).then(device => { + const getData = async () => { + try { + const lightLevel = await device.illuminance(); + + let result = { + lux: lightLevel.lux + } + + callback(null, result); + } catch (error) { + callback(error, null); + } + } + getData(); + }).catch(function (error) { + callback(error, null); + }); + }); + } + +} + +module.exports = GatewayDriver; diff --git a/drivers/gateway/pair/start.html b/drivers/gateway/pair/start.html new file mode 100644 index 0000000..bbcad7a --- /dev/null +++ b/drivers/gateway/pair/start.html @@ -0,0 +1,146 @@ + + + + +

Enter the details of your Mi Home device.

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+ +
+

Connection test successfull, you can now connect the device.

+

Mi Home device added succesfully.

+

+
diff --git a/drivers/philips-bulb/pair/start.html b/drivers/philips-bulb/pair/start.html index 3195e1f..ef2f8d7 100644 --- a/drivers/philips-bulb/pair/start.html +++ b/drivers/philips-bulb/pair/start.html @@ -11,7 +11,7 @@ var inputaddress = $('#address').val(); var inputtoken = $('#token').val(); - if( inputaddress != '' && inputtoken != '') { + if (inputaddress != '' && inputtoken != '') { var device_data = { address: inputaddress, token: inputtoken @@ -33,7 +33,6 @@ $('.mi-device-error-msg').html( __('pair.unknownerror') ); } }); - }); } else { $('.mi-device-error').show(); diff --git a/drivers/philips-eyecare/pair/start.html b/drivers/philips-eyecare/pair/start.html index caa081b..eeb5c6c 100644 --- a/drivers/philips-eyecare/pair/start.html +++ b/drivers/philips-eyecare/pair/start.html @@ -11,7 +11,7 @@ var inputaddress = $('#address').val(); var inputtoken = $('#token').val(); - if( inputaddress != '' && inputtoken != '') { + if (inputaddress != '' && inputtoken != '') { var device_data = { address: inputaddress, token: inputtoken @@ -35,7 +35,6 @@ $('.mi-device-error-msg').html( __('pair.unknownerror') ); } }); - }); } else { $('.mi-device-error').show(); diff --git a/drivers/yeelights/device.js b/drivers/yeelights/device.js index 24b7826..76720ed 100644 --- a/drivers/yeelights/device.js +++ b/drivers/yeelights/device.js @@ -257,14 +257,16 @@ class YeelightDevice extends Homey.Device { /* send commands to devices using their socket connection */ sendCommand(id, command) { - if (yeelights[id].connected === false) { + if (yeelights[id].connected === false && yeelights[id].socket !== null) { yeelights[id].socket.emit('error', new Error('Connection to device broken')); } else { yeelights[id].socket.write(command + '\r\n'); if (yeelights[id].timeout === null) { yeelights[id].timeout = setTimeout(() => { - yeelights[id].socket.emit('error', new Error('Error sending command')); + if (yeelights[id].connected === true && yeelights[id].socket !== null) { + yeelights[id].socket.emit('error', new Error('Error sending command')); + } }, 3000); } } diff --git a/locales/en.json b/locales/en.json index 80d7af7..11aa6c0 100644 --- a/locales/en.json +++ b/locales/en.json @@ -7,12 +7,14 @@ "humidifier": "Humidifier", "philipsbulb": "Philips Light Bulb", "philipseyecare": "Philips Eyecare Lamp", + "gateway": "Xiaomi Gateway", "airmonitor": "PM2.5 Air Monitor", "powerplug": "Mi Power Plug", "powerstrip": "Mi Power Strip", "powered": "Powered:", "mode": "Mode:", "dim": "Dim Level:", + "lux": "Lux:", "temperature": "Temperature:", "colortemperature": "Color Temperature:", "load": "Current Load In Watt:", diff --git a/locales/nl.json b/locales/nl.json index 3ad9986..82871e9 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -7,12 +7,14 @@ "humidifier": "Luchtbevochtiger", "philipsbulb": "Philips Light Bulb", "philipseyecare": "Philips Eyecare Lamp", + "gateway": "Xiaomi Gateway", "airmonitor": "PM2.5 Air Monitor", "powerplug": "Mi Power Plug", "powerstrip": "Mi Power Strip", "powered": "Aangezet:", "mode": "Modus:", "dim": "Dim niveau:", + "lux": "Lux:", "temperature": "Temperatuur:", "colortemperature": "Kleurtemperatuur:", "load": "Huidige gebruik in Watt:", diff --git a/package.json b/package.json index 49dda2b..99311c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.xiaomi-miio", - "version": "2.6.1", + "version": "2.6.2", "description": "Xiaomi Mi Home", "main": "app.js", "dependencies": {