Skip to content

Commit

Permalink
Initial deployment of positionURL #20
Browse files Browse the repository at this point in the history
  • Loading branch information
dxdc committed Jan 7, 2020
1 parent 285a73c commit 5d929b7
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 14 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Add the accessory in `config.json` in your home directory inside `.homebridge`.
"name": "Window",
"up_url": "http://1.2.3.4/window/up",
"down_url": "http://1.2.3.4/window/down",
"position_url": "http://1.2.3.4/position",
"stop_url": "http://1.2.3.4/window/stop",
"show_stop_button": false,
"use_same_url_for_stop": false,
Expand All @@ -48,11 +49,13 @@ Add the accessory in `config.json` in your home directory inside `.homebridge`.
}
```

You can omit any of the `up_url`, `down_url`, etc. if you don't want these to send a command.
You can omit any of the `up_url`, `down_url`, `stop_url`, `position_url` if you don't want these to send a command.

`position_url` is optional, but must report the current state of the blinds as an integer (0-100) and via GET. Headers or other methods specified in `http_method` are ignored.

If `use_same_url_for_stop` is set to true, it will send the previously sent url (either, `up_url` or `down_url`) again. This is for specific blind types that don't use a standard stop URL.
If `show_stop_button` is set to `true`, it will expose a HomeKit button for the stop command. Some logic has also been added to smoothly abort any currently running functions.

If `use_same_url_for_stop` is set to `true``, it will send the previously sent url (either, `up_url` or `down_url`) again. This is for specific blind types that don't use a standard stop URL.

You can omit `http_method`, it defaults to `POST`. Note that it can be configured to accept any number of additional arguments (headers, body, form, etc.) that [request](https://github.com/request/request) or [requestretry](https://github.com/FGRibreau/node-request-retry) supports.

Expand Down
7 changes: 6 additions & 1 deletion config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@
"up_url": {
"title": "Up URL",
"type": "string",
"description": "The URL to send the 'UP' command"
"description": "The URL to send the 'UP' command."
},
"down_url": {
"title": "Down URL",
"type": "string",
"description": "The URL to send the 'DOWN' command."
},
"position_url": {
"title": "Position URL",
"type": "string",
"description": "The URL to get the current blinds position (must be 0-100 response)."
},
"stop_url": {
"title": "Stop URL",
"type": "string",
Expand Down
55 changes: 44 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function BlindsHTTPAccessory(log, config) {
this.name = config.name;
this.upURL = config.up_url || false;
this.downURL = config.down_url || false;
this.positionURL = config.position_url || false;
this.stopURL = config.stop_url || false;
this.showStopButton = config.show_stop_button || false;
this.stopAtBoundaries = config.trigger_stop_at_boundaries || false;
Expand Down Expand Up @@ -56,14 +57,14 @@ function BlindsHTTPAccessory(log, config) {
// register the service and provide the functions
this.service = new Service.WindowCovering(this.name);

// the current position (0-100%)
// https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes.js#L493
// the current position (0-100)
// https://github.com/KhaosT/HAP-NodeJS/blob/master/src/lib/gen/HomeKit.ts#L712
this.service
.getCharacteristic(Characteristic.CurrentPosition)
.on('get', this.getCurrentPosition.bind(this));

// the target position (0-100%)
// https://github.com/KhaosT/HAP-NodeJS/blob/master/lib/gen/HomeKitTypes.js#L1564
// the target position (0-100)
// https://github.com/KhaosT/HAP-NodeJS/blob/master/src/lib/gen/HomeKit.ts#L2781
this.service
.getCharacteristic(Characteristic.TargetPosition)
.on('get', this.getTargetPosition.bind(this))
Expand All @@ -78,14 +79,44 @@ BlindsHTTPAccessory.prototype.getCurrentPosition = function(callback) {
if (this.verbose) {
this.log(`Requested CurrentPosition: ${this.lastPosition}%`);
}
callback(null, this.lastPosition);

if (this.positionURL) {
const options = {
url: this.positionURL,
maxAttempts: (this.maxHttpAttempts > 1) ? this.maxHttpAttempts : 1,
retryDelay: (this.retryDelay > 100) ? this.retryDelay : 100,
retryStrategy: request.RetryStrategies.HTTPOrNetworkError
};

request(options, function(err, response, body) {
if (!err && response && this.successCodes.includes(response.statusCode)) {
if (response.attempts > 1 || this.verbose) {
this.log.info(
`Request succeeded after ${response.attempts} / ${this.maxHttpAttempts} attempt${this.maxHttpAttempts > 1 ? 's' : ''}`
);
}

const pos = parseInt(body, 10);
if (pos >= 0 && pos <= 100) {
this.lastPosition = pos;
} else {
this.log.warn(`Invalid position response received (should be 0-100): ${pos}`);
}
} else {
this.log.error(`Error sending CurrentPosition request (HTTP status code ${response ? response.statusCode : 'not defined'}): ${err}`);
this.log.error(`${response ? response.attempts : this.maxHttpAttempts} / ${this.maxHttpAttempts} attempt${this.maxHttpAttempts > 1 ? 's' : ''} failed`);
}
}.bind(this));
}

return callback(null, this.lastPosition);
};

BlindsHTTPAccessory.prototype.getTargetPosition = function(callback) {
if (this.verbose) {
this.log(`Requested TargetPosition: ${this.currentTargetPosition}%`);
}
callback(null, this.currentTargetPosition);
return callback(null, this.currentTargetPosition);
};

BlindsHTTPAccessory.prototype.setTargetPosition = function(pos, callback) {
Expand Down Expand Up @@ -153,6 +184,7 @@ BlindsHTTPAccessory.prototype.setTargetPosition = function(pos, callback) {
self.currentTargetPosition = self.lastPosition;
}
if (self.lastPosition < self.currentTargetPosition) {
// TODO: should periodic polling of self.getCurrentPosition be performed if self.positionURL is set?
self.lastPosition += moveUp ? 1 : -1;
} else {
self.log(
Expand All @@ -162,6 +194,8 @@ BlindsHTTPAccessory.prototype.setTargetPosition = function(pos, callback) {
self.service
.getCharacteristic(Characteristic.CurrentPosition)
.updateValue(self.lastPosition);
// In case of overshoot
self.currentTargetPosition = self.lastPosition;
self.service
.getCharacteristic(Characteristic.PositionState)
.updateValue(Characteristic.PositionState.STOPPED);
Expand All @@ -171,7 +205,7 @@ BlindsHTTPAccessory.prototype.setTargetPosition = function(pos, callback) {
}, Math.max(this.responseLag, 0));
}.bind(this));

callback(null);
return callback(null);
};

BlindsHTTPAccessory.prototype.sendStopRequest = function(targetService, on, callback) {
Expand Down Expand Up @@ -208,8 +242,7 @@ BlindsHTTPAccessory.prototype.sendStopRequest = function(targetService, on, call

BlindsHTTPAccessory.prototype.httpRequest = function(url, methods, callback) {
if (!url) {
callback(null);
return;
return callback(null);
}

// backward compatibility
Expand All @@ -232,14 +265,14 @@ BlindsHTTPAccessory.prototype.httpRequest = function(url, methods, callback) {
`Request succeeded after ${response.attempts} / ${this.maxHttpAttempts} attempt${this.maxHttpAttempts > 1 ? 's' : ''}`
);
}
callback(null);
return callback(null);
} else {
this.log.error(
`Error sending request (HTTP status code ${response ? response.statusCode : 'not defined'}): ${err}`
);
this.log.error(`${response ? response.attempts : this.maxHttpAttempts} / ${this.maxHttpAttempts} attempt${this.maxHttpAttempts > 1 ? 's' : ''} failed`);
this.log.error(`Body: ${body}`);
callback(err);
return callback(err);
}
}.bind(this));
};
Expand Down

0 comments on commit 5d929b7

Please sign in to comment.