diff --git a/APPSTORE.md b/APPSTORE.md index d7616ad..ac3ea0f 100644 --- a/APPSTORE.md +++ b/APPSTORE.md @@ -10,7 +10,8 @@ Below is a list of supported devices and devices. Post a comment in the [support * Xiaomi Robot Vacuum Cleaner (tested) * Xiaomi Air Purifiers 2, 2S and Pro (tested) * Xiamomi Humidifier (tested) -* Xiaomi Single Power Plug WiFi version (untested) +* Xiaomi Single Power Plug and Power Strip WiFi version (tested) +* Xiaomi PM2.5 Air Monitor (tested) ## Support topic For support please use the official support topic on the forum [here](https://forum.athom.com/discussion/3295/). @@ -45,17 +46,12 @@ 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-02-17 -- v2.5.3 +### 2018-02-18 -- v2.6.0 +* NEW: add support for PM2.5 sensor +* NEW: add basic support for WiFi power strips (power load and power consumed are not available for now) * UPDATE: update miio library to 0.15.6 which fixes: * Power and color temperature setting fixed for Philips Light Bulbs * Refreshing of vacuumcleaner state after starting and stopping cleaning * FIX: update vacuumcleaner state directly when using onoff capability * FIX: fix flow cards not showing (un)succesful execution status * FIX: possible fix for showing temperature for purifiers and humidifiers - -### 2018-02-07 -- v2.5.2 -* FIX: removed powerLoad and powerConsumed capability in WiFi plug driver as these capabilities are not available for this device - -### 2018-02-04 -- v2.5.1 -* UPDATE: updated the miio library to 0.15.5 and rewritten all device drivers -* UPDATE: reintroduced support for the Mi Robot Vacuum Cleaner (needs re-pairing the device if coming from an older version) diff --git a/app.json b/app.json index c377c61..06349ef 100644 --- a/app.json +++ b/app.json @@ -9,7 +9,7 @@ "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" ] }, - "version": "2.5.3", + "version": "2.6.0", "compatibility": "1.x >=1.5.0", "author": { "name": "Jelger Haanstra", @@ -234,6 +234,10 @@ { "id": "picker", "capabilities": [ "vacuumcleaner_state" ] + }, + { + "id": "toggle", + "capabilities": [ "onoff" ] } ] }, @@ -271,7 +275,11 @@ { "id": "polling", "type": "number", - "value": 60, + "value": 30, + "attr": { + "min": 30, + "max": 3600 + }, "label": { "en": "Mi Robot Polling", "nl": "Mi Robot Polling" @@ -308,12 +316,13 @@ "id": "sensor", "capabilities": [ "measure_temperature", - "measure_humidity" + "measure_humidity", + "measure_pm25" ] }, { - "id": "sensor", - "capabilities": [ "measure_pm25" ] + "id": "toggle", + "capabilities": [ "onoff" ] } ] }, @@ -389,11 +398,14 @@ }, { "id": "sensor", - "capabilities": [ "measure_temperature" ] + "capabilities": [ + "measure_temperature", + "measure_humidity" + ] }, { - "id": "sensor", - "capabilities": [ "measure_humidity" ] + "id": "toggle", + "capabilities": [ "onoff" ] } ] }, @@ -445,6 +457,86 @@ } ] }, + { + "id": "air-monitor", + "name": { + "en": "PM2.5 Air Monitor", + "nl": "PM2.5 Air Monitor" + }, + "images": { + "large": "drivers/air-monitor/assets/images/large.jpg", + "small": "drivers/air-monitor/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "onoff", + "measure_battery", + "measure_pm25" + ], + "mobile": { + "components": [ + { + "id": "icon", + "capabilities": [ "onoff" ] + }, + { + "id": "sensor", + "capabilities": [ "measure_pm25" ] + }, + { + "id": "toggle", + "capabilities": [ "onoff" ] + } + ] + }, + "pair": [ + { + "id": "start" + } + ], + "settings": [ + { + "type": "group", + "label": { + "en": "PM2.5 Air Monitor Settings", + "nl": "PM2.5 Air Monitor 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": "PM2.5 Air Monitor Token", + "nl": "PM2.5 Air Monitor Token" + } + }, + { + "id": "polling", + "type": "number", + "value": 60, + "attr": { + "min": 5, + "max": 3600 + }, + "label": { + "en": "PM2.5 Air Monitor Polling", + "nl": "PM2.5 Air Monitor Polling" + } + } + ] + } + ] + }, { "id": "mi-power-plug", "name": { @@ -506,6 +598,70 @@ ] } ] + }, + { + "id": "mi-power-strip", + "name": { + "en": "Mi Power Strip", + "nl": "Mi Power Strip" + }, + "images": { + "large": "drivers/mi-power-strip/assets/images/large.jpg", + "small": "drivers/mi-power-strip/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "onoff", + "measure_power", + "meter_power" + ], + "pair": [ + { + "id": "start" + } + ], + "settings": [ + { + "type": "group", + "label": { + "en": "Mi Power Strip Settings", + "nl": "Mi Power Strip 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": "Mi Power Strip Token", + "nl": "Mi Power Strip Token" + } + }, + { + "id": "polling", + "type": "number", + "value": 30, + "attr": { + "min": 5, + "max": 3600 + }, + "label": { + "en": "Mi Power Strip Polling", + "nl": "Mi Power Strip Polling" + } + } + ] + } + ] } ], "flow": { diff --git a/drivers/air-monitor/assets/icon.svg b/drivers/air-monitor/assets/icon.svg new file mode 100644 index 0000000..4423cb9 --- /dev/null +++ b/drivers/air-monitor/assets/icon.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/air-monitor/assets/images/large.jpg b/drivers/air-monitor/assets/images/large.jpg new file mode 100644 index 0000000..838674b Binary files /dev/null and b/drivers/air-monitor/assets/images/large.jpg differ diff --git a/drivers/air-monitor/assets/images/small.jpg b/drivers/air-monitor/assets/images/small.jpg new file mode 100644 index 0000000..9841390 Binary files /dev/null and b/drivers/air-monitor/assets/images/small.jpg differ diff --git a/drivers/air-monitor/device.js b/drivers/air-monitor/device.js new file mode 100644 index 0000000..c52f0b9 --- /dev/null +++ b/drivers/air-monitor/device.js @@ -0,0 +1,73 @@ +'use strict'; + +const Homey = require('homey'); +const miio = require('miio'); + +class MiAirMonitorDevice extends Homey.Device { + + onInit() { + this.createDevice(); + + this.registerCapabilityListener('onoff', this.onCapabilityOnoff.bind(this)); + } + + onDeleted() { + clearInterval(this.pollingInterval); + this.miio.destroy(); + } + + // LISTENERS FOR UPDATING CAPABILITIES + onCapabilityOnoff(value, opts, callback) { + this.miio.setPower(value) + .then(result => { callback(null, value) }) + .catch(error => { callback(error, false) }); + } + + // HELPER FUNCTIONS + createDevice() { + miio.device({ + address: this.getSetting('address'), + token: this.getSetting('token') + }).then(miiodevice => { + this.miio = miiodevice; + + 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.power(); + const battery = await this.miio.batteryLevel(); + const aqi = await this.miio.pm2_5(); + + if (this.getCapabilityValue('onoff') != power) { + this.setCapabilityValue('onoff', power); + } + if (this.getCapabilityValue('measure_battery') != battery) { + this.setCapabilityValue('measure_battery', battery); + } + if (this.getCapabilityValue('measure_pm25') != aqi) { + this.setCapabilityValue('measure_pm25', aqi); + } + if (!this.getAvailable()) { + this.setAvailable(); + } + } catch (error) { + this.setUnavailable(Homey.__('unreachable')); + this.log(error); + } + } + getData(); + }, 1000 * interval); + } +} + +module.exports = MiAirMonitorDevice; diff --git a/drivers/air-monitor/driver.js b/drivers/air-monitor/driver.js new file mode 100644 index 0000000..91ccecd --- /dev/null +++ b/drivers/air-monitor/driver.js @@ -0,0 +1,40 @@ +"use strict"; + +const Homey = require('homey'); +const miio = require('miio'); + +class MiAirMonitorDriver 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 power = await device.power(); + const battery = await device.batteryLevel(); + const aqi = await device.pm2_5(); + + let result = { + onoff: power, + battery: battery, + aqi: aqi + } + + callback(null, result); + } catch (error) { + callback(error, null); + } + } + getData(); + }).catch(function (error) { + callback(error, null); + }); + }); + } + +} + +module.exports = MiAirMonitorDriver; diff --git a/drivers/air-monitor/pair/start.html b/drivers/air-monitor/pair/start.html new file mode 100644 index 0000000..d00b032 --- /dev/null +++ b/drivers/air-monitor/pair/start.html @@ -0,0 +1,147 @@ + + + + +

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/mi-airpurifier/driver.js b/drivers/mi-airpurifier/driver.js index 11f6dcd..61791fd 100644 --- a/drivers/mi-airpurifier/driver.js +++ b/drivers/mi-airpurifier/driver.js @@ -14,7 +14,7 @@ class MiAirPurifierDriver extends Homey.Driver { const getData = async () => { try { const power = await device.power(); - const temp = await device.temperature() + const temp = await device.temperature(); const rh = await device.relativeHumidity(); const aqi = await device.pm2_5(); const mode = await device.mode(); diff --git a/drivers/mi-humidifier/driver.js b/drivers/mi-humidifier/driver.js index dffc70d..d58c21c 100644 --- a/drivers/mi-humidifier/driver.js +++ b/drivers/mi-humidifier/driver.js @@ -14,7 +14,7 @@ class MiHumidifierDriver extends Homey.Driver { const getData = async () => { try { const power = await device.power(); - const temp = await device.temperature() + const temp = await device.temperature(); const rh = await device.relativeHumidity(); const mode = await device.mode(); diff --git a/drivers/mi-power-plug/pair/start.html b/drivers/mi-power-plug/pair/start.html index d2ded15..79a778a 100644 --- a/drivers/mi-power-plug/pair/start.html +++ b/drivers/mi-power-plug/pair/start.html @@ -126,7 +126,7 @@
- +
diff --git a/drivers/mi-power-strip/assets/icon.svg b/drivers/mi-power-strip/assets/icon.svg new file mode 100644 index 0000000..99ee87f --- /dev/null +++ b/drivers/mi-power-strip/assets/icon.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/mi-power-strip/assets/images/large.jpg b/drivers/mi-power-strip/assets/images/large.jpg new file mode 100644 index 0000000..2b5dc36 Binary files /dev/null and b/drivers/mi-power-strip/assets/images/large.jpg differ diff --git a/drivers/mi-power-strip/assets/images/small.jpg b/drivers/mi-power-strip/assets/images/small.jpg new file mode 100644 index 0000000..20c3848 Binary files /dev/null and b/drivers/mi-power-strip/assets/images/small.jpg differ diff --git a/drivers/mi-power-strip/device.js b/drivers/mi-power-strip/device.js new file mode 100644 index 0000000..d1a888c --- /dev/null +++ b/drivers/mi-power-strip/device.js @@ -0,0 +1,74 @@ +'use strict'; + +const Homey = require('homey'); +const miio = require('miio'); + +class PowerStripDevice extends Homey.Device { + + onInit() { + this.createDevice(); + + this.registerCapabilityListener('onoff', this.onCapabilityOnoff.bind(this)); + } + + onDeleted() { + clearInterval(this.pollingInterval); + } + + // LISTENERS FOR UPDATING CAPABILITIES + onCapabilityOnoff(value, opts, callback) { + this.miio.setPower(value) + .then(result => { callback(null, value) }) + .catch(error => { callback(error, false) }); + } + + // HELPER FUNCTIONS + createDevice() { + miio.device({ + address: this.getSetting('address'), + token: this.getSetting('token') + }).then(miiodevice => { + this.miio = miiodevice; + + var interval = this.getSetting('polling') || 30; + this.pollDevice(interval); + }).catch(function (error) { + return reject(error); + }); + } + + pollDevice(interval) { + clearInterval(this.pollingInterval); + + this.pollingInterval = setInterval(() => { + const getData = async () => { + try { + // TODO: implement measure_power and meter_power capability + const power = await this.miio.power(); + const powerload = 0; + const powerconsumed = 0; + + if (this.getCapabilityValue('onoff') != power) { + this.setCapabilityValue('onoff', power); + } + if (this.getCapabilityValue('measure_power') != powerload) { + this.setCapabilityValue('measure_power', powerload); + } + if (this.getCapabilityValue('meter_power') != powerconsumed) { + this.setCapabilityValue('meter_power', powerconsumed); + } + if (!this.getAvailable()) { + this.setAvailable(); + } + } catch (error) { + this.setUnavailable(Homey.__('unreachable')); + this.log(error); + } + } + getData(); + }, 1000 * interval); + } + +} + +module.exports = PowerStripDevice; diff --git a/drivers/mi-power-strip/driver.js b/drivers/mi-power-strip/driver.js new file mode 100644 index 0000000..142f2ce --- /dev/null +++ b/drivers/mi-power-strip/driver.js @@ -0,0 +1,41 @@ +"use strict"; + +const Homey = require('homey'); +const miio = require('miio'); + +class PowerStripDriver 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 { + // TODO: implement measure_power and meter_power capability + const power = await device.power(); + const powerload = 0; + const powerconsumed = 0; + + let result = { + onoff: power, + powerload: powerload, + powerconsumed: powerconsumed + } + + callback(null, result); + } catch (error) { + callback(error, null); + } + } + getData(); + }).catch(function (error) { + callback(error, null); + }); + }); + } + +} + +module.exports = PowerStripDriver; diff --git a/drivers/mi-power-strip/pair/start.html b/drivers/mi-power-strip/pair/start.html new file mode 100644 index 0000000..c3ea72a --- /dev/null +++ b/drivers/mi-power-strip/pair/start.html @@ -0,0 +1,145 @@ + + + + +

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/mi-robot/pair/start.html b/drivers/mi-robot/pair/start.html index 8cfe137..fc62200 100644 --- a/drivers/mi-robot/pair/start.html +++ b/drivers/mi-robot/pair/start.html @@ -134,7 +134,7 @@
- +
diff --git a/locales/en.json b/locales/en.json index d4a966b..80d7af7 100644 --- a/locales/en.json +++ b/locales/en.json @@ -7,7 +7,9 @@ "humidifier": "Humidifier", "philipsbulb": "Philips Light Bulb", "philipseyecare": "Philips Eyecare Lamp", + "airmonitor": "PM2.5 Air Monitor", "powerplug": "Mi Power Plug", + "powerstrip": "Mi Power Strip", "powered": "Powered:", "mode": "Mode:", "dim": "Dim Level:", diff --git a/locales/nl.json b/locales/nl.json index 9fb8780..3ad9986 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -7,7 +7,9 @@ "humidifier": "Luchtbevochtiger", "philipsbulb": "Philips Light Bulb", "philipseyecare": "Philips Eyecare Lamp", + "airmonitor": "PM2.5 Air Monitor", "powerplug": "Mi Power Plug", + "powerstrip": "Mi Power Strip", "powered": "Aangezet:", "mode": "Modus:", "dim": "Dim niveau:", diff --git a/package.json b/package.json index 1bb0623..72b00ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.xiaomi-miio", - "version": "2.5.3", + "version": "2.6.0", "description": "Xiaomi Mi Home", "main": "app.js", "dependencies": {