Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
# Conflicts:
#	package.json
  • Loading branch information
Keyes committed Feb 8, 2017
2 parents 74cd88f + e31b28f commit b67bcb8
Show file tree
Hide file tree
Showing 15 changed files with 566 additions and 223 deletions.
76 changes: 67 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,38 @@ 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:
-- `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
- 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
```
- 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:
- create new file `.env`
- add the mongodb location like `MONGODB='localhost/heater'`
Expand All @@ -29,14 +55,46 @@ 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
- 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
## Future features (maybe)
- browser interface
- server based, with own database and replication
- real support for multiple instances in the same home network
54 changes: 26 additions & 28 deletions accessories/Thermostat_accessory.js
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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";
Expand All @@ -33,27 +35,23 @@ thermostat
.addService(Service.Thermostat, 'Thermostat')
.getCharacteristic(Characteristic.CurrentHeatingCoolingState)
.on('get', function(callback) {
Status.findOne({ key: 'heaterOn' }).exec((err, data) => {
heater.get((status) => {
// 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?
switch(data.value) {
case 'false':
// shows if the heater is on or off - no cooling for now possible
switch(status.heater) {
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);
app.log('get: CurrentHeatingCoolingState', stateName, status.heater);
callback(null, state);
});
});
Expand All @@ -69,20 +67,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);
})
});
Expand All @@ -92,18 +87,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();
})
Expand Down
24 changes: 12 additions & 12 deletions control.js → bin/control.js
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -63,14 +59,18 @@ 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) => {
checkTemperatures();
_set();
Status.findOneAndUpdate({ key: 'heatingMode' }, { value: 'auto' }, { new: true, upsert: true }).exec((err, data) => {
Status.findOneAndUpdate({ key: 'heaterOn' }, { value: false }, { new: true, upsert: true }).exec((err, data) => {
checkTemperatures();
_set();
heater.checkHeaterStatus();
});
});

// interval checks
setInterval(checkTemperatures, 1000 * 30); // every 30s
setInterval(_set, 1000 * 60); // every 1min
setInterval(heater.checkHeaterStatus, 1000 * 60); // every 1min

/////////////
// on kill //
Expand Down
Loading

0 comments on commit b67bcb8

Please sign in to comment.