From 30a13ffae157678a8b6ede6eea4998050172be2e Mon Sep 17 00:00:00 2001 From: jtebbens Date: Wed, 7 Dec 2016 11:52:10 +0100 Subject: [PATCH 001/661] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d5cda3d..02979e26 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # HomeWizard - +# This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! V0.0.8: From 75d892613d079a2e800dab3032954a1321c9dc88 Mon Sep 17 00:00:00 2001 From: jtebbens Date: Tue, 20 Dec 2016 09:47:42 +0100 Subject: [PATCH 002/661] Revert "Update README.md" This reverts commit 30a13ffae157678a8b6ede6eea4998050172be2e. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc13faf0..f2c308db 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # HomeWizard -# + This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! From 76ee7a2e2c2c820c7067a6f10b036ee250d44cc0 Mon Sep 17 00:00:00 2001 From: jtebbens Date: Sat, 24 Dec 2016 14:15:31 +0100 Subject: [PATCH 003/661] Water Meter support Support for water meter on s1 or s2 pending on get-sensors t1 and t2 values --- app.json | 8 ++++- drivers/energylink/driver.js | 66 +++++++++++++++++++++++++++++++----- includes/homewizard.js | 2 +- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/app.json b/app.json index da302a15..f9dd254a 100755 --- a/app.json +++ b/app.json @@ -325,7 +325,7 @@ }, "class": "sensor", "capabilities": [ - "meter_power.used", "meter_power.s1", "meter_gas", "measure_power.used", "measure_power.s1" + "meter_power.used", "meter_power.s1", "meter_gas", "measure_power.used", "measure_power.s1", "meter_water" ], "capabilitiesOptions": { @@ -361,6 +361,12 @@ "en": "Gas", "nl": "Gas" } + }, + "meter_water": { + "title": { + "en": "Water", + "nl": "Water" + } } }, diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index e5f2a0e8..7dd7d013 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -129,7 +129,18 @@ module.exports.capabilities = { callback(null, device.last_meter_gas); } } - } + }, + meter_water: { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_water); + } + } + } }; // Start polling @@ -149,24 +160,61 @@ function getStatus(device_id) { if (Object.keys(callback).length > 0) { try { module.exports.setAvailable({id: device_id}); + + var value_s1 = ( callback[0].t1 ) ; // Read t1 from energylink (solar/water/null) + var value_s2 = ( callback[0].t2 ) ; // Read t2 from energylink (solar/water/null) + + console.log("t1- " + value_s1); + console.log("t2- " + value_s2); + + // Common Energylink data var energy_current_cons = ( callback[0].used.po ); // WATTS Energy used JSON $energylink[0]['used']['po'] - var energy_current_prod = ( callback[0].s1.po ); // WATTS Energy produced via S1 $energylink[0]['s1']['po'] var energy_daytotal_cons = ( callback[0].used.dayTotal ); // KWH Energy used JSON $energylink[0]['used']['po'] - var energy_daytotal_prod = ( callback[0].s1.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s1']['po'] var gas_daytotal_cons = ( callback[0].gas.dayTotal ); // m3 Energy produced via S1 $energylink[0]['gas']['dayTotal'] - - + // Consumed elec current module.exports.realtime( { id: device_id }, "measure_power.used", energy_current_cons ); // Consumed elec total day module.exports.realtime( { id: device_id }, "meter_power.used", energy_daytotal_cons ); - // Produced elec current - module.exports.realtime( { id: device_id }, "measure_power.s1", energy_current_prod ); - // Produced elec total day - module.exports.realtime( { id: device_id }, "meter_power.s1", energy_daytotal_prod ); // Consumed gas module.exports.realtime( { id: device_id }, "meter_gas", gas_daytotal_cons ); + if (value_s1 == 'solar' ) { + var energy_current_prod = ( callback[0].s1.po ); // WATTS Energy produced via S1 $energylink[0]['s1']['po'] + var energy_daytotal_prod = ( callback[0].s1.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s1']['po'] + + // Produced elec current + module.exports.realtime( { id: device_id }, "measure_power.s1", energy_current_prod ); + // Produced elec total day + module.exports.realtime( { id: device_id }, "meter_power.s1", energy_daytotal_prod ); + } + + if (value_s2 == 'solar' ) { + var energy_current_prod = ( callback[0].s2.po ); // WATTS Energy produced via S1 $energylink[0]['s2']['po'] + var energy_daytotal_prod = ( callback[0].s2.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s2']['dayTotal'] + + // Produced elec current + module.exports.realtime( { id: device_id }, "measure_power.s2", energy_current_prod ); + // Produced elec total day + module.exports.realtime( { id: device_id }, "meter_power.s2", energy_daytotal_prod ); + } + + if (value_s1 == 'water' ) { + // var water_current_cons = ( callback[0].s1.po ); // Water used via S1 $energylink[0]['s1']['po'] + var water_daytotal_cons = ( callback[0].s1.dayTotal ); // Water used via S1 $energylink[0]['s1']['dayTotal'] + console.log("Water- " + water_daytotal_cons); + // Used water m3 + module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); + } + + if (value_s2 == 'water' ) { + // var water_current_cons = ( callback[0].s2.po ); // Water used via S1 $energylink[0]['s1']['po'] + var water_daytotal_cons = ( callback[0].s2.dayTotal ); // Water used via S1 $energylink[0]['s2']['dayTotal'] + console.log("Water- " + water_daytotal_cons); + // Used water m3 + module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); + } + // Trigger flows if (energy_current_cons != devices[device_id].last_measure_power_used) { console.log("Current Power - "+ energy_current_cons); diff --git a/includes/homewizard.js b/includes/homewizard.js index 62bbdef2..cb346eff 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -134,7 +134,7 @@ module.exports = (function(){ if (typeof self.devices[device_id].polldata === 'undefined') { self.devices[device_id].polldata = []; } - homewizard.call(device_id, '/get-status', function(err, response) { + homewizard.call(device_id, '/get-sensors', function(err, response) { if (err === null) { self.devices[device_id].polldata.preset = response.preset; self.devices[device_id].polldata.heatlinks = response.heatlinks; From d4cc8770e94acd8e38270c773dd15775bdc045bd Mon Sep 17 00:00:00 2001 From: jtebbens Date: Sat, 21 Jan 2017 19:39:16 +0100 Subject: [PATCH 004/661] Update driver.js Conversion m3 to liter --- drivers/energylink/driver.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 7dd7d013..75d101ce 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -201,7 +201,7 @@ function getStatus(device_id) { if (value_s1 == 'water' ) { // var water_current_cons = ( callback[0].s1.po ); // Water used via S1 $energylink[0]['s1']['po'] - var water_daytotal_cons = ( callback[0].s1.dayTotal ); // Water used via S1 $energylink[0]['s1']['dayTotal'] + var water_daytotal_cons = ( callback[0].s1.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s1']['dayTotal'] console.log("Water- " + water_daytotal_cons); // Used water m3 module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); @@ -209,7 +209,7 @@ function getStatus(device_id) { if (value_s2 == 'water' ) { // var water_current_cons = ( callback[0].s2.po ); // Water used via S1 $energylink[0]['s1']['po'] - var water_daytotal_cons = ( callback[0].s2.dayTotal ); // Water used via S1 $energylink[0]['s2']['dayTotal'] + var water_daytotal_cons = ( callback[0].s2.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s2']['dayTotal'] console.log("Water- " + water_daytotal_cons); // Used water m3 module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); From ced00358df2f3e215bfa5a9679b0ac043ff62403 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Sun, 22 Jan 2017 00:26:59 +0100 Subject: [PATCH 005/661] Added Thermomters --- app.json | 29 +++- drivers/thermometer/assets/icon.svg | 69 ++++++++++ drivers/thermometer/assets/images/large.jpg | Bin 0 -> 27576 bytes drivers/thermometer/assets/images/small.jpg | Bin 0 -> 2534 bytes drivers/thermometer/driver.js | 141 ++++++++++++++++++++ drivers/thermometer/pair/start.html | 102 ++++++++++++++ includes/homewizard.js | 29 +++- locales/en.json | 1 + locales/nl.json | 1 + 9 files changed, 366 insertions(+), 6 deletions(-) create mode 100644 drivers/thermometer/assets/icon.svg create mode 100755 drivers/thermometer/assets/images/large.jpg create mode 100755 drivers/thermometer/assets/images/small.jpg create mode 100755 drivers/thermometer/driver.js create mode 100755 drivers/thermometer/pair/start.html diff --git a/app.json b/app.json index f9dd254a..82a2eea8 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.0", + "version": "0.1.1", "compatibility": ">=0.9", "description": { "en": "Control HomeWizard using Homey" @@ -371,6 +371,33 @@ }, + "pair": [{ + "id": "start" + }, { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, { + "id": "add_my_devices", + "template": "add_devices" + }] + }, { + "id": "thermometer", + "name": { + "en": "Thermometer", + "nl": "Thermometer" + }, + "images": { + "large": "drivers/thermometer/assets/images/large.jpg", + "small": "drivers/thermometer/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_temperature", + "measure_humidity" + ], "pair": [{ "id": "start" }, { diff --git a/drivers/thermometer/assets/icon.svg b/drivers/thermometer/assets/icon.svg new file mode 100644 index 00000000..1a732521 --- /dev/null +++ b/drivers/thermometer/assets/icon.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/thermometer/assets/images/large.jpg b/drivers/thermometer/assets/images/large.jpg new file mode 100755 index 0000000000000000000000000000000000000000..811c7c9d583cc42e0d95b4ee49dfc391dfacdd24 GIT binary patch literal 27576 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 ix#ioepsr<7E<+bI<2 0) { + startPolling(); + } + Homey.log('Thermometer driver init done'); + + callback (null, true); +}; + +module.exports.deleted = function( device_data ) { + clearInterval(refreshIntervalId); + Homey.log("--Stopped Polling--"); + devices = []; + Homey.log('deleted: ' + JSON.stringify(device_data)); +}; + + +module.exports.capabilities = { + + measure_temperature: { + get: function (device, callback) { + if (device instanceof Error) return callback(device); + console.log("measure_temperature"); + getStatus(device.id); + newvalue = devices[device.id].temperature; + // Callback ambient temperature + callback(null, newvalue); + } + }, +}; + +function getStatus(device_id) { + if(devices[device_id].settings.homewizard_id !== undefined ) { + var homewizard_id = devices[device_id].settings.homewizard_id; + homewizard.getDeviceData(homewizard_id, 'thermometers', function(callback) { + + if (Object.keys(callback).length > 0) { + try { + var te = (callback[0].te.toFixed(1) * 2) / 2; + var hu = (callback[0].hu.toFixed(1) * 2) / 2; + + //Check current temperature + if (devices[device_id].temperature != te) { + console.log("New TE - "+ te); + module.exports.realtime( { id: device_id }, "measure_temperature", te ); + devices[device_id].temperature = te; + } else { + console.log("TE: no change"); + } + + //Check current humidity + if (devices[device_id].humidity != hu) { + console.log("New HU - "+ hu); + module.exports.realtime( { id: device_id }, "measure_humidity", hu ); + devices[device_id].humidity = hu; + } else { + console.log("HU: no change"); + } + + } catch(err) { + console.log ("Thermometer data corrupt"); + } + } + }); + } else { + Homey.log('Removed Thermometer '+ device_id +' (old settings)'); + module.exports.setUnavailable({id: device_id}, "No Thermometer found" ); + clearInterval(refreshIntervalId); + } + } + + function startPolling() { + refreshIntervalId = setInterval(function () { + Homey.log("--Start Thermometer Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getStatus(device_id); + }); + }, 1000 * 10); + } \ No newline at end of file diff --git a/drivers/thermometer/pair/start.html b/drivers/thermometer/pair/start.html new file mode 100755 index 00000000..0c4d3a38 --- /dev/null +++ b/drivers/thermometer/pair/start.html @@ -0,0 +1,102 @@ + + + + + + + +

+
+ +
+ +
+
+

+ + + diff --git a/includes/homewizard.js b/includes/homewizard.js index cb346eff..0a90993d 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -5,9 +5,27 @@ module.exports = (function(){ var self = {}; self.devices = []; self.polls = []; - var testdata = {"preset":0,"time":"2016-12-07 20:26","switches":[{"id":0,"type":"dimmer","status":"on","dimlevel":39},{"id":1,"type":"switch","status":"on"},{"id":2,"type":"dimmer","status":"off","dimlevel":0},{"id":3,"type":"switch","status":"off"},{"id":4,"type":"dimmer","status":"off","dimlevel":0},{"id":5,"type":"virtual"},{"id":6,"type":"hue","status":"on","color":{"hue":60,"sat":57,"bri":65}},{"id":7,"type":"virtual"},{"id":8,"type":"virtual"},{"id":9,"type":"switch","status":"on"},{"id":10,"type":"hue","status":"off","color":{"hue":175,"sat":0,"bri":100}},{"id":11,"type":"hue","status":"off","color":{"hue":60,"sat":59,"bri":66}},{"id":12,"type":"virtual"},{"id":13,"type":"hue","status":"off","color":{"hue":68,"sat":88,"bri":10}},{"id":14,"type":"hue","status":"off","color":{"hue":68,"sat":88,"bri":57}},{"id":15,"type":"hue","status":"off","color":{"hue":68,"sat":88,"bri":98}},{"id":16,"type":"hue","status":"off","color":{"hue":68,"sat":88,"bri":19}},{"id":17,"type":"hue","status":"off","color":{"hue":8,"sat":0,"bri":0}},{"id":18,"type":"hue","status":"off","color":{"hue":43,"sat":96,"bri":21}},{"id":19,"type":"hue","status":"on","color":{"hue":307,"sat":99,"bri":18}},{"id":20,"type":"virtual"},{"id":21,"type":"virtual"},{"id":22,"type":"virtual"}],"uvmeters":[],"windmeters":[],"rainmeters":[],"thermometers":[{"id":0,"te":20.3,"hu":47,"favorite":"no"},{"id":1,"te":6.9,"hu":78,"favorite":"no"},{"id":2,"te":20.4,"hu":44,"favorite":"no"},{"id":3,"te":24.7,"hu":43,"favorite":"no"},{"id":4,"te":23.1,"hu":45,"favorite":"no"},{"id":5,"te":7.1,"hu":32,"favorite":"no"}],"weatherdisplays":[], "energymeters":[{"id": 0, "name": "Wattcher", "key": "0", "code": "xxxxxxxxxx", "po": 320, "dayTotal": 5.33, "po+": 2730, "po+t": "6:23", "po-": 120, "po-t": "8:25", "lowBattery": "no", "favorite": "no"}], "energylinks": [{"id":0,"tariff":2,"s1":{"po":114,"dayTotal":0.00,"po+":114,"po+t":"00:01","po-":114,"po-t":"00:01"},"s2":null,"aggregate":{"po":264,"dayTotal":0.00,"po+":264,"po+t":"00:01","po-":264,"po-t":"00:01"},"used":{"po":378,"dayTotal":0.00,"po+":378,"po+t":"00:01","po-":378,"po-t":"00:01"},"gas":{"lastHour":0.23,"dayTotal":0.00},"kwhindex":0.73}], "heatlinks": [{"id": 0, "pump": "off", "heating": "off", "dhw": "off", "rte": 20.230, "rsp": 20.000, "tte": 0.000, "ttm": null, "wp": 1.359, "wte": 0.000, "ofc": 0, "odc": 0}], "kakusensors": [{"id":0,"status":"yes","timestamp":"20:25"},{"id":1,"status":"no","timestamp":"19:17"},{"id":2,"status":"yes","timestamp":"20:25"}]}; - - homewizard.debug = false; + //var testdata = {"preset":0,"time":"2016-12-07 20:26","switches":[{"id":0,"type":"dimmer","status":"on","dimlevel":39},{"id":1,"type":"switch","status":"on"},{"id":2,"type":"dimmer","status":"off","dimlevel":0},{"id":3,"type":"switch","status":"off"},{"id":4,"type":"dimmer","status":"off","dimlevel":0},{"id":5,"type":"virtual"},{"id":6,"type":"hue","status":"on","color":{"hue":60,"sat":57,"bri":65}},{"id":7,"type":"virtual"},{"id":8,"type":"virtual"},{"id":9,"type":"switch","status":"on"},{"id":10,"type":"hue","status":"off","color":{"hue":175,"sat":0,"bri":100}},{"id":11,"type":"hue","status":"off","color":{"hue":60,"sat":59,"bri":66}},{"id":12,"type":"virtual"},{"id":13,"type":"hue","status":"off","color":{"hue":68,"sat":88,"bri":10}},{"id":14,"type":"hue","status":"off","color":{"hue":68,"sat":88,"bri":57}},{"id":15,"type":"hue","status":"off","color":{"hue":68,"sat":88,"bri":98}},{"id":16,"type":"hue","status":"off","color":{"hue":68,"sat":88,"bri":19}},{"id":17,"type":"hue","status":"off","color":{"hue":8,"sat":0,"bri":0}},{"id":18,"type":"hue","status":"off","color":{"hue":43,"sat":96,"bri":21}},{"id":19,"type":"hue","status":"on","color":{"hue":307,"sat":99,"bri":18}},{"id":20,"type":"virtual"},{"id":21,"type":"virtual"},{"id":22,"type":"virtual"}],"uvmeters":[],"windmeters":[],"rainmeters":[],"thermometers":[{"id":0,"te":20.3,"hu":47,"favorite":"no"},{"id":1,"te":6.9,"hu":78,"favorite":"no"},{"id":2,"te":20.4,"hu":44,"favorite":"no"},{"id":3,"te":24.7,"hu":43,"favorite":"no"},{"id":4,"te":23.1,"hu":45,"favorite":"no"},{"id":5,"te":7.1,"hu":32,"favorite":"no"}],"weatherdisplays":[], "energymeters":[{"id": 0, "name": "Wattcher", "key": "0", "code": "xxxxxxxxxx", "po": 320, "dayTotal": 5.33, "po+": 2730, "po+t": "6:23", "po-": 120, "po-t": "8:25", "lowBattery": "no", "favorite": "no"}], "energylinks": [{"id":0,"tariff":2,"s1":{"po":114,"dayTotal":0.00,"po+":114,"po+t":"00:01","po-":114,"po-t":"00:01"},"s2":null,"aggregate":{"po":264,"dayTotal":0.00,"po+":264,"po+t":"00:01","po-":264,"po-t":"00:01"},"used":{"po":378,"dayTotal":0.00,"po+":378,"po+t":"00:01","po-":378,"po-t":"00:01"},"gas":{"lastHour":0.23,"dayTotal":0.00},"kwhindex":0.73}], "heatlinks": [{"id": 0, "pump": "off", "heating": "off", "dhw": "off", "rte": 20.230, "rsp": 20.000, "tte": 0.000, "ttm": null, "wp": 1.359, "wte": 0.000, "ofc": 0, "odc": 0}], "kakusensors": [{"id":0,"status":"yes","timestamp":"20:25"},{"id":1,"status":"no","timestamp":"19:17"},{"id":2,"status":"yes","timestamp":"20:25"}]}; + var testdata = { + "preset":0, + "time":"2017-01-21 20:07", + "switches":[], + "uvmeters":[], + "windmeters":[], + "rainmeters":[], + "thermometers":[ + {"id":1,"name":"Kantoor","channel":2,"model":0,"te":20.0,"hu":5,"te+":20.4,"te+t":"12:15","te-":19.6,"te-t":"07:06","hu+":5,"hu+t":"00:00","hu-":5,"hu-t":"00:00","outside":"no","favorite":"no"}, + {"id":2,"name":"Slaapkamer","channel":5,"model":0,"te":18.8,"hu":47,"te+":19.1,"te+t":"09:06","te-":17.7,"te-t":"11:34","hu+":49,"hu+t":"07:39","hu-":43,"hu-t":"10:51","outside":"no","favorite":"no"} + ], + "weatherdisplays":[], + "energymeters": [], + "energylinks": [ + {"id":0,"favorite":"no","name":"EnergyLink","code":"942991","t1":"solar","c1":1000,"t2":"water","c2":1,"tariff":1,"s1":{"po":0,"dayTotal":10.24,"po+":2498,"po+t":"11:22","po-":0,"po-t":"00:01"},"s2":{"po":4,"dayTotal":162.00,"po+":7,"po+t":"08:49","po-":0,"po-t":"00:01"},"aggregate":{"po":511,"dayTotal":-3.19,"po+":2873,"po+t":"09:22","po-":-1857,"po-t":"11:55"},"used":{"po":511,"dayTotal":7.04,"po+":3791,"po+t":"11:45","po-":204,"po-t":"16:34"},"gas":{"lastHour":0.44,"dayTotal":4.07},"kwhindex":2.87,"wp":3570} + ], + "heatlinks": [{"id": 0, "favorite": "no", "name": "HeatLink", "code": "384699", "pump": "on", "heating": "off", "dhw": "off", "rte": 19.375, "rsp": 20.000, "tte": 0.000, "ttm": null, "wp": 1.628, "wte": 52.988, "ofc": 0, "odc": 0, "presets": [{ "id": 0, "te": 20.00},{ "id": 1, "te": 15.00},{ "id": 2, "te": 21.00},{ "id": 3, "te": 12.00}]}], + "hues": []}; + + homewizard.debug = true; homewizard.debug_devices = []; homewizard.debug_devices.HW12345 = { id: 'HW12345', @@ -47,7 +65,7 @@ module.exports = (function(){ callback(null, testdata); } else { Homey.log('Call device' + device_id); - if (typeof self.devices[device_id].settings !== 'undefined' && typeof self.devices[device_id].settings.homewizard_ip !== 'undefined' && typeof self.devices[device_id].settings.homewizard_pass !== 'undefined') { + if (("settings" in self.devices[device_id]) && ("homewizard_ip" in self.devices[device_id].settings) && ("homewizard_pass" in self.devices[device_id].settings)) { var homewizard_ip = self.devices[device_id].settings.homewizard_ip; var homewizard_pass = self.devices[device_id].settings.homewizard_pass; request({ @@ -124,6 +142,7 @@ module.exports = (function(){ }; homewizard.startpoll = function() { + homewizard.poll(); self.polls.device_id = setInterval(function () { homewizard.poll(); }, 1000 * 10); @@ -140,7 +159,7 @@ module.exports = (function(){ self.devices[device_id].polldata.heatlinks = response.heatlinks; self.devices[device_id].polldata.energylinks = response.energylinks; self.devices[device_id].polldata.energymeters = response.energymeters; - //self.devices[device_id].polldata.thermometers = response.thermometers; + self.devices[device_id].polldata.thermometers = response.thermometers; Homey.log('HW-Data polled for: '+device_id); } diff --git a/locales/en.json b/locales/en.json index cabe4f3e..c6c22b7d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -5,6 +5,7 @@ "heatlink_intro": "To connect to your Heatlink we need to know to wich HomeWizard it belongs.", "energylink_intro": "To connect to your Energylink we need to know to wich HomeWizard it belongs.", "wattcher_intro": "To connect to your Wattcher we need to know to wich HomeWizard it belongs.", + "thermometer_intro": "To connect to your thermometer we need to know to wich HomeWizard it belongs.", "password": "Password", "use_ledring": "Use ledring", "use_ledring_explain": "Ledring lights up green when preset switched successful, red when not successful", diff --git a/locales/nl.json b/locales/nl.json index 2a441ce0..4f70f289 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -5,6 +5,7 @@ "heatlink_intro": "Om verbinding te kunnen maken met uw Heatlink dient u aan te geven op welke HomeWizard deze is aangesloten.", "energylink_intro": "Om verbinding te kunnen maken met uw Energylink dient u aan te geven op welke HomeWizard deze is aangesloten.", "wattcher_intro": "Om verbinding te kunnen maken met uw Wattcher dient u aan te geven op welke HomeWizard deze is aangesloten.", + "thermometer_intro": "Om verbinding te kunnen maken met uw thermometer dient u aan te geven op welke HomeWizard deze is aangesloten.", "password": "Wachtwoord", "use_ledring": "Gebruik ledring", "use_ledring_explain": "Ledring licht groen op indien de preset aangepast is, rood indien niet succesvol", From f300b888e52873711f72334dd76014f1b3cc4cf3 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Sun, 22 Jan 2017 01:09:12 +0100 Subject: [PATCH 006/661] Bugfix --- drivers/thermometer/driver.js | 50 +++++++++++++++++++---------------- includes/homewizard.js | 2 +- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/drivers/thermometer/driver.js b/drivers/thermometer/driver.js index b611c4e2..9d55c036 100755 --- a/drivers/thermometer/driver.js +++ b/drivers/thermometer/driver.js @@ -94,32 +94,36 @@ module.exports.capabilities = { function getStatus(device_id) { if(devices[device_id].settings.homewizard_id !== undefined ) { var homewizard_id = devices[device_id].settings.homewizard_id; + var thermometer_id = devices[device_id].settings.thermometer_id; homewizard.getDeviceData(homewizard_id, 'thermometers', function(callback) { if (Object.keys(callback).length > 0) { - try { - var te = (callback[0].te.toFixed(1) * 2) / 2; - var hu = (callback[0].hu.toFixed(1) * 2) / 2; - - //Check current temperature - if (devices[device_id].temperature != te) { - console.log("New TE - "+ te); - module.exports.realtime( { id: device_id }, "measure_temperature", te ); - devices[device_id].temperature = te; - } else { - console.log("TE: no change"); - } - - //Check current humidity - if (devices[device_id].humidity != hu) { - console.log("New HU - "+ hu); - module.exports.realtime( { id: device_id }, "measure_humidity", hu ); - devices[device_id].humidity = hu; - } else { - console.log("HU: no change"); - } - - } catch(err) { + try { + for (var index in callback){ + if (callback[index].id == thermometer_id) { + var te = (callback[index].te.toFixed(1) * 2) / 2; + var hu = (callback[index].hu.toFixed(1) * 2) / 2; + + //Check current temperature + if (devices[device_id].temperature != te) { + console.log("New TE - "+ te); + module.exports.realtime( { id: device_id }, "measure_temperature", te ); + devices[device_id].temperature = te; + } else { + console.log("TE: no change"); + } + + //Check current humidity + if (devices[device_id].humidity != hu) { + console.log("New HU - "+ hu); + module.exports.realtime( { id: device_id }, "measure_humidity", hu ); + devices[device_id].humidity = hu; + } else { + console.log("HU: no change"); + } + } + } + } catch(err) { console.log ("Thermometer data corrupt"); } } diff --git a/includes/homewizard.js b/includes/homewizard.js index 0a90993d..8224698d 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -25,7 +25,7 @@ module.exports = (function(){ "heatlinks": [{"id": 0, "favorite": "no", "name": "HeatLink", "code": "384699", "pump": "on", "heating": "off", "dhw": "off", "rte": 19.375, "rsp": 20.000, "tte": 0.000, "ttm": null, "wp": 1.628, "wte": 52.988, "ofc": 0, "odc": 0, "presets": [{ "id": 0, "te": 20.00},{ "id": 1, "te": 15.00},{ "id": 2, "te": 21.00},{ "id": 3, "te": 12.00}]}], "hues": []}; - homewizard.debug = true; + homewizard.debug = false; homewizard.debug_devices = []; homewizard.debug_devices.HW12345 = { id: 'HW12345', From 6ed00cc24e887e6b897d7fc29d7b4d3bbcbbb0a5 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Sun, 22 Jan 2017 10:44:26 +0100 Subject: [PATCH 007/661] Added preset change --- app.json | 18 ++++++++++++++++++ includes/homewizard.js | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/app.json b/app.json index 82a2eea8..79d9957c 100755 --- a/app.json +++ b/app.json @@ -31,6 +31,24 @@ }, "flow": { "triggers": [{ + "id": "preset_changed", + "title": { + "en": "Preset changed", + "nl": "Preset veranderd" + }, + "args": [{ + "name": "Homewizard", + "type": "device", + "filter": "driver_id=homewizard" + }], + "tokens": [{ + "name": "preset", + "title": { + "en": "Preset", + "nl": "Preset" + } + }] + },{ "id": "power_used_changed", "title": { "en": "Power used changed", diff --git a/includes/homewizard.js b/includes/homewizard.js index 8224698d..525bf5c2 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -155,6 +155,12 @@ module.exports = (function(){ } homewizard.call(device_id, '/get-sensors', function(err, response) { if (err === null) { + if (('preset' in self.devices[device_id].polldata) && self.devices[device_id].polldata.preset != response.preset) { + Homey.manager('flow').trigger('preset_changed', { + preset: response.preset + }); + Homey.log('Preset was changed!'); + } self.devices[device_id].polldata.preset = response.preset; self.devices[device_id].polldata.heatlinks = response.heatlinks; self.devices[device_id].polldata.energylinks = response.energylinks; From 134837c09709d8257849600d10e32b9571566a73 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 21:52:33 +0100 Subject: [PATCH 008/661] General update --- README.md | 6 ++++++ app.json | 4 ++-- drivers/homewizard/driver.js | 36 +++++++++++++++++++++++++++++++++-- drivers/thermometer/driver.js | 4 ++-- includes/homewizard.js | 6 ------ 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 83888bce..26a217d5 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! + + +**If you like this app, then consider to buy me a beer :)** + +[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=77PULBQVXB8ZG) + V0.1.0: * Improved polling (far less requests to HomeWizard) diff --git a/app.json b/app.json index 79d9957c..f77d133d 100755 --- a/app.json +++ b/app.json @@ -33,8 +33,8 @@ "triggers": [{ "id": "preset_changed", "title": { - "en": "Preset changed", - "nl": "Preset veranderd" + "en": "Preset has changed", + "nl": "Preset is veranderd" }, "args": [{ "name": "Homewizard", diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index eb41112c..ecf68174 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -68,9 +68,12 @@ module.exports.init = function(devices_data, callback) { }); }); homewizard.setDevices(devices); - homewizard.startpoll(); + if (Object.keys(devices).length > 0) { + startPolling(); + } + Homey.log('HomeWizard driver init done'); callback (null, true); }; @@ -149,4 +152,33 @@ Homey.manager('flow').on('action.set_preset', function( callback, args ){ callback(err, false); // err } }); -}); \ No newline at end of file +}); + + +function getStatus(device_id) { + homewizard.getDeviceData(device_id, 'preset', function(callback) { + try { + if (('preset' in devices[device_id]) && devices[device_id].preset != response.preset) { + Homey.manager('flow').trigger('preset_changed', { + preset: response.preset + }); + Homey.log('Preset was changed!'); + } + + if (!('preset' in devices[device_id])) { + devices[device_id].preset = 0; + } + } catch(err) { + console.log ("HomeWizard data corrupt"); + } + }); +} + +function startPolling() { + refreshIntervalId = setInterval(function () { + Homey.log("--Start HomeWizard Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getStatus(device_id); + }); + }, 1000 * 10); +} \ No newline at end of file diff --git a/drivers/thermometer/driver.js b/drivers/thermometer/driver.js index 9d55c036..eafd7139 100755 --- a/drivers/thermometer/driver.js +++ b/drivers/thermometer/driver.js @@ -110,7 +110,7 @@ function getStatus(device_id) { module.exports.realtime( { id: device_id }, "measure_temperature", te ); devices[device_id].temperature = te; } else { - console.log("TE: no change"); + //console.log("TE: no change"); } //Check current humidity @@ -119,7 +119,7 @@ function getStatus(device_id) { module.exports.realtime( { id: device_id }, "measure_humidity", hu ); devices[device_id].humidity = hu; } else { - console.log("HU: no change"); + //console.log("HU: no change"); } } } diff --git a/includes/homewizard.js b/includes/homewizard.js index 525bf5c2..8224698d 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -155,12 +155,6 @@ module.exports = (function(){ } homewizard.call(device_id, '/get-sensors', function(err, response) { if (err === null) { - if (('preset' in self.devices[device_id].polldata) && self.devices[device_id].polldata.preset != response.preset) { - Homey.manager('flow').trigger('preset_changed', { - preset: response.preset - }); - Homey.log('Preset was changed!'); - } self.devices[device_id].polldata.preset = response.preset; self.devices[device_id].polldata.heatlinks = response.heatlinks; self.devices[device_id].polldata.energylinks = response.energylinks; From 262839be26d95006555ea28fc8e80e436365f469 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 21:56:45 +0100 Subject: [PATCH 009/661] Update readme --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 26a217d5..4b8ad6d3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,13 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard **If you like this app, then consider to buy me a beer :)** -[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=77PULBQVXB8ZG) +[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NMEY96Y252S5S) + + +V0.1.1: + +* Added temp sensors +* Added trigger on prest change V0.1.0: From e4d1e219bb2845f2c68251eb0dd3a11064ce6bd9 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 22:00:46 +0100 Subject: [PATCH 010/661] Update link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b8ad6d3..5fdb7b1f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard **If you like this app, then consider to buy me a beer :)** -[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NMEY96Y252S5S) +[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) V0.1.1: From 24d3052ecc9c6ad12512cc103bb4b91f6347b426 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 22:05:58 +0100 Subject: [PATCH 011/661] Bugfix --- drivers/homewizard/driver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index ecf68174..19414036 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -158,7 +158,7 @@ Homey.manager('flow').on('action.set_preset', function( callback, args ){ function getStatus(device_id) { homewizard.getDeviceData(device_id, 'preset', function(callback) { try { - if (('preset' in devices[device_id]) && devices[device_id].preset != response.preset) { + if (('preset' in devices[device_id]) && devices[device_id].preset != callback) { Homey.manager('flow').trigger('preset_changed', { preset: response.preset }); From 62d3e03d8308e431c9631a1a5c6c00080d025ad3 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 22:12:50 +0100 Subject: [PATCH 012/661] Bugfix --- drivers/homewizard/driver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 19414036..82da073f 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -160,7 +160,7 @@ function getStatus(device_id) { try { if (('preset' in devices[device_id]) && devices[device_id].preset != callback) { Homey.manager('flow').trigger('preset_changed', { - preset: response.preset + preset: callback }); Homey.log('Preset was changed!'); } From 4e3ee64d90a672abb074f197aaecf81ce66b54cf Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 22:13:34 +0100 Subject: [PATCH 013/661] Bugfix --- drivers/homewizard/driver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 82da073f..7e5c848a 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -166,7 +166,7 @@ function getStatus(device_id) { } if (!('preset' in devices[device_id])) { - devices[device_id].preset = 0; + devices[device_id].preset = callback; } } catch(err) { console.log ("HomeWizard data corrupt"); From 8f237fc3487d5d4004929885901189c32598d0c2 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 22:14:30 +0100 Subject: [PATCH 014/661] Added logging --- drivers/homewizard/driver.js | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 7e5c848a..67a66ff4 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -166,6 +166,7 @@ function getStatus(device_id) { } if (!('preset' in devices[device_id])) { + Homey.log('Preset was set to' + callback); devices[device_id].preset = callback; } } catch(err) { From 681776a1582e92a24a29ca27971c1230df77c253 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 22:28:38 +0100 Subject: [PATCH 015/661] Added logging --- drivers/homewizard/driver.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 67a66ff4..7b15198c 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -157,18 +157,19 @@ Homey.manager('flow').on('action.set_preset', function( callback, args ){ function getStatus(device_id) { homewizard.getDeviceData(device_id, 'preset', function(callback) { + Homey.log('PRESET:' + callback); try { + if (!('preset' in devices[device_id])) { + Homey.log('Preset was set to' + callback); + devices[device_id].preset = callback; + } + if (('preset' in devices[device_id]) && devices[device_id].preset != callback) { Homey.manager('flow').trigger('preset_changed', { preset: callback }); Homey.log('Preset was changed!'); } - - if (!('preset' in devices[device_id])) { - Homey.log('Preset was set to' + callback); - devices[device_id].preset = callback; - } } catch(err) { console.log ("HomeWizard data corrupt"); } From 940f35884ca326622311ab56390694cc1561d259 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 22:33:49 +0100 Subject: [PATCH 016/661] Set preset --- drivers/homewizard/driver.js | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 7b15198c..6fd83056 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -165,6 +165,7 @@ function getStatus(device_id) { } if (('preset' in devices[device_id]) && devices[device_id].preset != callback) { + devices[device_id].preset = callback; Homey.manager('flow').trigger('preset_changed', { preset: callback }); From 8718f989cd7450538505975e41d0a450d29a21df Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 22:35:49 +0100 Subject: [PATCH 017/661] Changed trigger --- drivers/homewizard/driver.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 6fd83056..85d56233 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -166,9 +166,7 @@ function getStatus(device_id) { if (('preset' in devices[device_id]) && devices[device_id].preset != callback) { devices[device_id].preset = callback; - Homey.manager('flow').trigger('preset_changed', { - preset: callback - }); + Homey.manager('flow').triggerDevice('preset_changed', { preset: callback }, null, { id: device_id } ); Homey.log('Preset was changed!'); } } catch(err) { From 59f83921a98bedfe81c307f8edba60e58005f8f2 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Mon, 23 Jan 2017 22:45:16 +0100 Subject: [PATCH 018/661] Update filter --- app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.json b/app.json index f77d133d..505f83b4 100755 --- a/app.json +++ b/app.json @@ -39,7 +39,7 @@ "args": [{ "name": "Homewizard", "type": "device", - "filter": "driver_id=homewizard" + "filter": "driver_uri=homey:app:com.homewizard&driver_id=homewizard" }], "tokens": [{ "name": "preset", From 10e68ae865dbf713b311ec44cba5bc9e30baa687 Mon Sep 17 00:00:00 2001 From: jtebbens Date: Tue, 24 Jan 2017 10:30:08 +0100 Subject: [PATCH 019/661] Tokens fixed app.json lacked type = number to handle the preset value. --- app.json | 8 +++++++- drivers/homewizard/driver.js | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index 505f83b4..83f0b83a 100755 --- a/app.json +++ b/app.json @@ -39,10 +39,12 @@ "args": [{ "name": "Homewizard", "type": "device", - "filter": "driver_uri=homey:app:com.homewizard&driver_id=homewizard" + "filter": "driver_id=homewizard" + }], "tokens": [{ "name": "preset", + "type": "number", "title": { "en": "Preset", "nl": "Preset" @@ -65,6 +67,7 @@ }], "tokens": [{ "name": "power_used", + "type": "number", "title": { "en": "Watt", "nl": "Watt" @@ -87,6 +90,7 @@ }], "tokens": [{ "name": "power_s1", + "type": "number", "title": { "en": "Watt", "nl": "Watt" @@ -109,6 +113,7 @@ }], "tokens": [{ "name": "power_daytotal_used", + "type": "number", "title": { "en": "kWh", "nl": "kWh" @@ -131,6 +136,7 @@ }], "tokens": [{ "name": "power_daytotal_s1", + "type": "number", "title": { "en": "kWh", "nl": "kWh" diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 85d56233..2b20e909 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -166,7 +166,10 @@ function getStatus(device_id) { if (('preset' in devices[device_id]) && devices[device_id].preset != callback) { devices[device_id].preset = callback; - Homey.manager('flow').triggerDevice('preset_changed', { preset: callback }, null, { id: device_id } ); + Homey.log('Flow call!' + callback); + Homey.manager('flow').triggerDevice('preset_changed', { preset: callback }, null, { id: device_id } , (err) => { + if (err) return Homey.error('Error triggeringDevice:', err); + }); Homey.log('Preset was changed!'); } } catch(err) { From 3154cb3e7c7e6cabab075788c895a003a68d0498 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Tue, 24 Jan 2017 11:21:57 +0100 Subject: [PATCH 020/661] Fixed driver filters --- app.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app.json b/app.json index 83f0b83a..2cb77daa 100755 --- a/app.json +++ b/app.json @@ -59,7 +59,7 @@ "args": [{ "name": "Energylink", "type": "device", - "filter": "driver_uri=homey:app:com.homewizard&driver_id=energylink", + "filter": "driver_id=energylink", "placeholder": { "en": "Which energylink", "nl": "Welke energylink" @@ -82,7 +82,7 @@ "args": [{ "name": "Energylink", "type": "device", - "filter": "driver_uri=homey:app:com.homewizard&driver_id=energylink", + "filter": "driver_id=energylink", "placeholder": { "en": "Which energylink", "nl": "Welke energylink" @@ -105,7 +105,7 @@ "args": [{ "name": "Energylink", "type": "device", - "filter": "driver_uri=homey:app:com.homewizard&driver_id=energylink", + "filter": "driver_id=energylink", "placeholder": { "en": "Which energylink", "nl": "Welke energylink" @@ -128,7 +128,7 @@ "args": [{ "name": "Energylink", "type": "device", - "filter": "driver_uri=homey:app:com.homewizard&driver_id=energylink", + "filter": "driver_id=energylink", "placeholder": { "en": "Which energylink", "nl": "Welke energylink" @@ -484,4 +484,4 @@ "permissions": [ "homey:manager:ledring" ] -} \ No newline at end of file +} From cc27cb88773b9facfbc3141f30a77b215f65c4d3 Mon Sep 17 00:00:00 2001 From: baskiers Date: Tue, 24 Jan 2017 21:10:57 +0100 Subject: [PATCH 021/661] Changed `devices` to an object since it is used as a object Fixed startPolling/stopPolling logic to accommodate for usecases with multiple devices on the same driver Fixed some instances where variables were implicitly declared (without `var`) --- .gitignore | 5 +++++ drivers/energylink/driver.js | 32 +++++++++++++++++++++----------- drivers/heatlink/driver.js | 35 +++++++++++++++++++++++------------ drivers/homewizard/driver.js | 29 +++++++++++++++++++---------- drivers/thermometer/driver.js | 34 ++++++++++++++++++++++------------ drivers/wattcher/driver.js | 32 +++++++++++++++++++++----------- 6 files changed, 111 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index 40eb83a5..1715b389 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,11 @@ Network Trash Folder Temporary Items .apdisk +################# +## Jetbrains IDEs +################# +.idea + ################# ## Eclipse ################# diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 75d101ce..05579d97 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -1,4 +1,4 @@ -var devices = []; +var devices = {}; var homewizard = require('./../../includes/homewizard.js'); var refreshIntervalId = 0; @@ -68,9 +68,11 @@ module.exports.init = function(devices_data, callback) { }; module.exports.deleted = function( device_data ) { - clearInterval(refreshIntervalId); - console.log("--Stopped Polling Energy Link--"); - devices = []; + delete devices[device_data.id]; + if (Object.keys(devices).length === 0) { + clearInterval(refreshIntervalId); + console.log("--Stopped Polling Energy Link--"); + } Homey.log('deleted: ' + JSON.stringify(device_data)); }; @@ -145,12 +147,15 @@ module.exports.capabilities = { // Start polling function startPolling() { - refreshIntervalId = setInterval(function () { - console.log("--Start Energylink Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); - }, 1000 * 10); + if(refreshIntervalId){ + clearInterval(refreshIntervalId); + } + refreshIntervalId = setInterval(function () { + console.log("--Start Energylink Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getStatus(device_id); + }); + }, 1000 * 10); } function getStatus(device_id) { @@ -243,6 +248,11 @@ function getStatus(device_id) { } else { Homey.log('Removed Energylink '+ device_id +' (wrong settings)'); module.exports.setUnavailable({id: device_id}, "No Energylink found" ); - clearInterval(refreshIntervalId); + // Only clear interval when the unavailable device is the only device on this driver + // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new + // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem + if(Object.keys(devices).length === 1) { + clearInterval(refreshIntervalId); + } } } diff --git a/drivers/heatlink/driver.js b/drivers/heatlink/driver.js index 3ae92c70..1e9a0009 100755 --- a/drivers/heatlink/driver.js +++ b/drivers/heatlink/driver.js @@ -1,4 +1,4 @@ -var devices = []; +var devices = {}; var homewizard = require('./../../includes/homewizard.js'); var refreshIntervalId = 0; @@ -69,9 +69,11 @@ module.exports.init = function(devices_data, callback) { }; module.exports.deleted = function( device_data ) { - clearInterval(refreshIntervalId); - Homey.log("--Stopped Polling--"); - devices = []; + delete devices[device_data.id]; + if (Object.keys(devices).length === 0) { + clearInterval(refreshIntervalId); + Homey.log("--Stopped Polling--"); + } Homey.log('deleted: ' + JSON.stringify(device_data)); }; @@ -83,7 +85,7 @@ module.exports.capabilities = { if (device instanceof Error) return callback(device); console.log("measure_temperature"); getStatus(device.id); - newvalue = devices[device.id].temperature; + var newvalue = devices[device.id].temperature; // Callback ambient temperature callback(null, newvalue); } @@ -95,6 +97,7 @@ module.exports.capabilities = { console.log("target_temperature:get"); // Retrieve updated data getStatus(device.id); + var newvalue; if (devices[device.id].setTemperature !== 0) { newvalue = devices[device.id].setTemperature; } else { @@ -178,15 +181,23 @@ function getStatus(device_id) { } else { Homey.log('Removed Heatlink '+ device_id +' (old settings)'); module.exports.setUnavailable({id: device_id}, "No Heatlink found" ); - clearInterval(refreshIntervalId); + // Only clear interval when the unavailable device is the only device on this driver + // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new + // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem + if(Object.keys(devices).length === 1) { + clearInterval(refreshIntervalId); + } } } function startPolling() { - refreshIntervalId = setInterval(function () { - Homey.log("--Start Heatlink Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); - }, 1000 * 10); + if (refreshIntervalId) { + clearInterval(refreshIntervalId); + } + refreshIntervalId = setInterval(function () { + Homey.log("--Start Heatlink Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getStatus(device_id); + }); + }, 1000 * 10); } \ No newline at end of file diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 2b20e909..36b99296 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -1,6 +1,7 @@ -var devices = []; +var devices = {}; var homewizard = require('./../../includes/homewizard.js'); var request = require('request'); +var refreshIntervalId; // SETTINGS module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { @@ -39,7 +40,8 @@ module.exports.pair = function( socket ) { homewizard.setDevices(devices); callback( null, devices ); socket.emit("success", device); - } else { + startPolling(); + } else { //false socket.emit("error", "no response"); } @@ -78,9 +80,13 @@ module.exports.init = function(devices_data, callback) { callback (null, true); }; -module.exports.deleted = function( device_data ) { +module.exports.deleted = function( device_data ) { + delete devices[device_data.id]; + if (Object.keys(devices).length === 0) { + clearInterval(refreshIntervalId); + Homey.log("--Stopped Polling--"); + } Homey.log('deleted: ' + JSON.stringify(device_data)); - devices[device_data.id] = []; }; // SCENES @@ -179,10 +185,13 @@ function getStatus(device_id) { } function startPolling() { - refreshIntervalId = setInterval(function () { - Homey.log("--Start HomeWizard Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); - }, 1000 * 10); + if (refreshIntervalId) { + clearInterval(refreshIntervalId); + } + refreshIntervalId = setInterval(function () { + Homey.log("--Start HomeWizard Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getStatus(device_id); + }); + }, 1000 * 10); } \ No newline at end of file diff --git a/drivers/thermometer/driver.js b/drivers/thermometer/driver.js index eafd7139..ebd5539c 100755 --- a/drivers/thermometer/driver.js +++ b/drivers/thermometer/driver.js @@ -1,4 +1,4 @@ -var devices = []; +var devices = {}; var homewizard = require('./../../includes/homewizard.js'); var refreshIntervalId = 0; @@ -20,7 +20,7 @@ module.exports.pair = function( socket ) { homewizard.getDevices(function(homewizard_devices) { var hw_devices = {}; Object.keys(homewizard_devices).forEach(function(key) { - thermometers = JSON.stringify(homewizard_devices[key].polldata.thermometers); + var thermometers = JSON.stringify(homewizard_devices[key].polldata.thermometers); hw_devices[key] = homewizard_devices[key]; hw_devices[key].polldata = {}; @@ -70,9 +70,11 @@ module.exports.init = function(devices_data, callback) { }; module.exports.deleted = function( device_data ) { - clearInterval(refreshIntervalId); - Homey.log("--Stopped Polling--"); - devices = []; + delete devices[device_data.id]; + if (Object.keys(devices).length === 0) { + clearInterval(refreshIntervalId); + Homey.log("--Stopped Polling--"); + } Homey.log('deleted: ' + JSON.stringify(device_data)); }; @@ -84,7 +86,7 @@ module.exports.capabilities = { if (device instanceof Error) return callback(device); console.log("measure_temperature"); getStatus(device.id); - newvalue = devices[device.id].temperature; + var newvalue = devices[device.id].temperature; // Callback ambient temperature callback(null, newvalue); } @@ -131,15 +133,23 @@ function getStatus(device_id) { } else { Homey.log('Removed Thermometer '+ device_id +' (old settings)'); module.exports.setUnavailable({id: device_id}, "No Thermometer found" ); - clearInterval(refreshIntervalId); + // Only clear interval when the unavailable device is the only device on this driver + // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new + // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem + if(Object.keys(devices).length === 1) { + clearInterval(refreshIntervalId); + } } } - function startPolling() { +function startPolling() { + if (refreshIntervalId) { + clearInterval(refreshIntervalId); + } refreshIntervalId = setInterval(function () { - Homey.log("--Start Thermometer Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); + Homey.log("--Start Thermometer Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getStatus(device_id); + }); }, 1000 * 10); } \ No newline at end of file diff --git a/drivers/wattcher/driver.js b/drivers/wattcher/driver.js index 0ac5aa24..3b9f5c12 100755 --- a/drivers/wattcher/driver.js +++ b/drivers/wattcher/driver.js @@ -1,4 +1,4 @@ -var devices = []; +var devices = {}; var homewizard = require('./../../includes/homewizard.js'); var refreshIntervalId = 0; @@ -67,9 +67,11 @@ module.exports.init = function(devices_data, callback) { }; module.exports.deleted = function( device_data ) { - clearInterval(refreshIntervalId); - console.log("--Stopped Polling Wattcher--"); - devices = []; + delete devices[device_data.id]; + if (Object.keys(devices).length === 0) { + clearInterval(refreshIntervalId); + console.log("--Stopped Polling Wattcher--"); + } Homey.log('deleted: ' + JSON.stringify(device_data)); }; @@ -101,12 +103,15 @@ module.exports.capabilities = { // Start polling function startPolling() { - refreshIntervalId = setInterval(function () { - console.log("--Start Wattcher Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); - }, 1000 * 10); + if(refreshIntervalId){ + clearInterval(refreshIntervalId); + } + refreshIntervalId = setInterval(function () { + console.log("--Start Wattcher Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getStatus(device_id); + }); + }, 1000 * 10); } function getStatus(device_id) { @@ -136,7 +141,12 @@ function getStatus(device_id) { } else { Homey.log('Removed Wattcher '+ device_id +' (old settings)'); module.exports.setUnavailable({id: device_id}, "No Wattcher found" ); - clearInterval(refreshIntervalId); + // Only clear interval when the unavailable device is the only device on this driver + // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new + // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem + if(Object.keys(devices).length === 1) { + clearInterval(refreshIntervalId); + } } } From 72136c84a9926974b157e336b5b2e4063b0d972c Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Tue, 24 Jan 2017 21:21:09 +0100 Subject: [PATCH 022/661] Prevent errors --- includes/homewizard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/homewizard.js b/includes/homewizard.js index 8224698d..533ee37d 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -65,7 +65,7 @@ module.exports = (function(){ callback(null, testdata); } else { Homey.log('Call device' + device_id); - if (("settings" in self.devices[device_id]) && ("homewizard_ip" in self.devices[device_id].settings) && ("homewizard_pass" in self.devices[device_id].settings)) { + if ((typeof self.devices[device_id] !== 'undefined') && ("settings" in self.devices[device_id]) && ("homewizard_ip" in self.devices[device_id].settings) && ("homewizard_pass" in self.devices[device_id].settings)) { var homewizard_ip = self.devices[device_id].settings.homewizard_ip; var homewizard_pass = self.devices[device_id].settings.homewizard_pass; request({ From 7fdddabb8bbe1276c29f023ba761eb2a01318a0c Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Tue, 24 Jan 2017 21:26:10 +0100 Subject: [PATCH 023/661] Added space --- drivers/homewizard/driver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 36b99296..9b678854 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -166,7 +166,7 @@ function getStatus(device_id) { Homey.log('PRESET:' + callback); try { if (!('preset' in devices[device_id])) { - Homey.log('Preset was set to' + callback); + Homey.log('Preset was set to ' + callback); devices[device_id].preset = callback; } From 14059b4815c12eb98ea4879eeab3e14619b8d5e2 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Wed, 25 Jan 2017 19:45:41 +0100 Subject: [PATCH 024/661] Added preset text --- README.md | 2 +- app.json | 15 +++++++++++---- drivers/homewizard/driver.js | 13 ++++++++++++- drivers/thermometer/assets/images/large.jpg | Bin 27576 -> 19855 bytes drivers/thermometer/assets/images/small.jpg | Bin 2534 -> 1797 bytes 5 files changed, 24 insertions(+), 6 deletions(-) mode change 100755 => 100644 drivers/thermometer/assets/images/large.jpg mode change 100755 => 100644 drivers/thermometer/assets/images/small.jpg diff --git a/README.md b/README.md index 5fdb7b1f..d0158fcc 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard V0.1.1: * Added temp sensors -* Added trigger on prest change +* Added trigger on preset change V0.1.0: diff --git a/app.json b/app.json index 2cb77daa..7f1339d4 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.1", + "version": "0.1.2", "compatibility": ">=0.9", "description": { "en": "Control HomeWizard using Homey" @@ -40,7 +40,7 @@ "name": "Homewizard", "type": "device", "filter": "driver_id=homewizard" - + }], "tokens": [{ "name": "preset", @@ -49,8 +49,15 @@ "en": "Preset", "nl": "Preset" } + }, { + "name": "preset_text", + "type": "string", + "title": { + "en": "Text", + "nl": "Tekst" + } }] - },{ + }, { "id": "power_used_changed", "title": { "en": "Power used changed", @@ -484,4 +491,4 @@ "permissions": [ "homey:manager:ledring" ] -} +} \ No newline at end of file diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 9b678854..5d3ac8a4 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -3,6 +3,11 @@ var homewizard = require('./../../includes/homewizard.js'); var request = require('request'); var refreshIntervalId; +var preset_text = ''; +var preset_text_nl = ['Thuis', 'Afwezig', 'Slapen', 'Vakantie']; +var preset_text_en = ['Home', 'Away', 'Sleep', 'Holiday']; +var homey_lang = Homey.manager('i18n').getLanguage(); + // SETTINGS module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); @@ -173,7 +178,13 @@ function getStatus(device_id) { if (('preset' in devices[device_id]) && devices[device_id].preset != callback) { devices[device_id].preset = callback; Homey.log('Flow call!' + callback); - Homey.manager('flow').triggerDevice('preset_changed', { preset: callback }, null, { id: device_id } , (err) => { + if (homey_lang == "nl") { + preset_text = preset_text_nl[callback]; + } else { + preset_text = preset_text_en[callback]; + } + Homey.log(preset_text); + Homey.manager('flow').triggerDevice('preset_changed', { preset: callback, preset_text: preset_text }, null, { id: device_id } , (err) => { if (err) return Homey.error('Error triggeringDevice:', err); }); Homey.log('Preset was changed!'); diff --git a/drivers/thermometer/assets/images/large.jpg b/drivers/thermometer/assets/images/large.jpg old mode 100755 new mode 100644 index 811c7c9d583cc42e0d95b4ee49dfc391dfacdd24..4d233459a6fcc502fc075336545e364919471f93 GIT binary patch delta 17662 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�LZtiPR;ggI<1gD&+$XS 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 literal 27576 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 E*{+fIL;wH) delta 1725 zcmV;u215CT4(1cEJ_CPI#ziRLQXT~;=|Big8KuP>Py%L_v-K|!X?igK08YQQwLO+u zLb3V|rm!wPCuyE(ipt*iQI{Kn#SX!bT*UU5Z zn`=8OM!9)o2LyktuF=FWa1Kudiun)5U+`5;V_%kdv=0s3EC7G6)~}sTdEh|;qBs5g zBi6o+{lC5eAB(>Sq}FuXFSPiV!oo}IWsNsUacn^QOap>RDxuX!SB)$3XH3#%)bHT5 zfur9$sz&bnV>$b$cVKwI_Z9Y;Hcf}jDLS&2+BMaA-~N9CI;c{ORPOdYr{XW|OYyT; z`%HH}Cy&Fnv!L4_hLSsF!*nd+eB6=TN!zV?*Tdh6-YWQg@e@z+ou;>Yd9PYbvVE%h z>E(Ij2j+rDa#+OUAgLq_?jJIPU5|$RA$55h=}miOs9lsX7<4;V)Nb8*PzuJp0gmAC zM@scS3;ut=-aqlBipOQ)E1e88D}QTviur9<9m2q&oQ!nbp@*(>Ur$dj%dj-k#M=Gd ziTs|Qbwij)t z-HDeaI!L4Vm;6Uj{{YvopMPXu*@ImDn>3AT%Ta&vbXx7ErqRwc>uW<4Hq$7>NUqFT zi!RW31LOx8&VGX_UzlSdMxA-agt?*7`F#5xOp;GSvW`WXHFP17hCv<0I=TG@{{Zar zeUd}_w>gZS$GZOjhvQuZdCgd)?tlN$`h)h*(r&&P_?y9>9Q25prSQGS*|n(S2Q%rC z;b(sb9d?XJ2dqGMuYR(;w$&_cuWaR6?Czpj;gt?UqYSAZfvt}l_)^ox{u9-7E9ll+ zYwLD}mBvRSiusfFFYz7LjiLCH;SRSmKDqGz@9eh5ULI{WOtrHQP=jyM1_X%mJ+`P87`#aF}KQJanTv#|y2GFN%VZ=`i93$s_(g9IRbI{w(p*wWUg>X>wDI*e`lq zt8?f!jJet}TejYy*H>XIlG()4kKSVpM{{2r{09A`zAk>yzZ7(+t^Oacf_@vuWSaK= z?kQS(lne4kj^Z#25~P^Jj4KVpAlHA{k)p(kh&HN>06GeIiZjE@lBWLveT#5#c4z<6 z{Zg-ve`+Wn;1|b#1^jEXLmrE8<3sTJ# z7{>*~dcwmJr$s(Yu)L5GG5fgkpyz2Ln)FWyf59>Cd`+mutb9`O)~VucLm`?t9l#yP z^XD%oUh%Adg?g_AhNtgTuC0I4%J2RM%+b?=-dY{k#DDlHSHeF8?t?%V{x-cVQ69!I z4XZXY#PXF!{{Vn)KGnc!pR{M~EAjA5z83I6@HUR_sU6gjv}CDLOL^A_r1f996L;kH z`lsQa?5FV8;Ymq!9}8Gp-l)O8l0Q1>zuulg&VMmpuLYbI-|C`NKDd9${QB1=YM50A z3NUy6X7&5FIq?vMT7J$AuQcq>gS;#M00jH-myNZi*FGxzMZVT Date: Thu, 16 Feb 2017 16:02:02 +0100 Subject: [PATCH 025/661] Try catch removed to force crash on Energylink --- drivers/energylink/driver.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 75d101ce..74bb89cc 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -158,7 +158,7 @@ function getStatus(device_id) { var homewizard_id = devices[device_id].settings.homewizard_id; homewizard.getDeviceData(homewizard_id, 'energylinks', function(callback) { if (Object.keys(callback).length > 0) { - try { + module.exports.setAvailable({id: device_id}); var value_s1 = ( callback[0].t1 ) ; // Read t1 from energylink (solar/water/null) @@ -232,12 +232,8 @@ function getStatus(device_id) { console.log("S1 Daytotal- "+ energy_daytotal_prod); Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: energy_daytotal_prod }, null, { id: device_id }); } - } - catch(err) { - // Error with Energylink no data in Energylink - console.log ("No Energylink found"); - module.exports.setUnavailable({id: device_id}, "No Energylink found" ); - } + + } }); } else { From 2408bb35f9fb1d81131d6729863eb8dc1be52a89 Mon Sep 17 00:00:00 2001 From: jtebbens Date: Thu, 16 Feb 2017 23:09:50 +0100 Subject: [PATCH 026/661] Try / catch around gas info Some "slimme meters" dont have gas information, hence fail on read data --- drivers/energylink/driver.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 99e5c032..7f6452d6 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -163,7 +163,7 @@ function getStatus(device_id) { var homewizard_id = devices[device_id].settings.homewizard_id; homewizard.getDeviceData(homewizard_id, 'energylinks', function(callback) { if (Object.keys(callback).length > 0) { - + try { module.exports.setAvailable({id: device_id}); var value_s1 = ( callback[0].t1 ) ; // Read t1 from energylink (solar/water/null) @@ -175,7 +175,17 @@ function getStatus(device_id) { // Common Energylink data var energy_current_cons = ( callback[0].used.po ); // WATTS Energy used JSON $energylink[0]['used']['po'] var energy_daytotal_cons = ( callback[0].used.dayTotal ); // KWH Energy used JSON $energylink[0]['used']['po'] - var gas_daytotal_cons = ( callback[0].gas.dayTotal ); // m3 Energy produced via S1 $energylink[0]['gas']['dayTotal'] + + // Some Energylink do not have gas information so try to get it else fail silently + try { + var gas_daytotal_cons = ( callback[0].gas.dayTotal ); // m3 Energy produced via S1 $energylink[0]['gas']['dayTotal'] + // Consumed gas + module.exports.realtime( { id: device_id }, "meter_gas", gas_daytotal_cons ); + } + catch(err) { + // Error with Energylink no data in Energylink + console.log ("No Gas information found"); + } // Consumed elec current module.exports.realtime( { id: device_id }, "measure_power.used", energy_current_cons ); @@ -237,8 +247,12 @@ function getStatus(device_id) { console.log("S1 Daytotal- "+ energy_daytotal_prod); Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: energy_daytotal_prod }, null, { id: device_id }); } - - + } + catch(err) { + // Error with Energylink no data in Energylink + console.log ("No Energylink found"); + module.exports.setUnavailable({id: device_id}, "No Energylink found" ); + } } }); } else { From 2a6c652c9793ce9748ef77d04f1f070f43630e73 Mon Sep 17 00:00:00 2001 From: jtebbens Date: Wed, 5 Apr 2017 22:52:01 +0200 Subject: [PATCH 027/661] Energylink aggregated kwh --- app.json | 31 ++++++++++++++- drivers/energylink/driver.js | 76 +++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index 7f1339d4..0f357a59 100755 --- a/app.json +++ b/app.json @@ -126,6 +126,29 @@ "nl": "kWh" } }] + }, { + "id": "meter_power_aggregated_changed", + "title": { + "en": "Overall usage changed", + "nl": "Netto verbruik veranderd" + }, + "args": [{ + "name": "Energylink", + "type": "device", + "filter": "driver_id=energylink", + "placeholder": { + "en": "Which energylink", + "nl": "Welke energylink" + } + }], + "tokens": [{ + "name": "power_daytotal_aggr", + "type": "number", + "title": { + "en": "kWh", + "nl": "kWh" + } + }] }, { "id": "meter_power_s1_changed", "title": { @@ -356,7 +379,7 @@ }, "class": "sensor", "capabilities": [ - "meter_power.used", "meter_power.s1", "meter_gas", "measure_power.used", "measure_power.s1", "meter_water" + "meter_power.used", "meter_power.aggr", "meter_power.s1", "meter_gas", "measure_power.used", "measure_power.s1", "meter_water" ], "capabilitiesOptions": { @@ -366,6 +389,12 @@ "nl": "Dag gebruik" } }, + "meter_power.aggr": { + "title": { + "en": "Overall usage", + "nl": "Netto gebruik" + } + }, "meter_power.s1": { "title": { "en": "Day production", diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 7f6452d6..7120f695 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -110,6 +110,17 @@ module.exports.capabilities = { } } }, + "meter_power.aggr": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_power_aggr); + } + } + }, "meter_power.s1": { get: function (device_data, callback) { var device = devices[device_data.id]; @@ -142,6 +153,61 @@ module.exports.capabilities = { callback(null, device.last_meter_water); } } + }, + "meter_power.cons-t1": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_power_cons_t1); + } + } + }, + "meter_power.prod-t1": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_power_prod_t1); + } + } + }, + "meter_power.cons-t2": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_power_cons_t2); + } + } + }, + "meter_power.prod-t2": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_power_prod_t2); + } + } + }, + meter_gas.smart: { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_gas_smart); + } + } } }; @@ -174,7 +240,8 @@ function getStatus(device_id) { // Common Energylink data var energy_current_cons = ( callback[0].used.po ); // WATTS Energy used JSON $energylink[0]['used']['po'] - var energy_daytotal_cons = ( callback[0].used.dayTotal ); // KWH Energy used JSON $energylink[0]['used']['po'] + var energy_daytotal_cons = ( callback[0].used.dayTotal ); // KWH Energy used JSON $energylink[0]['used']['dayTotal'] + var energy_daytotal_aggr = ( callback[0].aggregate.dayTotal ) ; // KWH Energy aggregated is used - generated $energylink[0]['aggregate']['dayTotal'] // Some Energylink do not have gas information so try to get it else fail silently try { @@ -191,6 +258,8 @@ function getStatus(device_id) { module.exports.realtime( { id: device_id }, "measure_power.used", energy_current_cons ); // Consumed elec total day module.exports.realtime( { id: device_id }, "meter_power.used", energy_daytotal_cons ); + // Consumed elec total day + module.exports.realtime( { id: device_id }, "meter_power.aggr", energy_daytotal_aggr ); // Consumed gas module.exports.realtime( { id: device_id }, "meter_gas", gas_daytotal_cons ); @@ -247,6 +316,11 @@ function getStatus(device_id) { console.log("S1 Daytotal- "+ energy_daytotal_prod); Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: energy_daytotal_prod }, null, { id: device_id }); } + if (energy_daytotal_aggr != devices[device_id].last_meter_power_aggr) { + console.log("Aggregated Daytotal- "+ energy_daytotal_aggr); + Homey.manager('flow').triggerDevice('meter_power_aggregated_changed', { power_daytotal_aggr: energy_daytotal_aggr }, null, { id: device_id }); + } + } catch(err) { // Error with Energylink no data in Energylink From aa792e7a50453e794b8cb6525020907ae6653505 Mon Sep 17 00:00:00 2001 From: jtebbens Date: Wed, 5 Apr 2017 23:36:24 +0200 Subject: [PATCH 028/661] Removed Unexpectoken error --- drivers/energylink/driver.js | 56 +----------------------------------- 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 7120f695..8743dfe1 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -153,62 +153,8 @@ module.exports.capabilities = { callback(null, device.last_meter_water); } } - }, - "meter_power.cons-t1": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_power_cons_t1); - } - } - }, - "meter_power.prod-t1": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_power_prod_t1); - } - } - }, - "meter_power.cons-t2": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_power_cons_t2); - } - } - }, - "meter_power.prod-t2": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_power_prod_t2); - } - } - }, - meter_gas.smart: { - get: function (device_data, callback) { - var device = devices[device_data.id]; + } - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_gas_smart); - } - } - } }; // Start polling From 61ccdc90260f1788bae35bd1a7b7fc4ebafef343 Mon Sep 17 00:00:00 2001 From: jtebbens Date: Fri, 7 Apr 2017 11:26:57 +0200 Subject: [PATCH 029/661] Energylink code fixes and firmware 1.2.0 bugfix --- README.md | 11 +++++++++++ drivers/energylink/driver.js | 3 +-- drivers/thermometer/driver.js | 2 +- includes/homewizard.js | 6 +++--- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d0158fcc..1403b6b9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,17 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) +v0.1.3: +Energylink updates: +* s1 and s2 are now giving information on either solar or water (whatever is connected to it) +* "Netto verbruik" feature added, it will now show the aggregated power usage which can be negative if your solarpanels do their job. ;) +* SIDENOTE: You need to redo/add Energylink device to make the "Netto verbruik" visible +Other updates: +* Made fixes for app to work on 1.2.0 Firmware (Thermometer & Scenes) + + +v0.1.2: +Updated Polling method to avoid traffic overhead and timing issues V0.1.1: diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 8743dfe1..fc1993cf 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -206,8 +206,7 @@ function getStatus(device_id) { module.exports.realtime( { id: device_id }, "meter_power.used", energy_daytotal_cons ); // Consumed elec total day module.exports.realtime( { id: device_id }, "meter_power.aggr", energy_daytotal_aggr ); - // Consumed gas - module.exports.realtime( { id: device_id }, "meter_gas", gas_daytotal_cons ); + if (value_s1 == 'solar' ) { var energy_current_prod = ( callback[0].s1.po ); // WATTS Energy produced via S1 $energylink[0]['s1']['po'] diff --git a/drivers/thermometer/driver.js b/drivers/thermometer/driver.js index ebd5539c..42741425 100755 --- a/drivers/thermometer/driver.js +++ b/drivers/thermometer/driver.js @@ -31,7 +31,7 @@ module.exports.pair = function( socket ) { }); socket.on('manual_add', function (device, callback) { - if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { + if (typeof device.settings.homewizard_id == "string" && device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { //true Homey.log('Thermometer added ' + device.data.id); devices[device.data.id] = { diff --git a/includes/homewizard.js b/includes/homewizard.js index 533ee37d..1bf48542 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -74,7 +74,7 @@ module.exports = (function(){ timeout: 10000, }, function (error, response, body) { if (response === null || response === undefined) { - callback(false); + callback('No response', []); return; } if (!error && response.statusCode == 200) { @@ -90,11 +90,11 @@ module.exports = (function(){ } catch (exception) { Homey.log('JSON: '+ body); jsonObject = null; - callback(false); + callback('Invalid data', []); } } else { if(typeof callback === 'function') { - callback(false); + callback('Error', []); } Homey.log('Error: '+error); } From 0cc3af2cc199aa72159165e67132abbb54181416 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Fri, 7 Apr 2017 14:10:13 +0200 Subject: [PATCH 030/661] Version bump --- app.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index 0f357a59..9fcc2341 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.2", + "version": "0.1.3", "compatibility": ">=0.9", "description": { "en": "Control HomeWizard using Homey" @@ -520,4 +520,4 @@ "permissions": [ "homey:manager:ledring" ] -} \ No newline at end of file +} From a49e1c39f68909a8bc69290070d1c76fc17fe5d2 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Sun, 9 Apr 2017 22:37:07 +0200 Subject: [PATCH 031/661] Version bump --- app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.json b/app.json index 9fcc2341..c2bd0052 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.3", + "version": "0.1.4", "compatibility": ">=0.9", "description": { "en": "Control HomeWizard using Homey" From 7676da44db488452540949f35e9de5325d92768d Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Fri, 10 Nov 2017 08:38:07 +0100 Subject: [PATCH 032/661] Bugfix for making scenes work again Big thanks to Jeroen Tebbens! --- README.md | 2 ++ app.json | 2 +- includes/homewizard.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1403b6b9..4ae0a667 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) +v0.1.5: Bugfix for making scenes work again (big thanks to Jeroen Tebbens!) + v0.1.3: Energylink updates: * s1 and s2 are now giving information on either solar or water (whatever is connected to it) diff --git a/app.json b/app.json index c2bd0052..92851cb0 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.4", + "version": "0.1.5", "compatibility": ">=0.9", "description": { "en": "Control HomeWizard using Homey" diff --git a/includes/homewizard.js b/includes/homewizard.js index 1bf48542..f851f283 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -106,7 +106,7 @@ module.exports = (function(){ }; homewizard.getScenes = function(args, callback) { - this.call(args.args.device.id, '/gplist', function(err, response) { + this.call(args.args.device.data.id, '/gplist', function(err, response) { if (err === null) { var output = []; for (var i = 0, len = response.length; i < len; i++) { From 24f3812c533af2b4cd2ae6c5d5f7d075f060693c Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Fri, 10 Nov 2017 14:04:10 +0100 Subject: [PATCH 033/661] Upped timeout for scene switching --- includes/homewizard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/homewizard.js b/includes/homewizard.js index f851f283..7f132bab 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -71,7 +71,7 @@ module.exports = (function(){ request({ uri: 'http://' + homewizard_ip + '/' + homewizard_pass + uri_part, method: "GET", - timeout: 10000, + timeout: 20000, }, function (error, response, body) { if (response === null || response === undefined) { callback('No response', []); From fa3e976b20bc8faf767b40420f8f05b08bfc4e6c Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Fri, 10 Nov 2017 22:40:08 +0100 Subject: [PATCH 034/661] Rain meter, water meter and bug fixes Rain meter added (not fully tested yet but doesnt crash) Water meter - liter per minute Bug fixes for scenes --- README.md | 9 ++ app.json | 59 ++++++++- drivers/energylink/driver.js | 18 ++- drivers/homewizard/driver.js | 3 + drivers/rainmeter/assets/images/large.jpg | Bin 0 -> 16595 bytes drivers/rainmeter/assets/images/meter.svg | Bin 0 -> 10482 bytes drivers/rainmeter/assets/images/small.jpg | Bin 0 -> 1641 bytes drivers/rainmeter/driver.js | 142 ++++++++++++++++++++++ drivers/rainmeter/pair/start.html | 84 +++++++++++++ includes/homewizard.js | 14 ++- 10 files changed, 317 insertions(+), 12 deletions(-) create mode 100644 drivers/rainmeter/assets/images/large.jpg create mode 100644 drivers/rainmeter/assets/images/meter.svg create mode 100644 drivers/rainmeter/assets/images/small.jpg create mode 100644 drivers/rainmeter/driver.js create mode 100644 drivers/rainmeter/pair/start.html diff --git a/README.md b/README.md index 1403b6b9..9287d226 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,15 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) +v0.1.5: +* Added rainmeter +* Added windmeter (Work in progress) +* SIDENOTE just as the Energylink, heatlink etc you need to add Rain and windmeter separately. + Verify homewizard has its windmeter units set to km/h else you get funny measures + +v0.1.4: +* Bug fixes + v0.1.3: Energylink updates: * s1 and s2 are now giving information on either solar or water (whatever is connected to it) diff --git a/app.json b/app.json index 0f357a59..daa9f6d9 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.2", + "version": "0.1.5", "compatibility": ">=0.9", "description": { "en": "Control HomeWizard using Homey" @@ -379,7 +379,7 @@ }, "class": "sensor", "capabilities": [ - "meter_power.used", "meter_power.aggr", "meter_power.s1", "meter_gas", "measure_power.used", "measure_power.s1", "meter_water" + "meter_power.used", "meter_power.aggr", "meter_power.s1", "meter_gas", "measure_power.used", "measure_power.s1", "meter_water", "measure_water" ], "capabilitiesOptions": { @@ -424,8 +424,14 @@ }, "meter_water": { "title": { - "en": "Water", - "nl": "Water" + "en": "Water Total", + "nl": "Water Totaal" + } + }, + "measure_water": { + "title": { + "en": "Water l./m", + "nl": "Water l./m" } } }, @@ -504,6 +510,51 @@ }, + "pair": [{ + "id": "start" + }, { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, { + "id": "add_my_devices", + "template": "add_devices" + }] + }, { + "id": "rainmeter", + "name": { + "en": "Rainmeter", + "nl": "Regen meter" + }, + "images": { + "large": "drivers/rainmeter/assets/images/large.jpg", + "small": "drivers/rainmeter/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_rain" + ], + + "capabilitiesOptions": { + "measure_rain.3h": { + "title": { + "en": "Last 3 hours rain", + "nl": "Laatste 3 uur regen" + } + }, + + "measure_rain.total": { + "title": { + "en": "Rainfall today", + "nl": "Regenval vandaag" + } + + } + + }, + "pair": [{ "id": "start" }, { diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index fc1993cf..eff55c3d 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -153,6 +153,17 @@ module.exports.capabilities = { callback(null, device.last_meter_water); } } + }, + measure_water: { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_water); + } + } } }; @@ -229,19 +240,22 @@ function getStatus(device_id) { } if (value_s1 == 'water' ) { - // var water_current_cons = ( callback[0].s1.po ); // Water used via S1 $energylink[0]['s1']['po'] + var water_current_cons = ( callback[0].s1.po ); // Water used via S1 $energylink[0]['s1']['po'] var water_daytotal_cons = ( callback[0].s1.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s1']['dayTotal'] console.log("Water- " + water_daytotal_cons); // Used water m3 module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); + module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); + } if (value_s2 == 'water' ) { - // var water_current_cons = ( callback[0].s2.po ); // Water used via S1 $energylink[0]['s1']['po'] + var water_current_cons = ( callback[0].s2.po ); // Water used via S2 $energylink[0]['s1']['po'] var water_daytotal_cons = ( callback[0].s2.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s2']['dayTotal'] console.log("Water- " + water_daytotal_cons); // Used water m3 module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); + module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); } // Trigger flows diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 5d3ac8a4..ed0bfd65 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -96,12 +96,15 @@ module.exports.deleted = function( device_data ) { // SCENES Homey.manager('flow').on('action.switch_scene_on.scene.autocomplete', function( callback, args ){ + Homey.log('getScenes autocomplete called'); homewizard.getScenes(args, function(err, response) { callback(err, response ); // err, results }); }); Homey.manager('flow').on('action.switch_scene_on', function( callback, args ){ + Homey.log('args.device.id: ' + JSON.stringify(args.device.id)); + Homey.log('args.scene.id: ' + JSON.stringify(args.scene.id)); homewizard.call(args.device.id, '/gp/' + args.scene.id + '/on', function(err, response) { if (err === null) { Homey.log('Scene is on'); diff --git a/drivers/rainmeter/assets/images/large.jpg b/drivers/rainmeter/assets/images/large.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2fdd85c2416da549dca4e36f65726336f9d3ca8d GIT binary patch literal 16595 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 P|KErIE5?!Ndh&k&Z%c6n literal 0 HcmV?d00001 diff --git a/drivers/rainmeter/assets/images/meter.svg b/drivers/rainmeter/assets/images/meter.svg new file mode 100644 index 0000000000000000000000000000000000000000..cf9411ac7dcb7422f6f5a22f44e47fac6c94bf9e GIT binary patch literal 10482 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 bvF9)S;xrS%&QY9)G%+=0EhW@_tLOg(nNjxk literal 0 HcmV?d00001 diff --git a/drivers/rainmeter/assets/images/small.jpg b/drivers/rainmeter/assets/images/small.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9574d024bc1bddc5f7f58d36c6bd5b25d164284c GIT binary patch literal 1641 zcmbW#dpML?7y$6|%{ODlxK3tVYO7(iRPL9Mdv@Hd8fwH+C~9QIxJPR!wG&Cor4&Xa z)pQ$LDpEqUxvfy?f~c*g88hTEd^>8N-RIfAcK1E+IscsZoag;L=O6>fE1>M|?B)z0 z2mnYr0I~qAooG}?UvJuaa`+B?#@?ObXtf?W zIxcw6?${)|UQSLyafz~mB0*73 zPFYo%ph6^(Nb*W*>Z(NbB}5W&@e&A@oIb5OBZ)c)*YrKoKCA z03pp_r4)&Qz6AI)Ae6L687xi~FDEUiQ3faohEZsEv1@5{s&pTq3793C#*Q*7n}V@g z(L@tgZW)fU;aU@k)-BLB4T(vY#jC2RYb;&$t&Z;MHD>1PEG(_oJAFrWc5!uc_wn7l zh3>c2KXk{=u<%{G8O+$Y_=Loy)W>FeQ<(Kln`Z{PhcoDhj8r#?^5%r3eh0Domk z*RQaDxCl}g3XO)**hLqFN{~*NfW~MV%Peu+gbj{X(K2D-h#PXtt~JS0OlbmANK7|g zRom?KD&ZpSi|pUQ(*Kw2FWBF%r$8Qtq>l#^fIXO}agMWr4?CN$mG$}@)ov6&oxo6dN|M8srh{9?)GW6Wa&Ex1 z=xuh-_jla1dK9FYg@8MI=m-ci$IYKfajCqpZ<}grNt9&5L}$WmYl0l*?NM9Zk)DPm z!P&auC~V@0XZI(G3-e-Aw8;kH_ZD1@T0wxG@!4j27w>@nH?*J5^1{+o+1{d_98pi2 zhDJxnhnHRSlEk@|`l-B&sTm$UL1Toac{UbPU7mgjh|^i-7dvhnV@*$rB1fD)!ROkG z2_A8wn6X~HeQdD}JO8vl)FM%x&WILT+}%=YUzIPlO&8__AWax)Q>BjqC-+opXsU%`dFG z&7ugq>VGI$?|eRrGcP{fQDjtNR`R$kb zbtSCg#Fj;O*32Ps%alqf{152`xwA$bF^ZaSO?t z7LpggVdMB^V+=8#lZJnDJ5n&w+L}IO_{p#|Q@K5)rckTxa#m)WtwW15t`kl7p@Pl5 zW*vcQp1j@<+dTr-y3%mutl-dW-!)Y$(!iQN;zo>@hx_#>k=Zr##cEGR(?jaAcX-ae uN?)U(a^K%Ov+R_Jw6yb?n2tqr<$aQWV>Ew~{|V2LAw2cfhLv literal 0 HcmV?d00001 diff --git a/drivers/rainmeter/driver.js b/drivers/rainmeter/driver.js new file mode 100644 index 00000000..0f978867 --- /dev/null +++ b/drivers/rainmeter/driver.js @@ -0,0 +1,142 @@ +var devices = {}; +var homewizard = require('./../../includes/homewizard.js'); +var refreshIntervalId = 0; + +// SETTINGS +module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { + Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); + try { + changedKeysArr.forEach(function (key) { + devices[device_data.id].settings[key] = newSettingsObj[key]; + }); + callback(null, true); + } catch (error) { + callback(error); + } +}; + +module.exports.pair = function( socket ) { + socket.on('get_homewizards', function () { + homewizard.getDevices(function(homewizard_devices) { + Homey.log(homewizard_devices); + var hw_devices = {}; + Object.keys(homewizard_devices).forEach(function(key) { + hw_devices[key] = homewizard_devices[key]; + }); + + socket.emit('hw_devices', hw_devices); + }); + }); + + socket.on('manual_add', function (device, callback) { + if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { + //true + Homey.log('Rainmeter added ' + device.data.id); + devices[device.data.id] = { + id: device.data.id, + name: device.name, + settings: device.settings, + }; + callback( null, devices ); + socket.emit("success", device); + startPolling(); + } else { + socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); + } + }); + + socket.on('disconnect', function(){ + console.log("User aborted pairing, or pairing is finished"); + }); +} + +module.exports.init = function(devices_data, callback) { + devices_data.forEach(function initdevice(device) { + Homey.log('add device: ' + JSON.stringify(device)); + devices[device.id] = device; + module.exports.getSettings(device, function(err, settings){ + devices[device.id].settings = settings; + }); + }); + if (Object.keys(devices).length > 0) { + startPolling(); + } + Homey.log('Rainmeter driver init done'); + + callback (null, true); +}; + +module.exports.deleted = function( device_data ) { + delete devices[device_data.id]; + if (Object.keys(devices).length === 0) { + clearInterval(refreshIntervalId); + console.log("--Stopped Polling Rainmeter--"); + } + Homey.log('deleted: ' + JSON.stringify(device_data)); +}; + +module.exports.capabilities = { + measure_rain: { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.raintotal); + } + } + } +}; + +// Start polling +function startPolling() { + if(refreshIntervalId){ + clearInterval(refreshIntervalId); + } + refreshIntervalId = setInterval(function () { + console.log("--Start Rainmeter Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getStatus(device_id); + }); + }, 1000 * 10); +} + +function getStatus(device_id) { + if(devices[device_id].settings.homewizard_id !== undefined ) { + var homewizard_id = devices[device_id].settings.homewizard_id; + homewizard.getDeviceData(homewizard_id, 'rainmeters', function(callback) { + if (Object.keys(callback).length > 0) { + try { + module.exports.setAvailable({id: device_id}); + var hours3 = "3h"; + var rain_daytotal = ( callback[0].mm ); // Total Rain in mm used JSON $rainmeters[0]['mm'] + var rain_last3h = ( callback[0].hours3 ); // Last 3 hours rain in mm used JSON $rainmeters[0]['3h'] + + // Rain last 3 hours + module.exports.realtime( { id: device_id }, "measure_rain.3h", rain_last3h ); + // Rain total day + module.exports.realtime( { id: device_id }, "measure_rain.total", rain_daytotal ); + + console.log("Rainmeter 3h- "+ rain_last3h); + console.log("Rainmeter Daytotal- "+ rain_daytotal); + } catch (err) { + // Error with Rain no data in Rainmeters + console.log ("No Rainmeter found"); + module.exports.setUnavailable({id: device_id}, "No Rainmeter found" ); + } + } + }); + } else { + Homey.log('Removed Rainmeter '+ device_id +' (old settings)'); + module.exports.setUnavailable({id: device_id}, "No Rainmeter found" ); + // Only clear interval when the unavailable device is the only device on this driver + // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new + // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem + if(Object.keys(devices).length === 1) { + clearInterval(refreshIntervalId); + } + } +} + + diff --git a/drivers/rainmeter/pair/start.html b/drivers/rainmeter/pair/start.html new file mode 100644 index 00000000..cc83dafd --- /dev/null +++ b/drivers/rainmeter/pair/start.html @@ -0,0 +1,84 @@ + + + + + + + +

+
+ +
+
+

+ + + diff --git a/includes/homewizard.js b/includes/homewizard.js index 1bf48542..05abf2ab 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -12,7 +12,7 @@ module.exports = (function(){ "switches":[], "uvmeters":[], "windmeters":[], - "rainmeters":[], + "rainmeters":[{"id":1,"name":"Regenmeter","code":"6996438","model":1,"lowBattery":"no","version":2.32,"mm":0.0,"3h":0.0,"favorite":"no"}], "thermometers":[ {"id":1,"name":"Kantoor","channel":2,"model":0,"te":20.0,"hu":5,"te+":20.4,"te+t":"12:15","te-":19.6,"te-t":"07:06","hu+":5,"hu+t":"00:00","hu-":5,"hu-t":"00:00","outside":"no","favorite":"no"}, {"id":2,"name":"Slaapkamer","channel":5,"model":0,"te":18.8,"hu":47,"te+":19.1,"te+t":"09:06","te-":17.7,"te-t":"11:34","hu+":49,"hu+t":"07:39","hu-":43,"hu-t":"10:51","outside":"no","favorite":"no"} @@ -71,7 +71,7 @@ module.exports = (function(){ request({ uri: 'http://' + homewizard_ip + '/' + homewizard_pass + uri_part, method: "GET", - timeout: 10000, + timeout: 20000, }, function (error, response, body) { if (response === null || response === undefined) { callback('No response', []); @@ -106,10 +106,12 @@ module.exports = (function(){ }; homewizard.getScenes = function(args, callback) { - this.call(args.args.device.id, '/gplist', function(err, response) { + this.call(args.args.device.data.id, '/gplist', function(err, response) { + Homey.log('Error: '+err); + Homey.log('Response: '+response); if (err === null) { var output = []; - for (var i = 0, len = response.length; i < len; i++) { + for (var i = 0, len = response.length; i < len; i++) { if (response[i].name.toLowerCase().indexOf(args.query.toLowerCase()) !== -1) { output[output.length] = response[i]; } @@ -119,7 +121,7 @@ module.exports = (function(){ } } else { callback(err); // err - } + } }); }; @@ -160,7 +162,7 @@ module.exports = (function(){ self.devices[device_id].polldata.energylinks = response.energylinks; self.devices[device_id].polldata.energymeters = response.energymeters; self.devices[device_id].polldata.thermometers = response.thermometers; - + self.devices[device_id].polldata.rainmeters = response.rainmeters; Homey.log('HW-Data polled for: '+device_id); } }); From 702ab9c9cc1cb2da1d74087f406a920f27040a9f Mon Sep 17 00:00:00 2001 From: Jeroen Date: Fri, 10 Nov 2017 22:57:30 +0100 Subject: [PATCH 035/661] Version bump --- app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.json b/app.json index 9be8b1d6..f6199ff6 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.5", + "version": "0.1.6", "compatibility": ">=0.9", "description": { "en": "Control HomeWizard using Homey" From f42d1b6bf9ea61e4f562bd129df556b718f1ebc0 Mon Sep 17 00:00:00 2001 From: Jeroen Date: Fri, 10 Nov 2017 22:58:56 +0100 Subject: [PATCH 036/661] Removed windmeter, not present yet --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5861f58d..b8d4a167 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard v0.1.6: * Added rainmeter -* Added windmeter (Work in progress) * SIDENOTE just as the Energylink, heatlink etc you need to add Rain and windmeter separately. Verify homewizard has its windmeter units set to km/h else you get funny measures From 9be59ed758e864f87794ad1d57551410e29ccc15 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Fri, 10 Nov 2017 23:57:53 +0100 Subject: [PATCH 037/661] Rain meter fix Rain meter fix. Capabilities missed and variable handling adjusted. --- app.json | 6 +++--- drivers/rainmeter/driver.js | 32 +++++++++++++++++++++----------- includes/homewizard.js | 2 +- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/app.json b/app.json index daa9f6d9..93f2a986 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.5", + "version": "0.1.6", "compatibility": ">=0.9", "description": { "en": "Control HomeWizard using Homey" @@ -534,11 +534,11 @@ }, "class": "sensor", "capabilities": [ - "measure_rain" + "measure_rain.last3h", "measure_rain.total" ], "capabilitiesOptions": { - "measure_rain.3h": { + "measure_rain.last3h": { "title": { "en": "Last 3 hours rain", "nl": "Laatste 3 uur regen" diff --git a/drivers/rainmeter/driver.js b/drivers/rainmeter/driver.js index 0f978867..1f10b1af 100644 --- a/drivers/rainmeter/driver.js +++ b/drivers/rainmeter/driver.js @@ -76,14 +76,25 @@ module.exports.deleted = function( device_data ) { }; module.exports.capabilities = { - measure_rain: { + "measure_rain.last3h": { get: function (device_data, callback) { var device = devices[device_data.id]; if (device === undefined) { callback(null, 0); } else { - callback(null, device.raintotal); + callback(null, device.last_rainlast3h); + } + } + }, + "measure_rain.total": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_raintotal); } } } @@ -108,18 +119,17 @@ function getStatus(device_id) { homewizard.getDeviceData(homewizard_id, 'rainmeters', function(callback) { if (Object.keys(callback).length > 0) { try { + Homey.log ("Callback: ") + JSON.stringify(callback); module.exports.setAvailable({id: device_id}); - var hours3 = "3h"; var rain_daytotal = ( callback[0].mm ); // Total Rain in mm used JSON $rainmeters[0]['mm'] - var rain_last3h = ( callback[0].hours3 ); // Last 3 hours rain in mm used JSON $rainmeters[0]['3h'] - - // Rain last 3 hours - module.exports.realtime( { id: device_id }, "measure_rain.3h", rain_last3h ); - // Rain total day - module.exports.realtime( { id: device_id }, "measure_rain.total", rain_daytotal ); + var rain_last3h = ( callback[0]['3h'] ); // Last 3 hours rain in mm used JSON $rainmeters[0]['3h'] + // Rain last 3 hours + module.exports.realtime( { id: device_id }, "measure_rain.last3h", rain_last3h ); + // Rain total day + module.exports.realtime( { id: device_id }, "measure_rain.total", rain_daytotal ); - console.log("Rainmeter 3h- "+ rain_last3h); - console.log("Rainmeter Daytotal- "+ rain_daytotal); + console.log("Rainmeter 3h- "+ rain_last3h); + console.log("Rainmeter Daytotal- "+ rain_daytotal); } catch (err) { // Error with Rain no data in Rainmeters console.log ("No Rainmeter found"); diff --git a/includes/homewizard.js b/includes/homewizard.js index 05abf2ab..1bdd0b0f 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -12,7 +12,7 @@ module.exports = (function(){ "switches":[], "uvmeters":[], "windmeters":[], - "rainmeters":[{"id":1,"name":"Regenmeter","code":"6996438","model":1,"lowBattery":"no","version":2.32,"mm":0.0,"3h":0.0,"favorite":"no"}], + "rainmeters":[{"id":1,"name":"Regenmeter","code":"6996438","model":1,"lowBattery":"no","version":2.32,"mm":3.0,"3h":1.0,"favorite":"no"}], "thermometers":[ {"id":1,"name":"Kantoor","channel":2,"model":0,"te":20.0,"hu":5,"te+":20.4,"te+t":"12:15","te-":19.6,"te-t":"07:06","hu+":5,"hu+t":"00:00","hu-":5,"hu-t":"00:00","outside":"no","favorite":"no"}, {"id":2,"name":"Slaapkamer","channel":5,"model":0,"te":18.8,"hu":47,"te+":19.1,"te+t":"09:06","te-":17.7,"te-t":"11:34","hu+":49,"hu+t":"07:39","hu-":43,"hu-t":"10:51","outside":"no","favorite":"no"} From b1566fb357645a7a7d3c4ccc406dc2abff639c8f Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Sat, 11 Nov 2017 11:43:07 +0100 Subject: [PATCH 038/661] Energylink preps Slimme meter polling --- includes/homewizard.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/includes/homewizard.js b/includes/homewizard.js index 4bc31ad6..021074f9 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -19,6 +19,7 @@ module.exports = (function(){ ], "weatherdisplays":[], "energymeters": [], + //"energylinks": [], "energylinks": [ {"id":0,"favorite":"no","name":"EnergyLink","code":"942991","t1":"solar","c1":1000,"t2":"water","c2":1,"tariff":1,"s1":{"po":0,"dayTotal":10.24,"po+":2498,"po+t":"11:22","po-":0,"po-t":"00:01"},"s2":{"po":4,"dayTotal":162.00,"po+":7,"po+t":"08:49","po-":0,"po-t":"00:01"},"aggregate":{"po":511,"dayTotal":-3.19,"po+":2873,"po+t":"09:22","po-":-1857,"po-t":"11:55"},"used":{"po":511,"dayTotal":7.04,"po+":3791,"po+t":"11:45","po-":204,"po-t":"16:34"},"gas":{"lastHour":0.44,"dayTotal":4.07},"kwhindex":2.87,"wp":3570} ], @@ -106,7 +107,9 @@ module.exports = (function(){ }; homewizard.getScenes = function(args, callback) { - this.call(args.args.device.data.id, '/gplist', function(err, response) { + this.call(args.args.device.data.id, '/gplist', function(err, response) { + Homey.log('Error: '+err); + Homey.log('Response: '+response); if (err === null) { var output = []; for (var i = 0, len = response.length; i < len; i++) { @@ -162,8 +165,22 @@ module.exports = (function(){ self.devices[device_id].polldata.thermometers = response.thermometers; self.devices[device_id].polldata.rainmeters = response.rainmeters; Homey.log('HW-Data polled for: '+device_id); + Homey.log('typeof: ' +typeof self.devices[device_id].polldata.energylinks); + Homey.log('Object details: ' +JSON.stringify(self.devices[device_id].polldata.energylinks)); + // Object details: [] + if (Object.keys(self.devices[device_id].polldata.energylinks).length === 2 && self.devices[device_id].polldata.energylinks.constructor === Object) {Homey.log('Object has no data')}; + if (JSON.stringify(self.devices[device_id].polldata.energylinks) == "[]") {Homey.log('Object has no data')}; + if (JSON.stringify(self.devices[device_id].polldata.energylinks) != "[]") { // if (self.devices[device_id].polldata.energylinks === null) + homewizard.call(device_id, '/el/get/0/readings', function(err, response2) { + if (err === null) { + self.devices[device_id].polldata.energylink_el = response2.el; + Homey.log('HW-Data polled for slimme meter: '+device_id); + } + }); + } } }); + }); }; From a878053161f76e46335b3a20f4a19da97d2d89e8 Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Sat, 11 Nov 2017 11:52:50 +0100 Subject: [PATCH 039/661] Removed logging, fixed indenting and key count --- includes/homewizard.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/includes/homewizard.js b/includes/homewizard.js index 021074f9..50209ace 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -163,21 +163,16 @@ module.exports = (function(){ self.devices[device_id].polldata.energylinks = response.energylinks; self.devices[device_id].polldata.energymeters = response.energymeters; self.devices[device_id].polldata.thermometers = response.thermometers; - self.devices[device_id].polldata.rainmeters = response.rainmeters; - Homey.log('HW-Data polled for: '+device_id); - Homey.log('typeof: ' +typeof self.devices[device_id].polldata.energylinks); - Homey.log('Object details: ' +JSON.stringify(self.devices[device_id].polldata.energylinks)); - // Object details: [] - if (Object.keys(self.devices[device_id].polldata.energylinks).length === 2 && self.devices[device_id].polldata.energylinks.constructor === Object) {Homey.log('Object has no data')}; - if (JSON.stringify(self.devices[device_id].polldata.energylinks) == "[]") {Homey.log('Object has no data')}; - if (JSON.stringify(self.devices[device_id].polldata.energylinks) != "[]") { // if (self.devices[device_id].polldata.energylinks === null) - homewizard.call(device_id, '/el/get/0/readings', function(err, response2) { - if (err === null) { - self.devices[device_id].polldata.energylink_el = response2.el; - Homey.log('HW-Data polled for slimme meter: '+device_id); - } - }); - } + self.devices[device_id].polldata.rainmeters = response.rainmeters; + + if (Object.keys(response.energylinks) !== 0) { + homewizard.call(device_id, '/el/get/0/readings', function(err, response2) { + if (err === null) { + self.devices[device_id].polldata.energylink_el = response2.el; + Homey.log('HW-Data polled for slimme meter: '+device_id); + } + }); + } } }); From 897d4d1707cc28cbf9c4d3e7eb1250a8a958baae Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Sat, 11 Nov 2017 11:55:25 +0100 Subject: [PATCH 040/661] Bugfix --- includes/homewizard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/homewizard.js b/includes/homewizard.js index 50209ace..1ee21df2 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -165,7 +165,7 @@ module.exports = (function(){ self.devices[device_id].polldata.thermometers = response.thermometers; self.devices[device_id].polldata.rainmeters = response.rainmeters; - if (Object.keys(response.energylinks) !== 0) { + if (Object.keys(response.energylinks).length !== 0) { homewizard.call(device_id, '/el/get/0/readings', function(err, response2) { if (err === null) { self.devices[device_id].polldata.energylink_el = response2.el; From fa5e6907c32354ffeb9652d10047c2c33838d4ac Mon Sep 17 00:00:00 2001 From: jeroenbos22 Date: Sun, 12 Nov 2017 10:15:06 +0100 Subject: [PATCH 041/661] Image resize --- drivers/rainmeter/assets/images/large.jpg | Bin 16595 -> 14415 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/drivers/rainmeter/assets/images/large.jpg b/drivers/rainmeter/assets/images/large.jpg index 2fdd85c2416da549dca4e36f65726336f9d3ca8d..38564b3403986550c2830c4f15ac044b5bc21384 100644 GIT binary patch literal 14415 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 P|KErIE5?!Ndh&k&Z%c6n From 8f4d289d4ab3dc0121ea85d9db2bd36a021efeb2 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Wed, 22 Nov 2017 23:19:59 +0100 Subject: [PATCH 042/661] Windmeter code added Windmeter code --- drivers/windmeter/assets/images/large.jpg | Bin 0 -> 22871 bytes drivers/windmeter/assets/images/small.jpg | Bin 0 -> 2157 bytes drivers/windmeter/assets/images/wind.svg | 13 ++ drivers/windmeter/driver.js | 229 ++++++++++++++++++++++ drivers/windmeter/pair/start.html | 84 ++++++++ includes/homewizard.js | 2 +- 6 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 drivers/windmeter/assets/images/large.jpg create mode 100644 drivers/windmeter/assets/images/small.jpg create mode 100644 drivers/windmeter/assets/images/wind.svg create mode 100644 drivers/windmeter/driver.js create mode 100644 drivers/windmeter/pair/start.html diff --git a/drivers/windmeter/assets/images/large.jpg b/drivers/windmeter/assets/images/large.jpg new file mode 100644 index 0000000000000000000000000000000000000000..da1009f486d5261877b034f701648371494019eb GIT binary patch literal 22871 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 J&%k|r`CkFcT`T|q literal 0 HcmV?d00001 diff --git a/drivers/windmeter/assets/images/small.jpg b/drivers/windmeter/assets/images/small.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8e380b102e96becdbab145c0a5b3f9d25c88ecec GIT binary patch literal 2157 zcmbW2X;f2L635@mOY%YtAd6^AfB-t8C~Ls58J1=rBr+(73!+34kjP?CWD!9WSv#Ph zARt3Skd|Rs6agDiP*9Y8-%LP3KqMeyfP_ra)2HX0`83o2sygSMQ&soW@79Ma8WeGW z%}#cXb^r*1z$vi-A}*j|dx?G)09;&vJpce;#5s8Y5}!b^{U*@{U}58CYvu0hW=in$ z-RB=f^&^Do>*x`*n4xrrAHmVdp5R2WwI_s;^mgwh1T%fk(JzD(EC~8~dRRa-0@wfu z5R~}uf>4k|Xe1Is!Dtwk1V(C;6b6IEz%W)Ci`|413zm|RmBz_P&XP+c?UF9B<1jEr zQt|&u)CkC70B67%0yO}H90?U!+qs`*+AqXKJ z7KKELtE0qu04ax(SJ$^fD;)BHHG&lluCfX+n${K1x43ojb{L)xiIu`CZQb_6_MLy( zrL}tx(a3l|$;8y=M_W642S=yBxgS2_;d#{Sj4$=9AC2xGdVv{s@lyEZxNGs(ZzSAI zOwYJ=J2UIf-R!>?{#;bdE_qN|Sylb$am|z3x))8&FJHB^zJAl))7#fSFgP^KotXUa zaccV0%Wuk12_m2FAtIf z$bgWq_{;YP`Wa57`zX!quW2dIly#$(1B}Crjl;ga-s?@f%^s^^OcZ#~@d9?nz2N8N z_G5{Q`NPXL0f7Z4^a4kU6K-2KC#z2b%Pn)3szsc%ZC?C^CgZx?yTY-?)y{j>Iwao% zoQvygV43`PaV)s*p!+MrZrxDy=~SVyC&8hyi3d z+C_vVMk-2mq1jLK6Ag+q`eEY9`>m1Tyl1iBgu2f4y_o@HM-vjwe5P0Z^|PXChieJA zrSL`)X|9Yh@oR#)fxUqqmlX%`6M+mOdQt?G+q-%(w7{0~Nyq+MxXu=qrSvE5HW5H` zMa<E8Nm=j$YTo&JBm%V4EU?cb~A&M%CueTRx*dw$al(9$h7j2U6b(qzI7LV636><-MCWs zf;pBYt15692z>V?pL_G8a)xVF3{^)rHukOBgU)X=NPleo8KR8Ki3PSA!9C-Abcd#E3ZjS~3L=Eo}*4uDic?EST9YlS^ff z<#?X1xz?C&eyXr_!dzQEwA~c7{4Sy)XZ^LGPs($n z5yR&fnT3AA@Xm?>w$X}G{x3uPD@{`qYqP`cV|Zxq(nFfmE41g;cYqJygQJtg8S^r0 z+f)`}9ABwn8h0nH;^gFTePDelX_s?Czr&DkN~15;1Se}PdpX#=6M5(1nD@)!Bk>Vi zeqCX+heloE@S&qS7^|F93V9AK{Uw4!y+z#(YX03#Q#Z3A`E5Vz*KJjYv=LZVy$P&| z_jpC#ECQ&`-pN%tWn|C3vMjwkgKblV3`-Q;kZX(BT;OHLo>cLNmpR-I~r^M_Ij^@nofIY$Et*O^z4qOw) zTsh;Sy`mtzOC%$PPC7meQ2x*t>HH7Z%-ThziXfY@@i#3~rqgv+$kp+i_(c_1QHt z{q9rp8N_S9DJA*9Jza5_EZX50$qK1x{h5=wi%Hy~>BI$rY1hLnr7-!{uXb@}Cmy3h z%(ylsn#GfICkzD!Jsr&s0vA>OLaZXoUR!;TvcJUa(lQCp+*LTMg~*F9F3u=wIlp~A zm0V|mIyUEg&+QBmY!{qZZBUt2TWMnpnNCa364c=H2lfgKvs+viWy-j@*593VHhY-;*cb z7nDFB>O}xwQv{?%n?z;U@E6wIBM1PMt;k~c zbP$I@H*h+M%4(ZvnU~LI`O(?*x^q$ibb0``3H4B`z>U;pdDxZ$P{beDo)`J7s75yP zKE$AXvM8CeoUxQHB%UEs5L4-iQfm}6{4#%a*V+wXRfkaE)WHwmoMU}`G(3-5yEKJV z8>=}U+G1_$;oHVaz0b2+aM&tcQ&R-&SLEosz|-DEq-6mO5YS%PYT-NW6@fQe?JjW1 X8U@S_nO3>tM4#dJ+={FQMMM7rf#$iV literal 0 HcmV?d00001 diff --git a/drivers/windmeter/assets/images/wind.svg b/drivers/windmeter/assets/images/wind.svg new file mode 100644 index 00000000..56ac32e9 --- /dev/null +++ b/drivers/windmeter/assets/images/wind.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/drivers/windmeter/driver.js b/drivers/windmeter/driver.js new file mode 100644 index 00000000..aff33601 --- /dev/null +++ b/drivers/windmeter/driver.js @@ -0,0 +1,229 @@ +var devices = {}; +var homewizard = require('./../../includes/homewizard.js'); +var refreshIntervalId = 0; + +// SETTINGS +module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { + Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); + try { + changedKeysArr.forEach(function (key) { + devices[device_data.id].settings[key] = newSettingsObj[key]; + }); + callback(null, true); + } catch (error) { + callback(error); + } +}; + +module.exports.pair = function( socket ) { + socket.on('get_homewizards', function () { + homewizard.getDevices(function(homewizard_devices) { + Homey.log(homewizard_devices); + var hw_devices = {}; + Object.keys(homewizard_devices).forEach(function(key) { + hw_devices[key] = homewizard_devices[key]; + }); + + socket.emit('hw_devices', hw_devices); + }); + }); + + socket.on('manual_add', function (device, callback) { + if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { + //true + Homey.log('Windmeter added ' + device.data.id); + devices[device.data.id] = { + id: device.data.id, + name: device.name, + settings: device.settings, + }; + callback( null, devices ); + socket.emit("success", device); + startPolling(); + } else { + socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); + } + }); + + socket.on('disconnect', function(){ + console.log("User aborted pairing, or pairing is finished"); + }); +} + +module.exports.init = function(devices_data, callback) { + devices_data.forEach(function initdevice(device) { + Homey.log('add device: ' + JSON.stringify(device)); + devices[device.id] = device; + module.exports.getSettings(device, function(err, settings){ + devices[device.id].settings = settings; + }); + }); + if (Object.keys(devices).length > 0) { + startPolling(); + } + Homey.log('Windmeter driver init done'); + + callback (null, true); +}; + +module.exports.deleted = function( device_data ) { + delete devices[device_data.id]; + if (Object.keys(devices).length === 0) { + clearInterval(refreshIntervalId); + console.log("--Stopped Polling Windmeter--"); + } + Homey.log('deleted: ' + JSON.stringify(device_data)); +}; + +module.exports.capabilities = { + "measure_wind_angle": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_wind_angle); + } + } + }, + "measure_wind_strength.cur": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_wind_strength_current); + } + } + }, + "measure_wind_strength.min": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_wind_strength_min); + } + } + }, + "measure_wind_strength.max": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_wind_strength_max); + } + } + }, + "measure_gust_strength": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_gust_strength); + } + } + }, + "measure_temperature.real": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_temperature_real); + } + } + }, + "measure_temperature.windchill": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_temperature_windchill); + } + } + } +}; + +// Start polling +function startPolling() { + if(refreshIntervalId){ + clearInterval(refreshIntervalId); + } + refreshIntervalId = setInterval(function () { + console.log("--Start Windmeter Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getStatus(device_id); + }); + }, 1000 * 10); +} + +function getStatus(device_id) { + if(devices[device_id].settings.homewizard_id !== undefined ) { + var homewizard_id = devices[device_id].settings.homewizard_id; + homewizard.getDeviceData(homewizard_id, 'windmeters', function(callback) { + if (Object.keys(callback).length > 0) { + try { + Homey.log ("Callback: ") + JSON.stringify(callback); + module.exports.setAvailable({id: device_id}); + var wind_angle_tmp = ( callback[0].dir ); // $windmeters[0]['dir'] SW 225 + var wind_angle = parseInt(wind_angle_tmp); // Capture only the angle portion (number) + var wind_strength_current = ( callback[0].ws ); // $windmeters[0]['ws'] Windspeed in km/h + var wind_strength_min = ( callback[0]["ws-"] ); // $windmeters[0]['ws-'] Min Windspeed in km/h + var wind_strength_max = ( callback[0]["ws+"] ); // $windmeters[0]['ws+'] Max Windspeed in km/h + var gust_strength = ( callback[0].gu ); // $windmeters[0]['gu'] Gust speed in km/h + var temp_real = ( callback[0].te ); // $windmeters[0]['te'] Temperature + var temp_windchill = ( callback[0].wc ); // $windmeters[0]['wc'] Windchill temperature + // Export the data + // Wind angle + module.exports.realtime( { id: device_id }, "measure_wind_angle", wind_angle ); + // Wind speed current + module.exports.realtime( { id: device_id }, "measure_wind_strength.cur", wind_strenght_current ); + // Wind speed min + module.exports.realtime( { id: device_id }, "measure_wind_strength.min", wind_strenght_min ); + // Wind speed max + module.exports.realtime( { id: device_id }, "measure_wind_strength.max", wind_strenght_max ); + // Wind speed + module.exports.realtime( { id: device_id }, "measure_gust_strength", gust_strength ); + // Temp real + module.exports.realtime( { id: device_id }, "measure_temperature.real", temp_real ); + // Temp Windchill + module.exports.realtime( { id: device_id }, "measure_temperature.windchill", temp_windchill ); + // Console data + console.log("Windangle in degrees: "+ wind_angle); + console.log("Windspeed in km/u: "+ wind_strength_current); + console.log("Min Windspeed in km/u: "+ wind_strength_min); + console.log("Min Windspeed in km/u: "+ wind_strength_max); + console.log("Gust strength in km/u: "+ gust_strength); + console.log("Temperature current: "+ temp_real); + console.log("Temperature windchill: "+ gust_strength); + } catch (err) { + // Error with Wind no data in Rainmeters + console.log ("No Windmeter found"); + module.exports.setUnavailable({id: device_id}, "No Windmeter found" ); + } + } + }); + } else { + Homey.log('Removed Windmeter '+ device_id +' (old settings)'); + module.exports.setUnavailable({id: device_id}, "No Windmeter found" ); + // Only clear interval when the unavailable device is the only device on this driver + // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new + // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem + if(Object.keys(devices).length === 1) { + clearInterval(refreshIntervalId); + } + } +} + + diff --git a/drivers/windmeter/pair/start.html b/drivers/windmeter/pair/start.html new file mode 100644 index 00000000..a4fd174e --- /dev/null +++ b/drivers/windmeter/pair/start.html @@ -0,0 +1,84 @@ + + + + + + + +

+
+ +
+
+

+ + + diff --git a/includes/homewizard.js b/includes/homewizard.js index 1ee21df2..c411ee1a 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -11,7 +11,7 @@ module.exports = (function(){ "time":"2017-01-21 20:07", "switches":[], "uvmeters":[], - "windmeters":[], + "windmeters":[{"id":4,"name":"Windmeter","code":"7001251","model":1,"lowBattery":"no","version":2.32,"unit":0,"ws":1.4,"dir":"SW 225","gu":3.5,"wc":26.1,"te":26.1,"ws+":3.0,"ws+t":"13:08","ws-":0.0,"ws-t":"00:00","favorite":"no"}], "rainmeters":[{"id":1,"name":"Regenmeter","code":"6996438","model":1,"lowBattery":"no","version":2.32,"mm":3.0,"3h":1.0,"favorite":"no"}], "thermometers":[ {"id":1,"name":"Kantoor","channel":2,"model":0,"te":20.0,"hu":5,"te+":20.4,"te+t":"12:15","te-":19.6,"te-t":"07:06","hu+":5,"hu+t":"00:00","hu-":5,"hu-t":"00:00","outside":"no","favorite":"no"}, From 69eb2ed99aa16ffb5baa57a2b0490addffe89f86 Mon Sep 17 00:00:00 2001 From: Freddie Welvering Date: Mon, 15 Jan 2018 14:29:45 +0100 Subject: [PATCH 043/661] Fixed solar readings from S2. + Added: save readings from you smart meater --- app.json | 40 +++++++++-- drivers/energylink/driver.js | 124 +++++++++++++++++++++++++++++------ includes/homewizard.js | 2 +- 3 files changed, 140 insertions(+), 26 deletions(-) diff --git a/app.json b/app.json index f816d2a3..42403225 100755 --- a/app.json +++ b/app.json @@ -4,7 +4,8 @@ "en": "HomeWizard" }, "version": "0.1.6", - "compatibility": ">=0.9", + "sdk" : "2", + "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" }, @@ -173,7 +174,6 @@ } }] } - ], "conditions": [{ "id": "check_preset", @@ -379,7 +379,7 @@ }, "class": "sensor", "capabilities": [ - "meter_power.used", "meter_power.aggr", "meter_power.s1", "meter_gas", "measure_power.used", "measure_power.s1", "meter_water", "measure_water" + "meter_power.used", "meter_power.aggr", "meter_power.s1", "measure_power.used", "measure_power.s1", "meter_gas.today", "meter_gas.reading", "meter_water", "measure_water","meter_power.consumed.t1","meter_power.produced.t1","meter_power.consumed.t2","meter_power.produced.t2" ], "capabilitiesOptions": { @@ -414,14 +414,19 @@ "en": "Solar current", "nl": "Huidige opbrengst" } - }, - "meter_gas": { + "meter_gas.today": { "title": { "en": "Gas", "nl": "Gas" } }, + "meter_gas.reading": { + "title": { + "en": "Meter reading gas", + "nl": "Meterstand Gas" + } + }, "meter_water": { "title": { "en": "Water Total", @@ -433,6 +438,30 @@ "en": "Water l./m", "nl": "Water l./m" } + }, + "meter_power.consumed.t1": { + "title": { + "en": "Meter reading low", + "nl": "Meterstand laag tarief" + } + }, + "meter_power.produced.t1": { + "title": { + "en": "Meter reading produced low", + "nl": "Stand terug levering laag" + } + }, + "meter_power.consumed.t2": { + "title": { + "en": "Meter reading normal", + "nl": "Meterstand verbruik normaal" + } + }, + "meter_power.produced.t2": { + "title": { + "en": "Meter reading produced normal", + "nl": "Stand terug levering normaal" + } } }, @@ -552,7 +581,6 @@ } } - }, "pair": [{ diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index eff55c3d..06248131 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -1,6 +1,7 @@ var devices = {}; var homewizard = require('./../../includes/homewizard.js'); var refreshIntervalId = 0; +var refreshIntervalIdReadings = 0; // SETTINGS module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { @@ -164,6 +165,28 @@ module.exports.capabilities = { callback(null, device.last_measure_water); } } + }, + "meter_reading_consumed.tarrif1": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_reading_consumed_t2); + } + } + }, + "meter_reading_consumed.tarrif2": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_reading_consumed_t1); + } + } } }; @@ -173,12 +196,24 @@ function startPolling() { if(refreshIntervalId){ clearInterval(refreshIntervalId); } + if(refreshIntervalIdReadings){ + clearInterval(refreshIntervalIdReadings); + } + refreshIntervalId = setInterval(function () { console.log("--Start Energylink Polling-- "); Object.keys(devices).forEach(function (device_id) { getStatus(device_id); }); }, 1000 * 10); + + // Request readings every minute + refreshIntervalIdReadings = setInterval(function () { + console.log("--Start Energylink Readings Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getReadings(device_id); + }); + }, 1000 * 60); } function getStatus(device_id) { @@ -204,7 +239,7 @@ function getStatus(device_id) { try { var gas_daytotal_cons = ( callback[0].gas.dayTotal ); // m3 Energy produced via S1 $energylink[0]['gas']['dayTotal'] // Consumed gas - module.exports.realtime( { id: device_id }, "meter_gas", gas_daytotal_cons ); + module.exports.realtime( { id: device_id }, "meter_gas.today", gas_daytotal_cons ); } catch(err) { // Error with Energylink no data in Energylink @@ -218,27 +253,32 @@ function getStatus(device_id) { // Consumed elec total day module.exports.realtime( { id: device_id }, "meter_power.aggr", energy_daytotal_aggr ); - + // Set solar used to zero before counting + var solar_current_prod = 0; + var solar_daytotal_prod = 0; + if (value_s1 == 'solar' ) { - var energy_current_prod = ( callback[0].s1.po ); // WATTS Energy produced via S1 $energylink[0]['s1']['po'] + var energy_current_prod = ( callback[0].s1.po ); // WATTS Energy produced via S1 $energylink[0]['s1']['po'] var energy_daytotal_prod = ( callback[0].s1.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s1']['po'] - - // Produced elec current - module.exports.realtime( { id: device_id }, "measure_power.s1", energy_current_prod ); - // Produced elec total day - module.exports.realtime( { id: device_id }, "meter_power.s1", energy_daytotal_prod ); + + var solar_current_prod = solar_current_prod + energy_current_prod; + var solar_daytotal_prod = solar_daytotal_prod + energy_daytotal_prod; + } if (value_s2 == 'solar' ) { - var energy_current_prod = ( callback[0].s2.po ); // WATTS Energy produced via S1 $energylink[0]['s2']['po'] + var energy_current_prod = ( callback[0].s2.po ); // WATTS Energy produced via S1 $energylink[0]['s2']['po'] var energy_daytotal_prod = ( callback[0].s2.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s2']['dayTotal'] - - // Produced elec current - module.exports.realtime( { id: device_id }, "measure_power.s2", energy_current_prod ); - // Produced elec total day - module.exports.realtime( { id: device_id }, "meter_power.s2", energy_daytotal_prod ); + + var solar_current_prod = solar_current_prod + energy_current_prod; + var solar_daytotal_prod = solar_daytotal_prod + energy_daytotal_prod; } - + + if(value_s1 == 'solar' || value_s2 == 'solar') { + module.exports.realtime( { id: device_id }, "measure_power.s1", solar_current_prod ); + module.exports.realtime( { id: device_id }, "meter_power.s1", solar_daytotal_prod ); + } + if (value_s1 == 'water' ) { var water_current_cons = ( callback[0].s1.po ); // Water used via S1 $energylink[0]['s1']['po'] var water_daytotal_cons = ( callback[0].s1.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s1']['dayTotal'] @@ -264,16 +304,16 @@ function getStatus(device_id) { Homey.manager('flow').triggerDevice('power_used_changed', { power_used: energy_current_cons }, null, { id: device_id } ); } if (energy_current_prod != devices[device_id].last_measure_power_s1) { - console.log("Current S1 - "+ energy_current_prod); - Homey.manager('flow').triggerDevice('power_s1_changed', { power_s1: energy_current_prod }, null, { id: device_id } ); + console.log("Current S1 - "+ solar_current_prod); + Homey.manager('flow').triggerDevice('power_s1_changed', { power_s1: solar_current_prod }, null, { id: device_id } ); } if (energy_daytotal_cons != devices[device_id].last_meter_power_used) { console.log("Used Daytotal- "+ energy_daytotal_cons); Homey.manager('flow').triggerDevice('meter_power_used_changed', { power_daytotal_used: energy_daytotal_cons }, null, { id: device_id }); } if (energy_daytotal_prod != devices[device_id].last_meter_power_s1) { - console.log("S1 Daytotal- "+ energy_daytotal_prod); - Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: energy_daytotal_prod }, null, { id: device_id }); + console.log("S1 Daytotal- "+ solar_daytotal_prod); + Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: solar_daytotal_prod }, null, { id: device_id }); } if (energy_daytotal_aggr != devices[device_id].last_meter_power_aggr) { console.log("Aggregated Daytotal- "+ energy_daytotal_aggr); @@ -299,3 +339,49 @@ function getStatus(device_id) { } } } + + +function getReadings(device_id) { + + if(devices[device_id].settings.homewizard_id !== undefined ) { + + var homewizard_id = devices[device_id].settings.homewizard_id; + homewizard.getDeviceData(homewizard_id, 'energylink_el', function(callback) { + + if (Object.keys(callback).length > 0) { + + try { + module.exports.setAvailable({id: device_id}) + + var metered_gas = callback[2].consumed; + var metered_electricity_consumed_t1 = callback[0].consumed; + var metered_electricity_produced_t1 = callback[0].produced; + var metered_electricity_consumed_t2 = callback[1].consumed; + var metered_electricity_produced_t2 = callback[1].produced; + + // Save export data + module.exports.realtime( { id: device_id }, "meter_gas.reading", metered_gas ); + module.exports.realtime( { id: device_id }, "meter_power.consumed.t1", metered_electricity_consumed_t1 ); + module.exports.realtime( { id: device_id }, "meter_power.produced.t1", metered_electricity_produced_t1 ); + module.exports.realtime( { id: device_id }, "meter_power.consumed.t2", metered_electricity_consumed_t2 ); + module.exports.realtime( { id: device_id }, "meter_power.produced.t2", metered_electricity_produced_t2 ); + + } + catch (err) { + // Error with Energylink no data in Energylink + console.log("No Energylink found"); + module.exports.setUnavailable({id: device_id}, "No Energylink found"); + } + } + }); + } else { + Homey.log('Removed Energylink '+ device_id +' (wrong settings)'); + module.exports.setUnavailable({id: device_id}, "No Energylink found" ); + // Only clear interval when the unavailable device is the only device on this driver + // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new + // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem + if(Object.keys(devices).length === 1) { + clearInterval(refreshIntervalIdReadings); + } + } +} diff --git a/includes/homewizard.js b/includes/homewizard.js index 1ee21df2..656c26c6 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -168,7 +168,7 @@ module.exports = (function(){ if (Object.keys(response.energylinks).length !== 0) { homewizard.call(device_id, '/el/get/0/readings', function(err, response2) { if (err === null) { - self.devices[device_id].polldata.energylink_el = response2.el; + self.devices[device_id].polldata.energylink_el = response2; Homey.log('HW-Data polled for slimme meter: '+device_id); } }); From 7e09d40a9ca451c5591b9dd6955e2befe45e80a5 Mon Sep 17 00:00:00 2001 From: Freddie Welvering Date: Mon, 15 Jan 2018 14:55:20 +0100 Subject: [PATCH 044/661] Updated readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b8d4a167..4d30ab31 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) +v0.1.7: +* Save readings from your smart meter +* Fixed EnergyLink not correctly saving solar from S2 port. + v0.1.6: * Added rainmeter * SIDENOTE just as the Energylink, heatlink etc you need to add Rain and windmeter separately. From 30e53fc6daa9e07a7439f27dc967f5f8135060df Mon Sep 17 00:00:00 2001 From: Freddie Welvering Date: Mon, 15 Jan 2018 15:05:03 +0100 Subject: [PATCH 045/661] Fixed some typos --- app.json | 6 +++--- drivers/energylink/driver.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app.json b/app.json index 42403225..f409917a 100755 --- a/app.json +++ b/app.json @@ -424,7 +424,7 @@ "meter_gas.reading": { "title": { "en": "Meter reading gas", - "nl": "Meterstand Gas" + "nl": "Stand Gas" } }, "meter_water": { @@ -442,7 +442,7 @@ "meter_power.consumed.t1": { "title": { "en": "Meter reading low", - "nl": "Meterstand laag tarief" + "nl": "Stand laag tarief" } }, "meter_power.produced.t1": { @@ -454,7 +454,7 @@ "meter_power.consumed.t2": { "title": { "en": "Meter reading normal", - "nl": "Meterstand verbruik normaal" + "nl": "Stand verbruik normaal" } }, "meter_power.produced.t2": { diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 06248131..67a3d052 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -206,7 +206,7 @@ function startPolling() { getStatus(device_id); }); }, 1000 * 10); - + // Request readings every minute refreshIntervalIdReadings = setInterval(function () { console.log("--Start Energylink Readings Polling-- "); From 24ad3f2d09350c03ee7cf3c6612635af7618d93d Mon Sep 17 00:00:00 2001 From: Jeroen Date: Sun, 28 Jan 2018 15:08:01 +0100 Subject: [PATCH 046/661] Upped version number and removed sdkv2 --- app.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app.json b/app.json index f409917a..c9b55055 100755 --- a/app.json +++ b/app.json @@ -3,8 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.6", - "sdk" : "2", + "version": "0.1.7", "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" From 7812691cce906f9fe7165bad4c5c912aaaa25390 Mon Sep 17 00:00:00 2001 From: Jeroen Date: Thu, 15 Feb 2018 19:35:29 +0100 Subject: [PATCH 047/661] Added examples --- app.json | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app.json b/app.json index c9b55055..57a3d52b 100755 --- a/app.json +++ b/app.json @@ -48,14 +48,16 @@ "title": { "en": "Preset", "nl": "Preset" - } + }, + "example": 1, }, { "name": "preset_text", "type": "string", "title": { "en": "Text", "nl": "Tekst" - } + }, + "example": "Home", }] }, { "id": "power_used_changed", @@ -78,7 +80,8 @@ "title": { "en": "Watt", "nl": "Watt" - } + }, + "example": 15, }] }, { "id": "power_s1_changed", @@ -101,7 +104,8 @@ "title": { "en": "Watt", "nl": "Watt" - } + }, + "example": 15, }] }, { "id": "meter_power_used_changed", @@ -124,7 +128,8 @@ "title": { "en": "kWh", "nl": "kWh" - } + }, + "example": 1, }] }, { "id": "meter_power_aggregated_changed", @@ -148,6 +153,7 @@ "en": "kWh", "nl": "kWh" } + "example": 1, }] }, { "id": "meter_power_s1_changed", @@ -170,7 +176,8 @@ "title": { "en": "kWh", "nl": "kWh" - } + }, + "example": 1, }] } ], From b720277f3156dc4178f2c04fa31b4c6556b3a45c Mon Sep 17 00:00:00 2001 From: Jeroen Date: Thu, 15 Feb 2018 19:37:37 +0100 Subject: [PATCH 048/661] Fixed broken app.json --- app.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app.json b/app.json index 57a3d52b..9ae80d76 100755 --- a/app.json +++ b/app.json @@ -49,7 +49,7 @@ "en": "Preset", "nl": "Preset" }, - "example": 1, + "example": 1 }, { "name": "preset_text", "type": "string", @@ -57,7 +57,7 @@ "en": "Text", "nl": "Tekst" }, - "example": "Home", + "example": "Home" }] }, { "id": "power_used_changed", @@ -81,7 +81,7 @@ "en": "Watt", "nl": "Watt" }, - "example": 15, + "example": 15 }] }, { "id": "power_s1_changed", @@ -105,7 +105,7 @@ "en": "Watt", "nl": "Watt" }, - "example": 15, + "example": 15 }] }, { "id": "meter_power_used_changed", @@ -129,7 +129,7 @@ "en": "kWh", "nl": "kWh" }, - "example": 1, + "example": 1 }] }, { "id": "meter_power_aggregated_changed", @@ -153,7 +153,7 @@ "en": "kWh", "nl": "kWh" } - "example": 1, + "example": 1 }] }, { "id": "meter_power_s1_changed", @@ -177,7 +177,7 @@ "en": "kWh", "nl": "kWh" }, - "example": 1, + "example": 1 }] } ], From b16faa54d1f6b030f745014993344a0eb8c67120 Mon Sep 17 00:00:00 2001 From: Jeroen Date: Thu, 15 Feb 2018 19:39:05 +0100 Subject: [PATCH 049/661] Missing comma --- app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.json b/app.json index 9ae80d76..e364a41b 100755 --- a/app.json +++ b/app.json @@ -152,7 +152,7 @@ "title": { "en": "kWh", "nl": "kWh" - } + }, "example": 1 }] }, { From 7fc8291960a7f03375c82314899c16a9d297e8e5 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Fri, 27 Jul 2018 11:26:21 +0200 Subject: [PATCH 050/661] Energymeter fixes v0.1.7 Energymeter fix, rainmeter, windmeter, --- README.md | 4 ++ app.json | 41 ++++++++++-- drivers/energylink/driver.js | 124 +++++++++++++++++++++++++++++------ includes/homewizard.js | 4 +- 4 files changed, 145 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b8d4a167..4d30ab31 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) +v0.1.7: +* Save readings from your smart meter +* Fixed EnergyLink not correctly saving solar from S2 port. + v0.1.6: * Added rainmeter * SIDENOTE just as the Energylink, heatlink etc you need to add Rain and windmeter separately. diff --git a/app.json b/app.json index f816d2a3..c9b55055 100755 --- a/app.json +++ b/app.json @@ -3,8 +3,8 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.6", - "compatibility": ">=0.9", + "version": "0.1.7", + "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" }, @@ -173,7 +173,6 @@ } }] } - ], "conditions": [{ "id": "check_preset", @@ -379,7 +378,7 @@ }, "class": "sensor", "capabilities": [ - "meter_power.used", "meter_power.aggr", "meter_power.s1", "meter_gas", "measure_power.used", "measure_power.s1", "meter_water", "measure_water" + "meter_power.used", "meter_power.aggr", "meter_power.s1", "measure_power.used", "measure_power.s1", "meter_gas.today", "meter_gas.reading", "meter_water", "measure_water","meter_power.consumed.t1","meter_power.produced.t1","meter_power.consumed.t2","meter_power.produced.t2" ], "capabilitiesOptions": { @@ -414,14 +413,19 @@ "en": "Solar current", "nl": "Huidige opbrengst" } - }, - "meter_gas": { + "meter_gas.today": { "title": { "en": "Gas", "nl": "Gas" } }, + "meter_gas.reading": { + "title": { + "en": "Meter reading gas", + "nl": "Stand Gas" + } + }, "meter_water": { "title": { "en": "Water Total", @@ -433,6 +437,30 @@ "en": "Water l./m", "nl": "Water l./m" } + }, + "meter_power.consumed.t1": { + "title": { + "en": "Meter reading low", + "nl": "Stand laag tarief" + } + }, + "meter_power.produced.t1": { + "title": { + "en": "Meter reading produced low", + "nl": "Stand terug levering laag" + } + }, + "meter_power.consumed.t2": { + "title": { + "en": "Meter reading normal", + "nl": "Stand verbruik normaal" + } + }, + "meter_power.produced.t2": { + "title": { + "en": "Meter reading produced normal", + "nl": "Stand terug levering normaal" + } } }, @@ -552,7 +580,6 @@ } } - }, "pair": [{ diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index eff55c3d..67a3d052 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -1,6 +1,7 @@ var devices = {}; var homewizard = require('./../../includes/homewizard.js'); var refreshIntervalId = 0; +var refreshIntervalIdReadings = 0; // SETTINGS module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { @@ -164,6 +165,28 @@ module.exports.capabilities = { callback(null, device.last_measure_water); } } + }, + "meter_reading_consumed.tarrif1": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_reading_consumed_t2); + } + } + }, + "meter_reading_consumed.tarrif2": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_meter_reading_consumed_t1); + } + } } }; @@ -173,12 +196,24 @@ function startPolling() { if(refreshIntervalId){ clearInterval(refreshIntervalId); } + if(refreshIntervalIdReadings){ + clearInterval(refreshIntervalIdReadings); + } + refreshIntervalId = setInterval(function () { console.log("--Start Energylink Polling-- "); Object.keys(devices).forEach(function (device_id) { getStatus(device_id); }); }, 1000 * 10); + + // Request readings every minute + refreshIntervalIdReadings = setInterval(function () { + console.log("--Start Energylink Readings Polling-- "); + Object.keys(devices).forEach(function (device_id) { + getReadings(device_id); + }); + }, 1000 * 60); } function getStatus(device_id) { @@ -204,7 +239,7 @@ function getStatus(device_id) { try { var gas_daytotal_cons = ( callback[0].gas.dayTotal ); // m3 Energy produced via S1 $energylink[0]['gas']['dayTotal'] // Consumed gas - module.exports.realtime( { id: device_id }, "meter_gas", gas_daytotal_cons ); + module.exports.realtime( { id: device_id }, "meter_gas.today", gas_daytotal_cons ); } catch(err) { // Error with Energylink no data in Energylink @@ -218,27 +253,32 @@ function getStatus(device_id) { // Consumed elec total day module.exports.realtime( { id: device_id }, "meter_power.aggr", energy_daytotal_aggr ); - + // Set solar used to zero before counting + var solar_current_prod = 0; + var solar_daytotal_prod = 0; + if (value_s1 == 'solar' ) { - var energy_current_prod = ( callback[0].s1.po ); // WATTS Energy produced via S1 $energylink[0]['s1']['po'] + var energy_current_prod = ( callback[0].s1.po ); // WATTS Energy produced via S1 $energylink[0]['s1']['po'] var energy_daytotal_prod = ( callback[0].s1.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s1']['po'] - - // Produced elec current - module.exports.realtime( { id: device_id }, "measure_power.s1", energy_current_prod ); - // Produced elec total day - module.exports.realtime( { id: device_id }, "meter_power.s1", energy_daytotal_prod ); + + var solar_current_prod = solar_current_prod + energy_current_prod; + var solar_daytotal_prod = solar_daytotal_prod + energy_daytotal_prod; + } if (value_s2 == 'solar' ) { - var energy_current_prod = ( callback[0].s2.po ); // WATTS Energy produced via S1 $energylink[0]['s2']['po'] + var energy_current_prod = ( callback[0].s2.po ); // WATTS Energy produced via S1 $energylink[0]['s2']['po'] var energy_daytotal_prod = ( callback[0].s2.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s2']['dayTotal'] - - // Produced elec current - module.exports.realtime( { id: device_id }, "measure_power.s2", energy_current_prod ); - // Produced elec total day - module.exports.realtime( { id: device_id }, "meter_power.s2", energy_daytotal_prod ); + + var solar_current_prod = solar_current_prod + energy_current_prod; + var solar_daytotal_prod = solar_daytotal_prod + energy_daytotal_prod; } - + + if(value_s1 == 'solar' || value_s2 == 'solar') { + module.exports.realtime( { id: device_id }, "measure_power.s1", solar_current_prod ); + module.exports.realtime( { id: device_id }, "meter_power.s1", solar_daytotal_prod ); + } + if (value_s1 == 'water' ) { var water_current_cons = ( callback[0].s1.po ); // Water used via S1 $energylink[0]['s1']['po'] var water_daytotal_cons = ( callback[0].s1.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s1']['dayTotal'] @@ -264,16 +304,16 @@ function getStatus(device_id) { Homey.manager('flow').triggerDevice('power_used_changed', { power_used: energy_current_cons }, null, { id: device_id } ); } if (energy_current_prod != devices[device_id].last_measure_power_s1) { - console.log("Current S1 - "+ energy_current_prod); - Homey.manager('flow').triggerDevice('power_s1_changed', { power_s1: energy_current_prod }, null, { id: device_id } ); + console.log("Current S1 - "+ solar_current_prod); + Homey.manager('flow').triggerDevice('power_s1_changed', { power_s1: solar_current_prod }, null, { id: device_id } ); } if (energy_daytotal_cons != devices[device_id].last_meter_power_used) { console.log("Used Daytotal- "+ energy_daytotal_cons); Homey.manager('flow').triggerDevice('meter_power_used_changed', { power_daytotal_used: energy_daytotal_cons }, null, { id: device_id }); } if (energy_daytotal_prod != devices[device_id].last_meter_power_s1) { - console.log("S1 Daytotal- "+ energy_daytotal_prod); - Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: energy_daytotal_prod }, null, { id: device_id }); + console.log("S1 Daytotal- "+ solar_daytotal_prod); + Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: solar_daytotal_prod }, null, { id: device_id }); } if (energy_daytotal_aggr != devices[device_id].last_meter_power_aggr) { console.log("Aggregated Daytotal- "+ energy_daytotal_aggr); @@ -299,3 +339,49 @@ function getStatus(device_id) { } } } + + +function getReadings(device_id) { + + if(devices[device_id].settings.homewizard_id !== undefined ) { + + var homewizard_id = devices[device_id].settings.homewizard_id; + homewizard.getDeviceData(homewizard_id, 'energylink_el', function(callback) { + + if (Object.keys(callback).length > 0) { + + try { + module.exports.setAvailable({id: device_id}) + + var metered_gas = callback[2].consumed; + var metered_electricity_consumed_t1 = callback[0].consumed; + var metered_electricity_produced_t1 = callback[0].produced; + var metered_electricity_consumed_t2 = callback[1].consumed; + var metered_electricity_produced_t2 = callback[1].produced; + + // Save export data + module.exports.realtime( { id: device_id }, "meter_gas.reading", metered_gas ); + module.exports.realtime( { id: device_id }, "meter_power.consumed.t1", metered_electricity_consumed_t1 ); + module.exports.realtime( { id: device_id }, "meter_power.produced.t1", metered_electricity_produced_t1 ); + module.exports.realtime( { id: device_id }, "meter_power.consumed.t2", metered_electricity_consumed_t2 ); + module.exports.realtime( { id: device_id }, "meter_power.produced.t2", metered_electricity_produced_t2 ); + + } + catch (err) { + // Error with Energylink no data in Energylink + console.log("No Energylink found"); + module.exports.setUnavailable({id: device_id}, "No Energylink found"); + } + } + }); + } else { + Homey.log('Removed Energylink '+ device_id +' (wrong settings)'); + module.exports.setUnavailable({id: device_id}, "No Energylink found" ); + // Only clear interval when the unavailable device is the only device on this driver + // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new + // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem + if(Object.keys(devices).length === 1) { + clearInterval(refreshIntervalIdReadings); + } + } +} diff --git a/includes/homewizard.js b/includes/homewizard.js index c411ee1a..656c26c6 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -11,7 +11,7 @@ module.exports = (function(){ "time":"2017-01-21 20:07", "switches":[], "uvmeters":[], - "windmeters":[{"id":4,"name":"Windmeter","code":"7001251","model":1,"lowBattery":"no","version":2.32,"unit":0,"ws":1.4,"dir":"SW 225","gu":3.5,"wc":26.1,"te":26.1,"ws+":3.0,"ws+t":"13:08","ws-":0.0,"ws-t":"00:00","favorite":"no"}], + "windmeters":[], "rainmeters":[{"id":1,"name":"Regenmeter","code":"6996438","model":1,"lowBattery":"no","version":2.32,"mm":3.0,"3h":1.0,"favorite":"no"}], "thermometers":[ {"id":1,"name":"Kantoor","channel":2,"model":0,"te":20.0,"hu":5,"te+":20.4,"te+t":"12:15","te-":19.6,"te-t":"07:06","hu+":5,"hu+t":"00:00","hu-":5,"hu-t":"00:00","outside":"no","favorite":"no"}, @@ -168,7 +168,7 @@ module.exports = (function(){ if (Object.keys(response.energylinks).length !== 0) { homewizard.call(device_id, '/el/get/0/readings', function(err, response2) { if (err === null) { - self.devices[device_id].polldata.energylink_el = response2.el; + self.devices[device_id].polldata.energylink_el = response2; Homey.log('HW-Data polled for slimme meter: '+device_id); } }); From e78b620b7f63e828bd8d0f415210a12295784386 Mon Sep 17 00:00:00 2001 From: Jeroen Bos Date: Sat, 22 Dec 2018 09:52:07 +0100 Subject: [PATCH 051/661] Version bump and changed device class to other --- README.md | 4 ++++ app.json | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d30ab31..b0fcfb7d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) +v0.1.8: +* Added windmeter +* Changed device class to make it work on Homey V2 + v0.1.7: * Save readings from your smart meter * Fixed EnergyLink not correctly saving solar from S2 port. diff --git a/app.json b/app.json index e364a41b..8627b58d 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.7", + "version": "0.1.8", "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" @@ -300,7 +300,7 @@ "large": "drivers/homewizard/assets/images/large.jpg", "small": "drivers/homewizard/assets/images/small.jpg" }, - "class": "appliances", + "class": "other", "capabilities": [], "pair": [{ "id": "start" From 626d0e52b374bad8ee30f45ce6c9650437254d41 Mon Sep 17 00:00:00 2001 From: Jeroen Bos Date: Sat, 22 Dec 2018 09:56:00 +0100 Subject: [PATCH 052/661] Updated readme and added contribution to app.json --- README.md | 10 +++++----- app.json | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b0fcfb7d..1f512b6b 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,6 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! - -**If you like this app, then consider to buy me a beer :)** - -[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) - v0.1.8: * Added windmeter * Changed device class to make it work on Homey V2 @@ -80,3 +75,8 @@ V0.0.1: * Use HomeWizard’s preset as a condition in flows * Set HomeWizard’s preset as action in flows + + +**If you like this app, then consider to buy me a beer :)** + +[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) diff --git a/app.json b/app.json index 8627b58d..4903b081 100755 --- a/app.json +++ b/app.json @@ -29,6 +29,13 @@ "email": "jeroen@tebbens.net" }] }, + "contributing": { + "donate": { + "paypal": { + "username": "jeroenbos22" + } + } + }, "flow": { "triggers": [{ "id": "preset_changed", From 499d251ff32253db51d0058c96a8c0181a9d2af9 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Sat, 22 Dec 2018 15:37:26 +0100 Subject: [PATCH 053/661] Update app.json Enable windmeter in app.json --- app.json | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/app.json b/app.json index 4903b081..9317090e 100755 --- a/app.json +++ b/app.json @@ -476,9 +476,158 @@ "nl": "Stand terug levering normaal" } } + }, + "pair": [{ + "id": "start" + }, { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, { + "id": "add_my_devices", + "template": "add_devices" + }] + }, { + "id": "thermometer", + "name": { + "en": "Thermometer", + "nl": "Thermometer" + }, + "images": { + "large": "drivers/thermometer/assets/images/large.jpg", + "small": "drivers/thermometer/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_temperature", + "measure_humidity" + ], + "pair": [{ + "id": "start" + }, { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, { + "id": "add_my_devices", + "template": "add_devices" + }] + }, { + "id": "wattcher", + "name": { + "en": "Wattcher", + "nl": "Wattcher" + }, + "images": { + "large": "drivers/wattcher/assets/images/large.jpg", + "small": "drivers/wattcher/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_power", + "meter_power" + ], + + "capabilitiesOptions": { + "meter_power": { + "title": { + "en": "Day usage", + "nl": "Dag totaal" + } + }, + + "measure_power": { + "title": { + "en": "Power current", + "nl": "Huidig vermogen" + } + + } + + }, + + "pair": [{ + "id": "start" + }, { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, { + "id": "add_my_devices", + "template": "add_devices" + }] + }, { + "id": "windmeter", + "name": { + "en": "Windmeter", + "nl": "Windmeter" + }, + "images": { + "large": "drivers/windmeter/assets/images/large.jpg", + "small": "drivers/windmeter/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_wind_angle", "measure_wind_strength.cur", "measure_wind_strength.min", "measure_wind_strength.max", "measure_gust_strength", "measure_temperature.real", "measure_temperature.windchill" + ], + + "capabilitiesOptions": { + "measure_wind_angle": { + "title": { + "en": "Wind Angle", + "nl": "Wind richting" + } + }, + "measure_wind_strength.cur": { + "title": { + "en": "Wind Strength current", + "nl": "Wind sterkte huidige" + } + }, + "measure_wind_strength.min": { + "title": { + "en": "Wind Strength lowest", + "nl": "Wind Sterkte laagste" + } + + }, + "measure_wind_strength.max": { + "title": { + "en": "Wind Strength highest", + "nl": "Wind Sterkte hoogste" + } + + }, + "measure_gust_strength": { + "title": { + "en": "Wind Gusts", + "nl": "Ruk wind sterkte" + } + }, + "measure_temperature.real": { + "title": { + "en": "Temperature Real", + "nl": "Temperatuur Echt" + } + }, + "measure_temperature.windchill": { + "title": { + "en": "Temperature windchill", + "nl": "Gevoelstemperatuur" + } + } +}, + + "pair": [{ "id": "start" }, { From c1d09a699ed995bec3d0e8ed02d455582d11511d Mon Sep 17 00:00:00 2001 From: Jeroen Bos Date: Sun, 23 Dec 2018 11:45:34 +0100 Subject: [PATCH 054/661] Final commit for v 0.2.0 --- README.md | 2 +- app.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f512b6b..7258a01c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! -v0.1.8: +v0.2.0: * Added windmeter * Changed device class to make it work on Homey V2 diff --git a/app.json b/app.json index 9317090e..68ca6321 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.8", + "version": "0.2.0", "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" From 6dbb72b4feb71767d87b74c1ce59c6af692dc1fd Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Fri, 15 Mar 2019 15:06:16 +0100 Subject: [PATCH 055/661] Windmeter fixes and Heatlink Off action card Windmeter fixes but code seems to stop at module.exports.realtime( { id: device_id }, "measure_wind_strength.cur", wind_strenght_current ); Heatlink off action card (TUrn off Heatlink and make Room Thermostat active again) --- app.json | 12 +++++++++++ drivers/heatlink/driver.js | 43 +++++++++++++++++++++++-------------- drivers/windmeter/driver.js | 21 +++++++++++++----- includes/homewizard.js | 3 ++- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/app.json b/app.json index 9317090e..10b76eea 100755 --- a/app.json +++ b/app.json @@ -295,6 +295,18 @@ "type": "device", "filter": "driver_id=homewizard" }] + }, { + "id": "heatlink_off", + "title": { + "en": "Heatlink off", + "nl": "Zet heatlink uit" + }, + "args": [{ + "name": "device", + "type": "device", + "filter": "driver_id=homewizard" + }] + }] }, "drivers": [{ diff --git a/drivers/heatlink/driver.js b/drivers/heatlink/driver.js index 1e9a0009..0e3f4e87 100755 --- a/drivers/heatlink/driver.js +++ b/drivers/heatlink/driver.js @@ -11,25 +11,25 @@ module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, }); callback(null, true); } catch (error) { - callback(error); + callback(error); } }; module.exports.pair = function( socket ) { socket.on('get_homewizards', function () { homewizard.getDevices(function(homewizard_devices) { - + Homey.log(homewizard_devices); var hw_devices = {}; Object.keys(homewizard_devices).forEach(function(key) { hw_devices[key] = homewizard_devices[key]; }); - + socket.emit('hw_devices', hw_devices); }); }); - - socket.on('manual_add', function (device, callback) { + + socket.on('manual_add', function (device, callback) { if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { //true Homey.log('HeatLink added ' + device.data.id); @@ -40,12 +40,12 @@ module.exports.pair = function( socket ) { }; callback( null, devices ); socket.emit("success", device); - startPolling(); + startPolling(); } else { socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); } }); - + socket.on('disconnect', function(){ console.log("User aborted pairing, or pairing is finished"); }); @@ -58,7 +58,7 @@ module.exports.init = function(devices_data, callback) { module.exports.getSettings(device, function(err, settings){ devices[device.id].settings = settings; }); - + }); if (Object.keys(devices).length > 0) { startPolling(); @@ -131,6 +131,17 @@ module.exports.capabilities = { }, }; +Homey.manager('flow').on('action.heatlink_off', function( callback, args ){ + homewizard.call(args.device.id, '/hl/0/settarget/0', function(err, response) { + if (err === null) { + Homey.log('Heatlink Off'); + callback( null, true ); + } else { + callback(err, false); // err + } + }); +}); + function getStatus(device_id) { if(devices[device_id].settings.homewizard_id !== undefined ) { var homewizard_id = devices[device_id].settings.homewizard_id; @@ -140,27 +151,27 @@ function getStatus(device_id) { var rte = (callback[0].rte.toFixed(1) * 2) / 2; var rsp = (callback[0].rsp.toFixed(1) * 2) / 2; var tte = (callback[0].tte.toFixed(1) * 2) / 2; - + //Check current temperature if (devices[device_id].temperature != rte) { console.log("New RTE - "+ rte); module.exports.realtime( { id: device_id }, "measure_temperature", rte ); - devices[device_id].temperature = rte; + devices[device_id].temperature = rte; } else { console.log("RTE: no change"); } - + //Check thermostat temperature if (devices[device_id].thermTemperature != rsp) { console.log("New RSP - "+ rsp); if (devices[device_id].setTemperature === 0) { module.exports.realtime( { id: device_id }, "target_temperature", rsp ); } - devices[device_id].thermTemperature = rsp; + devices[device_id].thermTemperature = rsp; } else { console.log("RSP: no change"); } - + //Check heatlink set temperature if (devices[device_id].setTemperature != tte) { console.log("New TTE - "+ tte); @@ -169,7 +180,7 @@ function getStatus(device_id) { } else { module.exports.realtime( { id: device_id }, "target_temperature", devices[device_id].thermTemperature ); } - devices[device_id].setTemperature = tte; + devices[device_id].setTemperature = tte; } else { console.log("TTE: no change"); } @@ -189,7 +200,7 @@ function getStatus(device_id) { } } } - + function startPolling() { if (refreshIntervalId) { clearInterval(refreshIntervalId); @@ -200,4 +211,4 @@ function getStatus(device_id) { getStatus(device_id); }); }, 1000 * 10); - } \ No newline at end of file + } diff --git a/drivers/windmeter/driver.js b/drivers/windmeter/driver.js index aff33601..4bbefeb2 100644 --- a/drivers/windmeter/driver.js +++ b/drivers/windmeter/driver.js @@ -176,17 +176,28 @@ function getStatus(device_id) { try { Homey.log ("Callback: ") + JSON.stringify(callback); module.exports.setAvailable({id: device_id}); + console.log ("Start capture var data"); var wind_angle_tmp = ( callback[0].dir ); // $windmeters[0]['dir'] SW 225 - var wind_angle = parseInt(wind_angle_tmp); // Capture only the angle portion (number) + var wind_angle = wind_angle_tmp.split(" "); + //var wind_angle = parseInt(wind_angle_tmp); // Capture only the angle portion (number) var wind_strength_current = ( callback[0].ws ); // $windmeters[0]['ws'] Windspeed in km/h var wind_strength_min = ( callback[0]["ws-"] ); // $windmeters[0]['ws-'] Min Windspeed in km/h var wind_strength_max = ( callback[0]["ws+"] ); // $windmeters[0]['ws+'] Max Windspeed in km/h var gust_strength = ( callback[0].gu ); // $windmeters[0]['gu'] Gust speed in km/h var temp_real = ( callback[0].te ); // $windmeters[0]['te'] Temperature - var temp_windchill = ( callback[0].wc ); // $windmeters[0]['wc'] Windchill temperature + var temp_windchill = ( callback[0].wc); // $windmeters[0]['wc'] Windchill temperature + console.log ("End capture var data"); // Export the data + // Console data + console.log("Windangle in degrees: "+ wind_angle[1]); + console.log("Windspeed in km/u: "+ wind_strength_current); + console.log("Min Windspeed in km/u: "+ wind_strength_min); + console.log("Min Windspeed in km/u: "+ wind_strength_max); + console.log("Gust strength in km/u: "+ gust_strength); + console.log("Temperature current: "+ temp_real); + console.log("Temperature windchill: "+ temp_windchill); // Wind angle - module.exports.realtime( { id: device_id }, "measure_wind_angle", wind_angle ); + module.exports.realtime( { id: device_id }, "measure_wind_angle", wind_angle[1] ); // Wind speed current module.exports.realtime( { id: device_id }, "measure_wind_strength.cur", wind_strenght_current ); // Wind speed min @@ -200,13 +211,13 @@ function getStatus(device_id) { // Temp Windchill module.exports.realtime( { id: device_id }, "measure_temperature.windchill", temp_windchill ); // Console data - console.log("Windangle in degrees: "+ wind_angle); + console.log("Windangle in degrees: "+ wind_angle[1]); console.log("Windspeed in km/u: "+ wind_strength_current); console.log("Min Windspeed in km/u: "+ wind_strength_min); console.log("Min Windspeed in km/u: "+ wind_strength_max); console.log("Gust strength in km/u: "+ gust_strength); console.log("Temperature current: "+ temp_real); - console.log("Temperature windchill: "+ gust_strength); + console.log("Temperature windchill: "+ temp_windchill); } catch (err) { // Error with Wind no data in Rainmeters console.log ("No Windmeter found"); diff --git a/includes/homewizard.js b/includes/homewizard.js index 656c26c6..ef7bc0e8 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -11,7 +11,7 @@ module.exports = (function(){ "time":"2017-01-21 20:07", "switches":[], "uvmeters":[], - "windmeters":[], + "windmeters":[{"id":3,"name":"Windmeter","code":"6219442","model":1,"lowBattery":"no","version":2.32,"unit":0,"ws":4.1,"dir":"W 270","gu":7.8,"wc":19.6,"te":19.6,"ws+":12.8,"ws+t":"11:03","ws-":0.0,"ws-t":"01:45","favorite":"no"}], "rainmeters":[{"id":1,"name":"Regenmeter","code":"6996438","model":1,"lowBattery":"no","version":2.32,"mm":3.0,"3h":1.0,"favorite":"no"}], "thermometers":[ {"id":1,"name":"Kantoor","channel":2,"model":0,"te":20.0,"hu":5,"te+":20.4,"te+t":"12:15","te-":19.6,"te-t":"07:06","hu+":5,"hu+t":"00:00","hu-":5,"hu-t":"00:00","outside":"no","favorite":"no"}, @@ -164,6 +164,7 @@ module.exports = (function(){ self.devices[device_id].polldata.energymeters = response.energymeters; self.devices[device_id].polldata.thermometers = response.thermometers; self.devices[device_id].polldata.rainmeters = response.rainmeters; + self.devices[device_id].polldata.windmeters = response.windmeters; if (Object.keys(response.energylinks).length !== 0) { homewizard.call(device_id, '/el/get/0/readings', function(err, response2) { From 1adc8a6bfc40d3aec5aa6c819e679d84f0db519a Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Fri, 15 Mar 2019 17:12:10 +0100 Subject: [PATCH 056/661] Fixed windmeter direction issue Direction was a string which had to be int type. --- README.md | 4 ++++ app.json | 2 +- drivers/windmeter/driver.js | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1f512b6b..32482920 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! +v0.1.9: +* Fixed windmeter +* Added Heatlink action card to turn off Heatlink and make your room thermostat active again + v0.1.8: * Added windmeter * Changed device class to make it work on Homey V2 diff --git a/app.json b/app.json index 10b76eea..a377a5e9 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.1.8", + "version": "0.1.9", "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" diff --git a/drivers/windmeter/driver.js b/drivers/windmeter/driver.js index 4bbefeb2..3bac5da2 100644 --- a/drivers/windmeter/driver.js +++ b/drivers/windmeter/driver.js @@ -178,8 +178,8 @@ function getStatus(device_id) { module.exports.setAvailable({id: device_id}); console.log ("Start capture var data"); var wind_angle_tmp = ( callback[0].dir ); // $windmeters[0]['dir'] SW 225 - var wind_angle = wind_angle_tmp.split(" "); - //var wind_angle = parseInt(wind_angle_tmp); // Capture only the angle portion (number) + var wind_angle_int = wind_angle_tmp.split(" "); + // var wind_angle = parseInt(wind_angle_tmp); // Capture only the angle portion (number) var wind_strength_current = ( callback[0].ws ); // $windmeters[0]['ws'] Windspeed in km/h var wind_strength_min = ( callback[0]["ws-"] ); // $windmeters[0]['ws-'] Min Windspeed in km/h var wind_strength_max = ( callback[0]["ws+"] ); // $windmeters[0]['ws+'] Max Windspeed in km/h @@ -189,7 +189,9 @@ function getStatus(device_id) { console.log ("End capture var data"); // Export the data // Console data - console.log("Windangle in degrees: "+ wind_angle[1]); + var wind_angle_str = wind_angle_int[1]; + var wind_angle = parseInt(wind_angle_str); + console.log("Windangle in degrees: "+ wind_angle); console.log("Windspeed in km/u: "+ wind_strength_current); console.log("Min Windspeed in km/u: "+ wind_strength_min); console.log("Min Windspeed in km/u: "+ wind_strength_max); @@ -197,13 +199,13 @@ function getStatus(device_id) { console.log("Temperature current: "+ temp_real); console.log("Temperature windchill: "+ temp_windchill); // Wind angle - module.exports.realtime( { id: device_id }, "measure_wind_angle", wind_angle[1] ); + module.exports.realtime( { id: device_id }, "measure_wind_angle", wind_angle ); // Wind speed current - module.exports.realtime( { id: device_id }, "measure_wind_strength.cur", wind_strenght_current ); + module.exports.realtime( { id: device_id }, "measure_wind_strength.cur", wind_strength_current ); // Wind speed min - module.exports.realtime( { id: device_id }, "measure_wind_strength.min", wind_strenght_min ); + module.exports.realtime( { id: device_id }, "measure_wind_strength.min", wind_strength_min ); // Wind speed max - module.exports.realtime( { id: device_id }, "measure_wind_strength.max", wind_strenght_max ); + module.exports.realtime( { id: device_id }, "measure_wind_strength.max", wind_strength_max ); // Wind speed module.exports.realtime( { id: device_id }, "measure_gust_strength", gust_strength ); // Temp real @@ -211,7 +213,7 @@ function getStatus(device_id) { // Temp Windchill module.exports.realtime( { id: device_id }, "measure_temperature.windchill", temp_windchill ); // Console data - console.log("Windangle in degrees: "+ wind_angle[1]); + console.log("Windangle in degrees: "+ wind_angle); console.log("Windspeed in km/u: "+ wind_strength_current); console.log("Min Windspeed in km/u: "+ wind_strength_min); console.log("Min Windspeed in km/u: "+ wind_strength_max); From 29c302f1198f89238d91486ad399e08cacb5b063 Mon Sep 17 00:00:00 2001 From: Jeroen Date: Sat, 30 Mar 2019 19:47:33 +0100 Subject: [PATCH 057/661] Version bump --- app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.json b/app.json index 5d9cbdec..65b8303c 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.2.0", + "version": "0.2.1", "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" From 7b5f3e06e910eebcbef8f23bea3be51e6cc44344 Mon Sep 17 00:00:00 2001 From: Jeroen Date: Sat, 30 Mar 2019 19:48:36 +0100 Subject: [PATCH 058/661] Update new version info --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 11757479..774f06a9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! + +v0.2.1: +Windmeter fix and heatlink action card added + v0.2.0: * Added windmeter * Changed device class to make it work on Homey V2 From 058d1a161629cb355821303c820002ea203cfcbf Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Fri, 6 Sep 2019 15:10:14 +0200 Subject: [PATCH 059/661] Rainfall trigger added --- README.md | 16 +++++++++------- app.json | 34 +++++++++++++++++++++++++++++----- drivers/rainmeter/driver.js | 29 +++++++++++++++++------------ 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 774f06a9..bd26152c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ # HomeWizard - + This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! +v0.2.2: +* Rainmeter flow trigger added (Rainfall total based) v0.2.1: -Windmeter fix and heatlink action card added +* Windmeter fix and heatlink action card added v0.2.0: * Added windmeter @@ -15,13 +17,13 @@ v0.1.7: * Fixed EnergyLink not correctly saving solar from S2 port. v0.1.6: -* Added rainmeter +* Added rainmeter * SIDENOTE just as the Energylink, heatlink etc you need to add Rain and windmeter separately. - Verify homewizard has its windmeter units set to km/h else you get funny measures - + Verify homewizard has its windmeter units set to km/h else you get funny measures + v0.1.5: Bugfix for making scenes work again (big thanks to Jeroen Tebbens!) -v0.1.3: +v0.1.3: Energylink updates: * s1 and s2 are now giving information on either solar or water (whatever is connected to it) * "Netto verbruik" feature added, it will now show the aggregated power usage which can be negative if your solarpanels do their job. ;) @@ -30,7 +32,7 @@ Other updates: * Made fixes for app to work on 1.2.0 Firmware (Thermometer & Scenes) -v0.1.2: +v0.1.2: Updated Polling method to avoid traffic overhead and timing issues V0.1.1: diff --git a/app.json b/app.json index 65b8303c..26100d60 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.2.1", + "version": "0.2.2", "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" @@ -14,8 +14,8 @@ "small": "assets/images/small.jpg" }, "author": { - "name": "Jeroen Bos", - "email": "jeroenbos22@gmail.com" + "name": "Jeroen Tebbens", + "email": "jeroen@tebbens.net" }, "contributors": { "developers": [{ @@ -32,7 +32,7 @@ "contributing": { "donate": { "paypal": { - "username": "jeroenbos22" + "username": "jtebbens" } } }, @@ -186,6 +186,30 @@ }, "example": 1 }] + }, { + "id": "rainmeter_value_changed", + "title": { + "en": "Rainmeter value changed", + "nl": "Regenmeter waarde veranderd" + }, + "args": [{ + "name": "Rainmeter", + "type": "device", + "filter": "driver_id=rainmeter", + "placeholder": { + "en": "Which Rainmeter", + "nl": "Welke Regenmeter" + } + }], + "tokens": [{ + "name": "rainmeter_changed", + "type": "number", + "title": { + "en": "mm", + "nl": "mm" + }, + "example": 15 + }] } ], "conditions": [{ @@ -306,7 +330,7 @@ "type": "device", "filter": "driver_id=homewizard" }] - + }] }, "drivers": [{ diff --git a/drivers/rainmeter/driver.js b/drivers/rainmeter/driver.js index 1f10b1af..f8856794 100644 --- a/drivers/rainmeter/driver.js +++ b/drivers/rainmeter/driver.js @@ -11,7 +11,7 @@ module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, }); callback(null, true); } catch (error) { - callback(error); + callback(error); } }; @@ -23,12 +23,12 @@ module.exports.pair = function( socket ) { Object.keys(homewizard_devices).forEach(function(key) { hw_devices[key] = homewizard_devices[key]; }); - + socket.emit('hw_devices', hw_devices); }); }); - - socket.on('manual_add', function (device, callback) { + + socket.on('manual_add', function (device, callback) { if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { //true Homey.log('Rainmeter added ' + device.data.id); @@ -39,12 +39,12 @@ module.exports.pair = function( socket ) { }; callback( null, devices ); socket.emit("success", device); - startPolling(); + startPolling(); } else { socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); } }); - + socket.on('disconnect', function(){ console.log("User aborted pairing, or pairing is finished"); }); @@ -127,11 +127,18 @@ function getStatus(device_id) { module.exports.realtime( { id: device_id }, "measure_rain.last3h", rain_last3h ); // Rain total day module.exports.realtime( { id: device_id }, "measure_rain.total", rain_daytotal ); - - console.log("Rainmeter 3h- "+ rain_last3h); - console.log("Rainmeter Daytotal- "+ rain_daytotal); + + console.log("Rainmeter 3h- "+ rain_last3h); + console.log("Rainmeter Daytotal- "+ rain_daytotal); + + // Trigger flows + if (rain_daytotal != devices[device_id].last_raintotal) { + console.log("Current Total Rainfall - "+ rain_daytotal); + Homey.manager('flow').triggerDevice('rainmeter_value_changed', { rainmeter_changed: rain_daytotal }, null, { id: device_id } ); + } + } catch (err) { - // Error with Rain no data in Rainmeters + // Error with Rain no data in Rainmeters console.log ("No Rainmeter found"); module.exports.setUnavailable({id: device_id}, "No Rainmeter found" ); } @@ -148,5 +155,3 @@ function getStatus(device_id) { } } } - - From 37ac9d9c3d76477a8291dc570af7377622d23e27 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Mon, 9 Sep 2019 21:34:01 +0200 Subject: [PATCH 060/661] Bug fix Rainmeter & Energylink triggers Rainmeter and Energylink kept triggering even while values were equal. --- drivers/energylink/driver.js | 55 ++++++++++++++++++++---------------- drivers/rainmeter/driver.js | 3 +- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 67a3d052..7984edd0 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -12,25 +12,25 @@ module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, }); callback(null, true); } catch (error) { - callback(error); + callback(error); } }; module.exports.pair = function( socket ) { socket.on('get_homewizards', function () { homewizard.getDevices(function(homewizard_devices) { - + Homey.log(homewizard_devices); var hw_devices = {}; Object.keys(homewizard_devices).forEach(function(key) { hw_devices[key] = homewizard_devices[key]; }); - + socket.emit('hw_devices', hw_devices); }); }); - - socket.on('manual_add', function (device, callback) { + + socket.on('manual_add', function (device, callback) { if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { //true Homey.log('Energylink added ' + device.data.id); @@ -41,12 +41,12 @@ module.exports.pair = function( socket ) { }; callback( null, devices ); socket.emit("success", device); - startPolling(); + startPolling(); } else { socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); } }); - + socket.on('disconnect', function(){ console.log("User aborted pairing, or pairing is finished"); }); @@ -223,29 +223,29 @@ function getStatus(device_id) { if (Object.keys(callback).length > 0) { try { module.exports.setAvailable({id: device_id}); - + var value_s1 = ( callback[0].t1 ) ; // Read t1 from energylink (solar/water/null) var value_s2 = ( callback[0].t2 ) ; // Read t2 from energylink (solar/water/null) - + console.log("t1- " + value_s1); console.log("t2- " + value_s2); - - // Common Energylink data + + // Common Energylink data var energy_current_cons = ( callback[0].used.po ); // WATTS Energy used JSON $energylink[0]['used']['po'] var energy_daytotal_cons = ( callback[0].used.dayTotal ); // KWH Energy used JSON $energylink[0]['used']['dayTotal'] var energy_daytotal_aggr = ( callback[0].aggregate.dayTotal ) ; // KWH Energy aggregated is used - generated $energylink[0]['aggregate']['dayTotal'] - + // Some Energylink do not have gas information so try to get it else fail silently try { var gas_daytotal_cons = ( callback[0].gas.dayTotal ); // m3 Energy produced via S1 $energylink[0]['gas']['dayTotal'] - // Consumed gas + // Consumed gas module.exports.realtime( { id: device_id }, "meter_gas.today", gas_daytotal_cons ); } catch(err) { // Error with Energylink no data in Energylink console.log ("No Gas information found"); } - + // Consumed elec current module.exports.realtime( { id: device_id }, "measure_power.used", energy_current_cons ); // Consumed elec total day @@ -265,7 +265,7 @@ function getStatus(device_id) { var solar_daytotal_prod = solar_daytotal_prod + energy_daytotal_prod; } - + if (value_s2 == 'solar' ) { var energy_current_prod = ( callback[0].s2.po ); // WATTS Energy produced via S1 $energylink[0]['s2']['po'] var energy_daytotal_prod = ( callback[0].s2.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s2']['dayTotal'] @@ -285,41 +285,46 @@ function getStatus(device_id) { console.log("Water- " + water_daytotal_cons); // Used water m3 module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); - module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); - + module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); + } - + if (value_s2 == 'water' ) { var water_current_cons = ( callback[0].s2.po ); // Water used via S2 $energylink[0]['s1']['po'] var water_daytotal_cons = ( callback[0].s2.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s2']['dayTotal'] console.log("Water- " + water_daytotal_cons); // Used water m3 module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); - module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); - } - + module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); + } + // Trigger flows if (energy_current_cons != devices[device_id].last_measure_power_used) { console.log("Current Power - "+ energy_current_cons); Homey.manager('flow').triggerDevice('power_used_changed', { power_used: energy_current_cons }, null, { id: device_id } ); + devices[device_id].last_measure_power_used = energy_current_cons; // Update last_measure_power_used } if (energy_current_prod != devices[device_id].last_measure_power_s1) { console.log("Current S1 - "+ solar_current_prod); Homey.manager('flow').triggerDevice('power_s1_changed', { power_s1: solar_current_prod }, null, { id: device_id } ); + devices[device_id].last_measure_power_s1 = energy_current_prod; // Update last_measure_power_s1 } if (energy_daytotal_cons != devices[device_id].last_meter_power_used) { - console.log("Used Daytotal- "+ energy_daytotal_cons); + console.log("Used Daytotal- "+ energy_daytotal_cons); Homey.manager('flow').triggerDevice('meter_power_used_changed', { power_daytotal_used: energy_daytotal_cons }, null, { id: device_id }); + devices[device_id].last_meter_power_used = energy_daytotal_cons; // Update last_measure_power_used } - if (energy_daytotal_prod != devices[device_id].last_meter_power_s1) { + if (energy_daytotal_prod != devices[device_id].last_meter_power_s1) { console.log("S1 Daytotal- "+ solar_daytotal_prod); Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: solar_daytotal_prod }, null, { id: device_id }); + devices[device_id].last_meter_power_s1 = energy_daytotal_prod; // Update last_meter_power_s1 } if (energy_daytotal_aggr != devices[device_id].last_meter_power_aggr) { - console.log("Aggregated Daytotal- "+ energy_daytotal_aggr); + console.log("Aggregated Daytotal- "+ energy_daytotal_aggr); Homey.manager('flow').triggerDevice('meter_power_aggregated_changed', { power_daytotal_aggr: energy_daytotal_aggr }, null, { id: device_id }); + devices[device_id].last_meter_power_aggr = energy_daytotal_aggr; // Update last_meter_power_aggr } - + } catch(err) { // Error with Energylink no data in Energylink diff --git a/drivers/rainmeter/driver.js b/drivers/rainmeter/driver.js index f8856794..88f85cfe 100644 --- a/drivers/rainmeter/driver.js +++ b/drivers/rainmeter/driver.js @@ -132,9 +132,10 @@ function getStatus(device_id) { console.log("Rainmeter Daytotal- "+ rain_daytotal); // Trigger flows - if (rain_daytotal != devices[device_id].last_raintotal) { + if (rain_daytotal != devices[device_id].last_raintotal && rain_daytotal != 0) { console.log("Current Total Rainfall - "+ rain_daytotal); Homey.manager('flow').triggerDevice('rainmeter_value_changed', { rainmeter_changed: rain_daytotal }, null, { id: device_id } ); + devices[device_id].last_raintotal = rain_daytotal; // Update last_raintotal } } catch (err) { From d7461688dba2c21183f95630c107fec41eec0a14 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Mon, 9 Sep 2019 21:36:21 +0200 Subject: [PATCH 061/661] Version bump --- README.md | 3 +++ app.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bd26152c..ea01a5a0 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! +v0.2.4: +* Bugfix Rainmeter and EnergyLink triggers + v0.2.2: * Rainmeter flow trigger added (Rainfall total based) diff --git a/app.json b/app.json index 26100d60..0710eb28 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.2.2", + "version": "0.2.4", "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" From c9ac738cf2eae58dacc4a31c2ce498a1585cbd07 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Wed, 18 Sep 2019 16:50:29 +0200 Subject: [PATCH 062/661] Rainmeter trigger fix 2 Ignore undefined and null values when Homey can't get data from Homewizard (connection issue). --- README.md | 70 +++--------------------------------- app.json | 2 +- drivers/energylink/driver.js | 4 +-- drivers/rainmeter/driver.js | 2 +- 4 files changed, 9 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index ea01a5a0..db710bc1 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # HomeWizard -This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Once done it will show up in the flow-editor, ready to be used! +This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. +Upon first deployment you need add the Homewizard unit first, then you can add the related/connected components from Homewizard to your Homey. -v0.2.4: +v0.2.5: * Bugfix Rainmeter and EnergyLink triggers v0.2.2: @@ -21,70 +22,9 @@ v0.1.7: v0.1.6: * Added rainmeter -* SIDENOTE just as the Energylink, heatlink etc you need to add Rain and windmeter separately. +* SIDENOTE just as the Energylink, heatlink etc you need to add Rain and Windmeter separately. Verify homewizard has its windmeter units set to km/h else you get funny measures -v0.1.5: Bugfix for making scenes work again (big thanks to Jeroen Tebbens!) - -v0.1.3: -Energylink updates: -* s1 and s2 are now giving information on either solar or water (whatever is connected to it) -* "Netto verbruik" feature added, it will now show the aggregated power usage which can be negative if your solarpanels do their job. ;) -* SIDENOTE: You need to redo/add Energylink device to make the "Netto verbruik" visible -Other updates: -* Made fixes for app to work on 1.2.0 Firmware (Thermometer & Scenes) - - -v0.1.2: -Updated Polling method to avoid traffic overhead and timing issues - -V0.1.1: - -* Added temp sensors -* Added trigger on preset change - -V0.1.0: - -* Improved polling (far less requests to HomeWizard) -* Various bugfixes and improvements - -V0.0.9: - -* Energylink + Wattcher support added (credits: Jeroen Tebbens) -* SIDENOTE: All devices paired before 0.0.9 (expect HomeWizard) should be re-paired! -* Make sure your solar meter is connected to s1 on energylink. - -V0.0.8: - -* Heatlink support added (credits: Nick Bockmeulen) - -V0.0.7: - -* Made fixes for app to work on 0.10.x - -V0.0.5 & V0.0.6: - -* Made fixes for app to work on 0.9.x - -V0.0.4: - -* Added switching on/off scenes - -V0.0.3: - -* Added a time-out of 10 sec -* Added extra logging - -V0.0.2: - -* Save HomeWizard’s as a device - -V0.0.1: - -* Use HomeWizard’s preset as a condition in flows -* Set HomeWizard’s preset as action in flows - - **If you like this app, then consider to buy me a beer :)** -[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4653ZKTPTPSLW) +[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/paypalme2/jtebbens) diff --git a/app.json b/app.json index 0710eb28..38a824e5 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.2.4", + "version": "0.2.5", "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 7984edd0..31026d4e 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -173,7 +173,7 @@ module.exports.capabilities = { if (device === undefined) { callback(null, 0); } else { - callback(null, device.last_meter_reading_consumed_t2); + callback(null, device.last_meter_reading_consumed_t1); } } }, @@ -184,7 +184,7 @@ module.exports.capabilities = { if (device === undefined) { callback(null, 0); } else { - callback(null, device.last_meter_reading_consumed_t1); + callback(null, device.last_meter_reading_consumed_t2); } } } diff --git a/drivers/rainmeter/driver.js b/drivers/rainmeter/driver.js index 88f85cfe..dde3fa14 100644 --- a/drivers/rainmeter/driver.js +++ b/drivers/rainmeter/driver.js @@ -132,7 +132,7 @@ function getStatus(device_id) { console.log("Rainmeter Daytotal- "+ rain_daytotal); // Trigger flows - if (rain_daytotal != devices[device_id].last_raintotal && rain_daytotal != 0) { + if (rain_daytotal != devices[device_id].last_raintotal && rain_daytotal != 0 && rain_daytotal != undefined && rain_daytotal != null) { console.log("Current Total Rainfall - "+ rain_daytotal); Homey.manager('flow').triggerDevice('rainmeter_value_changed', { rainmeter_changed: rain_daytotal }, null, { id: device_id } ); devices[device_id].last_raintotal = rain_daytotal; // Update last_raintotal From 21611b2d31b3e57767b9f703496d8776592b015e Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Fri, 4 Oct 2019 16:35:19 +0200 Subject: [PATCH 063/661] Netto power usage added --- app.json | 10 +++++++--- drivers/energylink/driver.js | 29 ++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/app.json b/app.json index 38a824e5..c6a84972 100755 --- a/app.json +++ b/app.json @@ -428,7 +428,7 @@ }, "class": "sensor", "capabilities": [ - "meter_power.used", "meter_power.aggr", "meter_power.s1", "measure_power.used", "measure_power.s1", "meter_gas.today", "meter_gas.reading", "meter_water", "measure_water","meter_power.consumed.t1","meter_power.produced.t1","meter_power.consumed.t2","meter_power.produced.t2" + "meter_power.used", "meter_power.aggr", "meter_power.s1", "measure_power.used","measure_power.netto", "measure_power.s1", "meter_gas.today", "meter_gas.reading", "meter_water", "measure_water","meter_power.consumed.t1","meter_power.produced.t1","meter_power.consumed.t2","meter_power.produced.t2" ], "capabilitiesOptions": { @@ -449,14 +449,18 @@ "en": "Day production", "nl": "Dag opbrengst" } - }, "measure_power.used": { "title": { "en": "Power current", "nl": "Huidig vermogen" } - + }, + "measure_power.netto": { + "title": { + "en": "Netto Power current", + "nl": "Netto Huidig vermogen" + } }, "measure_power.s1": { "title": { diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 31026d4e..334a3c0e 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -89,6 +89,17 @@ module.exports.capabilities = { } } }, + "measure_power.netto": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_power_netto); + } + } + }, "measure_power.s1": { get: function (device_data, callback) { var device = devices[device_data.id]; @@ -234,6 +245,7 @@ function getStatus(device_id) { var energy_current_cons = ( callback[0].used.po ); // WATTS Energy used JSON $energylink[0]['used']['po'] var energy_daytotal_cons = ( callback[0].used.dayTotal ); // KWH Energy used JSON $energylink[0]['used']['dayTotal'] var energy_daytotal_aggr = ( callback[0].aggregate.dayTotal ) ; // KWH Energy aggregated is used - generated $energylink[0]['aggregate']['dayTotal'] + var energy_current_netto = ( callback[0].aggregate.po ); // Netto power usage from aggregated value, this value can go negative // Some Energylink do not have gas information so try to get it else fail silently try { @@ -248,6 +260,8 @@ function getStatus(device_id) { // Consumed elec current module.exports.realtime( { id: device_id }, "measure_power.used", energy_current_cons ); + // Consumed elec current Netto + module.exports.realtime( { id: device_id }, "measure_power.netto", energy_current_netto ); // Consumed elec total day module.exports.realtime( { id: device_id }, "meter_power.used", energy_daytotal_cons ); // Consumed elec total day @@ -299,27 +313,32 @@ function getStatus(device_id) { } // Trigger flows - if (energy_current_cons != devices[device_id].last_measure_power_used) { + if (energy_current_cons != devices[device_id].last_measure_power_used && energy_current_cons != undefined && energy_current_cons != null) { console.log("Current Power - "+ energy_current_cons); Homey.manager('flow').triggerDevice('power_used_changed', { power_used: energy_current_cons }, null, { id: device_id } ); devices[device_id].last_measure_power_used = energy_current_cons; // Update last_measure_power_used } - if (energy_current_prod != devices[device_id].last_measure_power_s1) { + if (energy_current_netto != devices[device_id].last_measure_power_netto && energy_current_netto != undefined && energy_current_netto != null) { + console.log("Current Netto Power - "+ energy_current_netto); + Homey.manager('flow').triggerDevice('power_netto_changed', { power_used: energy_current_netto }, null, { id: device_id } ); + devices[device_id].last_measure_power_netto = energy_current_netto; // Update last_measure_power_netto + } + if (energy_current_prod != devices[device_id].last_measure_power_s1 && energy_current_prod != undefined && energy_current_prod != null) { console.log("Current S1 - "+ solar_current_prod); Homey.manager('flow').triggerDevice('power_s1_changed', { power_s1: solar_current_prod }, null, { id: device_id } ); devices[device_id].last_measure_power_s1 = energy_current_prod; // Update last_measure_power_s1 } - if (energy_daytotal_cons != devices[device_id].last_meter_power_used) { + if (energy_daytotal_cons != devices[device_id].last_meter_power_used && energy_daytotal_cons != undefined && energy_daytotal_cons != null) { console.log("Used Daytotal- "+ energy_daytotal_cons); Homey.manager('flow').triggerDevice('meter_power_used_changed', { power_daytotal_used: energy_daytotal_cons }, null, { id: device_id }); devices[device_id].last_meter_power_used = energy_daytotal_cons; // Update last_measure_power_used } - if (energy_daytotal_prod != devices[device_id].last_meter_power_s1) { + if (energy_daytotal_prod != devices[device_id].last_meter_power_s1 && energy_daytotal_prod != undefined && energy_daytotal_prod != null) { console.log("S1 Daytotal- "+ solar_daytotal_prod); Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: solar_daytotal_prod }, null, { id: device_id }); devices[device_id].last_meter_power_s1 = energy_daytotal_prod; // Update last_meter_power_s1 } - if (energy_daytotal_aggr != devices[device_id].last_meter_power_aggr) { + if (energy_daytotal_aggr != devices[device_id].last_meter_power_aggr && energy_daytotal_aggr != undefined && energy_daytotal_aggr != null) { console.log("Aggregated Daytotal- "+ energy_daytotal_aggr); Homey.manager('flow').triggerDevice('meter_power_aggregated_changed', { power_daytotal_aggr: energy_daytotal_aggr }, null, { id: device_id }); devices[device_id].last_meter_power_aggr = energy_daytotal_aggr; // Update last_meter_power_aggr From 856a163ff3f2157eafbbcadce1a8fb54c1ecf6de Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Fri, 4 Oct 2019 16:41:11 +0200 Subject: [PATCH 064/661] Netto power usage added. Version bump and Readme update --- README.md | 3 +++ app.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index db710bc1..0de9815f 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Upon first deployment you need add the Homewizard unit first, then you can add the related/connected components from Homewizard to your Homey. +v0.2.6: +* Netto power usage added (Current power/watts which will go negative if you produce power via solar) + v0.2.5: * Bugfix Rainmeter and EnergyLink triggers diff --git a/app.json b/app.json index c6a84972..ee399c17 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.2.5", + "version": "0.2.6", "compatibility": ">=1.5", "description": { "en": "Control HomeWizard using Homey" From bf42dac8fd11ccfc505690dcb3ca135246939821 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Fri, 4 Oct 2019 16:49:19 +0200 Subject: [PATCH 065/661] Comments added to driver --- drivers/energylink/driver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 334a3c0e..7db3fe3d 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -320,7 +320,7 @@ function getStatus(device_id) { } if (energy_current_netto != devices[device_id].last_measure_power_netto && energy_current_netto != undefined && energy_current_netto != null) { console.log("Current Netto Power - "+ energy_current_netto); - Homey.manager('flow').triggerDevice('power_netto_changed', { power_used: energy_current_netto }, null, { id: device_id } ); + Homey.manager('flow').triggerDevice('power_netto_changed', { netto_power_used: energy_current_netto }, null, { id: device_id } ); devices[device_id].last_measure_power_netto = energy_current_netto; // Update last_measure_power_netto } if (energy_current_prod != devices[device_id].last_measure_power_s1 && energy_current_prod != undefined && energy_current_prod != null) { From cf7899e53a36917c3317c9af6a0e63e73181ce06 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Thu, 31 Oct 2019 10:28:39 +0100 Subject: [PATCH 066/661] Homey 3.0 / Energy support added --- README.md | 4 ++++ app.json | 17 +++++++++++------ drivers/energylink/driver.js | 13 +++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0de9815f..80bd3975 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Upon first deployment you need add the Homewizard unit first, then you can add the related/connected components from Homewizard to your Homey. +v0.2.7: +* Adding Homey 3.0 support (Watcher & Energylink) + NOTE: YOU NEED TO PAIR YOUR ENERGYLINK AGAIN TO MAKE THIS WORK! + v0.2.6: * Netto power usage added (Current power/watts which will go negative if you produce power via solar) diff --git a/app.json b/app.json index ee399c17..004d5090 100755 --- a/app.json +++ b/app.json @@ -3,12 +3,13 @@ "name": { "en": "HomeWizard" }, - "version": "0.2.6", - "compatibility": ">=1.5", + "version": "0.2.7", + "compatibility": ">=3.0.0", + "homeyCommunityTopicId": 19267, "description": { "en": "Control HomeWizard using Homey" }, - "category": "appliances", + "category": ["appliances", "energy"], "images": { "large": "assets/images/large.jpg", "small": "assets/images/small.jpg" @@ -428,9 +429,11 @@ }, "class": "sensor", "capabilities": [ - "meter_power.used", "meter_power.aggr", "meter_power.s1", "measure_power.used","measure_power.netto", "measure_power.s1", "meter_gas.today", "meter_gas.reading", "meter_water", "measure_water","meter_power.consumed.t1","meter_power.produced.t1","meter_power.consumed.t2","meter_power.produced.t2" + "measure_power", "meter_power.used", "meter_power.aggr", "meter_power.s1", "measure_power.used","measure_power.netto", "measure_power.s1", "meter_gas.today", "meter_gas.reading", "meter_water", "measure_water","meter_power.consumed.t1","meter_power.produced.t1","meter_power.consumed.t2","meter_power.produced.t2" ], - + "energy": { + "cumulative": true + }, "capabilitiesOptions": { "meter_power.used": { "title": { @@ -722,7 +725,9 @@ "measure_power", "meter_power" ], - + "energy": { + "cumulative": true + }, "capabilitiesOptions": { "meter_power": { "title": { diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 7db3fe3d..7a27747d 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -78,6 +78,17 @@ module.exports.deleted = function( device_data ) { }; module.exports.capabilities = { + "measure_power": { + get: function (device_data, callback) { + var device = devices[device_data.id]; + + if (device === undefined) { + callback(null, 0); + } else { + callback(null, device.last_measure_power); + } + } + }, "measure_power.used": { get: function (device_data, callback) { var device = devices[device_data.id]; @@ -260,6 +271,8 @@ function getStatus(device_id) { // Consumed elec current module.exports.realtime( { id: device_id }, "measure_power.used", energy_current_cons ); + // Consumed elec current + module.exports.realtime( { id: device_id }, "measure_power", energy_current_cons ); // Consumed elec current Netto module.exports.realtime( { id: device_id }, "measure_power.netto", energy_current_netto ); // Consumed elec total day From 2acc101cf3a0c11a5e1be88e1bfa634c94badba9 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Sun, 3 Nov 2019 10:02:07 +0100 Subject: [PATCH 067/661] Code fix energy (using netto to compensate the solar production) --- README.md | 3 +++ app.json | 2 +- drivers/energylink/driver.js | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 80bd3975..408e2dc2 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Upon first deployment you need add the Homewizard unit first, then you can add the related/connected components from Homewizard to your Homey. +v0.2.8: +* Updated measure_power to reflect Netto power vs Used (Solar needs to add its own power generation in Homey) + v0.2.7: * Adding Homey 3.0 support (Watcher & Energylink) NOTE: YOU NEED TO PAIR YOUR ENERGYLINK AGAIN TO MAKE THIS WORK! diff --git a/app.json b/app.json index 004d5090..a44dd6a4 100755 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": { "en": "HomeWizard" }, - "version": "0.2.7", + "version": "0.2.8", "compatibility": ">=3.0.0", "homeyCommunityTopicId": 19267, "description": { diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 7a27747d..14e7d9b9 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -272,7 +272,7 @@ function getStatus(device_id) { // Consumed elec current module.exports.realtime( { id: device_id }, "measure_power.used", energy_current_cons ); // Consumed elec current - module.exports.realtime( { id: device_id }, "measure_power", energy_current_cons ); + module.exports.realtime( { id: device_id }, "measure_power", energy_current_netto ); // Consumed elec current Netto module.exports.realtime( { id: device_id }, "measure_power.netto", energy_current_netto ); // Consumed elec total day From 19f8634d90811e507fed05479e98b6abd9b9005b Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Sun, 3 Nov 2019 14:06:47 +0100 Subject: [PATCH 068/661] Enable wattcher for Energy --- app.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app.json b/app.json index a44dd6a4..14a7d40c 100755 --- a/app.json +++ b/app.json @@ -577,7 +577,9 @@ "measure_power", "meter_power" ], - + "energy": { + "cumulative": true + }, "capabilitiesOptions": { "meter_power": { "title": { From d72afd8e8b20aa3d40f69987789f090090448891 Mon Sep 17 00:00:00 2001 From: "Jeroen Tebbens (Group)" Date: Sun, 3 Nov 2019 14:24:34 +0100 Subject: [PATCH 069/661] Code cleanup --- app.json | 53 +---------------------------------------------------- 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/app.json b/app.json index 14a7d40c..2bb0d13e 100755 --- a/app.json +++ b/app.json @@ -643,7 +643,6 @@ "en": "Wind Strength lowest", "nl": "Wind Sterkte laagste" } - }, "measure_wind_strength.max": { "title": { @@ -670,9 +669,7 @@ "nl": "Gevoelstemperatuur" } } -}, - - + }, "pair": [{ "id": "start" }, { @@ -700,54 +697,6 @@ "measure_temperature", "measure_humidity" ], - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }] - }, { - "id": "wattcher", - "name": { - "en": "Wattcher", - "nl": "Wattcher" - }, - "images": { - "large": "drivers/wattcher/assets/images/large.jpg", - "small": "drivers/wattcher/assets/images/small.jpg" - }, - "class": "sensor", - "capabilities": [ - "measure_power", - "meter_power" - ], - "energy": { - "cumulative": true - }, - "capabilitiesOptions": { - "meter_power": { - "title": { - "en": "Day usage", - "nl": "Dag totaal" - } - }, - - "measure_power": { - "title": { - "en": "Power current", - "nl": "Huidig vermogen" - } - - } - - }, - "pair": [{ "id": "start" }, { From 5ffc3cccd8b4f90ab656e89fcdb17f6af9c721c7 Mon Sep 17 00:00:00 2001 From: Freddie Welvering Date: Mon, 27 Jul 2020 15:58:09 +0200 Subject: [PATCH 070/661] Working towards SDK2 --- app.js | 18 +- app.json | 1729 +++++++++++++++------------- drivers/energylink/driver.js | 822 ++++++------- drivers/heatlink/driver.js | 428 +++---- drivers/homewizard/device.js | 43 + drivers/homewizard/driver.js | 478 ++++---- drivers/homewizard/pair/start.html | 2 +- drivers/rainmeter/driver.js | 316 ++--- drivers/thermometer/device.js | 81 ++ drivers/thermometer/driver.js | 363 +++--- drivers/wattcher/driver.js | 306 ++--- drivers/windmeter/driver.js | 486 ++++---- includes/homewizard.js | 34 +- 13 files changed, 2763 insertions(+), 2343 deletions(-) create mode 100644 drivers/homewizard/device.js create mode 100644 drivers/thermometer/device.js diff --git a/app.js b/app.js index 0fbfd041..358d7d80 100644 --- a/app.js +++ b/app.js @@ -1,9 +1,17 @@ "use strict"; -function init() { - var request = require('request'); - Homey.log("HomeWizard app ready!"); - +const Homey = require('homey'); + +class HomeWizardApp extends Homey.App { + onInit() { + this.log("HomeWizard app ready!"); + } } -module.exports.init = init; \ No newline at end of file +// function init() { +// var request = require('request'); +// +// +// } + +module.exports = HomeWizardApp; \ No newline at end of file diff --git a/app.json b/app.json index ee399c17..cca631ce 100755 --- a/app.json +++ b/app.json @@ -1,804 +1,927 @@ { - "id": "com.homewizard", - "name": { - "en": "HomeWizard" - }, - "version": "0.2.6", - "compatibility": ">=1.5", - "description": { - "en": "Control HomeWizard using Homey" - }, - "category": "appliances", - "images": { - "large": "assets/images/large.jpg", - "small": "assets/images/small.jpg" - }, - "author": { - "name": "Jeroen Tebbens", - "email": "jeroen@tebbens.net" - }, - "contributors": { - "developers": [{ - "name": "Jeroen Bos", - "email": "jeroenbos22@gmail.com" - }, { - "name": "Nick Bockmeulen", - "email": "git@bockmeulen.nl" - }, { - "name": "Jeroen Tebbens", - "email": "jeroen@tebbens.net" - }] - }, - "contributing": { - "donate": { - "paypal": { - "username": "jtebbens" - } - } - }, - "flow": { - "triggers": [{ - "id": "preset_changed", - "title": { - "en": "Preset has changed", - "nl": "Preset is veranderd" - }, - "args": [{ - "name": "Homewizard", - "type": "device", - "filter": "driver_id=homewizard" - - }], - "tokens": [{ - "name": "preset", - "type": "number", - "title": { - "en": "Preset", - "nl": "Preset" - }, - "example": 1 - }, { - "name": "preset_text", - "type": "string", - "title": { - "en": "Text", - "nl": "Tekst" - }, - "example": "Home" - }] - }, { - "id": "power_used_changed", - "title": { - "en": "Power used changed", - "nl": "Huidig vermogen veranderd" - }, - "args": [{ - "name": "Energylink", - "type": "device", - "filter": "driver_id=energylink", - "placeholder": { - "en": "Which energylink", - "nl": "Welke energylink" - } - }], - "tokens": [{ - "name": "power_used", - "type": "number", - "title": { - "en": "Watt", - "nl": "Watt" - }, - "example": 15 - }] - }, { - "id": "power_s1_changed", - "title": { - "en": "Power production changed", - "nl": "Huidige productie veranderd" - }, - "args": [{ - "name": "Energylink", - "type": "device", - "filter": "driver_id=energylink", - "placeholder": { - "en": "Which energylink", - "nl": "Welke energylink" - } - }], - "tokens": [{ - "name": "power_s1", - "type": "number", - "title": { - "en": "Watt", - "nl": "Watt" - }, - "example": 15 - }] - }, { - "id": "meter_power_used_changed", - "title": { - "en": "Daily usage changed", - "nl": "Dag verbruik veranderd" - }, - "args": [{ - "name": "Energylink", - "type": "device", - "filter": "driver_id=energylink", - "placeholder": { - "en": "Which energylink", - "nl": "Welke energylink" - } - }], - "tokens": [{ - "name": "power_daytotal_used", - "type": "number", - "title": { - "en": "kWh", - "nl": "kWh" - }, - "example": 1 - }] - }, { - "id": "meter_power_aggregated_changed", - "title": { - "en": "Overall usage changed", - "nl": "Netto verbruik veranderd" - }, - "args": [{ - "name": "Energylink", - "type": "device", - "filter": "driver_id=energylink", - "placeholder": { - "en": "Which energylink", - "nl": "Welke energylink" - } - }], - "tokens": [{ - "name": "power_daytotal_aggr", - "type": "number", - "title": { - "en": "kWh", - "nl": "kWh" - }, - "example": 1 - }] - }, { - "id": "meter_power_s1_changed", - "title": { - "en": "Daily production changed", - "nl": "Dag productie veranderd" - }, - "args": [{ - "name": "Energylink", - "type": "device", - "filter": "driver_id=energylink", - "placeholder": { - "en": "Which energylink", - "nl": "Welke energylink" - } - }], - "tokens": [{ - "name": "power_daytotal_s1", - "type": "number", - "title": { - "en": "kWh", - "nl": "kWh" - }, - "example": 1 - }] - }, { - "id": "rainmeter_value_changed", - "title": { - "en": "Rainmeter value changed", - "nl": "Regenmeter waarde veranderd" - }, - "args": [{ - "name": "Rainmeter", - "type": "device", - "filter": "driver_id=rainmeter", - "placeholder": { - "en": "Which Rainmeter", - "nl": "Welke Regenmeter" - } - }], - "tokens": [{ - "name": "rainmeter_changed", - "type": "number", - "title": { - "en": "mm", - "nl": "mm" - }, - "example": 15 - }] - } - ], - "conditions": [{ - "id": "check_preset", - "title": { - "en": "Preset !{{is|isn't}}", - "nl": "Preset !{{is|is niet}}" - }, - "args": [{ - "name": "preset", - "type": "dropdown", - "values": [{ - "id": "0", - "label": { - "en": "Home", - "nl": "Thuis" - } - }, { - "id": "1", - "label": { - "en": "Away", - "nl": "Afwezig" - } - }, { - "id": "2", - "label": { - "en": "Sleep", - "nl": "Slapen" - } - }, { - "id": "3", - "label": { - "en": "Holiday", - "nl": "Vakantie" - } - }] - }, { - "name": "device", - "type": "device", - "filter": "driver_id=homewizard" - }] - }], - "actions": [{ - "id": "set_preset", - "title": { - "en": "Activate preset", - "nl": "Activeer preset" - }, - "args": [{ - "name": "preset", - "type": "dropdown", - "values": [{ - "id": "0", - "label": { - "en": "Home", - "nl": "Thuis" - } - }, { - "id": "1", - "label": { - "en": "Away", - "nl": "Afwezig" - } - }, { - "id": "2", - "label": { - "en": "Sleep", - "nl": "Slapen" - } - }, { - "id": "3", - "label": { - "en": "Holiday", - "nl": "Vakantie" - } - }] - }, { - "name": "device", - "type": "device", - "filter": "driver_id=homewizard" - }] - }, { - "id": "switch_scene_on", - "title": { - "en": "Switch scene on", - "nl": "Zet scene aan" - }, - "args": [{ - "name": "scene", - "type": "autocomplete" - }, { - "name": "device", - "type": "device", - "filter": "driver_id=homewizard" - }] - }, { - "id": "switch_scene_off", - "title": { - "en": "Switch scene off", - "nl": "Zet scene uit" - }, - "args": [{ - "name": "scene", - "type": "autocomplete" - }, { - "name": "device", - "type": "device", - "filter": "driver_id=homewizard" - }] - }, { - "id": "heatlink_off", - "title": { - "en": "Heatlink off", - "nl": "Zet heatlink uit" - }, - "args": [{ - "name": "device", - "type": "device", - "filter": "driver_id=homewizard" - }] - - }] - }, - "drivers": [{ - "id": "homewizard", - "name": { - "en": "HomeWizard", - "nl": "HomeWizard" - }, - "images": { - "large": "drivers/homewizard/assets/images/large.jpg", - "small": "drivers/homewizard/assets/images/small.jpg" - }, - "class": "other", - "capabilities": [], - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }], - "settings": [{ - "type": "group", - "label": { - "en": "HomeWizard settings", - "nl": "HomeWizard instellingen" - }, - "children": [{ - "id": "homewizard_ip", - "type": "text", - "label": { - "en": "IP address", - "nl": "IP adres" - }, - "value": "" - }, { - "id": "homewizard_pass", - "type": "text", - "label": { - "en": "Password", - "nl": "Wachtwoord" - }, - "value": "" - }, { - "id": "homewizard_ledring", - "type": "checkbox", - "label": { - "en": "Use ledring", - "nl": "Gebruik ledring" - }, - "value": false - }] - }] - }, { - "id": "heatlink", - "name": { - "en": "Heatlink", - "nl": "Heatlink" - }, - "images": { - "large": "drivers/heatlink/assets/images/large.jpg", - "small": "drivers/heatlink/assets/images/small.jpg" - }, - "class": "thermostat", - "capabilities": [ - "measure_temperature", - "target_temperature" - ], - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }] - }, { - "id": "energylink", - "name": { - "en": "Energylink", - "nl": "Energylink" - }, - "images": { - "large": "drivers/energylink/assets/images/large.jpg", - "small": "drivers/energylink/assets/images/small.jpg" - }, - "class": "sensor", - "capabilities": [ - "meter_power.used", "meter_power.aggr", "meter_power.s1", "measure_power.used","measure_power.netto", "measure_power.s1", "meter_gas.today", "meter_gas.reading", "meter_water", "measure_water","meter_power.consumed.t1","meter_power.produced.t1","meter_power.consumed.t2","meter_power.produced.t2" - ], - - "capabilitiesOptions": { - "meter_power.used": { - "title": { - "en": "Day usage", - "nl": "Dag gebruik" - } - }, - "meter_power.aggr": { - "title": { - "en": "Overall usage", - "nl": "Netto gebruik" - } - }, - "meter_power.s1": { - "title": { - "en": "Day production", - "nl": "Dag opbrengst" - } - }, - "measure_power.used": { - "title": { - "en": "Power current", - "nl": "Huidig vermogen" - } - }, - "measure_power.netto": { - "title": { - "en": "Netto Power current", - "nl": "Netto Huidig vermogen" - } - }, - "measure_power.s1": { - "title": { - "en": "Solar current", - "nl": "Huidige opbrengst" - } - }, - "meter_gas.today": { - "title": { - "en": "Gas", - "nl": "Gas" - } - }, - "meter_gas.reading": { - "title": { - "en": "Meter reading gas", - "nl": "Stand Gas" - } - }, - "meter_water": { - "title": { - "en": "Water Total", - "nl": "Water Totaal" - } - }, - "measure_water": { - "title": { - "en": "Water l./m", - "nl": "Water l./m" - } - }, - "meter_power.consumed.t1": { - "title": { - "en": "Meter reading low", - "nl": "Stand laag tarief" - } - }, - "meter_power.produced.t1": { - "title": { - "en": "Meter reading produced low", - "nl": "Stand terug levering laag" - } - }, - "meter_power.consumed.t2": { - "title": { - "en": "Meter reading normal", - "nl": "Stand verbruik normaal" - } - }, - "meter_power.produced.t2": { - "title": { - "en": "Meter reading produced normal", - "nl": "Stand terug levering normaal" - } - } - - }, - - - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }] - }, { - "id": "thermometer", - "name": { - "en": "Thermometer", - "nl": "Thermometer" - }, - "images": { - "large": "drivers/thermometer/assets/images/large.jpg", - "small": "drivers/thermometer/assets/images/small.jpg" - }, - "class": "sensor", - "capabilities": [ - "measure_temperature", - "measure_humidity" - ], - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }] - }, { - "id": "wattcher", - "name": { - "en": "Wattcher", - "nl": "Wattcher" - }, - "images": { - "large": "drivers/wattcher/assets/images/large.jpg", - "small": "drivers/wattcher/assets/images/small.jpg" - }, - "class": "sensor", - "capabilities": [ - "measure_power", - "meter_power" - ], - - "capabilitiesOptions": { - "meter_power": { - "title": { - "en": "Day usage", - "nl": "Dag totaal" - } - }, - - "measure_power": { - "title": { - "en": "Power current", - "nl": "Huidig vermogen" - } - - } - - }, - - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }] - }, { - "id": "windmeter", - "name": { - "en": "Windmeter", - "nl": "Windmeter" - }, - "images": { - "large": "drivers/windmeter/assets/images/large.jpg", - "small": "drivers/windmeter/assets/images/small.jpg" - }, - "class": "sensor", - "capabilities": [ - "measure_wind_angle", "measure_wind_strength.cur", "measure_wind_strength.min", "measure_wind_strength.max", "measure_gust_strength", "measure_temperature.real", "measure_temperature.windchill" - ], - - "capabilitiesOptions": { - "measure_wind_angle": { - "title": { - "en": "Wind Angle", - "nl": "Wind richting" - } - }, - "measure_wind_strength.cur": { - "title": { - "en": "Wind Strength current", - "nl": "Wind sterkte huidige" - } - }, - "measure_wind_strength.min": { - "title": { - "en": "Wind Strength lowest", - "nl": "Wind Sterkte laagste" - } - - }, - "measure_wind_strength.max": { - "title": { - "en": "Wind Strength highest", - "nl": "Wind Sterkte hoogste" - } - - }, - "measure_gust_strength": { - "title": { - "en": "Wind Gusts", - "nl": "Ruk wind sterkte" - } - }, - "measure_temperature.real": { - "title": { - "en": "Temperature Real", - "nl": "Temperatuur Echt" - } - }, - "measure_temperature.windchill": { - "title": { - "en": "Temperature windchill", - "nl": "Gevoelstemperatuur" - } - } -}, - - - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }] - }, { - "id": "thermometer", - "name": { - "en": "Thermometer", - "nl": "Thermometer" - }, - "images": { - "large": "drivers/thermometer/assets/images/large.jpg", - "small": "drivers/thermometer/assets/images/small.jpg" - }, - "class": "sensor", - "capabilities": [ - "measure_temperature", - "measure_humidity" - ], - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }] - }, { - "id": "wattcher", - "name": { - "en": "Wattcher", - "nl": "Wattcher" - }, - "images": { - "large": "drivers/wattcher/assets/images/large.jpg", - "small": "drivers/wattcher/assets/images/small.jpg" - }, - "class": "sensor", - "capabilities": [ - "measure_power", - "meter_power" - ], - - "capabilitiesOptions": { - "meter_power": { - "title": { - "en": "Day usage", - "nl": "Dag totaal" - } - }, - - "measure_power": { - "title": { - "en": "Power current", - "nl": "Huidig vermogen" - } - - } - - }, - - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }] - }, { - "id": "rainmeter", - "name": { - "en": "Rainmeter", - "nl": "Regen meter" - }, - "images": { - "large": "drivers/rainmeter/assets/images/large.jpg", - "small": "drivers/rainmeter/assets/images/small.jpg" - }, - "class": "sensor", - "capabilities": [ - "measure_rain.last3h", "measure_rain.total" - ], - - "capabilitiesOptions": { - "measure_rain.last3h": { - "title": { - "en": "Last 3 hours rain", - "nl": "Laatste 3 uur regen" - } - }, - - "measure_rain.total": { - "title": { - "en": "Rainfall today", - "nl": "Regenval vandaag" - } - - } - }, - - "pair": [{ - "id": "start" - }, { - "id": "list_my_devices", - "template": "list_devices", - "navigation": { - "next": "add_my_devices" - } - }, { - "id": "add_my_devices", - "template": "add_devices" - }] - }], - "permissions": [ - "homey:manager:ledring" - ] -} + "id": "com.homewizard", + "name": { + "en": "HomeWizard" + }, + "version": "0.2.7", + "sdk": 2, + "compatibility": ">=1.5", + "description": { + "en": "Control HomeWizard using Homey" + }, + "category": "appliances", + "images": { + "large": "assets/images/large.jpg", + "small": "assets/images/small.jpg" + }, + "author": { + "name": "Jeroen Tebbens", + "email": "jeroen@tebbens.net" + }, + "contributors": { + "developers": [ + { + "name": "Jeroen Bos", + "email": "jeroenbos22@gmail.com" + }, + { + "name": "Nick Bockmeulen", + "email": "git@bockmeulen.nl" + }, + { + "name": "Jeroen Tebbens", + "email": "jeroen@tebbens.net" + }, + { + "name": "Freddie Welvering", + "email": "freddie@welvering.eu" + } + ] + }, + "contributing": { + "donate": { + "paypal": { + "username": "jtebbens" + } + } + }, + "flow": { + "triggers": [ + { + "id": "preset_changed", + "title": { + "en": "Preset has changed", + "nl": "Preset is veranderd" + }, + "args": [ + { + "name": "Homewizard", + "type": "device", + "filter": "driver_id=homewizard" + } + ], + "tokens": [ + { + "name": "preset", + "type": "number", + "title": { + "en": "Preset", + "nl": "Preset" + }, + "example": 1 + }, + { + "name": "preset_text", + "type": "string", + "title": { + "en": "Text", + "nl": "Tekst" + }, + "example": "Home" + } + ] + }, + { + "id": "power_used_changed", + "title": { + "en": "Power used changed", + "nl": "Huidig vermogen veranderd" + }, + "args": [ + { + "name": "Energylink", + "type": "device", + "filter": "driver_id=energylink", + "placeholder": { + "en": "Which energylink", + "nl": "Welke energylink" + } + } + ], + "tokens": [ + { + "name": "power_used", + "type": "number", + "title": { + "en": "Watt", + "nl": "Watt" + }, + "example": 15 + } + ] + }, + { + "id": "power_s1_changed", + "title": { + "en": "Power production changed", + "nl": "Huidige productie veranderd" + }, + "args": [ + { + "name": "Energylink", + "type": "device", + "filter": "driver_id=energylink", + "placeholder": { + "en": "Which energylink", + "nl": "Welke energylink" + } + } + ], + "tokens": [ + { + "name": "power_s1", + "type": "number", + "title": { + "en": "Watt", + "nl": "Watt" + }, + "example": 15 + } + ] + }, + { + "id": "meter_power_used_changed", + "title": { + "en": "Daily usage changed", + "nl": "Dag verbruik veranderd" + }, + "args": [ + { + "name": "Energylink", + "type": "device", + "filter": "driver_id=energylink", + "placeholder": { + "en": "Which energylink", + "nl": "Welke energylink" + } + } + ], + "tokens": [ + { + "name": "power_daytotal_used", + "type": "number", + "title": { + "en": "kWh", + "nl": "kWh" + }, + "example": 1 + } + ] + }, + { + "id": "meter_power_aggregated_changed", + "title": { + "en": "Overall usage changed", + "nl": "Netto verbruik veranderd" + }, + "args": [ + { + "name": "Energylink", + "type": "device", + "filter": "driver_id=energylink", + "placeholder": { + "en": "Which energylink", + "nl": "Welke energylink" + } + } + ], + "tokens": [ + { + "name": "power_daytotal_aggr", + "type": "number", + "title": { + "en": "kWh", + "nl": "kWh" + }, + "example": 1 + } + ] + }, + { + "id": "meter_power_s1_changed", + "title": { + "en": "Daily production changed", + "nl": "Dag productie veranderd" + }, + "args": [ + { + "name": "Energylink", + "type": "device", + "filter": "driver_id=energylink", + "placeholder": { + "en": "Which energylink", + "nl": "Welke energylink" + } + } + ], + "tokens": [ + { + "name": "power_daytotal_s1", + "type": "number", + "title": { + "en": "kWh", + "nl": "kWh" + }, + "example": 1 + } + ] + }, + { + "id": "rainmeter_value_changed", + "title": { + "en": "Rainmeter value changed", + "nl": "Regenmeter waarde veranderd" + }, + "args": [ + { + "name": "Rainmeter", + "type": "device", + "filter": "driver_id=rainmeter", + "placeholder": { + "en": "Which Rainmeter", + "nl": "Welke Regenmeter" + } + } + ], + "tokens": [ + { + "name": "rainmeter_changed", + "type": "number", + "title": { + "en": "mm", + "nl": "mm" + }, + "example": 15 + } + ] + } + ], + "conditions": [ + { + "id": "check_preset", + "title": { + "en": "Preset !{{is|isn't}}", + "nl": "Preset !{{is|is niet}}" + }, + "args": [ + { + "name": "preset", + "type": "dropdown", + "values": [ + { + "id": "0", + "label": { + "en": "Home", + "nl": "Thuis" + } + }, + { + "id": "1", + "label": { + "en": "Away", + "nl": "Afwezig" + } + }, + { + "id": "2", + "label": { + "en": "Sleep", + "nl": "Slapen" + } + }, + { + "id": "3", + "label": { + "en": "Holiday", + "nl": "Vakantie" + } + } + ] + }, + { + "name": "device", + "type": "device", + "filter": "driver_id=homewizard" + } + ] + } + ], + "actions": [ + { + "id": "set_preset", + "title": { + "en": "Activate preset", + "nl": "Activeer preset" + }, + "args": [ + { + "name": "preset", + "type": "dropdown", + "values": [ + { + "id": "0", + "label": { + "en": "Home", + "nl": "Thuis" + } + }, + { + "id": "1", + "label": { + "en": "Away", + "nl": "Afwezig" + } + }, + { + "id": "2", + "label": { + "en": "Sleep", + "nl": "Slapen" + } + }, + { + "id": "3", + "label": { + "en": "Holiday", + "nl": "Vakantie" + } + } + ] + }, + { + "name": "device", + "type": "device", + "filter": "driver_id=homewizard" + } + ] + }, + { + "id": "switch_scene_on", + "title": { + "en": "Switch scene on", + "nl": "Zet scene aan" + }, + "args": [ + { + "name": "scene", + "type": "autocomplete" + }, + { + "name": "device", + "type": "device", + "filter": "driver_id=homewizard" + } + ] + }, + { + "id": "switch_scene_off", + "title": { + "en": "Switch scene off", + "nl": "Zet scene uit" + }, + "args": [ + { + "name": "scene", + "type": "autocomplete" + }, + { + "name": "device", + "type": "device", + "filter": "driver_id=homewizard" + } + ] + }, + { + "id": "heatlink_off", + "title": { + "en": "Heatlink off", + "nl": "Zet heatlink uit" + }, + "args": [ + { + "name": "device", + "type": "device", + "filter": "driver_id=homewizard" + } + ] + } + ] + }, + "drivers": [ + { + "id": "homewizard", + "name": { + "en": "HomeWizard", + "nl": "HomeWizard" + }, + "images": { + "large": "drivers/homewizard/assets/images/large.jpg", + "small": "drivers/homewizard/assets/images/small.jpg" + }, + "class": "other", + "capabilities": [], + "pair": [ + { + "id": "start" + }, + { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, + { + "id": "add_my_devices", + "template": "add_devices" + } + ], + "settings": [ + { + "type": "group", + "label": { + "en": "HomeWizard settings", + "nl": "HomeWizard instellingen" + }, + "children": [ + { + "id": "homewizard_ip", + "type": "text", + "label": { + "en": "IP address", + "nl": "IP adres" + }, + "value": "" + }, + { + "id": "homewizard_pass", + "type": "text", + "label": { + "en": "Password", + "nl": "Wachtwoord" + }, + "value": "" + }, + { + "id": "homewizard_ledring", + "type": "checkbox", + "label": { + "en": "Use ledring", + "nl": "Gebruik ledring" + }, + "value": false + } + ] + } + ] + }, + { + "id": "heatlink", + "name": { + "en": "Heatlink", + "nl": "Heatlink" + }, + "images": { + "large": "drivers/heatlink/assets/images/large.jpg", + "small": "drivers/heatlink/assets/images/small.jpg" + }, + "class": "thermostat", + "capabilities": [ + "measure_temperature", + "target_temperature" + ], + "pair": [ + { + "id": "start" + }, + { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, + { + "id": "add_my_devices", + "template": "add_devices" + } + ] + }, + { + "id": "energylink", + "name": { + "en": "Energylink", + "nl": "Energylink" + }, + "images": { + "large": "drivers/energylink/assets/images/large.jpg", + "small": "drivers/energylink/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "meter_power.used", + "meter_power.aggr", + "meter_power.s1", + "measure_power.used", + "measure_power.netto", + "measure_power.s1", + "meter_gas.today", + "meter_gas.reading", + "meter_water", + "measure_water", + "meter_power.consumed.t1", + "meter_power.produced.t1", + "meter_power.consumed.t2", + "meter_power.produced.t2" + ], + "capabilitiesOptions": { + "meter_power.used": { + "title": { + "en": "Day usage", + "nl": "Dag gebruik" + } + }, + "meter_power.aggr": { + "title": { + "en": "Overall usage", + "nl": "Netto gebruik" + } + }, + "meter_power.s1": { + "title": { + "en": "Day production", + "nl": "Dag opbrengst" + } + }, + "measure_power.used": { + "title": { + "en": "Power current", + "nl": "Huidig vermogen" + } + }, + "measure_power.netto": { + "title": { + "en": "Netto Power current", + "nl": "Netto Huidig vermogen" + } + }, + "measure_power.s1": { + "title": { + "en": "Solar current", + "nl": "Huidige opbrengst" + } + }, + "meter_gas.today": { + "title": { + "en": "Gas", + "nl": "Gas" + } + }, + "meter_gas.reading": { + "title": { + "en": "Meter reading gas", + "nl": "Stand Gas" + } + }, + "meter_water": { + "title": { + "en": "Water Total", + "nl": "Water Totaal" + } + }, + "measure_water": { + "title": { + "en": "Water l./m", + "nl": "Water l./m" + } + }, + "meter_power.consumed.t1": { + "title": { + "en": "Meter reading low", + "nl": "Stand laag tarief" + } + }, + "meter_power.produced.t1": { + "title": { + "en": "Meter reading produced low", + "nl": "Stand terug levering laag" + } + }, + "meter_power.consumed.t2": { + "title": { + "en": "Meter reading normal", + "nl": "Stand verbruik normaal" + } + }, + "meter_power.produced.t2": { + "title": { + "en": "Meter reading produced normal", + "nl": "Stand terug levering normaal" + } + } + }, + "pair": [ + { + "id": "start" + }, + { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, + { + "id": "add_my_devices", + "template": "add_devices" + } + ] + }, + { + "id": "thermometer", + "name": { + "en": "Thermometer", + "nl": "Thermometer" + }, + "images": { + "large": "drivers/thermometer/assets/images/large.jpg", + "small": "drivers/thermometer/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_temperature", + "measure_humidity" + ], + "pair": [ + { + "id": "start" + }, + { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, + { + "id": "add_my_devices", + "template": "add_devices" + } + ] + }, + { + "id": "wattcher", + "name": { + "en": "Wattcher", + "nl": "Wattcher" + }, + "images": { + "large": "drivers/wattcher/assets/images/large.jpg", + "small": "drivers/wattcher/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_power", + "meter_power" + ], + "capabilitiesOptions": { + "meter_power": { + "title": { + "en": "Day usage", + "nl": "Dag totaal" + } + }, + "measure_power": { + "title": { + "en": "Power current", + "nl": "Huidig vermogen" + } + } + }, + "pair": [ + { + "id": "start" + }, + { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, + { + "id": "add_my_devices", + "template": "add_devices" + } + ] + }, + { + "id": "windmeter", + "name": { + "en": "Windmeter", + "nl": "Windmeter" + }, + "images": { + "large": "drivers/windmeter/assets/images/large.jpg", + "small": "drivers/windmeter/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_wind_angle", + "measure_wind_strength.cur", + "measure_wind_strength.min", + "measure_wind_strength.max", + "measure_gust_strength", + "measure_temperature.real", + "measure_temperature.windchill" + ], + "capabilitiesOptions": { + "measure_wind_angle": { + "title": { + "en": "Wind Angle", + "nl": "Wind richting" + } + }, + "measure_wind_strength.cur": { + "title": { + "en": "Wind Strength current", + "nl": "Wind sterkte huidige" + } + }, + "measure_wind_strength.min": { + "title": { + "en": "Wind Strength lowest", + "nl": "Wind Sterkte laagste" + } + }, + "measure_wind_strength.max": { + "title": { + "en": "Wind Strength highest", + "nl": "Wind Sterkte hoogste" + } + }, + "measure_gust_strength": { + "title": { + "en": "Wind Gusts", + "nl": "Ruk wind sterkte" + } + }, + "measure_temperature.real": { + "title": { + "en": "Temperature Real", + "nl": "Temperatuur Echt" + } + }, + "measure_temperature.windchill": { + "title": { + "en": "Temperature windchill", + "nl": "Gevoelstemperatuur" + } + } + }, + "pair": [ + { + "id": "start" + }, + { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, + { + "id": "add_my_devices", + "template": "add_devices" + } + ] + }, + { + "id": "thermometer", + "name": { + "en": "Thermometer", + "nl": "Thermometer" + }, + "images": { + "large": "drivers/thermometer/assets/images/large.jpg", + "small": "drivers/thermometer/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_temperature", + "measure_humidity" + ], + "pair": [ + { + "id": "start" + }, + { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, + { + "id": "add_my_devices", + "template": "add_devices" + } + ] + }, + { + "id": "wattcher", + "name": { + "en": "Wattcher", + "nl": "Wattcher" + }, + "images": { + "large": "drivers/wattcher/assets/images/large.jpg", + "small": "drivers/wattcher/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_power", + "meter_power" + ], + "capabilitiesOptions": { + "meter_power": { + "title": { + "en": "Day usage", + "nl": "Dag totaal" + } + }, + "measure_power": { + "title": { + "en": "Power current", + "nl": "Huidig vermogen" + } + } + }, + "pair": [ + { + "id": "start" + }, + { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, + { + "id": "add_my_devices", + "template": "add_devices" + } + ] + }, + { + "id": "rainmeter", + "name": { + "en": "Rainmeter", + "nl": "Regen meter" + }, + "images": { + "large": "drivers/rainmeter/assets/images/large.jpg", + "small": "drivers/rainmeter/assets/images/small.jpg" + }, + "class": "sensor", + "capabilities": [ + "measure_rain.last3h", + "measure_rain.total" + ], + "capabilitiesOptions": { + "measure_rain.last3h": { + "title": { + "en": "Last 3 hours rain", + "nl": "Laatste 3 uur regen" + } + }, + "measure_rain.total": { + "title": { + "en": "Rainfall today", + "nl": "Regenval vandaag" + } + } + }, + "pair": [ + { + "id": "start" + }, + { + "id": "list_my_devices", + "template": "list_devices", + "navigation": { + "next": "add_my_devices" + } + }, + { + "id": "add_my_devices", + "template": "add_devices" + } + ] + } + ], + "permissions": [ + "homey:manager:ledring" + ] +} \ No newline at end of file diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 7db3fe3d..44cd7353 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -1,411 +1,411 @@ -var devices = {}; -var homewizard = require('./../../includes/homewizard.js'); -var refreshIntervalId = 0; -var refreshIntervalIdReadings = 0; - -// SETTINGS -module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { - Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); - try { - changedKeysArr.forEach(function (key) { - devices[device_data.id].settings[key] = newSettingsObj[key]; - }); - callback(null, true); - } catch (error) { - callback(error); - } -}; - -module.exports.pair = function( socket ) { - socket.on('get_homewizards', function () { - homewizard.getDevices(function(homewizard_devices) { - - Homey.log(homewizard_devices); - var hw_devices = {}; - Object.keys(homewizard_devices).forEach(function(key) { - hw_devices[key] = homewizard_devices[key]; - }); - - socket.emit('hw_devices', hw_devices); - }); - }); - - socket.on('manual_add', function (device, callback) { - if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { - //true - Homey.log('Energylink added ' + device.data.id); - devices[device.data.id] = { - id: device.data.id, - name: device.name, - settings: device.settings, - }; - callback( null, devices ); - socket.emit("success", device); - startPolling(); - } else { - socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); - } - }); - - socket.on('disconnect', function(){ - console.log("User aborted pairing, or pairing is finished"); - }); -} - -module.exports.init = function(devices_data, callback) { - devices_data.forEach(function initdevice(device) { - Homey.log('add device: ' + JSON.stringify(device)); - devices[device.id] = device; - module.exports.getSettings(device, function(err, settings){ - devices[device.id].settings = settings; - }); - }); - if (Object.keys(devices).length > 0) { - startPolling(); - } - Homey.log('Energylink driver init done'); - - callback (null, true); -}; - -module.exports.deleted = function( device_data ) { - delete devices[device_data.id]; - if (Object.keys(devices).length === 0) { - clearInterval(refreshIntervalId); - console.log("--Stopped Polling Energy Link--"); - } - Homey.log('deleted: ' + JSON.stringify(device_data)); -}; - -module.exports.capabilities = { - "measure_power.used": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_power_used); - } - } - }, - "measure_power.netto": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_power_netto); - } - } - }, - "measure_power.s1": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_power_s1); - } - } - }, - "meter_power.used": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_power_used); - } - } - }, - "meter_power.aggr": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_power_aggr); - } - } - }, - "meter_power.s1": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_power_s1); - } - } - }, - meter_gas: { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_gas); - } - } - }, - meter_water: { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_water); - } - } - }, - measure_water: { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_water); - } - } - }, - "meter_reading_consumed.tarrif1": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_reading_consumed_t1); - } - } - }, - "meter_reading_consumed.tarrif2": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_meter_reading_consumed_t2); - } - } - } - -}; - -// Start polling -function startPolling() { - if(refreshIntervalId){ - clearInterval(refreshIntervalId); - } - if(refreshIntervalIdReadings){ - clearInterval(refreshIntervalIdReadings); - } - - refreshIntervalId = setInterval(function () { - console.log("--Start Energylink Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); - }, 1000 * 10); - - // Request readings every minute - refreshIntervalIdReadings = setInterval(function () { - console.log("--Start Energylink Readings Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getReadings(device_id); - }); - }, 1000 * 60); -} - -function getStatus(device_id) { - if(devices[device_id].settings.homewizard_id !== undefined ) { - var homewizard_id = devices[device_id].settings.homewizard_id; - homewizard.getDeviceData(homewizard_id, 'energylinks', function(callback) { - if (Object.keys(callback).length > 0) { - try { - module.exports.setAvailable({id: device_id}); - - var value_s1 = ( callback[0].t1 ) ; // Read t1 from energylink (solar/water/null) - var value_s2 = ( callback[0].t2 ) ; // Read t2 from energylink (solar/water/null) - - console.log("t1- " + value_s1); - console.log("t2- " + value_s2); - - // Common Energylink data - var energy_current_cons = ( callback[0].used.po ); // WATTS Energy used JSON $energylink[0]['used']['po'] - var energy_daytotal_cons = ( callback[0].used.dayTotal ); // KWH Energy used JSON $energylink[0]['used']['dayTotal'] - var energy_daytotal_aggr = ( callback[0].aggregate.dayTotal ) ; // KWH Energy aggregated is used - generated $energylink[0]['aggregate']['dayTotal'] - var energy_current_netto = ( callback[0].aggregate.po ); // Netto power usage from aggregated value, this value can go negative - - // Some Energylink do not have gas information so try to get it else fail silently - try { - var gas_daytotal_cons = ( callback[0].gas.dayTotal ); // m3 Energy produced via S1 $energylink[0]['gas']['dayTotal'] - // Consumed gas - module.exports.realtime( { id: device_id }, "meter_gas.today", gas_daytotal_cons ); - } - catch(err) { - // Error with Energylink no data in Energylink - console.log ("No Gas information found"); - } - - // Consumed elec current - module.exports.realtime( { id: device_id }, "measure_power.used", energy_current_cons ); - // Consumed elec current Netto - module.exports.realtime( { id: device_id }, "measure_power.netto", energy_current_netto ); - // Consumed elec total day - module.exports.realtime( { id: device_id }, "meter_power.used", energy_daytotal_cons ); - // Consumed elec total day - module.exports.realtime( { id: device_id }, "meter_power.aggr", energy_daytotal_aggr ); - - // Set solar used to zero before counting - var solar_current_prod = 0; - var solar_daytotal_prod = 0; - - if (value_s1 == 'solar' ) { - var energy_current_prod = ( callback[0].s1.po ); // WATTS Energy produced via S1 $energylink[0]['s1']['po'] - var energy_daytotal_prod = ( callback[0].s1.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s1']['po'] - - var solar_current_prod = solar_current_prod + energy_current_prod; - var solar_daytotal_prod = solar_daytotal_prod + energy_daytotal_prod; - - } - - if (value_s2 == 'solar' ) { - var energy_current_prod = ( callback[0].s2.po ); // WATTS Energy produced via S1 $energylink[0]['s2']['po'] - var energy_daytotal_prod = ( callback[0].s2.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s2']['dayTotal'] - - var solar_current_prod = solar_current_prod + energy_current_prod; - var solar_daytotal_prod = solar_daytotal_prod + energy_daytotal_prod; - } - - if(value_s1 == 'solar' || value_s2 == 'solar') { - module.exports.realtime( { id: device_id }, "measure_power.s1", solar_current_prod ); - module.exports.realtime( { id: device_id }, "meter_power.s1", solar_daytotal_prod ); - } - - if (value_s1 == 'water' ) { - var water_current_cons = ( callback[0].s1.po ); // Water used via S1 $energylink[0]['s1']['po'] - var water_daytotal_cons = ( callback[0].s1.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s1']['dayTotal'] - console.log("Water- " + water_daytotal_cons); - // Used water m3 - module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); - module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); - - } - - if (value_s2 == 'water' ) { - var water_current_cons = ( callback[0].s2.po ); // Water used via S2 $energylink[0]['s1']['po'] - var water_daytotal_cons = ( callback[0].s2.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s2']['dayTotal'] - console.log("Water- " + water_daytotal_cons); - // Used water m3 - module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); - module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); - } - - // Trigger flows - if (energy_current_cons != devices[device_id].last_measure_power_used && energy_current_cons != undefined && energy_current_cons != null) { - console.log("Current Power - "+ energy_current_cons); - Homey.manager('flow').triggerDevice('power_used_changed', { power_used: energy_current_cons }, null, { id: device_id } ); - devices[device_id].last_measure_power_used = energy_current_cons; // Update last_measure_power_used - } - if (energy_current_netto != devices[device_id].last_measure_power_netto && energy_current_netto != undefined && energy_current_netto != null) { - console.log("Current Netto Power - "+ energy_current_netto); - Homey.manager('flow').triggerDevice('power_netto_changed', { netto_power_used: energy_current_netto }, null, { id: device_id } ); - devices[device_id].last_measure_power_netto = energy_current_netto; // Update last_measure_power_netto - } - if (energy_current_prod != devices[device_id].last_measure_power_s1 && energy_current_prod != undefined && energy_current_prod != null) { - console.log("Current S1 - "+ solar_current_prod); - Homey.manager('flow').triggerDevice('power_s1_changed', { power_s1: solar_current_prod }, null, { id: device_id } ); - devices[device_id].last_measure_power_s1 = energy_current_prod; // Update last_measure_power_s1 - } - if (energy_daytotal_cons != devices[device_id].last_meter_power_used && energy_daytotal_cons != undefined && energy_daytotal_cons != null) { - console.log("Used Daytotal- "+ energy_daytotal_cons); - Homey.manager('flow').triggerDevice('meter_power_used_changed', { power_daytotal_used: energy_daytotal_cons }, null, { id: device_id }); - devices[device_id].last_meter_power_used = energy_daytotal_cons; // Update last_measure_power_used - } - if (energy_daytotal_prod != devices[device_id].last_meter_power_s1 && energy_daytotal_prod != undefined && energy_daytotal_prod != null) { - console.log("S1 Daytotal- "+ solar_daytotal_prod); - Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: solar_daytotal_prod }, null, { id: device_id }); - devices[device_id].last_meter_power_s1 = energy_daytotal_prod; // Update last_meter_power_s1 - } - if (energy_daytotal_aggr != devices[device_id].last_meter_power_aggr && energy_daytotal_aggr != undefined && energy_daytotal_aggr != null) { - console.log("Aggregated Daytotal- "+ energy_daytotal_aggr); - Homey.manager('flow').triggerDevice('meter_power_aggregated_changed', { power_daytotal_aggr: energy_daytotal_aggr }, null, { id: device_id }); - devices[device_id].last_meter_power_aggr = energy_daytotal_aggr; // Update last_meter_power_aggr - } - - } - catch(err) { - // Error with Energylink no data in Energylink - console.log ("No Energylink found"); - module.exports.setUnavailable({id: device_id}, "No Energylink found" ); - } - } - }); - } else { - Homey.log('Removed Energylink '+ device_id +' (wrong settings)'); - module.exports.setUnavailable({id: device_id}, "No Energylink found" ); - // Only clear interval when the unavailable device is the only device on this driver - // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new - // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem - if(Object.keys(devices).length === 1) { - clearInterval(refreshIntervalId); - } - } -} - - -function getReadings(device_id) { - - if(devices[device_id].settings.homewizard_id !== undefined ) { - - var homewizard_id = devices[device_id].settings.homewizard_id; - homewizard.getDeviceData(homewizard_id, 'energylink_el', function(callback) { - - if (Object.keys(callback).length > 0) { - - try { - module.exports.setAvailable({id: device_id}) - - var metered_gas = callback[2].consumed; - var metered_electricity_consumed_t1 = callback[0].consumed; - var metered_electricity_produced_t1 = callback[0].produced; - var metered_electricity_consumed_t2 = callback[1].consumed; - var metered_electricity_produced_t2 = callback[1].produced; - - // Save export data - module.exports.realtime( { id: device_id }, "meter_gas.reading", metered_gas ); - module.exports.realtime( { id: device_id }, "meter_power.consumed.t1", metered_electricity_consumed_t1 ); - module.exports.realtime( { id: device_id }, "meter_power.produced.t1", metered_electricity_produced_t1 ); - module.exports.realtime( { id: device_id }, "meter_power.consumed.t2", metered_electricity_consumed_t2 ); - module.exports.realtime( { id: device_id }, "meter_power.produced.t2", metered_electricity_produced_t2 ); - - } - catch (err) { - // Error with Energylink no data in Energylink - console.log("No Energylink found"); - module.exports.setUnavailable({id: device_id}, "No Energylink found"); - } - } - }); - } else { - Homey.log('Removed Energylink '+ device_id +' (wrong settings)'); - module.exports.setUnavailable({id: device_id}, "No Energylink found" ); - // Only clear interval when the unavailable device is the only device on this driver - // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new - // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem - if(Object.keys(devices).length === 1) { - clearInterval(refreshIntervalIdReadings); - } - } -} +// var devices = {}; +// var homewizard = require('./../../includes/homewizard.js'); +// var refreshIntervalId = 0; +// var refreshIntervalIdReadings = 0; +// +// // SETTINGS +// module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { +// Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); +// try { +// changedKeysArr.forEach(function (key) { +// devices[device_data.id].settings[key] = newSettingsObj[key]; +// }); +// callback(null, true); +// } catch (error) { +// callback(error); +// } +// }; +// +// module.exports.pair = function( socket ) { +// socket.on('get_homewizards', function () { +// homewizard.getDevices(function(homewizard_devices) { +// +// Homey.log(homewizard_devices); +// var hw_devices = {}; +// Object.keys(homewizard_devices).forEach(function(key) { +// hw_devices[key] = homewizard_devices[key]; +// }); +// +// socket.emit('hw_devices', hw_devices); +// }); +// }); +// +// socket.on('manual_add', function (device, callback) { +// if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { +// //true +// Homey.log('Energylink added ' + device.data.id); +// devices[device.data.id] = { +// id: device.data.id, +// name: device.name, +// settings: device.settings, +// }; +// callback( null, devices ); +// socket.emit("success", device); +// startPolling(); +// } else { +// socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); +// } +// }); +// +// socket.on('disconnect', function(){ +// console.log("User aborted pairing, or pairing is finished"); +// }); +// } +// +// module.exports.init = function(devices_data, callback) { +// devices_data.forEach(function initdevice(device) { +// Homey.log('add device: ' + JSON.stringify(device)); +// devices[device.id] = device; +// module.exports.getSettings(device, function(err, settings){ +// devices[device.id].settings = settings; +// }); +// }); +// if (Object.keys(devices).length > 0) { +// startPolling(); +// } +// Homey.log('Energylink driver init done'); +// +// callback (null, true); +// }; +// +// module.exports.deleted = function( device_data ) { +// delete devices[device_data.id]; +// if (Object.keys(devices).length === 0) { +// clearInterval(refreshIntervalId); +// console.log("--Stopped Polling Energy Link--"); +// } +// Homey.log('deleted: ' + JSON.stringify(device_data)); +// }; +// +// module.exports.capabilities = { +// "measure_power.used": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_power_used); +// } +// } +// }, +// "measure_power.netto": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_power_netto); +// } +// } +// }, +// "measure_power.s1": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_power_s1); +// } +// } +// }, +// "meter_power.used": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_meter_power_used); +// } +// } +// }, +// "meter_power.aggr": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_meter_power_aggr); +// } +// } +// }, +// "meter_power.s1": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_meter_power_s1); +// } +// } +// }, +// meter_gas: { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_meter_gas); +// } +// } +// }, +// meter_water: { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_meter_water); +// } +// } +// }, +// measure_water: { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_water); +// } +// } +// }, +// "meter_reading_consumed.tarrif1": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_meter_reading_consumed_t1); +// } +// } +// }, +// "meter_reading_consumed.tarrif2": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_meter_reading_consumed_t2); +// } +// } +// } +// +// }; +// +// // Start polling +// function startPolling() { +// if(refreshIntervalId){ +// clearInterval(refreshIntervalId); +// } +// if(refreshIntervalIdReadings){ +// clearInterval(refreshIntervalIdReadings); +// } +// +// refreshIntervalId = setInterval(function () { +// console.log("--Start Energylink Polling-- "); +// Object.keys(devices).forEach(function (device_id) { +// getStatus(device_id); +// }); +// }, 1000 * 10); +// +// // Request readings every minute +// refreshIntervalIdReadings = setInterval(function () { +// console.log("--Start Energylink Readings Polling-- "); +// Object.keys(devices).forEach(function (device_id) { +// getReadings(device_id); +// }); +// }, 1000 * 60); +// } +// +// function getStatus(device_id) { +// if(devices[device_id].settings.homewizard_id !== undefined ) { +// var homewizard_id = devices[device_id].settings.homewizard_id; +// homewizard.getDeviceData(homewizard_id, 'energylinks', function(callback) { +// if (Object.keys(callback).length > 0) { +// try { +// module.exports.setAvailable({id: device_id}); +// +// var value_s1 = ( callback[0].t1 ) ; // Read t1 from energylink (solar/water/null) +// var value_s2 = ( callback[0].t2 ) ; // Read t2 from energylink (solar/water/null) +// +// console.log("t1- " + value_s1); +// console.log("t2- " + value_s2); +// +// // Common Energylink data +// var energy_current_cons = ( callback[0].used.po ); // WATTS Energy used JSON $energylink[0]['used']['po'] +// var energy_daytotal_cons = ( callback[0].used.dayTotal ); // KWH Energy used JSON $energylink[0]['used']['dayTotal'] +// var energy_daytotal_aggr = ( callback[0].aggregate.dayTotal ) ; // KWH Energy aggregated is used - generated $energylink[0]['aggregate']['dayTotal'] +// var energy_current_netto = ( callback[0].aggregate.po ); // Netto power usage from aggregated value, this value can go negative +// +// // Some Energylink do not have gas information so try to get it else fail silently +// try { +// var gas_daytotal_cons = ( callback[0].gas.dayTotal ); // m3 Energy produced via S1 $energylink[0]['gas']['dayTotal'] +// // Consumed gas +// module.exports.realtime( { id: device_id }, "meter_gas.today", gas_daytotal_cons ); +// } +// catch(err) { +// // Error with Energylink no data in Energylink +// console.log ("No Gas information found"); +// } +// +// // Consumed elec current +// module.exports.realtime( { id: device_id }, "measure_power.used", energy_current_cons ); +// // Consumed elec current Netto +// module.exports.realtime( { id: device_id }, "measure_power.netto", energy_current_netto ); +// // Consumed elec total day +// module.exports.realtime( { id: device_id }, "meter_power.used", energy_daytotal_cons ); +// // Consumed elec total day +// module.exports.realtime( { id: device_id }, "meter_power.aggr", energy_daytotal_aggr ); +// +// // Set solar used to zero before counting +// var solar_current_prod = 0; +// var solar_daytotal_prod = 0; +// +// if (value_s1 == 'solar' ) { +// var energy_current_prod = ( callback[0].s1.po ); // WATTS Energy produced via S1 $energylink[0]['s1']['po'] +// var energy_daytotal_prod = ( callback[0].s1.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s1']['po'] +// +// var solar_current_prod = solar_current_prod + energy_current_prod; +// var solar_daytotal_prod = solar_daytotal_prod + energy_daytotal_prod; +// +// } +// +// if (value_s2 == 'solar' ) { +// var energy_current_prod = ( callback[0].s2.po ); // WATTS Energy produced via S1 $energylink[0]['s2']['po'] +// var energy_daytotal_prod = ( callback[0].s2.dayTotal ); // KWH Energy produced via S1 $energylink[0]['s2']['dayTotal'] +// +// var solar_current_prod = solar_current_prod + energy_current_prod; +// var solar_daytotal_prod = solar_daytotal_prod + energy_daytotal_prod; +// } +// +// if(value_s1 == 'solar' || value_s2 == 'solar') { +// module.exports.realtime( { id: device_id }, "measure_power.s1", solar_current_prod ); +// module.exports.realtime( { id: device_id }, "meter_power.s1", solar_daytotal_prod ); +// } +// +// if (value_s1 == 'water' ) { +// var water_current_cons = ( callback[0].s1.po ); // Water used via S1 $energylink[0]['s1']['po'] +// var water_daytotal_cons = ( callback[0].s1.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s1']['dayTotal'] +// console.log("Water- " + water_daytotal_cons); +// // Used water m3 +// module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); +// module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); +// +// } +// +// if (value_s2 == 'water' ) { +// var water_current_cons = ( callback[0].s2.po ); // Water used via S2 $energylink[0]['s1']['po'] +// var water_daytotal_cons = ( callback[0].s2.dayTotal / 1000 ); // Water used via S1 $energylink[0]['s2']['dayTotal'] +// console.log("Water- " + water_daytotal_cons); +// // Used water m3 +// module.exports.realtime( { id: device_id }, "meter_water", water_daytotal_cons ); +// module.exports.realtime( { id: device_id }, "measure_water", water_current_cons ); +// } +// +// // Trigger flows +// if (energy_current_cons != devices[device_id].last_measure_power_used && energy_current_cons != undefined && energy_current_cons != null) { +// console.log("Current Power - "+ energy_current_cons); +// Homey.manager('flow').triggerDevice('power_used_changed', { power_used: energy_current_cons }, null, { id: device_id } ); +// devices[device_id].last_measure_power_used = energy_current_cons; // Update last_measure_power_used +// } +// if (energy_current_netto != devices[device_id].last_measure_power_netto && energy_current_netto != undefined && energy_current_netto != null) { +// console.log("Current Netto Power - "+ energy_current_netto); +// Homey.manager('flow').triggerDevice('power_netto_changed', { netto_power_used: energy_current_netto }, null, { id: device_id } ); +// devices[device_id].last_measure_power_netto = energy_current_netto; // Update last_measure_power_netto +// } +// if (energy_current_prod != devices[device_id].last_measure_power_s1 && energy_current_prod != undefined && energy_current_prod != null) { +// console.log("Current S1 - "+ solar_current_prod); +// Homey.manager('flow').triggerDevice('power_s1_changed', { power_s1: solar_current_prod }, null, { id: device_id } ); +// devices[device_id].last_measure_power_s1 = energy_current_prod; // Update last_measure_power_s1 +// } +// if (energy_daytotal_cons != devices[device_id].last_meter_power_used && energy_daytotal_cons != undefined && energy_daytotal_cons != null) { +// console.log("Used Daytotal- "+ energy_daytotal_cons); +// Homey.manager('flow').triggerDevice('meter_power_used_changed', { power_daytotal_used: energy_daytotal_cons }, null, { id: device_id }); +// devices[device_id].last_meter_power_used = energy_daytotal_cons; // Update last_measure_power_used +// } +// if (energy_daytotal_prod != devices[device_id].last_meter_power_s1 && energy_daytotal_prod != undefined && energy_daytotal_prod != null) { +// console.log("S1 Daytotal- "+ solar_daytotal_prod); +// Homey.manager('flow').triggerDevice('meter_power_s1_changed', { power_daytotal_s1: solar_daytotal_prod }, null, { id: device_id }); +// devices[device_id].last_meter_power_s1 = energy_daytotal_prod; // Update last_meter_power_s1 +// } +// if (energy_daytotal_aggr != devices[device_id].last_meter_power_aggr && energy_daytotal_aggr != undefined && energy_daytotal_aggr != null) { +// console.log("Aggregated Daytotal- "+ energy_daytotal_aggr); +// Homey.manager('flow').triggerDevice('meter_power_aggregated_changed', { power_daytotal_aggr: energy_daytotal_aggr }, null, { id: device_id }); +// devices[device_id].last_meter_power_aggr = energy_daytotal_aggr; // Update last_meter_power_aggr +// } +// +// } +// catch(err) { +// // Error with Energylink no data in Energylink +// console.log ("No Energylink found"); +// module.exports.setUnavailable({id: device_id}, "No Energylink found" ); +// } +// } +// }); +// } else { +// Homey.log('Removed Energylink '+ device_id +' (wrong settings)'); +// module.exports.setUnavailable({id: device_id}, "No Energylink found" ); +// // Only clear interval when the unavailable device is the only device on this driver +// // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new +// // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem +// if(Object.keys(devices).length === 1) { +// clearInterval(refreshIntervalId); +// } +// } +// } +// +// +// function getReadings(device_id) { +// +// if(devices[device_id].settings.homewizard_id !== undefined ) { +// +// var homewizard_id = devices[device_id].settings.homewizard_id; +// homewizard.getDeviceData(homewizard_id, 'energylink_el', function(callback) { +// +// if (Object.keys(callback).length > 0) { +// +// try { +// module.exports.setAvailable({id: device_id}) +// +// var metered_gas = callback[2].consumed; +// var metered_electricity_consumed_t1 = callback[0].consumed; +// var metered_electricity_produced_t1 = callback[0].produced; +// var metered_electricity_consumed_t2 = callback[1].consumed; +// var metered_electricity_produced_t2 = callback[1].produced; +// +// // Save export data +// module.exports.realtime( { id: device_id }, "meter_gas.reading", metered_gas ); +// module.exports.realtime( { id: device_id }, "meter_power.consumed.t1", metered_electricity_consumed_t1 ); +// module.exports.realtime( { id: device_id }, "meter_power.produced.t1", metered_electricity_produced_t1 ); +// module.exports.realtime( { id: device_id }, "meter_power.consumed.t2", metered_electricity_consumed_t2 ); +// module.exports.realtime( { id: device_id }, "meter_power.produced.t2", metered_electricity_produced_t2 ); +// +// } +// catch (err) { +// // Error with Energylink no data in Energylink +// console.log("No Energylink found"); +// module.exports.setUnavailable({id: device_id}, "No Energylink found"); +// } +// } +// }); +// } else { +// Homey.log('Removed Energylink '+ device_id +' (wrong settings)'); +// module.exports.setUnavailable({id: device_id}, "No Energylink found" ); +// // Only clear interval when the unavailable device is the only device on this driver +// // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new +// // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem +// if(Object.keys(devices).length === 1) { +// clearInterval(refreshIntervalIdReadings); +// } +// } +// } diff --git a/drivers/heatlink/driver.js b/drivers/heatlink/driver.js index 0e3f4e87..f08989a7 100755 --- a/drivers/heatlink/driver.js +++ b/drivers/heatlink/driver.js @@ -1,214 +1,214 @@ -var devices = {}; -var homewizard = require('./../../includes/homewizard.js'); -var refreshIntervalId = 0; - -// SETTINGS -module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { - Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); - try { - changedKeysArr.forEach(function (key) { - devices[device_data.id].settings[key] = newSettingsObj[key]; - }); - callback(null, true); - } catch (error) { - callback(error); - } -}; - -module.exports.pair = function( socket ) { - socket.on('get_homewizards', function () { - homewizard.getDevices(function(homewizard_devices) { - - Homey.log(homewizard_devices); - var hw_devices = {}; - Object.keys(homewizard_devices).forEach(function(key) { - hw_devices[key] = homewizard_devices[key]; - }); - - socket.emit('hw_devices', hw_devices); - }); - }); - - socket.on('manual_add', function (device, callback) { - if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { - //true - Homey.log('HeatLink added ' + device.data.id); - devices[device.data.id] = { - id: device.data.id, - name: device.name, - settings: device.settings, - }; - callback( null, devices ); - socket.emit("success", device); - startPolling(); - } else { - socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); - } - }); - - socket.on('disconnect', function(){ - console.log("User aborted pairing, or pairing is finished"); - }); -} - -module.exports.init = function(devices_data, callback) { - devices_data.forEach(function initdevice(device) { - Homey.log('add device: ' + JSON.stringify(device)); - devices[device.id] = device; - module.exports.getSettings(device, function(err, settings){ - devices[device.id].settings = settings; - }); - - }); - if (Object.keys(devices).length > 0) { - startPolling(); - } - Homey.log('Heatlink driver init done'); - - callback (null, true); -}; - -module.exports.deleted = function( device_data ) { - delete devices[device_data.id]; - if (Object.keys(devices).length === 0) { - clearInterval(refreshIntervalId); - Homey.log("--Stopped Polling--"); - } - Homey.log('deleted: ' + JSON.stringify(device_data)); -}; - - -module.exports.capabilities = { - - measure_temperature: { - get: function (device, callback) { - if (device instanceof Error) return callback(device); - console.log("measure_temperature"); - getStatus(device.id); - var newvalue = devices[device.id].temperature; - // Callback ambient temperature - callback(null, newvalue); - } - }, - target_temperature: { - - get: function (device, callback) { - if (device instanceof Error) return callback(device); - console.log("target_temperature:get"); - // Retrieve updated data - getStatus(device.id); - var newvalue; - if (devices[device.id].setTemperature !== 0) { - newvalue = devices[device.id].setTemperature; - } else { - newvalue = devices[device.id].thermTemperature; - } - callback(null, newvalue); - }, - - set: function (device, temperature, callback) { - if (device instanceof Error) return callback(device); - // Catch faulty trigger and max/min temp - if (!temperature) { - callback(true, temperature); - return false; - } - else if (temperature < 5) { - temperature = 5; - } - else if (temperature > 35) { - temperature = 35; - } - temperature = Math.round(temperature.toFixed(1) * 2) / 2; - var url = '/hl/0/settarget/'+temperature; - console.log(url); - var homewizard_id = devices[device.id].settings.homewizard_id; - homewizard.call(homewizard_id, '/hl/0/settarget/'+temperature, function(err, response) { - console.log(err); - if (callback) callback(err, temperature); - }); - } - }, -}; - -Homey.manager('flow').on('action.heatlink_off', function( callback, args ){ - homewizard.call(args.device.id, '/hl/0/settarget/0', function(err, response) { - if (err === null) { - Homey.log('Heatlink Off'); - callback( null, true ); - } else { - callback(err, false); // err - } - }); -}); - -function getStatus(device_id) { - if(devices[device_id].settings.homewizard_id !== undefined ) { - var homewizard_id = devices[device_id].settings.homewizard_id; - homewizard.getDeviceData(homewizard_id, 'heatlinks', function(callback) { - if (Object.keys(callback).length > 0) { - try { - var rte = (callback[0].rte.toFixed(1) * 2) / 2; - var rsp = (callback[0].rsp.toFixed(1) * 2) / 2; - var tte = (callback[0].tte.toFixed(1) * 2) / 2; - - //Check current temperature - if (devices[device_id].temperature != rte) { - console.log("New RTE - "+ rte); - module.exports.realtime( { id: device_id }, "measure_temperature", rte ); - devices[device_id].temperature = rte; - } else { - console.log("RTE: no change"); - } - - //Check thermostat temperature - if (devices[device_id].thermTemperature != rsp) { - console.log("New RSP - "+ rsp); - if (devices[device_id].setTemperature === 0) { - module.exports.realtime( { id: device_id }, "target_temperature", rsp ); - } - devices[device_id].thermTemperature = rsp; - } else { - console.log("RSP: no change"); - } - - //Check heatlink set temperature - if (devices[device_id].setTemperature != tte) { - console.log("New TTE - "+ tte); - if (tte > 0) { - module.exports.realtime( { id: device_id }, "target_temperature", tte ); - } else { - module.exports.realtime( { id: device_id }, "target_temperature", devices[device_id].thermTemperature ); - } - devices[device_id].setTemperature = tte; - } else { - console.log("TTE: no change"); - } - } catch(err) { - console.log ("Heatlink data corrupt"); - } - } - }); - } else { - Homey.log('Removed Heatlink '+ device_id +' (old settings)'); - module.exports.setUnavailable({id: device_id}, "No Heatlink found" ); - // Only clear interval when the unavailable device is the only device on this driver - // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new - // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem - if(Object.keys(devices).length === 1) { - clearInterval(refreshIntervalId); - } - } - } - - function startPolling() { - if (refreshIntervalId) { - clearInterval(refreshIntervalId); - } - refreshIntervalId = setInterval(function () { - Homey.log("--Start Heatlink Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); - }, 1000 * 10); - } +// var devices = {}; +// var homewizard = require('./../../includes/homewizard.js'); +// var refreshIntervalId = 0; +// +// // SETTINGS +// module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { +// Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); +// try { +// changedKeysArr.forEach(function (key) { +// devices[device_data.id].settings[key] = newSettingsObj[key]; +// }); +// callback(null, true); +// } catch (error) { +// callback(error); +// } +// }; +// +// module.exports.pair = function( socket ) { +// socket.on('get_homewizards', function () { +// homewizard.getDevices(function(homewizard_devices) { +// +// Homey.log(homewizard_devices); +// var hw_devices = {}; +// Object.keys(homewizard_devices).forEach(function(key) { +// hw_devices[key] = homewizard_devices[key]; +// }); +// +// socket.emit('hw_devices', hw_devices); +// }); +// }); +// +// socket.on('manual_add', function (device, callback) { +// if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { +// //true +// Homey.log('HeatLink added ' + device.data.id); +// devices[device.data.id] = { +// id: device.data.id, +// name: device.name, +// settings: device.settings, +// }; +// callback( null, devices ); +// socket.emit("success", device); +// startPolling(); +// } else { +// socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); +// } +// }); +// +// socket.on('disconnect', function(){ +// console.log("User aborted pairing, or pairing is finished"); +// }); +// } +// +// module.exports.init = function(devices_data, callback) { +// devices_data.forEach(function initdevice(device) { +// Homey.log('add device: ' + JSON.stringify(device)); +// devices[device.id] = device; +// module.exports.getSettings(device, function(err, settings){ +// devices[device.id].settings = settings; +// }); +// +// }); +// if (Object.keys(devices).length > 0) { +// startPolling(); +// } +// Homey.log('Heatlink driver init done'); +// +// callback (null, true); +// }; +// +// module.exports.deleted = function( device_data ) { +// delete devices[device_data.id]; +// if (Object.keys(devices).length === 0) { +// clearInterval(refreshIntervalId); +// Homey.log("--Stopped Polling--"); +// } +// Homey.log('deleted: ' + JSON.stringify(device_data)); +// }; +// +// +// module.exports.capabilities = { +// +// measure_temperature: { +// get: function (device, callback) { +// if (device instanceof Error) return callback(device); +// console.log("measure_temperature"); +// getStatus(device.id); +// var newvalue = devices[device.id].temperature; +// // Callback ambient temperature +// callback(null, newvalue); +// } +// }, +// target_temperature: { +// +// get: function (device, callback) { +// if (device instanceof Error) return callback(device); +// console.log("target_temperature:get"); +// // Retrieve updated data +// getStatus(device.id); +// var newvalue; +// if (devices[device.id].setTemperature !== 0) { +// newvalue = devices[device.id].setTemperature; +// } else { +// newvalue = devices[device.id].thermTemperature; +// } +// callback(null, newvalue); +// }, +// +// set: function (device, temperature, callback) { +// if (device instanceof Error) return callback(device); +// // Catch faulty trigger and max/min temp +// if (!temperature) { +// callback(true, temperature); +// return false; +// } +// else if (temperature < 5) { +// temperature = 5; +// } +// else if (temperature > 35) { +// temperature = 35; +// } +// temperature = Math.round(temperature.toFixed(1) * 2) / 2; +// var url = '/hl/0/settarget/'+temperature; +// console.log(url); +// var homewizard_id = devices[device.id].settings.homewizard_id; +// homewizard.call(homewizard_id, '/hl/0/settarget/'+temperature, function(err, response) { +// console.log(err); +// if (callback) callback(err, temperature); +// }); +// } +// }, +// }; +// +// Homey.manager('flow').on('action.heatlink_off', function( callback, args ){ +// homewizard.call(args.device.id, '/hl/0/settarget/0', function(err, response) { +// if (err === null) { +// Homey.log('Heatlink Off'); +// callback( null, true ); +// } else { +// callback(err, false); // err +// } +// }); +// }); +// +// function getStatus(device_id) { +// if(devices[device_id].settings.homewizard_id !== undefined ) { +// var homewizard_id = devices[device_id].settings.homewizard_id; +// homewizard.getDeviceData(homewizard_id, 'heatlinks', function(callback) { +// if (Object.keys(callback).length > 0) { +// try { +// var rte = (callback[0].rte.toFixed(1) * 2) / 2; +// var rsp = (callback[0].rsp.toFixed(1) * 2) / 2; +// var tte = (callback[0].tte.toFixed(1) * 2) / 2; +// +// //Check current temperature +// if (devices[device_id].temperature != rte) { +// console.log("New RTE - "+ rte); +// module.exports.realtime( { id: device_id }, "measure_temperature", rte ); +// devices[device_id].temperature = rte; +// } else { +// console.log("RTE: no change"); +// } +// +// //Check thermostat temperature +// if (devices[device_id].thermTemperature != rsp) { +// console.log("New RSP - "+ rsp); +// if (devices[device_id].setTemperature === 0) { +// module.exports.realtime( { id: device_id }, "target_temperature", rsp ); +// } +// devices[device_id].thermTemperature = rsp; +// } else { +// console.log("RSP: no change"); +// } +// +// //Check heatlink set temperature +// if (devices[device_id].setTemperature != tte) { +// console.log("New TTE - "+ tte); +// if (tte > 0) { +// module.exports.realtime( { id: device_id }, "target_temperature", tte ); +// } else { +// module.exports.realtime( { id: device_id }, "target_temperature", devices[device_id].thermTemperature ); +// } +// devices[device_id].setTemperature = tte; +// } else { +// console.log("TTE: no change"); +// } +// } catch(err) { +// console.log ("Heatlink data corrupt"); +// } +// } +// }); +// } else { +// Homey.log('Removed Heatlink '+ device_id +' (old settings)'); +// module.exports.setUnavailable({id: device_id}, "No Heatlink found" ); +// // Only clear interval when the unavailable device is the only device on this driver +// // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new +// // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem +// if(Object.keys(devices).length === 1) { +// clearInterval(refreshIntervalId); +// } +// } +// } +// +// function startPolling() { +// if (refreshIntervalId) { +// clearInterval(refreshIntervalId); +// } +// refreshIntervalId = setInterval(function () { +// Homey.log("--Start Heatlink Polling-- "); +// Object.keys(devices).forEach(function (device_id) { +// getStatus(device_id); +// }); +// }, 1000 * 10); +// } diff --git a/drivers/homewizard/device.js b/drivers/homewizard/device.js new file mode 100644 index 00000000..8a6b5878 --- /dev/null +++ b/drivers/homewizard/device.js @@ -0,0 +1,43 @@ +'use strict'; + +const Homey = require('homey'); +const { ManagerDrivers } = require('homey'); +const drivers = ManagerDrivers.getDriver('homewizard'); + +var homewizard = require('./../../includes/homewizard.js'); +var refreshIntervalId; +var homeWizard_devices = {}; + +class HomeWizardDevice extends Homey.Device { + + onInit() { + + this.log('HomeWizard Appliance has been inited'); + + const devices = drivers.getDevices(); + + devices.forEach(function initdevice(device) { + console.log('add device: ' + JSON.stringify(device.getName())); + + homeWizard_devices[device.getData().id] = {}; + homeWizard_devices[device.getData().id].name = device.getName(); + homeWizard_devices[device.getData().id].settings = device.getSettings(); + }); + + homewizard.setDevices(homeWizard_devices); + homewizard.startpoll(); + + if (Object.keys(homeWizard_devices).length > 0) { + this.startPolling(); + } + + } + + startPolling = function() { + + } +} + + + +module.exports = HomeWizardDevice; \ No newline at end of file diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index ed0bfd65..12760319 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -1,211 +1,293 @@ +'use strict'; + +const Homey = require('homey'); +const request = require('request'); + var devices = {}; var homewizard = require('./../../includes/homewizard.js'); -var request = require('request'); var refreshIntervalId; var preset_text = ''; var preset_text_nl = ['Thuis', 'Afwezig', 'Slapen', 'Vakantie']; var preset_text_en = ['Home', 'Away', 'Sleep', 'Holiday']; -var homey_lang = Homey.manager('i18n').getLanguage(); - -// SETTINGS -module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { - Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); - try { - changedKeysArr.forEach(function (key) { - devices[device_data.id].settings[key] = newSettingsObj[key]; - }); - homewizard.setDevices(devices); - callback(null, true); - } catch (error) { - callback(error); - } -}; - -module.exports.pair = function( socket ) { - socket.on('manual_add', function (device, callback) { - var url = 'http://' + device.settings.homewizard_ip + '/' + device.settings.homewizard_pass + '/get-status/'; - //Homey.log('Calling '+ url); - request(url, function (error, response, body) { - if (response === null || response === undefined) { - socket.emit("error", "http error"); - return; - } - if (!error && response.statusCode == 200) { - var jsonObject = JSON.parse(body); - if (jsonObject.status == 'ok') { - //true - Homey.log('HW added'); - devices[device.data.id] = { - id: device.data.id, - name: device.name, - settings: device.settings, - capabilities: device.capabilities - }; - homewizard.setDevices(devices); - callback( null, devices ); - socket.emit("success", device); - startPolling(); - } else { - //false - socket.emit("error", "no response"); - } - } else { - // false - socket.emit("error", "http error: "+response.statusCode); - } - }); - }); - - socket.on('disconnect', function(){ - console.log("User aborted pairing, or pairing is finished"); - }); -} +// var homey_lang = Homey.manager('i18n').getLanguage(); -module.exports.init = function(devices_data, callback) { - if (homewizard.debug) { - devices_data = homewizard.debug_devices_data; +class HomeWizardDriver extends Homey.Driver { + onInit() { + this.log('HomeWizard has been inited'); } - - devices_data.forEach(function initdevice(device) { - Homey.log('add device: ' + JSON.stringify(device)); - devices[device.id] = device; - module.exports.getSettings(device, function(err, settings){ - devices[device.id].settings = settings; + + onPair( socket ) { + // Show a specific view by ID + socket.showView('start'); + + // Show the next view + socket.nextView(); + + // Show the previous view + socket.prevView(); + + // Close the pair session + socket.done(); + + // Received when a view has changed + socket.on('showView', ( viewId, callback ) => { + callback(); + console.log('View: ' + viewId); }); - }); - homewizard.setDevices(devices); - homewizard.startpoll(); - - if (Object.keys(devices).length > 0) { - startPolling(); - } - - Homey.log('HomeWizard driver init done'); - callback (null, true); -}; - -module.exports.deleted = function( device_data ) { - delete devices[device_data.id]; - if (Object.keys(devices).length === 0) { - clearInterval(refreshIntervalId); - Homey.log("--Stopped Polling--"); - } - Homey.log('deleted: ' + JSON.stringify(device_data)); -}; - -// SCENES -Homey.manager('flow').on('action.switch_scene_on.scene.autocomplete', function( callback, args ){ - Homey.log('getScenes autocomplete called'); - homewizard.getScenes(args, function(err, response) { - callback(err, response ); // err, results - }); -}); - -Homey.manager('flow').on('action.switch_scene_on', function( callback, args ){ - Homey.log('args.device.id: ' + JSON.stringify(args.device.id)); - Homey.log('args.scene.id: ' + JSON.stringify(args.scene.id)); - homewizard.call(args.device.id, '/gp/' + args.scene.id + '/on', function(err, response) { - if (err === null) { - Homey.log('Scene is on'); - callback( null, true ); - } else { - callback(err, false); // err - } - }); -}); - -Homey.manager('flow').on('action.switch_scene_off.scene.autocomplete', function( callback, args ){ - homewizard.getScenes(args, function(err, response) { - callback(err, response ); // err, results - }); -}); - -Homey.manager('flow').on('action.switch_scene_off', function( callback, args ){ - homewizard.call(args.device.id, '/gp/' + args.scene.id + '/off', function(err, response) { - if (err === null) { - Homey.log('Scene is off'); - callback( null, true ); - } else { - callback(err, false); // err - } - }); -}); - - -// PRESETS -Homey.manager('flow').on('condition.check_preset', function( callback, args ){ - homewizard.call(args.device.id, '/get-status/', function(err, response) { - if (err === null) { - if (response.preset == args.preset) { - Homey.log('Yes, preset is: '+ response.preset+'!'); - if(typeof callback === 'function') { - callback(null, true); - } - } else { - Homey.log('Preset is: ' + response.preset+', not '+args.preset); - if(typeof callback === 'function') { - callback(null, false); - } - } - } else { - callback(err, false); // err - } - }); -}); - - -Homey.manager('flow').on('action.set_preset', function( callback, args ){ - var uri = '/preset/' + args.preset; - homewizard.call(args.device.id, uri, function(err, response) { - if (err === null) { - homewizard.ledring_pulse(args.device.id, 'green'); - callback(null, true); - } else { - homewizard.ledring_pulse(args.device.id, 'red'); - callback(err, false); // err - } - }); -}); - - -function getStatus(device_id) { - homewizard.getDeviceData(device_id, 'preset', function(callback) { - Homey.log('PRESET:' + callback); - try { - if (!('preset' in devices[device_id])) { - Homey.log('Preset was set to ' + callback); - devices[device_id].preset = callback; - } - - if (('preset' in devices[device_id]) && devices[device_id].preset != callback) { - devices[device_id].preset = callback; - Homey.log('Flow call!' + callback); - if (homey_lang == "nl") { - preset_text = preset_text_nl[callback]; - } else { - preset_text = preset_text_en[callback]; + + socket.on('manual_add', function (device, callback) { + + console.log(device); + var url = 'http://' + device.settings.homewizard_ip + '/' + device.settings.homewizard_pass + '/get-status/'; + + console.log('Calling '+ url); + + request(url, function (error, response, body) { + if (response === null || response === undefined) { + socket.emit("error", "http error"); + return; + } + if (!error && response.statusCode == 200) { + var jsonObject = JSON.parse(body); + + if (jsonObject.status == 'ok') { + console.log('Call OK'); + + devices[device.data.id] = { + id: device.data.id, + name: device.name, + settings: device.settings, + capabilities: device.capabilities + }; + homewizard.setDevices(devices); + + callback( null, devices ); + socket.emit("success", device); + } } - Homey.log(preset_text); - Homey.manager('flow').triggerDevice('preset_changed', { preset: callback, preset_text: preset_text }, null, { id: device_id } , (err) => { - if (err) return Homey.error('Error triggeringDevice:', err); }); - Homey.log('Preset was changed!'); - } - } catch(err) { - console.log ("HomeWizard data corrupt"); - } - }); -} - -function startPolling() { - if (refreshIntervalId) { - clearInterval(refreshIntervalId); - } - refreshIntervalId = setInterval(function () { - Homey.log("--Start HomeWizard Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); }); - }, 1000 * 10); -} \ No newline at end of file + + socket.on('disconnect', () => { + console.log("User aborted pairing, or pairing is finished"); + }); + } + + + +} + +module.exports = HomeWizardDriver; + +// var devices = {}; +// var homewizard = require('./../../includes/homewizard.js'); +// var request = require('request'); +// var refreshIntervalId; +// +// var preset_text = ''; +// var preset_text_nl = ['Thuis', 'Afwezig', 'Slapen', 'Vakantie']; +// var preset_text_en = ['Home', 'Away', 'Sleep', 'Holiday']; +// var homey_lang = Homey.manager('i18n').getLanguage(); +// +// // SETTINGS +// module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { +// this.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); +// try { +// changedKeysArr.forEach(function (key) { +// devices[device_data.id].settings[key] = newSettingsObj[key]; +// }); +// homewizard.setDevices(devices); +// callback(null, true); +// } catch (error) { +// callback(error); +// } +// }; +// +// module.exports.pair = function( socket ) { +// socket.on('manual_add', function (device, callback) { +// var url = 'http://' + device.settings.homewizard_ip + '/' + device.settings.homewizard_pass + '/get-status/'; +// //Homey.log('Calling '+ url); +// request(url, function (error, response, body) { +// if (response === null || response === undefined) { +// socket.emit("error", "http error"); +// return; +// } +// if (!error && response.statusCode == 200) { +// var jsonObject = JSON.parse(body); +// if (jsonObject.status == 'ok') { +// //true +// Homey.log('HW added'); +// devices[device.data.id] = { +// id: device.data.id, +// name: device.name, +// settings: device.settings, +// capabilities: device.capabilities +// }; +// homewizard.setDevices(devices); +// callback( null, devices ); +// socket.emit("success", device); +// startPolling(); +// } else { +// //false +// socket.emit("error", "no response"); +// } +// } else { +// // false +// socket.emit("error", "http error: "+response.statusCode); +// } +// }); +// }); +// +// socket.on('disconnect', function(){ +// console.log("User aborted pairing, or pairing is finished"); +// }); +// } +// +// module.exports.init = function(devices_data, callback) { +// if (homewizard.debug) { +// devices_data = homewizard.debug_devices_data; +// } +// +// devices_data.forEach(function initdevice(device) { +// Homey.log('add device: ' + JSON.stringify(device)); +// devices[device.id] = device; +// module.exports.getSettings(device, function(err, settings){ +// devices[device.id].settings = settings; +// }); +// }); +// homewizard.setDevices(devices); +// homewizard.startpoll(); +// +// if (Object.keys(devices).length > 0) { +// startPolling(); +// } +// +// Homey.log('HomeWizard driver init done'); +// callback (null, true); +// }; +// +// module.exports.deleted = function( device_data ) { +// delete devices[device_data.id]; +// if (Object.keys(devices).length === 0) { +// clearInterval(refreshIntervalId); +// Homey.log("--Stopped Polling--"); +// } +// Homey.log('deleted: ' + JSON.stringify(device_data)); +// }; +// +// // SCENES +// Homey.manager('flow').on('action.switch_scene_on.scene.autocomplete', function( callback, args ){ +// Homey.log('getScenes autocomplete called'); +// homewizard.getScenes(args, function(err, response) { +// callback(err, response ); // err, results +// }); +// }); +// +// Homey.manager('flow').on('action.switch_scene_on', function( callback, args ){ +// Homey.log('args.device.id: ' + JSON.stringify(args.device.id)); +// Homey.log('args.scene.id: ' + JSON.stringify(args.scene.id)); +// homewizard.call(args.device.id, '/gp/' + args.scene.id + '/on', function(err, response) { +// if (err === null) { +// Homey.log('Scene is on'); +// callback( null, true ); +// } else { +// callback(err, false); // err +// } +// }); +// }); +// +// Homey.manager('flow').on('action.switch_scene_off.scene.autocomplete', function( callback, args ){ +// homewizard.getScenes(args, function(err, response) { +// callback(err, response ); // err, results +// }); +// }); +// +// Homey.manager('flow').on('action.switch_scene_off', function( callback, args ){ +// homewizard.call(args.device.id, '/gp/' + args.scene.id + '/off', function(err, response) { +// if (err === null) { +// Homey.log('Scene is off'); +// callback( null, true ); +// } else { +// callback(err, false); // err +// } +// }); +// }); +// +// +// // PRESETS +// Homey.manager('flow').on('condition.check_preset', function( callback, args ){ +// homewizard.call(args.device.id, '/get-status/', function(err, response) { +// if (err === null) { +// if (response.preset == args.preset) { +// Homey.log('Yes, preset is: '+ response.preset+'!'); +// if(typeof callback === 'function') { +// callback(null, true); +// } +// } else { +// Homey.log('Preset is: ' + response.preset+', not '+args.preset); +// if(typeof callback === 'function') { +// callback(null, false); +// } +// } +// } else { +// callback(err, false); // err +// } +// }); +// }); +// +// +// Homey.manager('flow').on('action.set_preset', function( callback, args ){ +// var uri = '/preset/' + args.preset; +// homewizard.call(args.device.id, uri, function(err, response) { +// if (err === null) { +// homewizard.ledring_pulse(args.device.id, 'green'); +// callback(null, true); +// } else { +// homewizard.ledring_pulse(args.device.id, 'red'); +// callback(err, false); // err +// } +// }); +// }); +// +// +// function getStatus(device_id) { +// homewizard.getDeviceData(device_id, 'preset', function(callback) { +// Homey.log('PRESET:' + callback); +// try { +// if (!('preset' in devices[device_id])) { +// Homey.log('Preset was set to ' + callback); +// devices[device_id].preset = callback; +// } +// +// if (('preset' in devices[device_id]) && devices[device_id].preset != callback) { +// devices[device_id].preset = callback; +// Homey.log('Flow call!' + callback); +// if (homey_lang == "nl") { +// preset_text = preset_text_nl[callback]; +// } else { +// preset_text = preset_text_en[callback]; +// } +// Homey.log(preset_text); +// Homey.manager('flow').triggerDevice('preset_changed', { preset: callback, preset_text: preset_text }, null, { id: device_id } , (err) => { +// if (err) return Homey.error('Error triggeringDevice:', err); +// }); +// Homey.log('Preset was changed!'); +// } +// } catch(err) { +// console.log ("HomeWizard data corrupt"); +// } +// }); +// } +// +// function startPolling() { +// if (refreshIntervalId) { +// clearInterval(refreshIntervalId); +// } +// refreshIntervalId = setInterval(function () { +// Homey.log("--Start HomeWizard Polling-- "); +// Object.keys(devices).forEach(function (device_id) { +// getStatus(device_id); +// }); +// }, 1000 * 10); +// } \ No newline at end of file diff --git a/drivers/homewizard/pair/start.html b/drivers/homewizard/pair/start.html index e1c546cc..3b3fcf68 100644 --- a/drivers/homewizard/pair/start.html +++ b/drivers/homewizard/pair/start.html @@ -24,7 +24,7 @@ Homey.on('success', function(device){ $('#error').html('Connected, saving...'); $('#save').prop('disabled', true); - Homey.addDevice({ + Homey.createDevice({ data: { // this data object is saved to- and unique for the device. It is passed on the get and set functions as 1st argument id: 'HW'+Math.floor(Date.now() / 1000) }, diff --git a/drivers/rainmeter/driver.js b/drivers/rainmeter/driver.js index dde3fa14..a10bf5f3 100644 --- a/drivers/rainmeter/driver.js +++ b/drivers/rainmeter/driver.js @@ -1,158 +1,158 @@ -var devices = {}; -var homewizard = require('./../../includes/homewizard.js'); -var refreshIntervalId = 0; - -// SETTINGS -module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { - Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); - try { - changedKeysArr.forEach(function (key) { - devices[device_data.id].settings[key] = newSettingsObj[key]; - }); - callback(null, true); - } catch (error) { - callback(error); - } -}; - -module.exports.pair = function( socket ) { - socket.on('get_homewizards', function () { - homewizard.getDevices(function(homewizard_devices) { - Homey.log(homewizard_devices); - var hw_devices = {}; - Object.keys(homewizard_devices).forEach(function(key) { - hw_devices[key] = homewizard_devices[key]; - }); - - socket.emit('hw_devices', hw_devices); - }); - }); - - socket.on('manual_add', function (device, callback) { - if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { - //true - Homey.log('Rainmeter added ' + device.data.id); - devices[device.data.id] = { - id: device.data.id, - name: device.name, - settings: device.settings, - }; - callback( null, devices ); - socket.emit("success", device); - startPolling(); - } else { - socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); - } - }); - - socket.on('disconnect', function(){ - console.log("User aborted pairing, or pairing is finished"); - }); -} - -module.exports.init = function(devices_data, callback) { - devices_data.forEach(function initdevice(device) { - Homey.log('add device: ' + JSON.stringify(device)); - devices[device.id] = device; - module.exports.getSettings(device, function(err, settings){ - devices[device.id].settings = settings; - }); - }); - if (Object.keys(devices).length > 0) { - startPolling(); - } - Homey.log('Rainmeter driver init done'); - - callback (null, true); -}; - -module.exports.deleted = function( device_data ) { - delete devices[device_data.id]; - if (Object.keys(devices).length === 0) { - clearInterval(refreshIntervalId); - console.log("--Stopped Polling Rainmeter--"); - } - Homey.log('deleted: ' + JSON.stringify(device_data)); -}; - -module.exports.capabilities = { - "measure_rain.last3h": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_rainlast3h); - } - } - }, - "measure_rain.total": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_raintotal); - } - } - } -}; - -// Start polling -function startPolling() { - if(refreshIntervalId){ - clearInterval(refreshIntervalId); - } - refreshIntervalId = setInterval(function () { - console.log("--Start Rainmeter Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); - }, 1000 * 10); -} - -function getStatus(device_id) { - if(devices[device_id].settings.homewizard_id !== undefined ) { - var homewizard_id = devices[device_id].settings.homewizard_id; - homewizard.getDeviceData(homewizard_id, 'rainmeters', function(callback) { - if (Object.keys(callback).length > 0) { - try { - Homey.log ("Callback: ") + JSON.stringify(callback); - module.exports.setAvailable({id: device_id}); - var rain_daytotal = ( callback[0].mm ); // Total Rain in mm used JSON $rainmeters[0]['mm'] - var rain_last3h = ( callback[0]['3h'] ); // Last 3 hours rain in mm used JSON $rainmeters[0]['3h'] - // Rain last 3 hours - module.exports.realtime( { id: device_id }, "measure_rain.last3h", rain_last3h ); - // Rain total day - module.exports.realtime( { id: device_id }, "measure_rain.total", rain_daytotal ); - - console.log("Rainmeter 3h- "+ rain_last3h); - console.log("Rainmeter Daytotal- "+ rain_daytotal); - - // Trigger flows - if (rain_daytotal != devices[device_id].last_raintotal && rain_daytotal != 0 && rain_daytotal != undefined && rain_daytotal != null) { - console.log("Current Total Rainfall - "+ rain_daytotal); - Homey.manager('flow').triggerDevice('rainmeter_value_changed', { rainmeter_changed: rain_daytotal }, null, { id: device_id } ); - devices[device_id].last_raintotal = rain_daytotal; // Update last_raintotal - } - - } catch (err) { - // Error with Rain no data in Rainmeters - console.log ("No Rainmeter found"); - module.exports.setUnavailable({id: device_id}, "No Rainmeter found" ); - } - } - }); - } else { - Homey.log('Removed Rainmeter '+ device_id +' (old settings)'); - module.exports.setUnavailable({id: device_id}, "No Rainmeter found" ); - // Only clear interval when the unavailable device is the only device on this driver - // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new - // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem - if(Object.keys(devices).length === 1) { - clearInterval(refreshIntervalId); - } - } -} +// var devices = {}; +// var homewizard = require('./../../includes/homewizard.js'); +// var refreshIntervalId = 0; +// +// // SETTINGS +// module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { +// Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); +// try { +// changedKeysArr.forEach(function (key) { +// devices[device_data.id].settings[key] = newSettingsObj[key]; +// }); +// callback(null, true); +// } catch (error) { +// callback(error); +// } +// }; +// +// module.exports.pair = function( socket ) { +// socket.on('get_homewizards', function () { +// homewizard.getDevices(function(homewizard_devices) { +// Homey.log(homewizard_devices); +// var hw_devices = {}; +// Object.keys(homewizard_devices).forEach(function(key) { +// hw_devices[key] = homewizard_devices[key]; +// }); +// +// socket.emit('hw_devices', hw_devices); +// }); +// }); +// +// socket.on('manual_add', function (device, callback) { +// if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { +// //true +// Homey.log('Rainmeter added ' + device.data.id); +// devices[device.data.id] = { +// id: device.data.id, +// name: device.name, +// settings: device.settings, +// }; +// callback( null, devices ); +// socket.emit("success", device); +// startPolling(); +// } else { +// socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); +// } +// }); +// +// socket.on('disconnect', function(){ +// console.log("User aborted pairing, or pairing is finished"); +// }); +// } +// +// module.exports.init = function(devices_data, callback) { +// devices_data.forEach(function initdevice(device) { +// Homey.log('add device: ' + JSON.stringify(device)); +// devices[device.id] = device; +// module.exports.getSettings(device, function(err, settings){ +// devices[device.id].settings = settings; +// }); +// }); +// if (Object.keys(devices).length > 0) { +// startPolling(); +// } +// Homey.log('Rainmeter driver init done'); +// +// callback (null, true); +// }; +// +// module.exports.deleted = function( device_data ) { +// delete devices[device_data.id]; +// if (Object.keys(devices).length === 0) { +// clearInterval(refreshIntervalId); +// console.log("--Stopped Polling Rainmeter--"); +// } +// Homey.log('deleted: ' + JSON.stringify(device_data)); +// }; +// +// module.exports.capabilities = { +// "measure_rain.last3h": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_rainlast3h); +// } +// } +// }, +// "measure_rain.total": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_raintotal); +// } +// } +// } +// }; +// +// // Start polling +// function startPolling() { +// if(refreshIntervalId){ +// clearInterval(refreshIntervalId); +// } +// refreshIntervalId = setInterval(function () { +// console.log("--Start Rainmeter Polling-- "); +// Object.keys(devices).forEach(function (device_id) { +// getStatus(device_id); +// }); +// }, 1000 * 10); +// } +// +// function getStatus(device_id) { +// if(devices[device_id].settings.homewizard_id !== undefined ) { +// var homewizard_id = devices[device_id].settings.homewizard_id; +// homewizard.getDeviceData(homewizard_id, 'rainmeters', function(callback) { +// if (Object.keys(callback).length > 0) { +// try { +// Homey.log ("Callback: ") + JSON.stringify(callback); +// module.exports.setAvailable({id: device_id}); +// var rain_daytotal = ( callback[0].mm ); // Total Rain in mm used JSON $rainmeters[0]['mm'] +// var rain_last3h = ( callback[0]['3h'] ); // Last 3 hours rain in mm used JSON $rainmeters[0]['3h'] +// // Rain last 3 hours +// module.exports.realtime( { id: device_id }, "measure_rain.last3h", rain_last3h ); +// // Rain total day +// module.exports.realtime( { id: device_id }, "measure_rain.total", rain_daytotal ); +// +// console.log("Rainmeter 3h- "+ rain_last3h); +// console.log("Rainmeter Daytotal- "+ rain_daytotal); +// +// // Trigger flows +// if (rain_daytotal != devices[device_id].last_raintotal && rain_daytotal != 0 && rain_daytotal != undefined && rain_daytotal != null) { +// console.log("Current Total Rainfall - "+ rain_daytotal); +// Homey.manager('flow').triggerDevice('rainmeter_value_changed', { rainmeter_changed: rain_daytotal }, null, { id: device_id } ); +// devices[device_id].last_raintotal = rain_daytotal; // Update last_raintotal +// } +// +// } catch (err) { +// // Error with Rain no data in Rainmeters +// console.log ("No Rainmeter found"); +// module.exports.setUnavailable({id: device_id}, "No Rainmeter found" ); +// } +// } +// }); +// } else { +// Homey.log('Removed Rainmeter '+ device_id +' (old settings)'); +// module.exports.setUnavailable({id: device_id}, "No Rainmeter found" ); +// // Only clear interval when the unavailable device is the only device on this driver +// // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new +// // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem +// if(Object.keys(devices).length === 1) { +// clearInterval(refreshIntervalId); +// } +// } +// } diff --git a/drivers/thermometer/device.js b/drivers/thermometer/device.js new file mode 100644 index 00000000..5ded95eb --- /dev/null +++ b/drivers/thermometer/device.js @@ -0,0 +1,81 @@ +'use strict'; + +const Homey = require('homey'); +var homewizard = require('./../../includes/homewizard.js'); +const { ManagerDrivers } = require('homey'); +const driver = ManagerDrivers.getDriver('thermometer'); + +var refreshIntervalId; +var devices = {}; +const thermometers = {}; + +class HomeWizardThermometer extends Homey.Device { + + onInit() { + + this.log('HomeWizard Thermometer '+this.getName() +' has been inited'); + + const devices = driver.getDevices(); + + devices.forEach(function initdevice(device) { + console.log('add device: ' + JSON.stringify(device.getName())); + + devices[device.getData().id] = device; + devices[device.getData().id].settings = device.getSettings(); + }); + + if (Object.keys(devices).length > 0) { + this.startPolling(devices); + } + } + + startPolling = function(devices) { + if (refreshIntervalId) { + clearInterval(refreshIntervalId); + } + refreshIntervalId = setInterval(function () { + console.log("--Start Thermometer Polling-- "); + + for (var index in devices) { + + if(devices[index].settings.homewizard_id !== undefined ) { + var homewizard_id = devices[index].settings.homewizard_id; + var thermometer_id = devices[index].settings.thermometer_id; + homewizard.getDeviceData(homewizard_id, 'thermometers', function(callback) { + if (Object.keys(callback).length > 0) { + try { + for (var index in callback) { + + if (callback[index].id == thermometer_id) { + var te = (callback[index].te.toFixed(1) * 2) / 2; + var hu = (callback[index].hu.toFixed(1) * 2) / 2; + + //Check current temperature + if (devices[index].temperature != te) { + console.log("New TE - "+ te); + devices[index].setCapabilityValue('measure_temperature', te); + } + + //Check current temperature + if (devices[index].humidity != hu) { + console.log("New HU - "+ hu); + devices[index].setCapabilityValue('measure_humidity', hu); + } + } + } + } catch (err) { + console.log("Thermometer data corrupt"); + } + } + }); + } + } + + }, 1000 * 10); + } + + + +} + +module.exports = HomeWizardThermometer; \ No newline at end of file diff --git a/drivers/thermometer/driver.js b/drivers/thermometer/driver.js index 42741425..a0172453 100755 --- a/drivers/thermometer/driver.js +++ b/drivers/thermometer/driver.js @@ -1,155 +1,232 @@ +'use strict'; + +const Homey = require('homey'); +const request = require('request'); + + +const { ManagerDrivers } = require('homey'); +const driver = ManagerDrivers.getDriver('homewizard'); + var devices = {}; var homewizard = require('./../../includes/homewizard.js'); -var refreshIntervalId = 0; - -// SETTINGS -module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { - Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); - try { - changedKeysArr.forEach(function (key) { - devices[device_data.id].settings[key] = newSettingsObj[key]; - }); - callback(null, true); - } catch (error) { - callback(error); +var homewizard_devices; + +class HomeWizardThermometer extends Homey.Driver { + + onInit() { + this.log('HomeWizard Thermometer has been inited'); } -}; - -module.exports.pair = function( socket ) { - socket.on('get_homewizards', function () { - homewizard.getDevices(function(homewizard_devices) { - var hw_devices = {}; - Object.keys(homewizard_devices).forEach(function(key) { - var thermometers = JSON.stringify(homewizard_devices[key].polldata.thermometers); - - hw_devices[key] = homewizard_devices[key]; - hw_devices[key].polldata = {}; - hw_devices[key].thermometers = thermometers; - }); - socket.emit('hw_devices', hw_devices); - }); - }); - - socket.on('manual_add', function (device, callback) { - if (typeof device.settings.homewizard_id == "string" && device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { - //true - Homey.log('Thermometer added ' + device.data.id); - devices[device.data.id] = { - id: device.data.id, - name: device.name, - settings: device.settings, - }; - callback( null, devices ); - socket.emit("success", device); - startPolling(); - } else { - socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); - } - }); - - socket.on('disconnect', function(){ - console.log("User aborted pairing, or pairing is finished"); - }); -} -module.exports.init = function(devices_data, callback) { - devices_data.forEach(function initdevice(device) { - Homey.log('add device: ' + JSON.stringify(device)); - devices[device.id] = device; - module.exports.getSettings(device, function(err, settings){ - devices[device.id].settings = settings; + onPair(socket) { + // Show a specific view by ID + socket.showView('start'); + + // Show the next view + socket.nextView(); + + // Show the previous view + socket.prevView(); + + // Close the pair session + socket.done(); + + // Received when a view has changed + socket.on('showView', (viewId, callback) => { + callback(); + console.log('View: ' + viewId); }); - - }); - if (Object.keys(devices).length > 0) { - startPolling(); - } - Homey.log('Thermometer driver init done'); - callback (null, true); -}; -module.exports.deleted = function( device_data ) { - delete devices[device_data.id]; - if (Object.keys(devices).length === 0) { - clearInterval(refreshIntervalId); - Homey.log("--Stopped Polling--"); - } - Homey.log('deleted: ' + JSON.stringify(device_data)); -}; + socket.on('get_homewizards', function () { + homewizard_devices = driver.getDevices(); -module.exports.capabilities = { + homewizard.getDevices(function ( homewizard_devices) { + var hw_devices = {}; - measure_temperature: { - get: function (device, callback) { - if (device instanceof Error) return callback(device); - console.log("measure_temperature"); - getStatus(device.id); - var newvalue = devices[device.id].temperature; - // Callback ambient temperature - callback(null, newvalue); - } - }, -}; - -function getStatus(device_id) { - if(devices[device_id].settings.homewizard_id !== undefined ) { - var homewizard_id = devices[device_id].settings.homewizard_id; - var thermometer_id = devices[device_id].settings.thermometer_id; - homewizard.getDeviceData(homewizard_id, 'thermometers', function(callback) { - - if (Object.keys(callback).length > 0) { - try { - for (var index in callback){ - if (callback[index].id == thermometer_id) { - var te = (callback[index].te.toFixed(1) * 2) / 2; - var hu = (callback[index].hu.toFixed(1) * 2) / 2; - - //Check current temperature - if (devices[device_id].temperature != te) { - console.log("New TE - "+ te); - module.exports.realtime( { id: device_id }, "measure_temperature", te ); - devices[device_id].temperature = te; - } else { - //console.log("TE: no change"); - } - - //Check current humidity - if (devices[device_id].humidity != hu) { - console.log("New HU - "+ hu); - module.exports.realtime( { id: device_id }, "measure_humidity", hu ); - devices[device_id].humidity = hu; - } else { - //console.log("HU: no change"); - } - } - } - } catch(err) { - console.log ("Thermometer data corrupt"); - } - } + }); + // homewizard.getDevices(function (homewizard_devices) { + // var hw_devices = {}; + // Object.keys(homewizard_devices).forEach(function (key) { + // var thermometers = JSON.stringify(homewizard_devices[key].polldata.thermometers); + // + // hw_devices[key] = homewizard_devices[key]; + // hw_devices[key].polldata = {}; + // hw_devices[key].thermometers = thermometers; + // }); + // socket.emit('hw_devices', hw_devices); + // }); }); - } else { - Homey.log('Removed Thermometer '+ device_id +' (old settings)'); - module.exports.setUnavailable({id: device_id}, "No Thermometer found" ); - // Only clear interval when the unavailable device is the only device on this driver - // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new - // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem - if(Object.keys(devices).length === 1) { - clearInterval(refreshIntervalId); - } - } - } - -function startPolling() { - if (refreshIntervalId) { - clearInterval(refreshIntervalId); - } - refreshIntervalId = setInterval(function () { - Homey.log("--Start Thermometer Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); + + socket.on('disconnect', () => { + console.log("User aborted pairing, or pairing is finished"); }); - }, 1000 * 10); - } \ No newline at end of file + }; + + onPairListDevices( data, callback ) { + const devices = [ + + ] + + callback(null, devices); + }; + +} + +module.exports = HomeWizardThermometer; + +// var devices = {}; +// var homewizard = require('./../../includes/homewizard.js'); +// var refreshIntervalId = 0; +// +// // SETTINGS +// module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { +// Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); +// try { +// changedKeysArr.forEach(function (key) { +// devices[device_data.id].settings[key] = newSettingsObj[key]; +// }); +// callback(null, true); +// } catch (error) { +// callback(error); +// } +// }; +// +// module.exports.pair = function( socket ) { +// socket.on('get_homewizards', function () { +// homewizard.getDevices(function(homewizard_devices) { +// var hw_devices = {}; +// Object.keys(homewizard_devices).forEach(function(key) { +// var thermometers = JSON.stringify(homewizard_devices[key].polldata.thermometers); +// +// hw_devices[key] = homewizard_devices[key]; +// hw_devices[key].polldata = {}; +// hw_devices[key].thermometers = thermometers; +// }); +// socket.emit('hw_devices', hw_devices); +// }); +// }); +// +// socket.on('manual_add', function (device, callback) { +// if (typeof device.settings.homewizard_id == "string" && device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { +// //true +// Homey.log('Thermometer added ' + device.data.id); +// devices[device.data.id] = { +// id: device.data.id, +// name: device.name, +// settings: device.settings, +// }; +// callback( null, devices ); +// socket.emit("success", device); +// startPolling(); +// } else { +// socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); +// } +// }); +// +// socket.on('disconnect', function(){ +// console.log("User aborted pairing, or pairing is finished"); +// }); +// } +// +// module.exports.init = function(devices_data, callback) { +// devices_data.forEach(function initdevice(device) { +// Homey.log('add device: ' + JSON.stringify(device)); +// devices[device.id] = device; +// module.exports.getSettings(device, function(err, settings){ +// devices[device.id].settings = settings; +// }); +// +// }); +// if (Object.keys(devices).length > 0) { +// startPolling(); +// } +// Homey.log('Thermometer driver init done'); +// +// callback (null, true); +// }; +// +// module.exports.deleted = function( device_data ) { +// delete devices[device_data.id]; +// if (Object.keys(devices).length === 0) { +// clearInterval(refreshIntervalId); +// Homey.log("--Stopped Polling--"); +// } +// Homey.log('deleted: ' + JSON.stringify(device_data)); +// }; +// +// +// module.exports.capabilities = { +// +// measure_temperature: { +// get: function (device, callback) { +// if (device instanceof Error) return callback(device); +// console.log("measure_temperature"); +// getStatus(device.id); +// var newvalue = devices[device.id].temperature; +// // Callback ambient temperature +// callback(null, newvalue); +// } +// }, +// }; +// +// function getStatus(device_id) { +// if(devices[device_id].settings.homewizard_id !== undefined ) { +// var homewizard_id = devices[device_id].settings.homewizard_id; +// var thermometer_id = devices[device_id].settings.thermometer_id; +// homewizard.getDeviceData(homewizard_id, 'thermometers', function(callback) { +// +// if (Object.keys(callback).length > 0) { +// try { +// for (var index in callback){ +// if (callback[index].id == thermometer_id) { +// var te = (callback[index].te.toFixed(1) * 2) / 2; +// var hu = (callback[index].hu.toFixed(1) * 2) / 2; +// +// //Check current temperature +// if (devices[device_id].temperature != te) { +// console.log("New TE - "+ te); +// module.exports.realtime( { id: device_id }, "measure_temperature", te ); +// devices[device_id].temperature = te; +// } else { +// //console.log("TE: no change"); +// } +// +// //Check current humidity +// if (devices[device_id].humidity != hu) { +// console.log("New HU - "+ hu); +// module.exports.realtime( { id: device_id }, "measure_humidity", hu ); +// devices[device_id].humidity = hu; +// } else { +// //console.log("HU: no change"); +// } +// } +// } +// } catch(err) { +// console.log ("Thermometer data corrupt"); +// } +// } +// }); +// } else { +// this.log('Removed Thermometer '+ device_id +' (old settings)'); +// module.exports.setUnavailable({id: device_id}, "No Thermometer found" ); +// // Only clear interval when the unavailable device is the only device on this driver +// // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new +// // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem +// if(Object.keys(devices).length === 1) { +// clearInterval(refreshIntervalId); +// } +// } +// } +// +// function startPolling() { +// if (refreshIntervalId) { +// clearInterval(refreshIntervalId); +// } +// refreshIntervalId = setInterval(function () { +// Homey.log("--Start Thermometer Polling-- "); +// Object.keys(devices).forEach(function (device_id) { +// getStatus(device_id); +// }); +// }, 1000 * 10); +// } \ No newline at end of file diff --git a/drivers/wattcher/driver.js b/drivers/wattcher/driver.js index 3b9f5c12..8f0422f8 100755 --- a/drivers/wattcher/driver.js +++ b/drivers/wattcher/driver.js @@ -1,153 +1,153 @@ -var devices = {}; -var homewizard = require('./../../includes/homewizard.js'); -var refreshIntervalId = 0; - -// SETTINGS -module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { - Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); - try { - changedKeysArr.forEach(function (key) { - devices[device_data.id].settings[key] = newSettingsObj[key]; - }); - callback(null, true); - } catch (error) { - callback(error); - } -}; - -module.exports.pair = function( socket ) { - socket.on('get_homewizards', function () { - homewizard.getDevices(function(homewizard_devices) { - Homey.log(homewizard_devices); - var hw_devices = {}; - Object.keys(homewizard_devices).forEach(function(key) { - hw_devices[key] = homewizard_devices[key]; - }); - - socket.emit('hw_devices', hw_devices); - }); - }); - - socket.on('manual_add', function (device, callback) { - if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { - //true - Homey.log('Wattcher added ' + device.data.id); - devices[device.data.id] = { - id: device.data.id, - name: device.name, - settings: device.settings, - }; - callback( null, devices ); - socket.emit("success", device); - startPolling(); - } else { - socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); - } - }); - - socket.on('disconnect', function(){ - console.log("User aborted pairing, or pairing is finished"); - }); -} - -module.exports.init = function(devices_data, callback) { - devices_data.forEach(function initdevice(device) { - Homey.log('add device: ' + JSON.stringify(device)); - devices[device.id] = device; - module.exports.getSettings(device, function(err, settings){ - devices[device.id].settings = settings; - }); - }); - if (Object.keys(devices).length > 0) { - startPolling(); - } - Homey.log('Wattcher driver init done'); - - callback (null, true); -}; - -module.exports.deleted = function( device_data ) { - delete devices[device_data.id]; - if (Object.keys(devices).length === 0) { - clearInterval(refreshIntervalId); - console.log("--Stopped Polling Wattcher--"); - } - Homey.log('deleted: ' + JSON.stringify(device_data)); -}; - -module.exports.capabilities = { - measure_power: { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.power); - } - } - }, - meter_power: { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.energy); - } - } - } - -}; - -// Start polling -function startPolling() { - if(refreshIntervalId){ - clearInterval(refreshIntervalId); - } - refreshIntervalId = setInterval(function () { - console.log("--Start Wattcher Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); - }, 1000 * 10); -} - -function getStatus(device_id) { - if(devices[device_id].settings.homewizard_id !== undefined ) { - var homewizard_id = devices[device_id].settings.homewizard_id; - homewizard.getDeviceData(homewizard_id, 'energymeters', function(callback) { - if (Object.keys(callback).length > 0) { - try { - module.exports.setAvailable({id: device_id}); - var energy_current_cons = ( callback[0].po ); // WATTS Energy used JSON $energymeters[0]['po'] - var energy_daytotal_cons = ( callback[0].dayTotal ); // KWH Energy used JSON $energymeters[0]['dayTotal'] - - // Wattcher elec current - module.exports.realtime( { id: device_id }, "measure_power", energy_current_cons ); - // Wattcher elec total day - module.exports.realtime( { id: device_id }, "meter_power", energy_daytotal_cons ); - - console.log("Wattcher usage- "+ energy_current_cons); - console.log("Wattcher Daytotal- "+ energy_daytotal_cons); - } catch (err) { - // Error with Wattcher no data in Energymeters - console.log ("No Wattcher found"); - module.exports.setUnavailable({id: device_id}, "No Wattcher found" ); - } - } - }); - } else { - Homey.log('Removed Wattcher '+ device_id +' (old settings)'); - module.exports.setUnavailable({id: device_id}, "No Wattcher found" ); - // Only clear interval when the unavailable device is the only device on this driver - // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new - // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem - if(Object.keys(devices).length === 1) { - clearInterval(refreshIntervalId); - } - } -} - - +// var devices = {}; +// var homewizard = require('./../../includes/homewizard.js'); +// var refreshIntervalId = 0; +// +// // SETTINGS +// module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { +// Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); +// try { +// changedKeysArr.forEach(function (key) { +// devices[device_data.id].settings[key] = newSettingsObj[key]; +// }); +// callback(null, true); +// } catch (error) { +// callback(error); +// } +// }; +// +// module.exports.pair = function( socket ) { +// socket.on('get_homewizards', function () { +// homewizard.getDevices(function(homewizard_devices) { +// Homey.log(homewizard_devices); +// var hw_devices = {}; +// Object.keys(homewizard_devices).forEach(function(key) { +// hw_devices[key] = homewizard_devices[key]; +// }); +// +// socket.emit('hw_devices', hw_devices); +// }); +// }); +// +// socket.on('manual_add', function (device, callback) { +// if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { +// //true +// Homey.log('Wattcher added ' + device.data.id); +// devices[device.data.id] = { +// id: device.data.id, +// name: device.name, +// settings: device.settings, +// }; +// callback( null, devices ); +// socket.emit("success", device); +// startPolling(); +// } else { +// socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); +// } +// }); +// +// socket.on('disconnect', function(){ +// console.log("User aborted pairing, or pairing is finished"); +// }); +// } +// +// module.exports.init = function(devices_data, callback) { +// devices_data.forEach(function initdevice(device) { +// Homey.log('add device: ' + JSON.stringify(device)); +// devices[device.id] = device; +// module.exports.getSettings(device, function(err, settings){ +// devices[device.id].settings = settings; +// }); +// }); +// if (Object.keys(devices).length > 0) { +// startPolling(); +// } +// Homey.log('Wattcher driver init done'); +// +// callback (null, true); +// }; +// +// module.exports.deleted = function( device_data ) { +// delete devices[device_data.id]; +// if (Object.keys(devices).length === 0) { +// clearInterval(refreshIntervalId); +// console.log("--Stopped Polling Wattcher--"); +// } +// Homey.log('deleted: ' + JSON.stringify(device_data)); +// }; +// +// module.exports.capabilities = { +// measure_power: { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.power); +// } +// } +// }, +// meter_power: { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.energy); +// } +// } +// } +// +// }; +// +// // Start polling +// function startPolling() { +// if(refreshIntervalId){ +// clearInterval(refreshIntervalId); +// } +// refreshIntervalId = setInterval(function () { +// console.log("--Start Wattcher Polling-- "); +// Object.keys(devices).forEach(function (device_id) { +// getStatus(device_id); +// }); +// }, 1000 * 10); +// } +// +// function getStatus(device_id) { +// if(devices[device_id].settings.homewizard_id !== undefined ) { +// var homewizard_id = devices[device_id].settings.homewizard_id; +// homewizard.getDeviceData(homewizard_id, 'energymeters', function(callback) { +// if (Object.keys(callback).length > 0) { +// try { +// module.exports.setAvailable({id: device_id}); +// var energy_current_cons = ( callback[0].po ); // WATTS Energy used JSON $energymeters[0]['po'] +// var energy_daytotal_cons = ( callback[0].dayTotal ); // KWH Energy used JSON $energymeters[0]['dayTotal'] +// +// // Wattcher elec current +// module.exports.realtime( { id: device_id }, "measure_power", energy_current_cons ); +// // Wattcher elec total day +// module.exports.realtime( { id: device_id }, "meter_power", energy_daytotal_cons ); +// +// console.log("Wattcher usage- "+ energy_current_cons); +// console.log("Wattcher Daytotal- "+ energy_daytotal_cons); +// } catch (err) { +// // Error with Wattcher no data in Energymeters +// console.log ("No Wattcher found"); +// module.exports.setUnavailable({id: device_id}, "No Wattcher found" ); +// } +// } +// }); +// } else { +// Homey.log('Removed Wattcher '+ device_id +' (old settings)'); +// module.exports.setUnavailable({id: device_id}, "No Wattcher found" ); +// // Only clear interval when the unavailable device is the only device on this driver +// // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new +// // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem +// if(Object.keys(devices).length === 1) { +// clearInterval(refreshIntervalId); +// } +// } +// } +// +// diff --git a/drivers/windmeter/driver.js b/drivers/windmeter/driver.js index 3bac5da2..3b4f49bb 100644 --- a/drivers/windmeter/driver.js +++ b/drivers/windmeter/driver.js @@ -1,242 +1,244 @@ -var devices = {}; -var homewizard = require('./../../includes/homewizard.js'); -var refreshIntervalId = 0; - -// SETTINGS -module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { - Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); - try { - changedKeysArr.forEach(function (key) { - devices[device_data.id].settings[key] = newSettingsObj[key]; - }); - callback(null, true); - } catch (error) { - callback(error); - } -}; - -module.exports.pair = function( socket ) { - socket.on('get_homewizards', function () { - homewizard.getDevices(function(homewizard_devices) { - Homey.log(homewizard_devices); - var hw_devices = {}; - Object.keys(homewizard_devices).forEach(function(key) { - hw_devices[key] = homewizard_devices[key]; - }); - - socket.emit('hw_devices', hw_devices); - }); - }); - - socket.on('manual_add', function (device, callback) { - if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { - //true - Homey.log('Windmeter added ' + device.data.id); - devices[device.data.id] = { - id: device.data.id, - name: device.name, - settings: device.settings, - }; - callback( null, devices ); - socket.emit("success", device); - startPolling(); - } else { - socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); - } - }); - - socket.on('disconnect', function(){ - console.log("User aborted pairing, or pairing is finished"); - }); -} - -module.exports.init = function(devices_data, callback) { - devices_data.forEach(function initdevice(device) { - Homey.log('add device: ' + JSON.stringify(device)); - devices[device.id] = device; - module.exports.getSettings(device, function(err, settings){ - devices[device.id].settings = settings; - }); - }); - if (Object.keys(devices).length > 0) { - startPolling(); - } - Homey.log('Windmeter driver init done'); - - callback (null, true); -}; - -module.exports.deleted = function( device_data ) { - delete devices[device_data.id]; - if (Object.keys(devices).length === 0) { - clearInterval(refreshIntervalId); - console.log("--Stopped Polling Windmeter--"); - } - Homey.log('deleted: ' + JSON.stringify(device_data)); -}; - -module.exports.capabilities = { - "measure_wind_angle": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_wind_angle); - } - } - }, - "measure_wind_strength.cur": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_wind_strength_current); - } - } - }, - "measure_wind_strength.min": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_wind_strength_min); - } - } - }, - "measure_wind_strength.max": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_wind_strength_max); - } - } - }, - "measure_gust_strength": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_gust_strength); - } - } - }, - "measure_temperature.real": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_temperature_real); - } - } - }, - "measure_temperature.windchill": { - get: function (device_data, callback) { - var device = devices[device_data.id]; - - if (device === undefined) { - callback(null, 0); - } else { - callback(null, device.last_measure_temperature_windchill); - } - } - } -}; - -// Start polling -function startPolling() { - if(refreshIntervalId){ - clearInterval(refreshIntervalId); - } - refreshIntervalId = setInterval(function () { - console.log("--Start Windmeter Polling-- "); - Object.keys(devices).forEach(function (device_id) { - getStatus(device_id); - }); - }, 1000 * 10); -} - -function getStatus(device_id) { - if(devices[device_id].settings.homewizard_id !== undefined ) { - var homewizard_id = devices[device_id].settings.homewizard_id; - homewizard.getDeviceData(homewizard_id, 'windmeters', function(callback) { - if (Object.keys(callback).length > 0) { - try { - Homey.log ("Callback: ") + JSON.stringify(callback); - module.exports.setAvailable({id: device_id}); - console.log ("Start capture var data"); - var wind_angle_tmp = ( callback[0].dir ); // $windmeters[0]['dir'] SW 225 - var wind_angle_int = wind_angle_tmp.split(" "); - // var wind_angle = parseInt(wind_angle_tmp); // Capture only the angle portion (number) - var wind_strength_current = ( callback[0].ws ); // $windmeters[0]['ws'] Windspeed in km/h - var wind_strength_min = ( callback[0]["ws-"] ); // $windmeters[0]['ws-'] Min Windspeed in km/h - var wind_strength_max = ( callback[0]["ws+"] ); // $windmeters[0]['ws+'] Max Windspeed in km/h - var gust_strength = ( callback[0].gu ); // $windmeters[0]['gu'] Gust speed in km/h - var temp_real = ( callback[0].te ); // $windmeters[0]['te'] Temperature - var temp_windchill = ( callback[0].wc); // $windmeters[0]['wc'] Windchill temperature - console.log ("End capture var data"); - // Export the data - // Console data - var wind_angle_str = wind_angle_int[1]; - var wind_angle = parseInt(wind_angle_str); - console.log("Windangle in degrees: "+ wind_angle); - console.log("Windspeed in km/u: "+ wind_strength_current); - console.log("Min Windspeed in km/u: "+ wind_strength_min); - console.log("Min Windspeed in km/u: "+ wind_strength_max); - console.log("Gust strength in km/u: "+ gust_strength); - console.log("Temperature current: "+ temp_real); - console.log("Temperature windchill: "+ temp_windchill); - // Wind angle - module.exports.realtime( { id: device_id }, "measure_wind_angle", wind_angle ); - // Wind speed current - module.exports.realtime( { id: device_id }, "measure_wind_strength.cur", wind_strength_current ); - // Wind speed min - module.exports.realtime( { id: device_id }, "measure_wind_strength.min", wind_strength_min ); - // Wind speed max - module.exports.realtime( { id: device_id }, "measure_wind_strength.max", wind_strength_max ); - // Wind speed - module.exports.realtime( { id: device_id }, "measure_gust_strength", gust_strength ); - // Temp real - module.exports.realtime( { id: device_id }, "measure_temperature.real", temp_real ); - // Temp Windchill - module.exports.realtime( { id: device_id }, "measure_temperature.windchill", temp_windchill ); - // Console data - console.log("Windangle in degrees: "+ wind_angle); - console.log("Windspeed in km/u: "+ wind_strength_current); - console.log("Min Windspeed in km/u: "+ wind_strength_min); - console.log("Min Windspeed in km/u: "+ wind_strength_max); - console.log("Gust strength in km/u: "+ gust_strength); - console.log("Temperature current: "+ temp_real); - console.log("Temperature windchill: "+ temp_windchill); - } catch (err) { - // Error with Wind no data in Rainmeters - console.log ("No Windmeter found"); - module.exports.setUnavailable({id: device_id}, "No Windmeter found" ); - } - } - }); - } else { - Homey.log('Removed Windmeter '+ device_id +' (old settings)'); - module.exports.setUnavailable({id: device_id}, "No Windmeter found" ); - // Only clear interval when the unavailable device is the only device on this driver - // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new - // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem - if(Object.keys(devices).length === 1) { - clearInterval(refreshIntervalId); - } - } -} - - +// +// +// var devices = {}; +// var homewizard = require('./../../includes/homewizard.js'); +// var refreshIntervalId = 0; +// +// // SETTINGS +// module.exports.settings = function( device_data, newSettingsObj, oldSettingsObj, changedKeysArr, callback ) { +// Homey.log ('Changed settings: ' + JSON.stringify(device_data) + ' / ' + JSON.stringify(newSettingsObj) + ' / old = ' + JSON.stringify(oldSettingsObj)); +// try { +// changedKeysArr.forEach(function (key) { +// devices[device_data.id].settings[key] = newSettingsObj[key]; +// }); +// callback(null, true); +// } catch (error) { +// callback(error); +// } +// }; +// +// module.exports.pair = function( socket ) { +// socket.on('get_homewizards', function () { +// homewizard.getDevices(function(homewizard_devices) { +// Homey.log(homewizard_devices); +// var hw_devices = {}; +// Object.keys(homewizard_devices).forEach(function(key) { +// hw_devices[key] = homewizard_devices[key]; +// }); +// +// socket.emit('hw_devices', hw_devices); +// }); +// }); +// +// socket.on('manual_add', function (device, callback) { +// if (device.settings.homewizard_id.indexOf('HW_') === -1 && device.settings.homewizard_id.indexOf('HW') === 0) { +// //true +// Homey.log('Windmeter added ' + device.data.id); +// devices[device.data.id] = { +// id: device.data.id, +// name: device.name, +// settings: device.settings, +// }; +// callback( null, devices ); +// socket.emit("success", device); +// startPolling(); +// } else { +// socket.emit("error", "No valid HomeWizard found, re-pair if problem persists"); +// } +// }); +// +// socket.on('disconnect', function(){ +// console.log("User aborted pairing, or pairing is finished"); +// }); +// } +// +// module.exports.init = function(devices_data, callback) { +// devices_data.forEach(function initdevice(device) { +// Homey.log('add device: ' + JSON.stringify(device)); +// devices[device.id] = device; +// module.exports.getSettings(device, function(err, settings){ +// devices[device.id].settings = settings; +// }); +// }); +// if (Object.keys(devices).length > 0) { +// startPolling(); +// } +// Homey.log('Windmeter driver init done'); +// +// callback (null, true); +// }; +// +// module.exports.deleted = function( device_data ) { +// delete devices[device_data.id]; +// if (Object.keys(devices).length === 0) { +// clearInterval(refreshIntervalId); +// console.log("--Stopped Polling Windmeter--"); +// } +// Homey.log('deleted: ' + JSON.stringify(device_data)); +// }; +// +// module.exports.capabilities = { +// "measure_wind_angle": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_wind_angle); +// } +// } +// }, +// "measure_wind_strength.cur": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_wind_strength_current); +// } +// } +// }, +// "measure_wind_strength.min": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_wind_strength_min); +// } +// } +// }, +// "measure_wind_strength.max": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_wind_strength_max); +// } +// } +// }, +// "measure_gust_strength": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_gust_strength); +// } +// } +// }, +// "measure_temperature.real": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_temperature_real); +// } +// } +// }, +// "measure_temperature.windchill": { +// get: function (device_data, callback) { +// var device = devices[device_data.id]; +// +// if (device === undefined) { +// callback(null, 0); +// } else { +// callback(null, device.last_measure_temperature_windchill); +// } +// } +// } +// }; +// +// // Start polling +// function startPolling() { +// if(refreshIntervalId){ +// clearInterval(refreshIntervalId); +// } +// refreshIntervalId = setInterval(function () { +// console.log("--Start Windmeter Polling-- "); +// Object.keys(devices).forEach(function (device_id) { +// getStatus(device_id); +// }); +// }, 1000 * 10); +// } +// +// function getStatus(device_id) { +// if(devices[device_id].settings.homewizard_id !== undefined ) { +// var homewizard_id = devices[device_id].settings.homewizard_id; +// homewizard.getDeviceData(homewizard_id, 'windmeters', function(callback) { +// if (Object.keys(callback).length > 0) { +// try { +// Homey.log ("Callback: ") + JSON.stringify(callback); +// module.exports.setAvailable({id: device_id}); +// console.log ("Start capture var data"); +// var wind_angle_tmp = ( callback[0].dir ); // $windmeters[0]['dir'] SW 225 +// var wind_angle_int = wind_angle_tmp.split(" "); +// // var wind_angle = parseInt(wind_angle_tmp); // Capture only the angle portion (number) +// var wind_strength_current = ( callback[0].ws ); // $windmeters[0]['ws'] Windspeed in km/h +// var wind_strength_min = ( callback[0]["ws-"] ); // $windmeters[0]['ws-'] Min Windspeed in km/h +// var wind_strength_max = ( callback[0]["ws+"] ); // $windmeters[0]['ws+'] Max Windspeed in km/h +// var gust_strength = ( callback[0].gu ); // $windmeters[0]['gu'] Gust speed in km/h +// var temp_real = ( callback[0].te ); // $windmeters[0]['te'] Temperature +// var temp_windchill = ( callback[0].wc); // $windmeters[0]['wc'] Windchill temperature +// console.log ("End capture var data"); +// // Export the data +// // Console data +// var wind_angle_str = wind_angle_int[1]; +// var wind_angle = parseInt(wind_angle_str); +// console.log("Windangle in degrees: "+ wind_angle); +// console.log("Windspeed in km/u: "+ wind_strength_current); +// console.log("Min Windspeed in km/u: "+ wind_strength_min); +// console.log("Min Windspeed in km/u: "+ wind_strength_max); +// console.log("Gust strength in km/u: "+ gust_strength); +// console.log("Temperature current: "+ temp_real); +// console.log("Temperature windchill: "+ temp_windchill); +// // Wind angle +// module.exports.realtime( { id: device_id }, "measure_wind_angle", wind_angle ); +// // Wind speed current +// module.exports.realtime( { id: device_id }, "measure_wind_strength.cur", wind_strength_current ); +// // Wind speed min +// module.exports.realtime( { id: device_id }, "measure_wind_strength.min", wind_strength_min ); +// // Wind speed max +// module.exports.realtime( { id: device_id }, "measure_wind_strength.max", wind_strength_max ); +// // Wind speed +// module.exports.realtime( { id: device_id }, "measure_gust_strength", gust_strength ); +// // Temp real +// module.exports.realtime( { id: device_id }, "measure_temperature.real", temp_real ); +// // Temp Windchill +// module.exports.realtime( { id: device_id }, "measure_temperature.windchill", temp_windchill ); +// // Console data +// console.log("Windangle in degrees: "+ wind_angle); +// console.log("Windspeed in km/u: "+ wind_strength_current); +// console.log("Min Windspeed in km/u: "+ wind_strength_min); +// console.log("Min Windspeed in km/u: "+ wind_strength_max); +// console.log("Gust strength in km/u: "+ gust_strength); +// console.log("Temperature current: "+ temp_real); +// console.log("Temperature windchill: "+ temp_windchill); +// } catch (err) { +// // Error with Wind no data in Rainmeters +// console.log ("No Windmeter found"); +// module.exports.setUnavailable({id: device_id}, "No Windmeter found" ); +// } +// } +// }); +// } else { +// Homey.log('Removed Windmeter '+ device_id +' (old settings)'); +// module.exports.setUnavailable({id: device_id}, "No Windmeter found" ); +// // Only clear interval when the unavailable device is the only device on this driver +// // This will prevent stopping the polling when a user has 1 device with old settings and 1 with new +// // In the event that a user has multiple devices with old settings this function will get called every 10 seconds but that should not be a problem +// if(Object.keys(devices).length === 1) { +// clearInterval(refreshIntervalId); +// } +// } +// } +// +// diff --git a/includes/homewizard.js b/includes/homewizard.js index ef7bc0e8..e014f402 100644 --- a/includes/homewizard.js +++ b/includes/homewizard.js @@ -1,5 +1,9 @@ +'use strict'; + var request = require('request'); +const Homey = require('homey'); + module.exports = (function(){ var homewizard = {}; var self = {}; @@ -40,8 +44,8 @@ module.exports = (function(){ homewizard.debug_devices_data = [ { id: 'HW12345' }]; homewizard.setDevices = function(devices){ - Homey.log('Devices SET!'); - Homey.log(devices); + console.log('Devices SET!'); + console.log(devices); if (homewizard.debug) { self.devices = homewizard.debug_devices; } else { @@ -65,7 +69,7 @@ module.exports = (function(){ if (homewizard.debug) { callback(null, testdata); } else { - Homey.log('Call device' + device_id); + console.log('Call device ' + device_id); if ((typeof self.devices[device_id] !== 'undefined') && ("settings" in self.devices[device_id]) && ("homewizard_ip" in self.devices[device_id].settings) && ("homewizard_pass" in self.devices[device_id].settings)) { var homewizard_ip = self.devices[device_id].settings.homewizard_ip; var homewizard_pass = self.devices[device_id].settings.homewizard_pass; @@ -85,11 +89,11 @@ module.exports = (function(){ if (jsonObject.status == 'ok') { if(typeof callback === 'function') { - callback(null, jsonObject.response); + callback(null, jsonObject.response); } } } catch (exception) { - Homey.log('JSON: '+ body); + console.log('EXCEPTION JSON : '+ body); jsonObject = null; callback('Invalid data', []); } @@ -97,11 +101,11 @@ module.exports = (function(){ if(typeof callback === 'function') { callback('Error', []); } - Homey.log('Error: '+error); + console.log('Error: '+error); } }); } else { - Homey.log('Homewizard '+ device_id +': settings not found!'); + console.log('Homewizard '+ device_id +': settings not found!'); } } }; @@ -166,14 +170,14 @@ module.exports = (function(){ self.devices[device_id].polldata.rainmeters = response.rainmeters; self.devices[device_id].polldata.windmeters = response.windmeters; - if (Object.keys(response.energylinks).length !== 0) { - homewizard.call(device_id, '/el/get/0/readings', function(err, response2) { - if (err === null) { - self.devices[device_id].polldata.energylink_el = response2; - Homey.log('HW-Data polled for slimme meter: '+device_id); - } - }); - } + // if (Object.keys(response.energylinks).length !== 0) { + // homewizard.call(device_id, '/el/get/0/readings', function(err, response2) { + // if (err === null) { + // self.devices[device_id].polldata.energylink_el = response2; + // Homey.log('HW-Data polled for slimme meter: '+device_id); + // } + // }); + // } } }); From 959308ed9fff44ae2a3b078544217c553b057355 Mon Sep 17 00:00:00 2001 From: Freddie Welvering Date: Mon, 27 Jul 2020 16:00:17 +0200 Subject: [PATCH 071/661] Working towards SDK2 --- README.md | 2 ++ drivers/thermometer/device.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0de9815f..f3c5b39c 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Upon first deployment you need add the Homewizard unit first, then you can add the related/connected components from Homewizard to your Homey. +v0.2.7: +* Progress SDK2 support for HomeWizard,thermometers v0.2.6: * Netto power usage added (Current power/watts which will go negative if you produce power via solar) diff --git a/drivers/thermometer/device.js b/drivers/thermometer/device.js index 5ded95eb..d37fa5ff 100644 --- a/drivers/thermometer/device.js +++ b/drivers/thermometer/device.js @@ -45,7 +45,7 @@ class HomeWizardThermometer extends Homey.Device { if (Object.keys(callback).length > 0) { try { for (var index in callback) { - + if (callback[index].id == thermometer_id) { var te = (callback[index].te.toFixed(1) * 2) / 2; var hu = (callback[index].hu.toFixed(1) * 2) / 2; From 98c9b2f2123ad3e38edd1f5773b3a7c1768be4d4 Mon Sep 17 00:00:00 2001 From: Freddie Welvering Date: Mon, 27 Jul 2020 16:00:42 +0200 Subject: [PATCH 072/661] Working towards SDK2 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f3c5b39c..18a5a936 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ This app let's you connect your HomeWizard to Homey. You can add your HomeWizard in the device section. Upon first deployment you need add the Homewizard unit first, then you can add the related/connected components from Homewizard to your Homey. + v0.2.7: * Progress SDK2 support for HomeWizard,thermometers From ae4d39708655ef0404f3f7efe744fa009907471b Mon Sep 17 00:00:00 2001 From: Freddie Welvering Date: Mon, 27 Jul 2020 16:04:56 +0200 Subject: [PATCH 073/661] Working towards SDK2 --- drivers/energylink/driver.js | 9 +++++++++ drivers/heatlink/driver.js | 9 +++++++++ drivers/rainmeter/driver.js | 9 +++++++++ drivers/wattcher/driver.js | 9 +++++++++ drivers/windmeter/driver.js | 8 ++++++++ 5 files changed, 44 insertions(+) diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 44cd7353..9a76854a 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -1,3 +1,12 @@ +'use strict'; + +const Homey = require('homey'); + +class HomeWizardEnergylink extends Homey.Device { +} + +module.exports = HomeWizardEnergylink; + // var devices = {}; // var homewizard = require('./../../includes/homewizard.js'); // var refreshIntervalId = 0; diff --git a/drivers/heatlink/driver.js b/drivers/heatlink/driver.js index f08989a7..2e7fb44d 100755 --- a/drivers/heatlink/driver.js +++ b/drivers/heatlink/driver.js @@ -1,3 +1,12 @@ +'use strict'; + +const Homey = require('homey'); + +class HomeWizardHeatlink extends Homey.Device { +} + +module.exports = HomeWizardHeatlink; + // var devices = {}; // var homewizard = require('./../../includes/homewizard.js'); // var refreshIntervalId = 0; diff --git a/drivers/rainmeter/driver.js b/drivers/rainmeter/driver.js index a10bf5f3..444c19b0 100644 --- a/drivers/rainmeter/driver.js +++ b/drivers/rainmeter/driver.js @@ -1,3 +1,12 @@ +'use strict'; + +const Homey = require('homey'); + +class HomeWizardRainmeter extends Homey.Device { +} + +module.exports = HomeWizardRainmeter; + // var devices = {}; // var homewizard = require('./../../includes/homewizard.js'); // var refreshIntervalId = 0; diff --git a/drivers/wattcher/driver.js b/drivers/wattcher/driver.js index 8f0422f8..824cd151 100755 --- a/drivers/wattcher/driver.js +++ b/drivers/wattcher/driver.js @@ -1,3 +1,12 @@ +'use strict'; + +const Homey = require('homey'); + +class HomeWizardWattcher extends Homey.Device { +} + +module.exports = HomeWizardWattcher; + // var devices = {}; // var homewizard = require('./../../includes/homewizard.js'); // var refreshIntervalId = 0; diff --git a/drivers/windmeter/driver.js b/drivers/windmeter/driver.js index 3b4f49bb..d333a95d 100644 --- a/drivers/windmeter/driver.js +++ b/drivers/windmeter/driver.js @@ -1,3 +1,11 @@ +'use strict'; + +const Homey = require('homey'); + +class HomeWizardWindmeter extends Homey.Device { +} + +module.exports = HomeWizardWindmeter; // // // var devices = {}; From e2a90077c5f9974d68833f4778c5c407230238e2 Mon Sep 17 00:00:00 2001 From: Freddie Welvering Date: Mon, 27 Jul 2020 16:49:39 +0200 Subject: [PATCH 074/661] Work on pairing --- drivers/energylink/driver.js | 13 +++++++++++-- drivers/heatlink/device.js | 15 +++++++++++++++ drivers/heatlink/driver.js | 10 +++++++++- drivers/homewizard/driver.js | 1 - drivers/rainmeter/driver.js | 10 +++++++++- drivers/thermometer/driver.js | 11 +++++++++++ drivers/thermometer/pair/start.html | 7 +++++-- drivers/wattcher/driver.js | 11 ++++++++++- drivers/windmeter/driver.js | 11 ++++++++++- includes/homewizard.js | 2 -- 10 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 drivers/heatlink/device.js diff --git a/drivers/energylink/driver.js b/drivers/energylink/driver.js index 9a76854a..e952f0f1 100755 --- a/drivers/energylink/driver.js +++ b/drivers/energylink/driver.js @@ -1,11 +1,20 @@ 'use strict'; const Homey = require('homey'); +const request = require('request'); + +const { ManagerDrivers } = require('homey'); + +class HomeWizardEnergyLink extends Homey.Driver { + + onInit() { + this.log('HomeWizard EnergyLink has been inited'); + } -class HomeWizardEnergylink extends Homey.Device { } -module.exports = HomeWizardEnergylink; +module.exports = HomeWizardEnergyLink; + // var devices = {}; // var homewizard = require('./../../includes/homewizard.js'); diff --git a/drivers/heatlink/device.js b/drivers/heatlink/device.js new file mode 100644 index 00000000..9ea66d81 --- /dev/null +++ b/drivers/heatlink/device.js @@ -0,0 +1,15 @@ +'use strict'; + +const Homey = require('homey'); +var homewizard = require('./../../includes/homewizard.js'); + +class HomeWizardHeatlink extends Homey.Device { + + onInit() { + + this.log('Heatlink Thermometer '+this.getName() +' has been inited'); + } + +} + +module.exports = HomeWizardHeatlink; \ No newline at end of file diff --git a/drivers/heatlink/driver.js b/drivers/heatlink/driver.js index 2e7fb44d..c262fd64 100755 --- a/drivers/heatlink/driver.js +++ b/drivers/heatlink/driver.js @@ -1,8 +1,16 @@ 'use strict'; const Homey = require('homey'); +const request = require('request'); + +const { ManagerDrivers } = require('homey'); + +class HomeWizardHeatlink extends Homey.Driver { + + onInit() { + this.log('HomeWizard Heatlink has been inited'); + } -class HomeWizardHeatlink extends Homey.Device { } module.exports = HomeWizardHeatlink; diff --git a/drivers/homewizard/driver.js b/drivers/homewizard/driver.js index 12760319..2e4a5221 100644 --- a/drivers/homewizard/driver.js +++ b/drivers/homewizard/driver.js @@ -38,7 +38,6 @@ class HomeWizardDriver extends Homey.Driver { socket.on('manual_add', function (device, callback) { - console.log(device); var url = 'http://' + device.settings.homewizard_ip + '/' + device.settings.homewizard_pass + '/get-status/'; console.log('Calling '+ url); diff --git a/drivers/rainmeter/driver.js b/drivers/rainmeter/driver.js index 444c19b0..4e0b1564 100644 --- a/drivers/rainmeter/driver.js +++ b/drivers/rainmeter/driver.js @@ -1,8 +1,16 @@ 'use strict'; const Homey = require('homey'); +const request = require('request'); + +const { ManagerDrivers } = require('homey'); + +class HomeWizardRainmeter extends Homey.Driver { + + onInit() { + this.log('HomeWizard Rainmeter has been inited'); + } -class HomeWizardRainmeter extends Homey.Device { } module.exports = HomeWizardRainmeter; diff --git a/drivers/thermometer/driver.js b/drivers/thermometer/driver.js index a0172453..4b27571c 100755 --- a/drivers/thermometer/driver.js +++ b/drivers/thermometer/driver.js @@ -44,6 +44,17 @@ class HomeWizardThermometer extends Homey.Driver { homewizard.getDevices(function ( homewizard_devices) { var hw_devices = {}; + Object.keys(homewizard_devices).forEach(function (key) { + var thermometers = JSON.stringify(homewizard_devices[key].polldata.thermometers); + + hw_devices[key] = homewizard_devices[key]; + hw_devices[key].polldata = {} + hw_devices[key].thermometers = thermometers; + }); + + console.log(hw_devices); + socket.emit('hw_devices', hw_devices); + }); // homewizard.getDevices(function (homewizard_devices) { // var hw_devices = {}; diff --git a/drivers/thermometer/pair/start.html b/drivers/thermometer/pair/start.html index 0c4d3a38..10f19ecf 100755 --- a/drivers/thermometer/pair/start.html +++ b/drivers/thermometer/pair/start.html @@ -5,16 +5,19 @@ Homey.emit('get_homewizards'); Homey.on('hw_devices', function(hw_devices){ + hw_device_list = hw_devices; - $('#homewizard_select').find('option').remove().append(''); + $('#homewizard_select').find('option').remove(); + $('#homewizard_select').append(''); $.each(hw_devices, function( index, value ) { - $('#homewizard_select').append($("'); $.each(JSON.parse(hw_device_list[$('#homewizard_select').val()].thermometers), function( index, value ) { $('#thermometer_select').append($("