Skip to content

Commit c30f5d5

Browse files
authored
Merge pull request #13 from rsporny/issue-12-save-state-after-power-loss
Issue 12 save state after power loss
2 parents f43b8c1 + 40b9f94 commit c30f5d5

4 files changed

Lines changed: 55 additions & 17 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Sample accessory:
2222
"pinDown": 11,
2323
"durationUp": 13000,
2424
"durationDown": 13000,
25+
"durationOffset": 1000,
2526
"pinClosed": 17,
2627
"pinOpen": 18,
2728
"activeLow": false,
@@ -38,6 +39,7 @@ Fields:
3839
- `pinDown` pin for moving down
3940
- `durationUp` milliseconds to open blinds completely
4041
- `durationDown` milliseconds to close blinds completely
42+
- `durationOffset` [optional, default: *0*] milliseconds added to durationUp and durationDown to make sure that blinds are completely open or closed
4143
- `pinClosed` [optional] pin connected to reed switch which is active when blind is closed, see *reedActiveLow*
4244
- `pinOpen` [optional] pin connected to reed switch which is active when blind is open, see *reedActiveLow*
4345
- `activeLow` [optional, default: *true*] true: relay activated by low state (0), false: relay activated by high state (1), affects *pinUp*, *pinDown*

config-sample.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"pinDown": 3,
2323
"durationUp": 27000,
2424
"durationDown": 25000,
25+
"durationOffset": 1000,
2526
"pinClosed": 17,
2627
"pinOpen": 18,
2728
"activeLow": false,

index.js

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var _ = require('underscore');
22
var rpio = require('rpio');
3-
var Service, Characteristic;
3+
var Service, Characteristic, HomebridgeAPI;
44

55
const STATE_DECREASING = 0;
66
const STATE_INCREASING = 1;
@@ -9,27 +9,38 @@ const STATE_STOPPED = 2;
99
module.exports = function(homebridge) {
1010
Service = homebridge.hap.Service;
1111
Characteristic = homebridge.hap.Characteristic;
12-
12+
HomebridgeAPI = homebridge;
1313
homebridge.registerAccessory('homebridge-gpio-blinds', 'Blinds', BlindsAccessory);
1414
}
1515

1616
function BlindsAccessory(log, config) {
17-
_.defaults(config, {activeLow: true, reedSwitchActiveLow: true});
17+
_.defaults(config, {durationOffset: 0, activeLow: true, reedSwitchActiveLow: true});
1818

1919
this.log = log;
2020
this.name = config['name'];
2121
this.pinUp = config['pinUp'];
2222
this.pinDown = config['pinDown'];
2323
this.durationUp = config['durationUp'];
2424
this.durationDown = config['durationDown'];
25+
this.durationOffset = config['durationOffset'];
2526
this.pinClosed = config['pinClosed'];
2627
this.pinOpen = config['pinOpen'];
2728
this.initialState = config['activeLow'] ? rpio.HIGH : rpio.LOW;
2829
this.activeState = config['activeLow'] ? rpio.LOW : rpio.HIGH;
2930
this.reedSwitchActiveState = config['reedSwitchActiveLow'] ? rpio.LOW : rpio.HIGH;
3031

31-
this.currentPosition = 0; // down by default
32-
this.targetPosition = 0; // down by default
32+
this.cacheDirectory = HomebridgeAPI.user.persistPath();
33+
this.storage = require('node-persist');
34+
this.storage.initSync({dir:this.cacheDirectory, forgiveParseErrors: true});
35+
36+
var cachedCurrentPosition = this.storage.getItemSync(this.name);
37+
if((cachedCurrentPosition === undefined) || (cachedCurrentPosition === false)) {
38+
this.currentPosition = 0; // down by default
39+
} else {
40+
this.currentPosition = cachedCurrentPosition;
41+
}
42+
43+
this.targetPosition = this.currentPosition;
3344
this.positionState = STATE_STOPPED; // stopped by default
3445

3546
this.service = new Service.WindowCovering(this.name);
@@ -38,7 +49,7 @@ function BlindsAccessory(log, config) {
3849
this.infoService
3950
.setCharacteristic(Characteristic.Manufacturer, 'Radoslaw Sporny')
4051
.setCharacteristic(Characteristic.Model, 'RaspberryPi GPIO Blinds')
41-
.setCharacteristic(Characteristic.SerialNumber, 'Version 1.1.1');
52+
.setCharacteristic(Characteristic.SerialNumber, 'Version 1.1.2');
4253

4354
this.finalBlindsStateTimeout;
4455
this.togglePinTimeout;
@@ -80,18 +91,24 @@ BlindsAccessory.prototype.getCurrentPosition = function(callback) {
8091
}
8192

8293
BlindsAccessory.prototype.getTargetPosition = function(callback) {
83-
if (this.closedAndOutOfSync()) {
94+
var updatedPosition;
95+
if (this.openCloseSensorMalfunction()) {
96+
this.log("Open and close reed switches are active, setting to 50");
97+
updatedPosition = 50;
98+
} else if (this.closedAndOutOfSync()) {
8499
this.log("Current position is out of sync, setting to 0");
85-
this.currentPosition = 0;
86-
this.targetPosition = 0;
100+
updatedPosition = 0;
87101
} else if (this.openAndOutOfSync()) {
88102
this.log("Current position is out of sync, setting to 100");
89-
this.currentPosition = 100;
90-
this.targetPosition = 100;
103+
updatedPosition = 100;
91104
} else if (this.partiallyOpenAndOutOfSync()) {
92105
this.log("Current position is out of sync, setting to 50");
93-
this.currentPosition = 50;
94-
this.targetPosition = 50;
106+
updatedPosition = 50;
107+
}
108+
if (updatedPosition !== undefined) {
109+
this.currentPosition = updatedPosition;
110+
this.targetPosition = updatedPosition;
111+
this.storage.setItemSync(this.name, updatedPosition);
95112
}
96113
this.log("Target position: %s", this.targetPosition);
97114
callback(null, this.targetPosition);
@@ -122,10 +139,10 @@ BlindsAccessory.prototype.setTargetPosition = function(position, callback) {
122139

123140
if (moveUp) {
124141
duration = Math.round((this.targetPosition - this.currentPosition) / 100 * this.durationUp);
125-
this.currentPositionInterval = setInterval(function(){ this.currentPosition++; }.bind(this), this.intervalUp);
142+
this.currentPositionInterval = setInterval(this.setCurrentPosition.bind(this, moveUp), this.intervalUp);
126143
} else {
127144
duration = Math.round((this.currentPosition - this.targetPosition) / 100 * this.durationDown);
128-
this.currentPositionInterval = setInterval(function(){ this.currentPosition--; }.bind(this), this.intervalDown);
145+
this.currentPositionInterval = setInterval(this.setCurrentPosition.bind(this, moveUp), this.intervalDown);
129146
}
130147

131148
this.log((moveUp ? 'Moving up' : 'Moving down') + ". Duration: %s ms.", duration);
@@ -142,9 +159,10 @@ BlindsAccessory.prototype.setTargetPosition = function(position, callback) {
142159

143160
BlindsAccessory.prototype.togglePin = function(pin, duration) {
144161
if (rpio.read(pin) != this.activeState) rpio.write(pin, this.activeState);
162+
if (this.durationOffset && (this.targetPosition == 0 || this.targetPosition == 100)) this.duration += this.durationOffset;
145163
this.togglePinTimeout = setTimeout(function() {
146164
rpio.write(pin, this.initialState);
147-
}.bind(this), duration);
165+
}.bind(this), parseInt(duration));
148166
}
149167

150168
BlindsAccessory.prototype.setFinalBlindsState = function() {
@@ -153,9 +171,19 @@ BlindsAccessory.prototype.setFinalBlindsState = function() {
153171
this.service.setCharacteristic(Characteristic.PositionState, STATE_STOPPED);
154172
this.service.setCharacteristic(Characteristic.CurrentPosition, this.targetPosition);
155173
this.currentPosition = this.targetPosition;
174+
this.storage.setItemSync(this.name, this.currentPosition);
156175
this.log("Successfully moved to target position: %s", this.targetPosition);
157176
}
158177

178+
BlindsAccessory.prototype.setCurrentPosition = function(moveUp) {
179+
if (moveUp) {
180+
this.currentPosition++;
181+
} else {
182+
this.currentPosition--;
183+
}
184+
this.storage.setItemSync(this.name, this.currentPosition);
185+
}
186+
159187
BlindsAccessory.prototype.closedAndOutOfSync = function() {
160188
return this.currentPosition != 0 && this.pinClosed && (rpio.read(this.pinClosed) == this.reedSwitchActiveState);
161189
}
@@ -169,6 +197,12 @@ BlindsAccessory.prototype.partiallyOpenAndOutOfSync = function() {
169197
(this.currentPosition == 100 && this.pinOpen && (rpio.read(this.pinOpen) != this.reedSwitchActiveState));
170198
}
171199

200+
BlindsAccessory.prototype.openCloseSensorMalfunction = function() {
201+
return (this.pinClosed && this.pinOpen &&
202+
(rpio.read(this.pinClosed) == this.reedSwitchActiveState) &&
203+
(rpio.read(this.pinOpen) == this.reedSwitchActiveState));
204+
}
205+
172206
BlindsAccessory.prototype.oppositeDirection = function(moveUp) {
173207
return (this.positionState == STATE_INCREASING && !moveUp) || (this.positionState == STATE_DECREASING && moveUp);
174208
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
},
1919
"dependencies": {
2020
"rpio": "^0.9.12",
21-
"underscore": "^1.8.3"
21+
"underscore": "^1.8.3",
22+
"node-persist": "^2.1.0"
2223
},
2324
"engines": {
2425
"homebridge": ">=0.4.6",

0 commit comments

Comments
 (0)