From 3db2b8f293bb0381d75c420cc82f130709b472dc Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Fri, 20 Jan 2017 15:22:55 +0100 Subject: [PATCH 01/38] ig refactoring, fixing manual mode --- accessories/Thermostat_accessory.js | 35 ++++--- control.js => bin/control.js | 16 ++-- display.js => bin/display.js | 138 ++++++++++++++-------------- homekit.js => bin/homekit.js | 36 ++------ config.json | 4 +- lib/global.js => controller/app.js | 49 +++++++--- controller/heater.js | 8 +- controller/homekit.js | 22 +++++ lib/heater.js | 3 +- lib/lcd.js | 3 +- package.json | 2 +- process.json | 6 +- 12 files changed, 177 insertions(+), 145 deletions(-) rename control.js => bin/control.js (87%) rename display.js => bin/display.js (88%) rename homekit.js => bin/homekit.js (54%) rename lib/global.js => controller/app.js (74%) create mode 100644 controller/homekit.js diff --git a/accessories/Thermostat_accessory.js b/accessories/Thermostat_accessory.js index 3331a7b..774870a 100644 --- a/accessories/Thermostat_accessory.js +++ b/accessories/Thermostat_accessory.js @@ -37,8 +37,7 @@ thermostat // return our current value var state, stateName; - // maybe i should find a more suitable solution here, but it works for now - it actually shows in homekit if the heater is turned on now - // - which isn't that bad, right? + // shows if the heater is on or off - no cooling for now possible switch(data.value) { case 'false': stateName = 'off'; @@ -69,20 +68,17 @@ thermostat // same as above, should be changed maybe switch(data.value) { - case '0': - stateName = 'off'; + case 'off': state = Characteristic.TargetHeatingCoolingState.OFF; break; - case '1': - stateName = 'heat'; + case 'on': state = Characteristic.TargetHeatingCoolingState.HEAT; break; default: - stateName = 'auto'; state = Characteristic.TargetHeatingCoolingState.AUTO; } - app.log('get: TargetHeatingCoolingState', stateName, data.value); + app.log('get: TargetHeatingCoolingState', data.value); callback(null, state); }) }); @@ -92,18 +88,21 @@ thermostat .getService(Service.Thermostat) .getCharacteristic(Characteristic.TargetHeatingCoolingState) .on('set', function(value, callback) { + var state; // ignore state "2" which would be "cooling", and set to "auto" instead - if (value === 2 || value === '2') value === 3; + switch(value) { + case Characteristic.TargetHeatingCoolingState.OFF: + state = 'off'; + break; + case Characteristic.TargetHeatingCoolingState.HEAT: + state = 'on'; + break; + default: + state = 'auto'; + } + // save the state - Status.findOneAndUpdate({ key: 'heatingMode' }, { value: value }, { new: true, upsert: true }).exec((err, data) => { - setTimeout(function() { - // reset the state to off after 15 minutes again -> 15 minutes of heating - // - the fun part is: we don't need to do more than setting it. the actual changes happens, because this one gets called again - and control.js sees the new status - thermostat - .getService(Service.Thermostat) - .setCharacteristic(Characteristic.TargetHeatingCoolingState, Characteristic.TargetHeatingCoolingState.OFF); - }, 1000 * 60 * 60) // 1h - + Status.findOneAndUpdate({ key: 'heatingMode' }, { value: state }, { new: true, upsert: true }).exec((err, data) => { app.log('set: TargetHeatingCoolingState', value); callback(); }) diff --git a/control.js b/bin/control.js similarity index 87% rename from control.js rename to bin/control.js index 4b8cedd..2f7352c 100644 --- a/control.js +++ b/bin/control.js @@ -1,17 +1,13 @@ -var cfg = require('./config.json'); -var _ = require('lodash'); +var cfg = require('../config.json'); var mongoose = require('mongoose'); var moment = require('moment'); -var gpio = require('./lib/gpio_wrapper'); +var gpio = require('../lib/gpio_wrapper'); -var sensor = require('./lib/sensor.js'); -var heater = require('./controller/heater.js'); +var heater = require('../controller/heater.js'); +var app = require('../controller/app.js'); -var Zone = require('./models/zone.js'); -var Status = require('./models/status.js'); - -var app = require('./lib/global'); +var Status = require('../models/status.js'); var displayTimeout; @@ -63,7 +59,7 @@ gpio.setup(cfg.hardware.button, 'in').then((data) => { //////////////////////////////////// // first check after launch -Status.findOneAndUpdate({ key: 'heatingMode' }, { value: 0 }, { new: true, upsert: true }).exec((err, data) => { +Status.findOneAndUpdate({ key: 'heatingMode' }, { value: 'auto' }, { new: true, upsert: true }).exec((err, data) => { checkTemperatures(); _set(); }); diff --git a/display.js b/bin/display.js similarity index 88% rename from display.js rename to bin/display.js index 2cbbcd7..a970dcc 100644 --- a/display.js +++ b/bin/display.js @@ -1,70 +1,70 @@ -var status, lines; -var mongoose = require('mongoose'); -var moment = require('moment'); -var _ = require('lodash'); - -var lcd = require('./lib/lcd.js'); -var app = require('./lib/global'); - -var Status = require('./models/status.js'); -var Zone = require('./models/zone.js'); - -require('dotenv').load(); -var cfg = require('./config.json'); - -// set the mongoose promise library to the nodejs one, required by mongoose now -mongoose.Promise = global.Promise; -// connect to the mongodb -mongoose.connect(process.env.MONGODB); -// output an error if the connection fails - kill the app -mongoose.connection.on('error', () => { - console.error('ERROR - MongoDB Connection Error. Please make sure that MongoDB is running.'); - process.exit(1); -}); - -lcd.on('ready', () => { - app.log('LCD READY!!'); -}); - -// update display every 10s now -setInterval(updateDisplay, 1000 * 10); - -function updateDisplay() { - app.log('update display....'); - lines = []; - - app.log('...get the current status...'); - app.getCurrentStatus((status, data, statuses) => { - app.log('...current status:', status); - app.log('...get temperatures...'); - - // get current temperatures for display - Zone.findOne({ number: cfg.zone }).exec((err, zone) => { - app.log('...got temperatures!'); - - // set the first line - lines.push( - zone.currentTemperature.toFixed(1) + - 'C > ' + - zone.targetTemperature.toFixed(1) + 'C ' + (statuses.heaterOn.value === 'true' ? '#': ' ') - ); - - // put the status in the second line, fill it up with spaces to prevent display bugs - no idea why they keep appearing - // - looking funny though - lines.push(_.padEnd(status, 16)); - - // print those lines to the display - lcd.clear((err) => { - if (err) { - throw err; - } - - lcd.printLines(lines).then(() => { - app.log('DISPLAY printed all lines'); - }); - // debug output - app.log('print lines:', lines); - }); - }); - }) +var status, lines; +var mongoose = require('mongoose'); +var moment = require('moment'); +var _ = require('lodash'); + +var lcd = require('../lib/lcd.js'); +var app = require('../controller/app.js'); + +var Status = require('../models/status.js'); +var Zone = require('../models/zone.js'); + +require('dotenv').load(); +var cfg = require('../config.json'); + +// set the mongoose promise library to the nodejs one, required by mongoose now +mongoose.Promise = global.Promise; +// connect to the mongodb +mongoose.connect(process.env.MONGODB); +// output an error if the connection fails - kill the app +mongoose.connection.on('error', () => { + console.error('ERROR - MongoDB Connection Error. Please make sure that MongoDB is running.'); + process.exit(1); +}); + +lcd.on('ready', () => { + app.log('LCD READY!!'); +}); + +// update display every 10s now +setInterval(updateDisplay, 1000 * 10); + +function updateDisplay() { + app.log('update display....'); + lines = []; + + app.log('...get the current status...'); + app.getCurrentStatus((status, data, statuses) => { + app.log('...current status:', status); + app.log('...get temperatures...'); + + // get current temperatures for display + Zone.findOne({ number: cfg.zone }).exec((err, zone) => { + app.log('...got temperatures!'); + + // set the first line + lines.push( + zone.currentTemperature.toFixed(1) + + 'C > ' + + zone.targetTemperature.toFixed(1) + 'C ' + (statuses.heaterOn.value === 'true' ? '#': ' ') + ); + + // put the status in the second line, fill it up with spaces to prevent display bugs - no idea why they keep appearing + // - looking funny though + lines.push(_.padEnd(status, 16)); + + // print those lines to the display + lcd.clear((err) => { + if (err) { + throw err; + } + + lcd.printLines(lines).then(() => { + app.log('DISPLAY printed all lines'); + }); + // debug output + app.log('print lines:', lines); + }); + }); + }) } \ No newline at end of file diff --git a/homekit.js b/bin/homekit.js similarity index 54% rename from homekit.js rename to bin/homekit.js index e9417e2..de5caec 100644 --- a/homekit.js +++ b/bin/homekit.js @@ -1,19 +1,13 @@ var mongoose = require('mongoose'); -var path = require('path'); var HAP = require('hap-nodejs'); -var app = require('./lib/global'); -var Zone = require('./models/zone.js'); +var app = require('../controller/app.js'); +var homekit = require('../controller/homekit.js'); require('dotenv').load(); -var cfg = require('./config.json'); +var cfg = require('../config.json'); -// init the HAP-server -HAP.init(); -var storage = require('node-persist'); -var uuid = HAP.uuid; -var Accessory = HAP.Accessory; -var accessoryLoader = require('hap-nodejs/lib/AccessoryLoader'); +app.log("HAP-NodeJS starting..."); // set the mongoose promise library to the nodejs one, required by mongoose now mongoose.Promise = global.Promise; @@ -25,30 +19,20 @@ mongoose.connection.on('error', function() { process.exit(1); }); +// init the HAP-server +HAP.init(); +var storage = require('node-persist'); +var uuid = HAP.uuid; + // Initialize our storage system storage.initSync(); // Our Accessories will each have their own HAP server; we will assign ports sequentially var targetPort = 51826; -// Load up all accessories in the /accessories folder -var dir = path.join(__dirname, "accessories"); -var accessories = accessoryLoader.loadDirectory(dir); - -app.log("HAP-NodeJS starting..."); // Publish them all separately (as opposed to BridgedCore which publishes them behind a single Bridge accessory) -accessories.forEach(function(accessory) { - - // To push Accessories separately, we'll need a few extra properties - if (!accessory.username) - throw new Error("Username not found on accessory '" + accessory.displayName + - "'. Core.js requires all accessories to define a unique 'username' property."); - - if (!accessory.pincode) - throw new Error("Pincode not found on accessory '" + accessory.displayName + - "'. Core.js requires all accessories to define a 'pincode' property."); +homekit.accessories.forEach(function(accessory) { - // app.log('test', accessory.services[0].characteristics); // publish this Accessory on the local network accessory.publish({ port: targetPort++, diff --git a/config.json b/config.json index 7f09129..69dcfe9 100644 --- a/config.json +++ b/config.json @@ -12,10 +12,12 @@ "data": [5, 6, 17, 18] } }, + "manualModeDuration": 120, "defaults": { "home": 20, "away": 18, - "holiday": 17 + "holiday": 17, + "manual": 20 }, "days": [ { diff --git a/lib/global.js b/controller/app.js similarity index 74% rename from lib/global.js rename to controller/app.js index 3f4e45c..70cf48c 100644 --- a/lib/global.js +++ b/controller/app.js @@ -2,7 +2,7 @@ var moment = require('moment'); var _ = require('lodash'); var cfg = require('../config.json'); -var sensor = require('./sensor'); +var sensor = require('../lib/sensor'); var Status = require('../models/status.js'); var Zone = require('../models/zone.js'); @@ -22,10 +22,12 @@ function getCurrentStatus(cb) { status = 'home'; } - _getCurrentConfig().then((data) => { - if (statuses.heatingMode && statuses.heatingMode.value === '1') { - status = 'timer'; + _getCurrentConfig(null, statuses.heatingMode).then((data) => { + if (data.manual) { + status = 'manual'; } + + clog('CONTROL current data:', data); clog('CONTROL current status:', status); @@ -112,10 +114,11 @@ function updateTargetTemperature(cb) { * @param {object} now moment.js Obj for setting the current time - if undefined use real now * @return {object} object containing data and the temperature set */ -function _getCurrentConfig(now) { +function _getCurrentConfig(now, heatingMode) { now = now || moment(); var dateArray = []; var foundTime; + var manualMode = false; for (let day of cfg.days) { for(let time of day.times) { @@ -131,6 +134,11 @@ function _getCurrentConfig(now) { dateArray.push({ datetime: now._d, now: true }); + if (heatingMode && heatingMode.value !== 'auto') { + manualMode = true; + dateArray.push({ on: (heatingMode.value === 'on'), datetime: heatingMode.updatedAt, manual: true }); + } + dateArray.sort((a, b) => { if (moment(a.datetime).unix() > moment(b.datetime).unix()) { return 1; @@ -150,15 +158,32 @@ function _getCurrentConfig(now) { foundIndex = foundIndex === 0 ? dateArray.length : foundIndex; foundTime = dateArray[foundIndex - 1]; - return Zone.findOne({ number: cfg.zone }) - .then((data) => { + return Zone.findOne({ number: cfg.zone }).then((zoneData) => { + if (foundTime.manual && now.diff(moment(foundTime.datetime), 'minutes') < cfg.manualModeDuration) { + // manual mode is not overwritten AND not older than config (120min) and not older than two hours + return { - day: foundTime.day, - dayIndex: foundTime.dayIndex, - time: foundTime.time, - temperatures: _.extend(cfg.defaults, foundTime.temperatures, { timer: data.customTemperature }) + manual: true, + temperatures: { manual: zoneData.customTemperature || cfg.defaults.manual } }; - }); + + } else if (manualMode) { + // manualMode tryes to be still active, although it already too old + // => reset it + Status.findOneAndUpdate({ key: 'heatingMode' }, { value: 'auto' }, { new: true, upsert: true }).exec(); + + foundIndex = foundIndex === 0 ? dateArray.length : foundIndex; + foundTime = dateArray[foundIndex - 1]; + } + + return { + day: foundTime.day, + dayIndex: foundTime.dayIndex, + time: foundTime.time, + temperatures: _.extend(cfg.defaults, foundTime.temperatures) + }; + }) + } // exports diff --git a/controller/heater.js b/controller/heater.js index 45df632..bb7cc5a 100644 --- a/controller/heater.js +++ b/controller/heater.js @@ -1,3 +1,5 @@ +var app = require('../controller/app.js'); + var Status = require('../models/status.js'); var heater = require('../lib/heater.js'); @@ -20,7 +22,7 @@ module.exports = (function() { Status.findOne({ 'key': 'heaterOn' }).select('key value').exec((err, oldStatus) => { var newSetting = false; - console.log('HEATER: old status:', oldStatus.value); + app.log('HEATER: old status:', oldStatus.value); if (!oldStatus && typeof oldStatus.value !== 'string') { newSetting = true; @@ -30,10 +32,10 @@ module.exports = (function() { }); } - console.log('HEATER new heater status?:', state); + app.log('HEATER new heater status?:', state); if (state !== (oldStatus.value === 'true') || newSetting) { - console.log('HEATER really toggle heater!'); + app.log('HEATER really toggle heater!'); oldStatus.value = state; oldStatus.save((err, status) => { diff --git a/controller/homekit.js b/controller/homekit.js new file mode 100644 index 0000000..9f845c3 --- /dev/null +++ b/controller/homekit.js @@ -0,0 +1,22 @@ +var path = require('path'); + +var accessoryLoader = require('hap-nodejs/lib/AccessoryLoader'); + +// Load up all accessories in the /accessories folder +var dir = path.join(__dirname, 'accessories'); +var accessories = accessoryLoader.loadDirectory(dir); + +// Publish them all separately (as opposed to BridgedCore which publishes them behind a single Bridge accessory) +accessories.forEach(function(accessory) { + + // To push Accessories separately, we'll need a few extra properties + if (!accessory.username) + throw new Error('Username not found on accessory "' + accessory.displayName + '". Core.js requires all accessories to define a unique "username" property.'); + + if (!accessory.pincode) + throw new Error('Pincode not found on accessory "' + accessory.displayName + '". Core.js requires all accessories to define a "pincode" property.'); +}); + +module.exports = { + accessories: accessories +} \ No newline at end of file diff --git a/lib/heater.js b/lib/heater.js index 0c06c01..0197ffd 100644 --- a/lib/heater.js +++ b/lib/heater.js @@ -1,5 +1,6 @@ -var app = require('./global'); var gpio = require('./gpio_wrapper'); + +var app = require('../controller/app.js'); var cfg = require('../config.json'); diff --git a/lib/lcd.js b/lib/lcd.js index 0386573..9528595 100644 --- a/lib/lcd.js +++ b/lib/lcd.js @@ -1,5 +1,6 @@ -var app = require('./global'); var _ = require('lodash'); + +var app = require('../controller/app.js'); var cfg = require('../config.json'); var Lcd; diff --git a/package.json b/package.json index 878cbc4..4430500 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dependencies": { "dotenv": "^2.0.0", "ds1820-temp": "^1.0.0", - "hap-nodejs": "github:khaost/HAP-NodeJS", + "hap-nodejs": "^0.4.21", "lcd": "^1.1.4", "lodash": "^4.16.4", "moment": "^2.15.2", diff --git a/process.json b/process.json index 60f577f..c90da97 100644 --- a/process.json +++ b/process.json @@ -2,17 +2,17 @@ "apps" : [ { "name" : "Control Server", - "script" : "./control.js", + "script" : "./bin/control.js", "watch" : true }, { "name" : "HomeKit Server", - "script" : "./homekit.js", + "script" : "./bin/homekit.js", "watch" : true }, { "name" : "Display Server", - "script" : "./display.js", + "script" : "./bin/display.js", "watch" : true } ] From 6d6fffbeb542dd5cc6df10bb4ec0f03b6e1868a2 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 23 Jan 2017 00:30:03 +0100 Subject: [PATCH 02/38] Refactoring & documentation --- config.json | 5 ++--- controller/app.js | 17 +++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/config.json b/config.json index 69dcfe9..3414c0e 100644 --- a/config.json +++ b/config.json @@ -13,11 +13,10 @@ } }, "manualModeDuration": 120, - "defaults": { + "defaultTemperatures": { "home": 20, "away": 18, - "holiday": 17, - "manual": 20 + "holiday": 17 }, "days": [ { diff --git a/controller/app.js b/controller/app.js index 70cf48c..0a2ec4d 100644 --- a/controller/app.js +++ b/controller/app.js @@ -23,12 +23,14 @@ function getCurrentStatus(cb) { } _getCurrentConfig(null, statuses.heatingMode).then((data) => { - if (data.manual) { + // manual mode may be globally active, but not in this zone + // => use the actual status if not + // => maybe not the best solution, but the best way for my own heater setup + if (data.manual && data.temperatures['manual']) { status = 'manual'; } clog('CONTROL current data:', data); - clog('CONTROL current status:', status); cb(status, data, statuses); @@ -160,13 +162,12 @@ function _getCurrentConfig(now, heatingMode) { return Zone.findOne({ number: cfg.zone }).then((zoneData) => { if (foundTime.manual && now.diff(moment(foundTime.datetime), 'minutes') < cfg.manualModeDuration) { - // manual mode is not overwritten AND not older than config (120min) and not older than two hours + // manual mode is not overwritten by config AND not older than config (120min) return { manual: true, - temperatures: { manual: zoneData.customTemperature || cfg.defaults.manual } - }; - + temperatures: { manual: zoneData.customTemperature || 20 } + } } else if (manualMode) { // manualMode tryes to be still active, although it already too old // => reset it @@ -180,8 +181,8 @@ function _getCurrentConfig(now, heatingMode) { day: foundTime.day, dayIndex: foundTime.dayIndex, time: foundTime.time, - temperatures: _.extend(cfg.defaults, foundTime.temperatures) - }; + temperatures: _.extend(cfg.defaultTemperatures, foundTime.temperatures) + } }) } From 135fa983c835d97d7d9a1ec6793c957d50ecf432 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 23 Jan 2017 11:16:33 +0100 Subject: [PATCH 03/38] added more details to readme --- README.md | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9904c41..99dba6b 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,31 @@ This is a NodeJS based software for raspberry pi to control my heater at home. M Right now the software just activates a relay when the temperature gets too low. In theory it should be possible to extend it in order to support digital thermostats. ## Requirements -- NodeJS 6 -- MongoDB +- RaspberryPi or similar +- relay module (I used the Foxnovo 2-channel relay) +- temperature sensor (I used a DS18B20 sensor) +- some wiring +- NodeJS 6 (tested with 6.9.x) +- MongoDB (tested with 3.4.x) +- optional: +-- 1602 LCD display module ## Setup -- checkout this repository -- run `npm install` +- setup your RaspberryPi (or something similar) like in the matching guidelines +- connect your relay to GND and a GPIO port +- connect and setup your temerature sensor according to it's manual +- install NodeJS like this: + ```shell + $ wget http://node-arm.herokuapp.com/node_latest_armhf.deb + $ sudo dpkg -i node_latest_armhf.deb + ``` +- `node -v` should now return something like this: + ```shell + $ node -v + v6.9.1 + ``` +- checkout this repository, probably in `~/raspi-heater/` or `/etc/raspi-heater/` +- run `npm install` in that directory - create environment config file: - create new file `.env` - add the mongodb location like `MONGODB='localhost/heater'` @@ -29,14 +48,17 @@ Right now the software just activates a relay when the temperature gets too low. - Change the GPIO ports to your ones and your sensor name - Change the desired days, times and temperatures - if no temerature is set for a status, the default temperatures will be used - Change the locale - - If you want to use multiple raspi-heaters with the same database, give each one a different zone id + - If you want to use multiple raspi-heaters: + -- use the same central database + -- give each one a different zone id + -- deactivate the home- and holiday status accessories on every instance exept the main one - start the processes you need: - - `control.js` for the general controller - - `homekit.js` for homekit support - - `display.js` if you've connected a display + - `bin/control.js` for the general controller + - `bin/homekit.js` for homekit support + - `bin/display.js` if you've connected a display - you can start all three with the pm2 process manager with the added process.json -## Future features +## Future features (maybe) - browser interface - server based, with own database and replication - real support for multiple instances in the same home network From 591ad51fce777690ae971a2382cab37f16b7f3da Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 23 Jan 2017 11:17:09 +0100 Subject: [PATCH 04/38] updated display to reopen every 1 minute - i hope this finally fixes the buggy behavior --- bin/display.js | 19 ++++++++++++++----- lib/lcd.js | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/bin/display.js b/bin/display.js index a970dcc..4ceb144 100644 --- a/bin/display.js +++ b/bin/display.js @@ -3,7 +3,7 @@ var mongoose = require('mongoose'); var moment = require('moment'); var _ = require('lodash'); -var lcd = require('../lib/lcd.js'); +var lcd; var app = require('../controller/app.js'); var Status = require('../models/status.js'); @@ -22,13 +22,22 @@ mongoose.connection.on('error', () => { process.exit(1); }); -lcd.on('ready', () => { - app.log('LCD READY!!'); -}); - +// call start display +reopenDisplay(); +updateDisplay(); // update display every 10s now +setInterval(reopenDisplay, 1000 * 60) setInterval(updateDisplay, 1000 * 10); +function reopenDisplay() { + if(lcd && lcd.close) lcd.close(); + lcd = require('../lib/lcd.js'); + + lcd.on('ready', () => { + app.log('LCD READY!!'); + }); +} + function updateDisplay() { app.log('update display....'); lines = []; diff --git a/lib/lcd.js b/lib/lcd.js index 9528595..f1f218d 100644 --- a/lib/lcd.js +++ b/lib/lcd.js @@ -29,7 +29,7 @@ if (process.platform === 'win32' || process.platform === 'darwin') { } }; - Lcd.prototype.close = function() {}; + Lcd.prototype.close = function() { app.log('LCD closed!'); }; Lcd.prototype.clear = function(cb) { app.log('LCD DEBUG: #### Display cleared ####'); cb(); }; Lcd.prototype.setCursor = function(x, y) { this.line = y; From c95fbcb31756f7eb963e352388c711bc452dbfb1 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 24 Jan 2017 17:21:36 +0100 Subject: [PATCH 05/38] first implementations for cooldown mode --- config.json | 2 ++ controller/heater.js | 61 +++++++++++++++++++++++++++++++++++++++++++- lib/heater.js | 40 +++++++++++++++++++---------- 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/config.json b/config.json index 3414c0e..6180941 100644 --- a/config.json +++ b/config.json @@ -12,6 +12,8 @@ "data": [5, 6, 17, 18] } }, + "maxOnDuration": 0.1, + "maxCooldownDuration": 0.1, "manualModeDuration": 120, "defaultTemperatures": { "home": 20, diff --git a/controller/heater.js b/controller/heater.js index bb7cc5a..28689cd 100644 --- a/controller/heater.js +++ b/controller/heater.js @@ -1,7 +1,10 @@ var app = require('../controller/app.js'); +var cfg = require('../config.json'); var Status = require('../models/status.js'); var heater = require('../lib/heater.js'); +var cooldownTimer; +var onTimer; module.exports = (function() { return { @@ -18,7 +21,7 @@ module.exports = (function() { return toggle(false); } - function toggle(state) { + function toggleOLD(state) { Status.findOne({ 'key': 'heaterOn' }).select('key value').exec((err, oldStatus) => { var newSetting = false; @@ -48,4 +51,60 @@ module.exports = (function() { } }) } + + function toggle(newState, timeout) { + Status.findOneAndUpdate({ key: 'heaterOn' }, { value: newState }, { upsert: true }).select('key value').exec((err, oldState) => { + app.log('HEATER trying to toggle status...'); + app.log('HEATER ...old status:', oldState.value); + + if (newState !== (oldState.value === 'true')) { + app.log('HEATER ...toggle status! To:', newState); + + if (newState === false) { + // turn off + if (!timeout) { + // without timeout + app.log('HEATER turn OFF heater - clearing cooldown timeout'); + + clearTimeout(cooldownTimer); + } else { + // with timeout + app.log('HEATER starting timer for turning it back on...'); + + cooldownTimer = setTimeout(function() { + app.log('HEATER ... heater cooled down, turn it on again!'); + + toggle(true, true); + }, 1000 * 60 * cfg.maxCooldownDuration); + } + + app.log('HEATER - ACTUALLY turn OFF now'); + } else { + // turn on + if (!timeout) { + // without timeout + app.log('HEATER turn ON heater - clearing cooldown timeout'); + + clearTimeout(onTimer); + } else { + // with timeout + app.log('HEATER starting cooldown timer...'); + + clearTimeout(onTimer); + onTimer = setTimeout(function() { + app.log('HEATER ... heater was on for too long, cooldown turns it off!'); + + toggle(false, true); + }, 1000 * 60 * cfg.maxOnDuration); + } + + app.log('HEATER - ACTUALLY turn ON now'); + } + + + } else { + app.log('HEATER ... nope. Nothing changed.'); + } + }); + } })(); \ No newline at end of file diff --git a/lib/heater.js b/lib/heater.js index 0197ffd..7f38a4c 100644 --- a/lib/heater.js +++ b/lib/heater.js @@ -3,19 +3,33 @@ var gpio = require('./gpio_wrapper'); var app = require('../controller/app.js'); var cfg = require('../config.json'); +function on() { + app.log('HARDWARE turned heater on'); + gpio.setFalse(cfg.hardware.relay); +} -module.exports = { - on: function() { - app.log('HARDWARE turned heater on'); - gpio.setFalse(cfg.hardware.relay); - }, - off: function() { - app.log('HARDWARE turned heater off'); - gpio.setTrue(cfg.hardware.relay); - }, - get: function() { - return gpio.get(cfg.hardware.relay).then((status) => { - return !!status; - }); +function off() { + app.log('HARDWARE turned heater off'); + gpio.setTrue(cfg.hardware.relay); +} + +function get() { + return gpio.get(cfg.hardware.relay).then((status) => { + return !!status; + }); +} + +function set(state) { + if (state) { + on(); + } else { + off(); } +} + +module.exports = { + on: on, + off: off, + get: get; + set: set } \ No newline at end of file From b6d7da55df1a00531052d40f3d946d94a41349d1 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Thu, 26 Jan 2017 16:43:24 +0100 Subject: [PATCH 06/38] fix typing error --- lib/heater.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/heater.js b/lib/heater.js index 7f38a4c..974cd3f 100644 --- a/lib/heater.js +++ b/lib/heater.js @@ -30,6 +30,6 @@ function set(state) { module.exports = { on: on, off: off, - get: get; + get: get, set: set } \ No newline at end of file From fc889a5e647fd16117d485783378a4170a9905f3 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Thu, 26 Jan 2017 16:43:49 +0100 Subject: [PATCH 07/38] better attempt for cooldown --- controller/heater.js | 103 +++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 37 deletions(-) diff --git a/controller/heater.js b/controller/heater.js index 28689cd..31e51da 100644 --- a/controller/heater.js +++ b/controller/heater.js @@ -1,3 +1,5 @@ +var moment = require('moment'); + var app = require('../controller/app.js'); var cfg = require('../config.json'); @@ -7,6 +9,8 @@ var cooldownTimer; var onTimer; module.exports = (function() { + setInterval(checkHeaterStatus, 1000 * 60 / 60); + return { on, off, @@ -59,52 +63,77 @@ module.exports = (function() { if (newState !== (oldState.value === 'true')) { app.log('HEATER ...toggle status! To:', newState); + } else { + app.log('HEATER ... nope. Nothing changed.'); + } + }); + } - if (newState === false) { - // turn off - if (!timeout) { - // without timeout - app.log('HEATER turn OFF heater - clearing cooldown timeout'); - - clearTimeout(cooldownTimer); - } else { - // with timeout - app.log('HEATER starting timer for turning it back on...'); - - cooldownTimer = setTimeout(function() { - app.log('HEATER ... heater cooled down, turn it on again!'); + function checkHeaterStatus() { + var heater = false; + var cooldown = false; - toggle(true, true); - }, 1000 * 60 * cfg.maxCooldownDuration); - } + Status.find({}).exec((err, statuses) => { + if (statuses.heaterOn && statuses.heaterOn.value === 'true') { + heater = true; + } - app.log('HEATER - ACTUALLY turn OFF now'); - } else { - // turn on - if (!timeout) { - // without timeout - app.log('HEATER turn ON heater - clearing cooldown timeout'); + if (statuses.cooldownOn && statuses.cooldownOn.value === 'true') { + cooldown = true; + } + }); + } + + + function toggleHeater(state) { + Status.findOne({ key: 'cooldownOn' }).exec((err, cooldownState) => { + if (cooldownState.value === 'true') { + // cooldown active + if (moment().unix() - moment(cooldownState.updatedAt).unix() > (cfg.maxCooldownDuration * 60)) { + // cooldown too old + } + } + }); - clearTimeout(onTimer); - } else { - // with timeout - app.log('HEATER starting cooldown timer...'); + if (newState === false) { + // turn off + if (!timeout) { + // without timeout + app.log('HEATER turn OFF heater - clearing cooldown timeout'); - clearTimeout(onTimer); - onTimer = setTimeout(function() { - app.log('HEATER ... heater was on for too long, cooldown turns it off!'); + clearTimeout(cooldownTimer); + } else { + // with timeout + app.log('HEATER ...starting timer for turning it back on.'); - toggle(false, true); - }, 1000 * 60 * cfg.maxOnDuration); - } - - app.log('HEATER - ACTUALLY turn ON now'); - } + cooldownTimer = setTimeout(function() { + app.log('HEATER ... heater cooled down, turn it on again!'); + Status.findOneAndUpdate({ key: 'cooldownOn' }, { value: false }, { upsert: true }).exec(); + toggle(true, true); + }, 1000 * 60 * cfg.maxCooldownDuration); + } + app.log('HEATER - ACTUALLY turn OFF now'); + heater.off(); + } else { + // turn on + if (!timeout) { + app.log('HEATER turn ON heater - starting new timeout...'); } else { - app.log('HEATER ... nope. Nothing changed.'); + app.log('HEATER turn heater back on from cooldown...'); } - }); + + clearTimeout(onTimer); + onTimer = setTimeout(function() { + app.log('HEATER ... heater was on for too long, cooldown turns it off!'); + Status.findOneAndUpdate({ key: 'cooldownOn' }, { value: true }, { upsert: true }).exec(); + + toggle(false, true); + }, 1000 * 60 * cfg.maxOnDuration); + + app.log('HEATER - ACTUALLY turn ON now'); + heater.on(); + } } })(); \ No newline at end of file From cfef66a0be0fbe1c602bb490bc344e315030f90a Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 30 Jan 2017 08:01:32 +0100 Subject: [PATCH 08/38] Next changes for cooldown --- accessories/Thermostat_accessory.js | 15 +- bin/control.js | 5 +- controller/heater.js | 289 +++++++++++++++++----------- 3 files changed, 190 insertions(+), 119 deletions(-) diff --git a/accessories/Thermostat_accessory.js b/accessories/Thermostat_accessory.js index 774870a..78b3687 100644 --- a/accessories/Thermostat_accessory.js +++ b/accessories/Thermostat_accessory.js @@ -10,6 +10,8 @@ var uuid = HAP.uuid; var Zone = require('../models/zone.js'); var Status = require('../models/status.js'); +var heater = require('../controller/heater.js'); + var thermostat = exports.accessory = new Accessory('Thermostat', uuid.generate('hap-nodejs:accessories:thermostat')); thermostat.username = "C1:5D:3A:AE:5E:F3"; @@ -33,23 +35,20 @@ thermostat .addService(Service.Thermostat, 'Thermostat') .getCharacteristic(Characteristic.CurrentHeatingCoolingState) .on('get', function(callback) { - Status.findOne({ key: 'heaterOn' }).exec((err, data) => { + heater.get().then((status) => { // return our current value var state, stateName; // shows if the heater is on or off - no cooling for now possible - switch(data.value) { - case 'false': + switch(status) { + case false: stateName = 'off'; state = Characteristic.CurrentHeatingCoolingState.OFF; break; - case 'true': + case true: stateName = 'heat'; state = Characteristic.CurrentHeatingCoolingState.HEAT; - break; - default: - stateName = 'off'; - state = Characteristic.CurrentHeatingCoolingState.OFF; + break; } app.log('get: CurrentHeatingCoolingState', stateName, data.value); diff --git a/bin/control.js b/bin/control.js index 2f7352c..0408535 100644 --- a/bin/control.js +++ b/bin/control.js @@ -65,8 +65,9 @@ Status.findOneAndUpdate({ key: 'heatingMode' }, { value: 'auto' }, { new: true, }); // interval checks -setInterval(checkTemperatures, 1000 * 30); // every 30s -setInterval(_set, 1000 * 60); // every 1min +// setInterval(checkTemperatures, 1000 * 30); // every 30s +// setInterval(_set, 1000 * 60); // every 1min +setInterval(heater.checkHeaterStatus, 1000 * 60 / 60); ///////////// // on kill // diff --git a/controller/heater.js b/controller/heater.js index 31e51da..78523fb 100644 --- a/controller/heater.js +++ b/controller/heater.js @@ -4,136 +4,207 @@ var app = require('../controller/app.js'); var cfg = require('../config.json'); var Status = require('../models/status.js'); -var heater = require('../lib/heater.js'); -var cooldownTimer; -var onTimer; -module.exports = (function() { - setInterval(checkHeaterStatus, 1000 * 60 / 60); +var heaterHrdwr = require('../lib/heater.js'); - return { - on, - off, - toggle - }; +var cooldownTimer; +var onTimer; - function on() { - return toggle(true); - } +function on() { + return toggle(true); +} - function off() { - return toggle(false); - } +function off() { + return toggle(false); +} - function toggleOLD(state) { - Status.findOne({ 'key': 'heaterOn' }).select('key value').exec((err, oldStatus) => { - var newSetting = false; +function get() { + var heater = false; + var targetHeater = false; + var cooldown = false; + + return Status.findOne({}).select('key value').then((statuses) => { console.log('getdatshit', Object.keys(statuses)); + if (statuses.heaterOn && statuses.heaterOn.value === 'true') { + heater = true; + } + + if (statuses.targetHeaterOn && statuses.targetHeaterOn.value === 'true') { + targetHeater = true; + } - app.log('HEATER: old status:', oldStatus.value); - - if (!oldStatus && typeof oldStatus.value !== 'string') { - newSetting = true; - var oldStatus = new Status({ - key: 'heaterOn', - value: state - }); - } - - app.log('HEATER new heater status?:', state); + if (statuses.cooldownOn && statuses.cooldownOn.value === 'true') { + cooldown = true; + } + + return { + heater, + targetHeater, + cooldown, + statuses + }; + }).catch((err) => { throw err; }); +} + +function toggleOLD(state) { + Status.findOne({ 'key': 'heaterOn' }).select('key value').exec((err, oldStatus) => { + var newSetting = false; + + app.log('HEATER: old status:', oldStatus.value); + + if (!oldStatus && typeof oldStatus.value !== 'string') { + newSetting = true; + var oldStatus = new Status({ + key: 'heaterOn', + value: state + }); + } + + app.log('HEATER new heater status?:', state); + + if (state !== (oldStatus.value === 'true') || newSetting) { + app.log('HEATER really toggle heater!'); - if (state !== (oldStatus.value === 'true') || newSetting) { - app.log('HEATER really toggle heater!'); - - oldStatus.value = state; - oldStatus.save((err, status) => { - if (status && status.value !== 'false') { - heater.on(); - } else { - heater.off(); - } - }); - } - }) - } + oldStatus.value = state; + oldStatus.save((err, status) => { + if (status && status.value !== 'false') { + heater.on(); + } else { + heater.off(); + } + }); + } + }) +} - function toggle(newState, timeout) { - Status.findOneAndUpdate({ key: 'heaterOn' }, { value: newState }, { upsert: true }).select('key value').exec((err, oldState) => { - app.log('HEATER trying to toggle status...'); - app.log('HEATER ...old status:', oldState.value); +function toggle(newState) { + Status.findOneAndUpdate({ key: 'targetHeaterOn' }, { value: newState }, { upsert: true }).select('key value').exec((err, oldState) => { + app.log('HEATER trying to toggle status...'); + app.log('HEATER ...old status:', oldState.value || false); - if (newState !== (oldState.value === 'true')) { - app.log('HEATER ...toggle status! To:', newState); - } else { - app.log('HEATER ... nope. Nothing changed.'); - } - }); - } - - function checkHeaterStatus() { - var heater = false; - var cooldown = false; + if (!oldState || newState !== (oldState.value === 'true')) { + app.log('HEATER ...toggle status! To:', newState); + } else { + app.log('HEATER ... nope. Nothing changed.'); + } + }); +} - Status.find({}).exec((err, statuses) => { - if (statuses.heaterOn && statuses.heaterOn.value === 'true') { +function checkHeaterStatus() { + var heater = false; + var targetHeater = false; + var cooldown = false; + + app.log('HEATER checkHeaterStatus'); + + get().then((data) => { + var statuses = data.statuses; + cooldown = data.cooldown; + targetHeater = data.targetHeater; + heater = data.heater; + + app.log('HEATER checkHeaterStatus - cooldown:', cooldown); + app.log('HEATER checkHeaterStatus - heater:', heater); + app.log('HEATER checkHeaterStatus - targetHeater:', targetHeater); + + if (cooldown && (moment().unix() - moment(statuses.cooldownOn.updatedAt).unix() > (cfg.maxCooldownDuration * 60))) { + // cooldown state is too old - turn cooldown off + app.log('HEATER checkHeaterStatus - cooldown state too old, turn it off'); + Status.findOneAndUpdate({ key: 'cooldownOn' }, { value: false }, { upsert: true }).exec(); + cooldown = false; + } + + if (targetHeater) { + // heater is supposed to be on + if (heater && (moment().unix() - moment(statuses.heaterOn.updatedAt).unix() > (cfg.maxOnDuration * 60))) { + // heater is on for too long => start cooldown + app.log('HEATER checkHeaterStatus - heater is on for too long, turn it off'); + Status.findOneAndUpdate({ key: 'heaterOn' }, { value: false }, { upsert: true }).exec(); + Status.findOneAndUpdate({ key: 'cooldownOn' }, { value: true }, { upsert: true }).exec(); + heater = false; + cooldown = true; + } + + if(!heater && !cooldown) { + // heater is off, should be on AND cooldown off => turn it on! + app.log('HEATER checkHeaterStatus - heater is off for too long, turn it on'); + Status.findOneAndUpdate({ key: 'heaterOn' }, { value: true }, { upsert: true }).exec(); heater = true; } + } + + app.log('HEATER checkHeaterStatus - ACTUALLY cooldown:', cooldown); + app.log('HEATER checkHeaterStatus - ACTUALLY heater:', heater); + + + if (heater && !cooldown) { + // heater on - but no cooldown + heaterHrdwr.on(); + } + + if (!heater) { + heaterHrdwr.off(); + } + }); +} - if (statuses.cooldownOn && statuses.cooldownOn.value === 'true') { - cooldown = true; - } - }); - } - - function toggleHeater(state) { - Status.findOne({ key: 'cooldownOn' }).exec((err, cooldownState) => { - if (cooldownState.value === 'true') { - // cooldown active - if (moment().unix() - moment(cooldownState.updatedAt).unix() > (cfg.maxCooldownDuration * 60)) { - // cooldown too old - } +function toggleHeater(state) { + Status.findOne({ key: 'cooldownOn' }).exec((err, cooldownState) => { + if (cooldownState.value === 'true') { + // cooldown active + if (moment().unix() - moment(cooldownState.updatedAt).unix() > (cfg.maxCooldownDuration * 60)) { + // cooldown too old } - }); + } + }); - if (newState === false) { - // turn off - if (!timeout) { - // without timeout - app.log('HEATER turn OFF heater - clearing cooldown timeout'); + if (newState === false) { + // turn off + if (!timeout) { + // without timeout + app.log('HEATER turn OFF heater - clearing cooldown timeout'); - clearTimeout(cooldownTimer); - } else { - // with timeout - app.log('HEATER ...starting timer for turning it back on.'); + clearTimeout(cooldownTimer); + } else { + // with timeout + app.log('HEATER ...starting timer for turning it back on.'); - cooldownTimer = setTimeout(function() { - app.log('HEATER ... heater cooled down, turn it on again!'); - Status.findOneAndUpdate({ key: 'cooldownOn' }, { value: false }, { upsert: true }).exec(); + cooldownTimer = setTimeout(function() { + app.log('HEATER ... heater cooled down, turn it on again!'); + Status.findOneAndUpdate({ key: 'cooldownOn' }, { value: false }, { upsert: true }).exec(); - toggle(true, true); - }, 1000 * 60 * cfg.maxCooldownDuration); - } + toggle(true, true); + }, 1000 * 60 * cfg.maxCooldownDuration); + } - app.log('HEATER - ACTUALLY turn OFF now'); - heater.off(); + app.log('HEATER - ACTUALLY turn OFF now'); + heater.off(); + } else { + // turn on + if (!timeout) { + app.log('HEATER turn ON heater - starting new timeout...'); } else { - // turn on - if (!timeout) { - app.log('HEATER turn ON heater - starting new timeout...'); - } else { - app.log('HEATER turn heater back on from cooldown...'); - } + app.log('HEATER turn heater back on from cooldown...'); + } - clearTimeout(onTimer); - onTimer = setTimeout(function() { - app.log('HEATER ... heater was on for too long, cooldown turns it off!'); - Status.findOneAndUpdate({ key: 'cooldownOn' }, { value: true }, { upsert: true }).exec(); + clearTimeout(onTimer); + onTimer = setTimeout(function() { + app.log('HEATER ... heater was on for too long, cooldown turns it off!'); + Status.findOneAndUpdate({ key: 'cooldownOn' }, { value: true }, { upsert: true }).exec(); - toggle(false, true); - }, 1000 * 60 * cfg.maxOnDuration); - - app.log('HEATER - ACTUALLY turn ON now'); - heater.on(); - } + toggle(false, true); + }, 1000 * 60 * cfg.maxOnDuration); + + app.log('HEATER - ACTUALLY turn ON now'); + heater.on(); } -})(); \ No newline at end of file +} + + +module.exports = { + on, + off, + toggle, + get, + checkHeaterStatus +}; \ No newline at end of file From b0004584107d68f916ba3928de315587a1e6d399 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 31 Jan 2017 00:05:37 +0100 Subject: [PATCH 09/38] Another fix --- bin/control.js | 2 +- controller/heater.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bin/control.js b/bin/control.js index 0408535..317f6ae 100644 --- a/bin/control.js +++ b/bin/control.js @@ -67,7 +67,7 @@ Status.findOneAndUpdate({ key: 'heatingMode' }, { value: 'auto' }, { new: true, // interval checks // setInterval(checkTemperatures, 1000 * 30); // every 30s // setInterval(_set, 1000 * 60); // every 1min -setInterval(heater.checkHeaterStatus, 1000 * 60 / 60); +setInterval(heater.checkHeaterStatus, 1000 * 60 / 10); ///////////// // on kill // diff --git a/controller/heater.js b/controller/heater.js index 78523fb..6d521ae 100644 --- a/controller/heater.js +++ b/controller/heater.js @@ -18,12 +18,12 @@ function off() { return toggle(false); } -function get() { +function get(cb) { var heater = false; var targetHeater = false; var cooldown = false; - return Status.findOne({}).select('key value').then((statuses) => { console.log('getdatshit', Object.keys(statuses)); + app.getCurrentStatus((status, data, statuses) => { console.log('getdatshit', Object.keys(statuses)); if (statuses.heaterOn && statuses.heaterOn.value === 'true') { heater = true; } @@ -36,13 +36,13 @@ function get() { cooldown = true; } - return { + cb({ heater, targetHeater, cooldown, statuses - }; - }).catch((err) => { throw err; }); + }); + }); } function toggleOLD(state) { @@ -96,7 +96,7 @@ function checkHeaterStatus() { app.log('HEATER checkHeaterStatus'); - get().then((data) => { + get((data) => { var statuses = data.statuses; cooldown = data.cooldown; targetHeater = data.targetHeater; From d3e6c7695e967fd3c19e99ddf11b773a83d5e07a Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Sun, 5 Feb 2017 17:20:56 +0100 Subject: [PATCH 10/38] last changes for testing --- bin/control.js | 6 +++--- config.json | 4 ++-- controller/app.js | 1 - controller/heater.js | 10 ++++++++-- lib/sensor.js | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/bin/control.js b/bin/control.js index 317f6ae..a598223 100644 --- a/bin/control.js +++ b/bin/control.js @@ -65,9 +65,9 @@ Status.findOneAndUpdate({ key: 'heatingMode' }, { value: 'auto' }, { new: true, }); // interval checks -// setInterval(checkTemperatures, 1000 * 30); // every 30s -// setInterval(_set, 1000 * 60); // every 1min -setInterval(heater.checkHeaterStatus, 1000 * 60 / 10); +setInterval(checkTemperatures, 1000 * 30); // every 30s +setInterval(_set, 1000 * 60); // every 1min +setInterval(heater.checkHeaterStatus, 1000 * 60); // every 1min ///////////// // on kill // diff --git a/config.json b/config.json index 6180941..7929ed2 100644 --- a/config.json +++ b/config.json @@ -12,8 +12,8 @@ "data": [5, 6, 17, 18] } }, - "maxOnDuration": 0.1, - "maxCooldownDuration": 0.1, + "maxOnDuration": 30, + "maxCooldownDuration": 30, "manualModeDuration": 120, "defaultTemperatures": { "home": 20, diff --git a/controller/app.js b/controller/app.js index 0a2ec4d..d68a89e 100644 --- a/controller/app.js +++ b/controller/app.js @@ -30,7 +30,6 @@ function getCurrentStatus(cb) { status = 'manual'; } - clog('CONTROL current data:', data); clog('CONTROL current status:', status); cb(status, data, statuses); diff --git a/controller/heater.js b/controller/heater.js index 6d521ae..fda1456 100644 --- a/controller/heater.js +++ b/controller/heater.js @@ -23,7 +23,7 @@ function get(cb) { var targetHeater = false; var cooldown = false; - app.getCurrentStatus((status, data, statuses) => { console.log('getdatshit', Object.keys(statuses)); + app.getCurrentStatus((status, data, statuses) => { if (statuses.heaterOn && statuses.heaterOn.value === 'true') { heater = true; } @@ -130,7 +130,13 @@ function checkHeaterStatus() { Status.findOneAndUpdate({ key: 'heaterOn' }, { value: true }, { upsert: true }).exec(); heater = true; } - } + } else { + // heater is supposed to be off + if (heater) { + //...but actually its on :( + heater = false; + } + } app.log('HEATER checkHeaterStatus - ACTUALLY cooldown:', cooldown); app.log('HEATER checkHeaterStatus - ACTUALLY heater:', heater); diff --git a/lib/sensor.js b/lib/sensor.js index e8bbcd1..9d3f231 100644 --- a/lib/sensor.js +++ b/lib/sensor.js @@ -7,7 +7,7 @@ if (process.platform === 'win32' || process.platform === 'darwin') { readDevice: function(sensor) { return new Promise((resolve, reject) => { console.log('FAKE sensor read!', sensor); - resolve({ value: randomIntFromInterval(12, 25) }); + resolve({ value: randomIntFromInterval(15, 21) }); }); } } From bc3a54c33b3b158e8dd25e753d1f5da08153a19b Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Sun, 5 Feb 2017 17:40:36 +0100 Subject: [PATCH 11/38] Fixed path for accessories --- controller/homekit.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/controller/homekit.js b/controller/homekit.js index 9f845c3..4bbaaf5 100644 --- a/controller/homekit.js +++ b/controller/homekit.js @@ -3,8 +3,7 @@ var path = require('path'); var accessoryLoader = require('hap-nodejs/lib/AccessoryLoader'); // Load up all accessories in the /accessories folder -var dir = path.join(__dirname, 'accessories'); -var accessories = accessoryLoader.loadDirectory(dir); +var accessories = accessoryLoader.loadDirectory('../accessories'); // Publish them all separately (as opposed to BridgedCore which publishes them behind a single Bridge accessory) accessories.forEach(function(accessory) { From c2dd6d1752601053e7411729f572de0e61b9053a Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Sun, 5 Feb 2017 18:02:08 +0100 Subject: [PATCH 12/38] fixed paths --- accessories/Thermostat_accessory.js | 2 +- controller/homekit.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/accessories/Thermostat_accessory.js b/accessories/Thermostat_accessory.js index 78b3687..1e437cc 100644 --- a/accessories/Thermostat_accessory.js +++ b/accessories/Thermostat_accessory.js @@ -1,5 +1,5 @@ var HAP = require('hap-nodejs'); -var app = require('../lib/global'); +var app = require('../controller/app.js'); var cfg = require('../config.json'); var Accessory = HAP.Accessory; diff --git a/controller/homekit.js b/controller/homekit.js index 4bbaaf5..953196b 100644 --- a/controller/homekit.js +++ b/controller/homekit.js @@ -3,7 +3,8 @@ var path = require('path'); var accessoryLoader = require('hap-nodejs/lib/AccessoryLoader'); // Load up all accessories in the /accessories folder -var accessories = accessoryLoader.loadDirectory('../accessories'); +var dir = path.join(__dirname, '../accessories'); +var accessories = accessoryLoader.loadDirectory(dir); // Publish them all separately (as opposed to BridgedCore which publishes them behind a single Bridge accessory) accessories.forEach(function(accessory) { From 00fff8045ad56845072e6948455682d95fd97d1b Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Sun, 5 Feb 2017 18:29:35 +0100 Subject: [PATCH 13/38] Fix for homekit --- accessories/Thermostat_accessory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accessories/Thermostat_accessory.js b/accessories/Thermostat_accessory.js index 1e437cc..e222eb8 100644 --- a/accessories/Thermostat_accessory.js +++ b/accessories/Thermostat_accessory.js @@ -35,7 +35,7 @@ thermostat .addService(Service.Thermostat, 'Thermostat') .getCharacteristic(Characteristic.CurrentHeatingCoolingState) .on('get', function(callback) { - heater.get().then((status) => { + heater.get((status) => { // return our current value var state, stateName; From bda10cccff07afea1ab12fddc6078873ecc87c4c Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Sun, 5 Feb 2017 21:25:36 +0100 Subject: [PATCH 14/38] no message --- controller/app.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/controller/app.js b/controller/app.js index d68a89e..d699cfd 100644 --- a/controller/app.js +++ b/controller/app.js @@ -22,7 +22,7 @@ function getCurrentStatus(cb) { status = 'home'; } - _getCurrentConfig(null, statuses.heatingMode).then((data) => { + _getCurrentConfig(null, statuses.heatingMode, (data) => { // manual mode may be globally active, but not in this zone // => use the actual status if not // => maybe not the best solution, but the best way for my own heater setup @@ -115,7 +115,7 @@ function updateTargetTemperature(cb) { * @param {object} now moment.js Obj for setting the current time - if undefined use real now * @return {object} object containing data and the temperature set */ -function _getCurrentConfig(now, heatingMode) { +function _getCurrentConfig(now, heatingMode, cb) { now = now || moment(); var dateArray = []; var foundTime; @@ -159,14 +159,14 @@ function _getCurrentConfig(now, heatingMode) { foundIndex = foundIndex === 0 ? dateArray.length : foundIndex; foundTime = dateArray[foundIndex - 1]; - return Zone.findOne({ number: cfg.zone }).then((zoneData) => { + Zone.findOne({ number: cfg.zone }).exec((err, zoneData) => { if (foundTime.manual && now.diff(moment(foundTime.datetime), 'minutes') < cfg.manualModeDuration) { // manual mode is not overwritten by config AND not older than config (120min) - return { + cb({ manual: true, temperatures: { manual: zoneData.customTemperature || 20 } - } + }); } else if (manualMode) { // manualMode tryes to be still active, although it already too old // => reset it @@ -176,12 +176,12 @@ function _getCurrentConfig(now, heatingMode) { foundTime = dateArray[foundIndex - 1]; } - return { + cb({ day: foundTime.day, dayIndex: foundTime.dayIndex, time: foundTime.time, temperatures: _.extend(cfg.defaultTemperatures, foundTime.temperatures) - } + }); }) } From 0d096a3102a06637393b9f3b9ff571ca735debe6 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Sun, 5 Feb 2017 21:27:26 +0100 Subject: [PATCH 15/38] no message --- accessories/Thermostat_accessory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accessories/Thermostat_accessory.js b/accessories/Thermostat_accessory.js index e222eb8..ab0c68b 100644 --- a/accessories/Thermostat_accessory.js +++ b/accessories/Thermostat_accessory.js @@ -51,7 +51,7 @@ thermostat break; } - app.log('get: CurrentHeatingCoolingState', stateName, data.value); + app.log('get: CurrentHeatingCoolingState', stateName, status); callback(null, state); }); }); From 771781149b23939b7a4bbfcbafb4e227724037bc Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Sun, 5 Feb 2017 21:30:40 +0100 Subject: [PATCH 16/38] no message --- controller/app.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/controller/app.js b/controller/app.js index d699cfd..d68a89e 100644 --- a/controller/app.js +++ b/controller/app.js @@ -22,7 +22,7 @@ function getCurrentStatus(cb) { status = 'home'; } - _getCurrentConfig(null, statuses.heatingMode, (data) => { + _getCurrentConfig(null, statuses.heatingMode).then((data) => { // manual mode may be globally active, but not in this zone // => use the actual status if not // => maybe not the best solution, but the best way for my own heater setup @@ -115,7 +115,7 @@ function updateTargetTemperature(cb) { * @param {object} now moment.js Obj for setting the current time - if undefined use real now * @return {object} object containing data and the temperature set */ -function _getCurrentConfig(now, heatingMode, cb) { +function _getCurrentConfig(now, heatingMode) { now = now || moment(); var dateArray = []; var foundTime; @@ -159,14 +159,14 @@ function _getCurrentConfig(now, heatingMode, cb) { foundIndex = foundIndex === 0 ? dateArray.length : foundIndex; foundTime = dateArray[foundIndex - 1]; - Zone.findOne({ number: cfg.zone }).exec((err, zoneData) => { + return Zone.findOne({ number: cfg.zone }).then((zoneData) => { if (foundTime.manual && now.diff(moment(foundTime.datetime), 'minutes') < cfg.manualModeDuration) { // manual mode is not overwritten by config AND not older than config (120min) - cb({ + return { manual: true, temperatures: { manual: zoneData.customTemperature || 20 } - }); + } } else if (manualMode) { // manualMode tryes to be still active, although it already too old // => reset it @@ -176,12 +176,12 @@ function _getCurrentConfig(now, heatingMode, cb) { foundTime = dateArray[foundIndex - 1]; } - cb({ + return { day: foundTime.day, dayIndex: foundTime.dayIndex, time: foundTime.time, temperatures: _.extend(cfg.defaultTemperatures, foundTime.temperatures) - }); + } }) } From 9bc15d1586072846b3626c52dee7c90f6c8b2416 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Sun, 5 Feb 2017 21:33:42 +0100 Subject: [PATCH 17/38] fixed return value of get() for heater --- accessories/Thermostat_accessory.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accessories/Thermostat_accessory.js b/accessories/Thermostat_accessory.js index ab0c68b..9a4d8ab 100644 --- a/accessories/Thermostat_accessory.js +++ b/accessories/Thermostat_accessory.js @@ -40,7 +40,7 @@ thermostat var state, stateName; // shows if the heater is on or off - no cooling for now possible - switch(status) { + switch(status.heater) { case false: stateName = 'off'; state = Characteristic.CurrentHeatingCoolingState.OFF; @@ -51,7 +51,7 @@ thermostat break; } - app.log('get: CurrentHeatingCoolingState', stateName, status); + app.log('get: CurrentHeatingCoolingState', stateName, status.heater); callback(null, state); }); }); From f7abc26e30fcc1e49b465751e3d61d8df5d818cf Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Sun, 5 Feb 2017 22:21:58 +0100 Subject: [PATCH 18/38] Updated display for cooldown mode --- bin/display.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bin/display.js b/bin/display.js index 4ceb144..75008f4 100644 --- a/bin/display.js +++ b/bin/display.js @@ -38,6 +38,10 @@ function reopenDisplay() { }); } +function getHeaterOn(statuses) { + return (statuses.heaterOn.value === 'true' ? (statuses.cooldownOn.value === 'true' ? '0' : '#') : ' '); +} + function updateDisplay() { app.log('update display....'); lines = []; @@ -55,7 +59,7 @@ function updateDisplay() { lines.push( zone.currentTemperature.toFixed(1) + 'C > ' + - zone.targetTemperature.toFixed(1) + 'C ' + (statuses.heaterOn.value === 'true' ? '#': ' ') + zone.targetTemperature.toFixed(1) + 'C ' + getHeaterOn(statuses) ); // put the status in the second line, fill it up with spaces to prevent display bugs - no idea why they keep appearing From 20c74a135d2e6c0a36854da2199bc682f98ed1c2 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 6 Feb 2017 18:05:08 +0100 Subject: [PATCH 19/38] Reset default value on startup for heaterOn --- bin/control.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/control.js b/bin/control.js index a598223..9fbf009 100644 --- a/bin/control.js +++ b/bin/control.js @@ -60,8 +60,10 @@ gpio.setup(cfg.hardware.button, 'in').then((data) => { // first check after launch Status.findOneAndUpdate({ key: 'heatingMode' }, { value: 'auto' }, { new: true, upsert: true }).exec((err, data) => { - checkTemperatures(); - _set(); + Status.findOneAndUpdate({ key: 'heaterOn' }, { value: false }, { new: true, upsert: true }).exec((err, data) => { + checkTemperatures(); + _set(); + }); }); // interval checks From b372559328acb728176261d028fe63e9c2cce151 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 6 Feb 2017 18:39:54 +0100 Subject: [PATCH 20/38] add initial check for heater --- bin/control.js | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/control.js b/bin/control.js index 9fbf009..01dbe20 100644 --- a/bin/control.js +++ b/bin/control.js @@ -63,6 +63,7 @@ Status.findOneAndUpdate({ key: 'heatingMode' }, { value: 'auto' }, { new: true, Status.findOneAndUpdate({ key: 'heaterOn' }, { value: false }, { new: true, upsert: true }).exec((err, data) => { checkTemperatures(); _set(); + heater.checkHeaterStatus(); }); }); From 805747fd7ed188633ec3440cccc23b2b7761d53c Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 6 Feb 2017 18:40:29 +0100 Subject: [PATCH 21/38] Removed throw in display to prevent restart on fail --- bin/display.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/display.js b/bin/display.js index 75008f4..316ddd1 100644 --- a/bin/display.js +++ b/bin/display.js @@ -69,7 +69,7 @@ function updateDisplay() { // print those lines to the display lcd.clear((err) => { if (err) { - throw err; + app.log('DISPLAY got ERROR while clearing:', err); } lcd.printLines(lines).then(() => { From d81d4c423bea6f427074cc73aa1795f78648b1f6 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 6 Feb 2017 18:41:00 +0100 Subject: [PATCH 22/38] removed heater check if already set or not - was useless anyway --- controller/heater.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/controller/heater.js b/controller/heater.js index fda1456..c41ce24 100644 --- a/controller/heater.js +++ b/controller/heater.js @@ -80,12 +80,7 @@ function toggle(newState) { Status.findOneAndUpdate({ key: 'targetHeaterOn' }, { value: newState }, { upsert: true }).select('key value').exec((err, oldState) => { app.log('HEATER trying to toggle status...'); app.log('HEATER ...old status:', oldState.value || false); - - if (!oldState || newState !== (oldState.value === 'true')) { - app.log('HEATER ...toggle status! To:', newState); - } else { - app.log('HEATER ... nope. Nothing changed.'); - } + app.log('HEATER ...toggle status to:', newState); }); } From d2512154e58300d6d9e6f45d60baf07abcd6ee33 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 6 Feb 2017 19:27:11 +0100 Subject: [PATCH 23/38] Added more logging to display --- bin/display.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/display.js b/bin/display.js index 316ddd1..f646d2e 100644 --- a/bin/display.js +++ b/bin/display.js @@ -27,7 +27,7 @@ reopenDisplay(); updateDisplay(); // update display every 10s now setInterval(reopenDisplay, 1000 * 60) -setInterval(updateDisplay, 1000 * 10); +setInterval(updateDisplay, 1000 * 31); function reopenDisplay() { if(lcd && lcd.close) lcd.close(); @@ -39,6 +39,7 @@ function reopenDisplay() { } function getHeaterOn(statuses) { + app.log('DISPLAY getHeaterOn', status); return (statuses.heaterOn.value === 'true' ? (statuses.cooldownOn.value === 'true' ? '0' : '#') : ' '); } From 05427842107deac187e3ad280997800b997d67f6 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 6 Feb 2017 19:42:38 +0100 Subject: [PATCH 24/38] add more logging --- controller/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/controller/app.js b/controller/app.js index d68a89e..bfa7867 100644 --- a/controller/app.js +++ b/controller/app.js @@ -13,6 +13,7 @@ function getCurrentStatus(cb) { if (err) { console.error(err); cb(err); } else { statuses = _.keyBy(statuses, 'key'); + app.log('CONTROL getCurrentStatus', statuses); if (statuses.isHoliday && statuses.isHoliday.value === 'true') { status = 'holiday'; From a5a8ced16b4e5dd475390576d346e90830e42166 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 6 Feb 2017 21:11:16 +0100 Subject: [PATCH 25/38] Fix log in app --- controller/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/app.js b/controller/app.js index bfa7867..ecf56b6 100644 --- a/controller/app.js +++ b/controller/app.js @@ -13,7 +13,7 @@ function getCurrentStatus(cb) { if (err) { console.error(err); cb(err); } else { statuses = _.keyBy(statuses, 'key'); - app.log('CONTROL getCurrentStatus', statuses); + log('CONTROL getCurrentStatus', statuses); if (statuses.isHoliday && statuses.isHoliday.value === 'true') { status = 'holiday'; From 160e11f51c581b16d9ee0cc3838149f2dda7c4ad Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 6 Feb 2017 22:10:11 +0100 Subject: [PATCH 26/38] no message --- controller/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/app.js b/controller/app.js index ecf56b6..5dd7164 100644 --- a/controller/app.js +++ b/controller/app.js @@ -13,7 +13,6 @@ function getCurrentStatus(cb) { if (err) { console.error(err); cb(err); } else { statuses = _.keyBy(statuses, 'key'); - log('CONTROL getCurrentStatus', statuses); if (statuses.isHoliday && statuses.isHoliday.value === 'true') { status = 'holiday'; @@ -31,6 +30,7 @@ function getCurrentStatus(cb) { status = 'manual'; } + clog('CONTROL getCurrentStatus', statuses); clog('CONTROL current status:', status); cb(status, data, statuses); From 6d3b1f99718c44b4e6d8b85f88a16248ebde17f4 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Mon, 6 Feb 2017 22:17:23 +0100 Subject: [PATCH 27/38] no message --- bin/display.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/display.js b/bin/display.js index f646d2e..f6ecf81 100644 --- a/bin/display.js +++ b/bin/display.js @@ -39,7 +39,7 @@ function reopenDisplay() { } function getHeaterOn(statuses) { - app.log('DISPLAY getHeaterOn', status); + app.log('DISPLAY getHeaterOn', statuses); return (statuses.heaterOn.value === 'true' ? (statuses.cooldownOn.value === 'true' ? '0' : '#') : ' '); } From bd7e697a52d8648886f4a364ab182974b52dd6e3 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 09:20:25 +0100 Subject: [PATCH 28/38] fixed display of cooldown in display --- bin/display.js | 4 ++-- controller/app.js | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bin/display.js b/bin/display.js index f6ecf81..21afe13 100644 --- a/bin/display.js +++ b/bin/display.js @@ -39,8 +39,8 @@ function reopenDisplay() { } function getHeaterOn(statuses) { - app.log('DISPLAY getHeaterOn', statuses); - return (statuses.heaterOn.value === 'true' ? (statuses.cooldownOn.value === 'true' ? '0' : '#') : ' '); + app.log('DISPLAY getHeaterOn', Object.keys(statuses)); + return (statuses.heaterOn.value === 'true' ? '#' : (statuses.cooldownOn.value === 'true' ? '0' : ' ')); } function updateDisplay() { diff --git a/controller/app.js b/controller/app.js index 5dd7164..d68a89e 100644 --- a/controller/app.js +++ b/controller/app.js @@ -30,7 +30,6 @@ function getCurrentStatus(cb) { status = 'manual'; } - clog('CONTROL getCurrentStatus', statuses); clog('CONTROL current status:', status); cb(status, data, statuses); From fb35cbb1c612993867b7ed55d38336cb3feb2bd9 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 10:01:05 +0100 Subject: [PATCH 29/38] Added error catch and status check cli --- bin/status.js | 42 ++++++++++++++++++++++++++++++++++++++++++ controller/app.js | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 bin/status.js diff --git a/bin/status.js b/bin/status.js new file mode 100644 index 0000000..7ae140f --- /dev/null +++ b/bin/status.js @@ -0,0 +1,42 @@ +var mongoose = require('mongoose'); +var moment = require('moment'); + +var app = require('../controller/app.js'); + +var Status = require('../models/status.js'); +var Zone = require('../models/zone.js'); + +require('dotenv').load(); +var cfg = require('../config.json'); + +// set the mongoose promise library to the nodejs one, required by mongoose now +mongoose.Promise = global.Promise; +// connect to the mongodb +mongoose.connect(process.env.MONGODB); +// output an error if the connection fails - kill the app +mongoose.connection.on('error', () => { + console.error('ERROR - MongoDB Connection Error. Please make sure that MongoDB is running.'); + process.exit(1); +}); + +app.getCurrentStatus((status, data, statuses) => { + console.log('######################'); + console.log('raspi-heater Status'); + console.log('######################'); + if (statuses.heaterOn) console.log('heaterOn -', statuses.heaterOn.value, '- since', moment(statuses.heaterOn.updatedAt).fromNow(true)); + if (statuses.targetHeaterOn) console.log('targetHeaterOn -', statuses.targetHeaterOn.value, '- since', moment(statuses.targetHeaterOn.updatedAt).fromNow(true)); + if (statuses.cooldownOn) console.log('cooldownOn -', statuses.cooldownOn.value, '- since', moment(statuses.cooldownOn.updatedAt).fromNow(true)); + if (statuses.heatingMode) console.log('heatingMode -', statuses.heatingMode.value, '- changed', moment(statuses.heatingMode.updatedAt).fromNow()); + console.log('######################'); + if (statuses.isHome) console.log('isHome - ', statuses.isHome.value); + if (statuses.isHoliday) console.log('isHoliday - ', statuses.isHoliday.value); + console.log('######################'); + + Zone.findOne({ number: cfg.zone }).exec((err, zone) => { + console.log('current temerature:', zone.currentTemperature.toFixed(1)); + console.log('target temerature:', zone.targetTemperature.toFixed(1)); + console.log('######################'); + + process.exit(1); + }); +}); \ No newline at end of file diff --git a/controller/app.js b/controller/app.js index d68a89e..ecad5c7 100644 --- a/controller/app.js +++ b/controller/app.js @@ -33,7 +33,7 @@ function getCurrentStatus(cb) { clog('CONTROL current status:', status); cb(status, data, statuses); - }); + }).catch((err) => { clog('APP caught error', err); }); } }); } From 1dbdc3a1b714b11a24a7d5b25f98f3afe3f6e92f Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 10:19:43 +0100 Subject: [PATCH 30/38] added table layout to status --- bin/status.js | 32 +++++++++++++++++++++----------- package.json | 3 ++- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/bin/status.js b/bin/status.js index 7ae140f..4f67792 100644 --- a/bin/status.js +++ b/bin/status.js @@ -1,5 +1,6 @@ var mongoose = require('mongoose'); var moment = require('moment'); +var table = require('text-table'); var app = require('../controller/app.js'); @@ -19,23 +20,32 @@ mongoose.connection.on('error', () => { process.exit(1); }); +var lines = []; + app.getCurrentStatus((status, data, statuses) => { + console.log(); + console.log('########################'); + console.log('#### raspi-heater Status'); + console.log('########################'); + if (statuses.heaterOn) lines.push([ 'heaterOn', statuses.heaterOn.value, 'since ' + moment(statuses.heaterOn.updatedAt).fromNow(true) ]); + if (statuses.targetHeaterOn) lines.push([ 'targetHeaterOn', statuses.targetHeaterOn.value, 'since ' + moment(statuses.targetHeaterOn.updatedAt).fromNow(true) ]); + if (statuses.cooldownOn) lines.push([ 'cooldownOn', statuses.cooldownOn.value, 'since ' + moment(statuses.cooldownOn.updatedAt).fromNow(true) ]); + if (statuses.heatingMode) lines.push([ 'heatingMode', statuses.heatingMode.value, 'changed ' + moment(statuses.heatingMode.updatedAt).fromNow() ]); + console.log(table(lines)); + lines = []; console.log('######################'); - console.log('raspi-heater Status'); - console.log('######################'); - if (statuses.heaterOn) console.log('heaterOn -', statuses.heaterOn.value, '- since', moment(statuses.heaterOn.updatedAt).fromNow(true)); - if (statuses.targetHeaterOn) console.log('targetHeaterOn -', statuses.targetHeaterOn.value, '- since', moment(statuses.targetHeaterOn.updatedAt).fromNow(true)); - if (statuses.cooldownOn) console.log('cooldownOn -', statuses.cooldownOn.value, '- since', moment(statuses.cooldownOn.updatedAt).fromNow(true)); - if (statuses.heatingMode) console.log('heatingMode -', statuses.heatingMode.value, '- changed', moment(statuses.heatingMode.updatedAt).fromNow()); - console.log('######################'); - if (statuses.isHome) console.log('isHome - ', statuses.isHome.value); - if (statuses.isHoliday) console.log('isHoliday - ', statuses.isHoliday.value); + if (statuses.isHome) lines.push([ 'isHome', statuses.isHome.value ]); + if (statuses.isHoliday) lines.push([ 'isHoliday', statuses.isHoliday.value ]); + console.log(table(lines)); + lines = []; console.log('######################'); Zone.findOne({ number: cfg.zone }).exec((err, zone) => { - console.log('current temerature:', zone.currentTemperature.toFixed(1)); - console.log('target temerature:', zone.targetTemperature.toFixed(1)); + lines.push([ 'current temerature:', zone.currentTemperature.toFixed(1) ]); + lines.push([ 'target temerature:', zone.targetTemperature.toFixed(1) ]); + console.log(table(lines)); console.log('######################'); + console.log(); process.exit(1); }); diff --git a/package.json b/package.json index 4430500..c7efcd1 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "moment": "^2.15.2", "mongoose": "^4.6.4", "node-persist": "^2.0.7", - "rpi-gpio": "^0.7.0" + "rpi-gpio": "^0.7.0", + "text-table": "^0.2.0" } } From 29f2fb65853e6538a7b084385aa21cb43ad3b0ba Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 10:22:06 +0100 Subject: [PATCH 31/38] Changed max cooldown duration to a better value --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 7929ed2..04cc5db 100644 --- a/config.json +++ b/config.json @@ -13,7 +13,7 @@ } }, "maxOnDuration": 30, - "maxCooldownDuration": 30, + "maxCooldownDuration": 20, "manualModeDuration": 120, "defaultTemperatures": { "home": 20, From 07caa56037b15f4be66d405be5a3df184ecaece1 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 11:07:36 +0100 Subject: [PATCH 32/38] Changed max cooldown duration to a better value - again --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 04cc5db..3156260 100644 --- a/config.json +++ b/config.json @@ -13,7 +13,7 @@ } }, "maxOnDuration": 30, - "maxCooldownDuration": 20, + "maxCooldownDuration": 15, "manualModeDuration": 120, "defaultTemperatures": { "home": 20, From d16394ff13145a33e151dd9a1a6d8b57eded2a92 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 12:48:16 +0100 Subject: [PATCH 33/38] Changed max cooldown duration to a better value - again again --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 3156260..e10eb49 100644 --- a/config.json +++ b/config.json @@ -13,7 +13,7 @@ } }, "maxOnDuration": 30, - "maxCooldownDuration": 15, + "maxCooldownDuration": 10, "manualModeDuration": 120, "defaultTemperatures": { "home": 20, From c7fa58a0bfbb2916264ff15a6efb3dd11efc1723 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 13:05:01 +0100 Subject: [PATCH 34/38] Changed max on duration to a better value --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index e10eb49..089fde8 100644 --- a/config.json +++ b/config.json @@ -12,7 +12,7 @@ "data": [5, 6, 17, 18] } }, - "maxOnDuration": 30, + "maxOnDuration": 15, "maxCooldownDuration": 10, "manualModeDuration": 120, "defaultTemperatures": { From 78ad1acc6b58fac4715375754426c8550ec1f68c Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 13:31:48 +0100 Subject: [PATCH 35/38] updated readme --- README.md | 36 ++++++++++++++++++++++++++++++++++++ config.json | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 99dba6b..d7b7f19 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Right now the software just activates a relay when the temperature gets too low. - NodeJS 6 (tested with 6.9.x) - MongoDB (tested with 3.4.x) - optional: +-- `forever` a node deamonizer, OR: +-- `pm2` and avanced process manager, which consumes quite a lot of ressouces (wasn't able to run it on a raspberry 1, you should use `forever` instead -- 1602 LCD display module ## Setup @@ -38,6 +40,11 @@ Right now the software just activates a relay when the temperature gets too low. $ node -v v6.9.1 ``` +- optionally install `pm2` or `forever` like this: + ```shell + $ sudo npm install pm2 -g + $ sudo npm install forever -g + ``` - checkout this repository, probably in `~/raspi-heater/` or `/etc/raspi-heater/` - run `npm install` in that directory - create environment config file: @@ -57,6 +64,35 @@ Right now the software just activates a relay when the temperature gets too low. - `bin/homekit.js` for homekit support - `bin/display.js` if you've connected a display - you can start all three with the pm2 process manager with the added process.json +- if you want to start the processes via a custom `/etc/init.d`-Script, you probably want to install `raspi-heater` into a public location like `/etc/raspi-heater` instead of one users home folder + - then you can add the processes to your custom `/etc/init.d`-script (assuming you want to use `forever`): + ```shell + cd /etc/raspi-heater && sudo forever bin/control.js + cd /etc/raspi-heater && sudo forever bin/homekit.js + cd /etc/raspi-heater && sudo forever bin/display.js + ``` + - if you want to use `pm2` you can simply add: + ```shell + cd /etc/raspi-heater && sudo pm2 start process.json + ``` +- to check the current status: + ```shell + $ node bin/status.js + ######################## + #### raspi-heater Status + ######################## + heaterOn true since 8 minutes + targetHeaterOn false since a few seconds + cooldownOn false since 25 minutes + heatingMode auto changed 8 minutes ago + ###################### + isHome true + isHoliday false + ###################### + current temerature: 19.9 + target temerature: 20.0 + ###################### + ``` ## Future features (maybe) - browser interface diff --git a/config.json b/config.json index 089fde8..e10eb49 100644 --- a/config.json +++ b/config.json @@ -12,7 +12,7 @@ "data": [5, 6, 17, 18] } }, - "maxOnDuration": 15, + "maxOnDuration": 30, "maxCooldownDuration": 10, "manualModeDuration": 120, "defaultTemperatures": { From 6966c9f27aa1b8d70fed5f9593bbfbc98adb8733 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 20:15:15 +0100 Subject: [PATCH 36/38] Added status zu cli status --- bin/status.js | 7 +++++-- controller/app.js | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bin/status.js b/bin/status.js index 4f67792..0a42d36 100644 --- a/bin/status.js +++ b/bin/status.js @@ -1,3 +1,4 @@ + var mongoose = require('mongoose'); var moment = require('moment'); var table = require('text-table'); @@ -8,6 +9,7 @@ var Status = require('../models/status.js'); var Zone = require('../models/zone.js'); require('dotenv').load(); +process.env.LOGGING = false; var cfg = require('../config.json'); // set the mongoose promise library to the nodejs one, required by mongoose now @@ -34,6 +36,7 @@ app.getCurrentStatus((status, data, statuses) => { console.log(table(lines)); lines = []; console.log('######################'); + lines.push([ 'status', status ]); if (statuses.isHome) lines.push([ 'isHome', statuses.isHome.value ]); if (statuses.isHoliday) lines.push([ 'isHoliday', statuses.isHoliday.value ]); console.log(table(lines)); @@ -41,8 +44,8 @@ app.getCurrentStatus((status, data, statuses) => { console.log('######################'); Zone.findOne({ number: cfg.zone }).exec((err, zone) => { - lines.push([ 'current temerature:', zone.currentTemperature.toFixed(1) ]); - lines.push([ 'target temerature:', zone.targetTemperature.toFixed(1) ]); + lines.push([ 'current temerature:', zone.currentTemperature.toFixed(2) + '°C' ]); + lines.push([ 'target temerature:', zone.targetTemperature.toFixed(2) + '°C' ]); console.log(table(lines)); console.log('######################'); console.log(); diff --git a/controller/app.js b/controller/app.js index ecad5c7..ccedb7f 100644 --- a/controller/app.js +++ b/controller/app.js @@ -57,7 +57,7 @@ function hex2String(input) { function clog() { var args = Array.prototype.slice.call(arguments); args.unshift('[' + moment().format('YYYY-MM-DD HH:mm:ss') + ']'); - console.log.apply(null, args); + if(process.env.LOGGING !== 'false') console.log.apply(null, args); } function updateCurrentTemperature(temp, cb) { From 1457e3f7675809ef224bdacdb7d2670cc972f9b2 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Tue, 7 Feb 2017 20:21:46 +0100 Subject: [PATCH 37/38] updated status with true on/off value --- bin/status.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bin/status.js b/bin/status.js index 0a42d36..118149b 100644 --- a/bin/status.js +++ b/bin/status.js @@ -22,6 +22,11 @@ mongoose.connection.on('error', () => { process.exit(1); }); +function onoff(val) { + if (typeof val === 'boolean') return val ? 'on' : 'off'; + else return val === 'true' ? 'on' : 'off'; +} + var lines = []; app.getCurrentStatus((status, data, statuses) => { @@ -29,9 +34,9 @@ app.getCurrentStatus((status, data, statuses) => { console.log('########################'); console.log('#### raspi-heater Status'); console.log('########################'); - if (statuses.heaterOn) lines.push([ 'heaterOn', statuses.heaterOn.value, 'since ' + moment(statuses.heaterOn.updatedAt).fromNow(true) ]); - if (statuses.targetHeaterOn) lines.push([ 'targetHeaterOn', statuses.targetHeaterOn.value, 'since ' + moment(statuses.targetHeaterOn.updatedAt).fromNow(true) ]); - if (statuses.cooldownOn) lines.push([ 'cooldownOn', statuses.cooldownOn.value, 'since ' + moment(statuses.cooldownOn.updatedAt).fromNow(true) ]); + if (statuses.heaterOn) lines.push([ 'heaterOn', onoff(statuses.heaterOn.value), 'since ' + moment(statuses.heaterOn.updatedAt).fromNow(true) ]); + if (statuses.targetHeaterOn) lines.push([ 'targetHeaterOn', onoff(statuses.targetHeaterOn.value), 'since ' + moment(statuses.targetHeaterOn.updatedAt).fromNow(true) ]); + if (statuses.cooldownOn) lines.push([ 'cooldownOn', onoff(statuses.cooldownOn.value), 'since ' + moment(statuses.cooldownOn.updatedAt).fromNow(true) ]); if (statuses.heatingMode) lines.push([ 'heatingMode', statuses.heatingMode.value, 'changed ' + moment(statuses.heatingMode.updatedAt).fromNow() ]); console.log(table(lines)); lines = []; From e31b28f5fc5e93ed662ac655bcc6d5d428352fb2 Mon Sep 17 00:00:00 2001 From: Julian Kern Date: Wed, 8 Feb 2017 20:16:35 +0100 Subject: [PATCH 38/38] 2.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c7efcd1..3b2f395 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "raspi-heater", - "version": "2.0.0", + "version": "2.1.0", "description": "", "main": "index.js", "scripts": {