homebridge-http-garagedoorcontroller
Advanced tools
Comparing version 1.1.2 to 1.1.3
142
index.js
var locks = require("locks"); | ||
var mutex = locks.createMutex(); | ||
var checkStateMutex = locks.createMutex(); | ||
var httpRequestMutex = locks.createMutex(); | ||
@@ -53,2 +54,8 @@ var request = require("request"); | ||
this.httpRequestTimeoutMilliseconds = parseInt(getConfigValue(config, "httpRequestTimeoutMilliseconds", 10000)); | ||
if (!this.httpRequestTimeoutMilliseconds || isNaN(this.httpRequestTimeoutMilliseconds)) { | ||
this.log.error("ERROR - Missing or invalid configuration field 'httpRequestTimeoutMilliseconds'"); | ||
configurationValid = false; | ||
} | ||
this.httpHeaderName = getConfigValue(config, "httpHeaderName", null); | ||
@@ -196,4 +203,4 @@ if (this.httpHeaderName) { | ||
} | ||
if (this._hasDoorState() || this._hasLightState()) { | ||
if (this._hasStates()) { | ||
this._checkStates(true); | ||
@@ -206,13 +213,9 @@ } | ||
var that = this; | ||
this._determineDoorState(function(error, state) { | ||
if (error) { | ||
var error = new Error("ERROR in getDoorCurrentState() - " + error.message); | ||
that.log.error(error.message); | ||
callback(error); | ||
return; | ||
} | ||
var error = null; | ||
if (this._hasStates() && ((Date.now() - this._doorCurrentStateSetAt) >= (this.httpStatusPollMilliseconds * 3))) { | ||
error = new Error("The Garage Door current state is unknown (last known: " + this._doorStateToString(this._doorCurrentState) + "), it hasn't been reported since " + (new Date(this._doorCurrentStateSetAt)).toString()); | ||
this.log.error(error.message); | ||
} | ||
callback(null, state); | ||
}); | ||
callback(error, this._doorCurrentState); | ||
}, | ||
@@ -233,3 +236,3 @@ | ||
this.log.info("Received request to operate the Garage Door: %s (currently %s, target %s)", this._doorStateToString(newState), this._doorStateToString(this._doorCurrentState), this._doorStateToString(this._doorTargetState)); | ||
this.log.info("Received request to operate the Garage Door: %s (currently: %s, target: %s)", this._doorStateToString(newState), this._doorStateToString(this._doorCurrentState), this._doorStateToString(this._doorTargetState)); | ||
@@ -263,13 +266,9 @@ var that = this; | ||
var that = this; | ||
this._determineLightState(function(error, state) { | ||
if (error) { | ||
var error = new Error("ERROR in getLightCurrentState() - " + error.message); | ||
that.log.error(error.message); | ||
callback(error); | ||
return; | ||
} | ||
var error = null; | ||
if (this._hasStates() && ((Date.now() - this._lightCurrentStateSetAt) >= (this.httpStatusPollMilliseconds * 3))) { | ||
error = new Error("The Garage Light current state is unknown (last known: " + this._lightStateToString(this._lightCurrentState) + "), it hasn't been reported since " + (new Date(this._lightCurrentStateSetAt)).toString()); | ||
this.log.error(error.message); | ||
} | ||
callback(null, state); | ||
}); | ||
callback(error, this._lightCurrentState); | ||
}, | ||
@@ -285,3 +284,3 @@ | ||
this.log.info("Received request to operate the Garage Light: %s (currently %s)", (newState ? "ON" : "OFF"), this._lightCurrentState); | ||
this.log.info("Received request to operate the Garage Light: %s (currently: %s)", this._lightStateToString(newState), this._lightStateToString(this._lightCurrentState)); | ||
@@ -308,12 +307,16 @@ var that = this; | ||
if (this._hasDoorState()) { | ||
this._determineDoorState(function(error, doorState, lightState) { | ||
if (error) { | ||
that.log.error("ERROR in _checkStates() - " + error.message); | ||
} else { | ||
that._setDoorCurrentState(doorState, initial); | ||
checkStateMutex.lock(function() { | ||
that._determineDoorState(function(error, doorState, lightState) { | ||
if (error) { | ||
that.log.error("ERROR in _checkStates() - " + error.message); | ||
} else { | ||
that._setDoorCurrentState(doorState, initial); | ||
if (lightState != null) { | ||
that._setLightCurrentState(lightState, initial); | ||
if (lightState != null) { | ||
that._setLightCurrentState(lightState, initial); | ||
} | ||
} | ||
} | ||
checkStateMutex.unlock(); | ||
}); | ||
}); | ||
@@ -324,12 +327,19 @@ } | ||
if (this._hasLightState() && (this.doorStateUrl != this.lightStateUrl)) { | ||
this._determineLightState(function(error, lightState) { | ||
if (error) { | ||
that.log.error("ERROR in _checkStates() - " + error.message); | ||
} else { | ||
that._setLightCurrentState(lightState, initial); | ||
} | ||
checkStateMutex.lock(function() { | ||
that._determineLightState(function(error, lightState) { | ||
if (error) { | ||
that.log.error("ERROR in _checkStates() - " + error.message); | ||
} else { | ||
that._setLightCurrentState(lightState, initial); | ||
} | ||
checkStateMutex.unlock(); | ||
}); | ||
}); | ||
} | ||
setTimeout(that._checkStates.bind(that), that.httpStatusPollMilliseconds); | ||
checkStateMutex.lock(function() { | ||
setTimeout(that._checkStates.bind(that), that.httpStatusPollMilliseconds); | ||
checkStateMutex.unlock(); | ||
}); | ||
}, | ||
@@ -392,3 +402,4 @@ | ||
this.log.debug("Entered _setDoorCurrentState(state: %s, initial: %s, isFromTargetState: %s)", this._doorStateToString(state), (initial || false), (isFromTargetState || false)); | ||
this._doorCurrentStateSetAt = Date.now(); | ||
if ((this._doorCurrentState == state) && (!initial)) { | ||
@@ -414,2 +425,3 @@ return; | ||
this.log.debug("Entered _setDoorTargetState(state: %s, initial: %s, isFromCurrentState: %s)", this._doorStateToString(state), (initial || false), (isFromCurrentState || false)); | ||
this._doorTargetStateSetAt = Date.now(); | ||
@@ -436,2 +448,3 @@ if ((this._doorTargetState == state) && (!initial)) { | ||
this.log.debug("Entered _setLightCurrentState(state: %s, initial: %s)", state, (initial || false)); | ||
this._lightCurrentStateSetAt = Date.now(); | ||
@@ -442,3 +455,3 @@ if ((this._lightCurrentState == state) && (!initial)) { | ||
this.log.info("%s Garage Light state is: %s", (initial ? "INITIAL" : "NEW"), state); | ||
this.log.info("%s Garage Light state is: %s", (initial ? "INITIAL" : "NEW"), this._lightStateToString(state)); | ||
@@ -466,2 +479,10 @@ this._lightCurrentState = state; | ||
_lightStateToString: function(lightState) { | ||
if (lightState) { | ||
return "ON"; | ||
} else { | ||
return "OFF"; | ||
} | ||
}, | ||
_doorStateToState: function(doorState) { | ||
@@ -487,2 +508,6 @@ switch (doorState.toUpperCase()) { | ||
_hasStates: function() { | ||
return (this._hasDoorState() || this._hasLightState()); | ||
}, | ||
_hasDoorState: function() { | ||
@@ -497,6 +522,6 @@ return (this.doorStateUrl != null); | ||
_httpRequest: function(method, url, expectedJsonField, expectedJsonFieldValue, done) { | ||
mutex.lock(function() { | ||
httpRequestMutex.lock(function() { | ||
var options = { | ||
timeout: 5000, | ||
method: method, | ||
timeout: this.httpRequestTimeoutMilliseconds, | ||
url: "http://" + this.httpHost + ((this.httpPort == 80) ? "" : ":" + this.httpPort) + url | ||
@@ -525,16 +550,17 @@ }; | ||
error = new Error("The status code of the HTTP response was unexpected: " + response.statusCode); | ||
} | ||
} else { | ||
try { | ||
json = JSON.parse(body); | ||
} catch (jsonError) { | ||
json = null; | ||
that.log(body); | ||
error = new Error("The JSON body of the HTTP response could not be parsed: " + jsonError.message); | ||
} | ||
try { | ||
json = JSON.parse(body); | ||
} catch (jsonError) { | ||
json = null; | ||
error = new Error("The JSON body of the HTTP response could not be parsed: " + jsonError.message); | ||
} | ||
if ((json != null) && (expectedJsonField != null)) { | ||
if (!json.hasOwnProperty(expectedJsonField)) { | ||
error = new Error("The JSON body of the HTTP response does not contain the field: " + expectedJsonField); | ||
} else if ((expectedJsonFieldValue != null) && (json[expectedJsonField] != expectedJsonFieldValue)) { | ||
error = new Error("The JSON field value of the HTTP response was unexpected: " + success); | ||
if ((json != null) && (expectedJsonField != null)) { | ||
if (!json.hasOwnProperty(expectedJsonField)) { | ||
error = new Error("The JSON body of the HTTP response does not contain the field: " + expectedJsonField); | ||
} else if ((expectedJsonFieldValue != null) && (json[expectedJsonField] != expectedJsonFieldValue)) { | ||
error = new Error("The JSON field value of the HTTP response was unexpected: " + success); | ||
} | ||
} | ||
@@ -544,3 +570,3 @@ } | ||
mutex.unlock(); | ||
httpRequestMutex.unlock(); | ||
done(error, response, ((json != null) ? json : body)); | ||
@@ -547,0 +573,0 @@ }); |
{ | ||
"name": "homebridge-http-garagedoorcontroller", | ||
"version": "1.1.2", | ||
"version": "1.1.3", | ||
"description": "Exposes a garage door accessory and garage light to HomeBridge that operates via HTTP requests", | ||
@@ -35,2 +35,2 @@ "main": "index.js", | ||
} | ||
} | ||
} |
# homebridge-http-garagedoorcontroller | ||
An HTTP based Garage Door/Light plugin for [HomeBridge](https://github.com/nfarina/homebridge) | ||
![Demo](https://github.com/washcroft/HttpGarageDoorController/raw/master/reference/demo.gif "Demo") | ||
# About | ||
@@ -27,2 +29,3 @@ This HomeBridge (HomeKit) plugin exposes a garage door accessory and garage light, allowing control of your garage door and/or garage light via HTTP requests. State reporting is also supported but it is optional. | ||
"httpStatusPollMilliseconds": 4000, | ||
"httpRequestTimeoutMilliseconds": 10000, | ||
"httpHeaderName": "X-API-Key", | ||
@@ -54,2 +57,3 @@ "httpHeaderValue": "MyAPIKey", | ||
* httpStatusPollMilliseconds - The number of milliseconds to wait between requests when polling for updated statuses | ||
* httpRequestTimeoutMilliseconds - The number of milliseconds to wait for an HTTP request to complete before timing out | ||
* httpHeaderName - The name of an HTTP header to be included with every request (optional) | ||
@@ -56,0 +60,0 @@ * httpHeaderValue - The value of the above HTTP header to be included with every request (optional) |
23683
449
75