diff --git a/README.md b/README.md index 0a947bc..5b4cd9e 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Add the accessory in `config.json` in your home directory inside `.homebridge`. "up_url": "http://1.2.3.4/window/up", "down_url": "http://1.2.3.4/window/down", "stop_url": "http://1.2.3.4/window/stop", - "http_method": { + "http_options": { "method": "GET" }, "success_codes": [ 200 ], @@ -57,7 +57,7 @@ Add the accessory in `config.json` in your home directory inside `.homebridge`. "position_url": "http://1.2.3.4/window/position", "position_jsonata": "ShutterPosition1", "stop_url": "http://1.2.3.4/window/stop", - "http_method": { + "http_options": { "body": "{}", "headers": { "API-Token": "aaabbbcccddd" @@ -92,7 +92,7 @@ Add the accessory in `config.json` in your home directory inside `.homebridge`. You can omit any of the `up_url`, `down_url`, `stop_url` if you don't want these to send a command, and `position_url` if you want the blinds to only emulate the position (using a timer). -You can omit `http_method`, it defaults to `POST`. Note that it can also 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. +You can omit `http_options`, it defaults to `{ method: 'POST' }`. Note that it can also 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. `success_codes` allows you to define which HTTP response codes indicate a successful server response. If omitted, it defaults to 200. @@ -118,7 +118,7 @@ Alternatively, for more advanced configuration of URL's, each URL can be set to }, ``` -If an object is used for the configuration, `http_method`, `max_http_attempts`, and `retry_delay` are ignored, and these values must be instead specified directly inside the object. `success_codes` are still used globally. +If an object is used for the configuration, `http_options`, `max_http_attempts`, and `retry_delay` are ignored, and these values must be instead specified directly inside the object. `success_codes` are still used globally. If `time` is set to true, a full request timing profile (wait, dns, tcp, firstByte, download, total) will be logged (see [timingPhases](https://github.com/request/request/blob/master/README.md)). @@ -159,7 +159,7 @@ This implementation does take into account whether or not the blinds are moving, `position_url` must report the current state of the blinds as an integer (0-100) in either plain text or JSON format, e.g. `{"current_position": 40}`. If JSON is used, JSON keys are filtered to look for a **single** numeric response, but the JSON handling is not very robust and will cause unexpected results if multiple numeric keys are present. -`position_url` defaults to a simple GET request, ignoring headers or other methods specified in `http_method`. If more robust handling is required, `position_url` can be defined as a complete `request`/`requestretry` object as specified in `Advanced URL` above. +`position_url` defaults to a simple GET request, ignoring headers or other methods specified in `http_options`. If more robust handling is required, `position_url` can be defined as a complete `request`/`requestretry` object as specified in `Advanced URL` above. If more robust handling of `position_url` responses in JSON format is needed, `position_jsonata` can be defined. This allows a [JSONata](https://jsonata.org/) expression to be set to parse the result. For example, considering the following JSON response: @@ -271,7 +271,7 @@ These values can be obtained from the Bond app, under `Device settings` for any "up_url": "http://1.2.3.4/v2/devices//actions/Open", "down_url": "http://1.2.3.4/v2/devices//actions/Close", "stop_url": "http://1.2.3.4/v2/devices//actions/Hold", - "http_method": { + "http_options": { "body": "{}", "headers": { "BOND-Token": "" @@ -320,7 +320,7 @@ Sample `config.json`, noting that you need to replace `1.2.3.4` with your Tasmot "up_url": "http://1.2.3.4/cm?cmnd=ShutterPosition%20%%POS%%", "down_url": "http://1.2.3.4/cm?cmnd=ShutterPosition%20%%POS%%", "stop_url": "http://1.2.3.4/cm?cmnd=Power3%20ON", - "http_method": { + "http_options": { "method": "GET" }, "success_codes": [ 200 ], diff --git a/config.schema.json b/config.schema.json index 2661ec5..04bf9be 100644 --- a/config.schema.json +++ b/config.schema.json @@ -1,125 +1,389 @@ { - "pluginAlias": "BlindsHTTP", - "pluginType": "accessory", - "singular": false, - "schema": { - "type": "object", - "properties": { - "name": { - "title": "Name", - "type": "string", - "required": true - }, - "up_url": { - "title": "Up URL", - "type": "string", - "description": "The URL to send the 'UP' command.", - "placeholder": "http://1.2.3.4/window/up" - }, - "down_url": { - "title": "Down URL", - "type": "string", - "description": "The URL to send the 'DOWN' command.", - "placeholder": "http://1.2.3.4/window/down" - }, - "position_url": { - "title": "Position URL", - "type": "string", - "description": "The URL to get the current blinds position (must be 0-100 response).", - "placeholder": "http://1.2.3.4/window/position" - }, - "position_jsonata": { - "title": "Jsonata for Position URL", - "type": "string", - "description": "Jsonata expression to parse position URL response.", - "placeholder": "ShutterPosition1" - }, - "stop_url": { - "title": "Stop URL", - "type": "string", - "description": "The URL to send the 'STOP' command.", - "placeholder": "http://1.2.3.4/window/stop" - }, - "use_same_url_for_stop": { - "title": "Use Same URL for Stop", - "type": "boolean", - "default": false, - "description": "Instead of using the 'STOP' command, just re-send the 'UP' or 'DOWN' command." - }, - "show_stop_button": { - "title": "Show Stop Button", - "type": "boolean", - "default": false, - "description": "Exposes a 'STOP' button within HomeKit." - }, - "show_toggle_button": { - "title": "Show Toggle Button", - "type": "boolean", - "default": false, - "description": "Exposes a 'TOGGLE' button within HomeKit." - }, - "http_method": { - "title": "HTTP Parameters", - "type": "object", - "default": { - "method": "POST" - }, - "description": "JSON object with custom request HTTP parameters/headers for up/down/stop URL's." - }, - "motion_time": { - "title": "Motion Time", - "type": "number", - "default": 10000, - "description": "Time (in ms) for blinds to move completely up or down." - }, - "motion_up_time": { - "title": "Motion Upward Time", - "type": "number", - "default": 10000, - "description": "Time (in ms) for blinds to move completely from down to up." - }, - "motion_down_time": { - "title": "Motion Downward Time", - "type": "number", - "default": 10000, - "description": "Time (in ms) for blinds to move completely from up to down." - }, - "response_lag": { - "title": "Response Lag", - "type": "number", - "default": 0, - "description": "Time (in ms) for device to respond after HTTP request. Optional parameter to improve time calculation when setting intermediate values." - }, - "trigger_stop_at_boundaries": { - "title": "Send Stop at Boundaries", - "type": "boolean", - "default": false, - "description": "Send STOP command at boundaries (0/100) - used if blinds do not stop automatically." - }, - "success_codes": { - "title": "HTTP success codes", - "type": "array", - "default": [200], - "description": "Array of response codes that are interpreted as a successful reply." - }, - "max_http_attempts": { - "title": "Maximum HTTP attempts", - "type": "number", - "default": 5, - "description": "Maximum number of HTTP attempts (on error or timeout) per request." - }, - "retry_delay": { - "title": "Maximum HTTP attempts", - "type": "number", - "default": 2000, - "description": "Time (in ms) between HTTP attempts." - }, - "verbose": { - "title": "Verbose logging", - "type": "boolean", - "default": false, - "description": "Verbose logging - show getTargetPosition / getTargetState / getCurrentPosition requests." - } - } - } + "pluginAlias": "BlindsHTTP", + "pluginType": "accessory", + "singular": false, + "headerDisplay": "Find the full documentation and report bugs [here](https://github.com/dxdc/homebridge-blinds).", + "schema": { + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string", + "required": true + }, + "up_url": { + "title": "Up URL", + "type": "string", + "description": "URL to send the UP command", + "placeholder": "http://1.2.3.4/window/up", + "format": "uri" + }, + "motion_up_time": { + "title": "Motion Upward Time", + "type": "integer", + "placeholder": 10000, + "minimum": 1, + "description": "Overrides default motion time (in milliseconds)" + }, + "down_url": { + "title": "Down URL", + "type": "string", + "description": "URL to send the DOWN command", + "placeholder": "http://1.2.3.4/window/down", + "format": "uri" + }, + "motion_down_time": { + "title": "Motion Downward Time", + "type": "integer", + "placeholder": 10000, + "minimum": 1, + "description": "Overrides default motion time (in milliseconds)" + }, + "motion_time": { + "title": "Motion Time", + "type": "integer", + "placeholder": 10000, + "minimum": 1, + "description": "Milliseconds for blinds to move completely up or down" + }, + "show_toggle_button": { + "title": "Show Toggle Button", + "type": "boolean", + "description": "Exposes a toggle (up/down) button" + }, + "stop_url": { + "title": "Stop URL", + "type": "string", + "description": "URL to send the STOP command", + "placeholder": "http://1.2.3.4/window/stop", + "format": "uri" + }, + "use_same_url_for_stop": { + "title": "Use Same URL for Stop", + "type": "boolean", + "description": "Instead of using the STOP url, just re-send the UP or DOWN url" + }, + "show_stop_button": { + "title": "Show Stop Button", + "type": "boolean" + }, + "trigger_stop_at_boundaries": { + "title": "Send Stop at Boundaries", + "type": "boolean", + "description": "Only needed if blinds do not stop automatically" + }, + "position_url": { + "title": "Position URL", + "type": "string", + "description": "URL to get the current blinds position (must be 0-100 response)", + "placeholder": "http://1.2.3.4/window/position", + "format": "uri" + }, + "position_jsonata": { + "title": "Jsonata for Position URL", + "type": "string", + "description": "Expression to parse position URL response", + "placeholder": "ShutterPosition1" + }, + "http_options": { + "notitle": true, + "type": "object", + "properties": { + "method": { + "title": "HTTP Method", + "type": "string", + "placeholder": "POST", + "typeahead": { + "source": [ + "GET", + "POST", + "PUT" + ] + } + }, + "headers": { + "notitle": true, + "type": "object", + "properties": { + "Authorization": { + "type": "string" + }, + "BOND-Token": { + "type": "string" + } + } + }, + "body": { + "title": "HTTP Body", + "type": "string" + } + } + }, + "response_lag": { + "title": "Response Lag", + "type": "integer", + "placeholder": 0, + "minimum": 0, + "description": "Milliseconds for device to respond after HTTP request. Improves accuracy for non-boundary positions due to network or motor lag." + }, + "success_codes": { + "notitle": true, + "type": "array", + "uniqueItems": true, + "displayFlex": true, + "flex-direction": "row", + "items": { + "type": "integer", + "placeholder": 200, + "minimum": 1 + } + }, + "max_http_attempts": { + "title": "Maximum HTTP attempts", + "type": "integer", + "placeholder": 5, + "minimum": 1, + "description": "Maximum number of HTTP attempts (on error or timeout) per request" + }, + "retry_delay": { + "title": "Wait between HTTP attempts", + "type": "integer", + "placeholder": 2000, + "minimum": 0, + "description": "Milliseconds between HTTP retries" + }, + "webhook_port": { + "title": "Port", + "type": "integer", + "placeholder": 51828, + "minimum": 1 + }, + "webhook_http_auth_user": { + "title": "Username", + "type": "string" + }, + "webhook_http_auth_pass": { + "title": "Password", + "type": "string" + }, + "webhook_https": { + "title": "Use SSL", + "type": "boolean" + }, + "webhook_https_keyfile": { + "title": "Path To Private Key", + "type": "string" + }, + "webhook_https_certfile": { + "title": "Path To Certificate", + "type": "string" + }, + "unique_serial": { + "title": "Unique serial", + "type": "boolean", + "description": "Uuid-based serial/model number instead of default 'BlindsHTTPAccessory'" + }, + "verbose": { + "title": "Verbose logging", + "type": "boolean", + "description": "Show getTargetPosition / getTargetState / getCurrentPosition requests" + } + } + }, + "layout": [ + "name", + { + "title": "HTTP Configuration", + "description": "for UP/DOWN/STOP URLs", + "expandable": true, + "type": "fieldset", + "items": [ + { + "type": "help", + "helpvalue": "If a more advanced configuration is desired, see documentation. Each url parameter can be customized as a complete HTTP object." + }, + "http_options", + { + "type": "fieldset", + "items": [ + { + "key": "http_options.method" + }, + { + "type": "help", + "helpvalue": "Headers:" + }, + "http_options.headers.Authorization", + "http_options.headers.BOND-Token", + { + "key": "http_options.body" + }, + { + "type": "help", + "helpvalue": "HTTP Success Codes" + }, + { + "key": "success_codes", + "items": [ + { + "key": "success_codes[]" + } + ] + }, + { + "title": "HTTP Retries", + "description": "for failed HTTP requests", + "type": "fieldset", + "expandable": true, + "items": [ + "max_http_attempts", + "retry_delay" + ] + } + ] + } + ] + }, + { + "title": "Up URL", + "type": "fieldset", + "expandable": true, + "items": [ + "up_url", + { + "key": "motion_up_time", + "condition": { + "functionBody": "return model.motion_up_time || model.up_url" + } + } + ] + }, + { + "title": "Down URL", + "type": "fieldset", + "expandable": true, + "items": [ + "down_url", + { + "key": "motion_down_time", + "condition": { + "functionBody": "return model.motion_down_time || model.down_url" + } + } + ] + }, + { + "title": "Stop URL", + "expandable": true, + "type": "fieldset", + "items": [ + "stop_url", + { + "type": "flex", + "flex-flow": "row wrap", + "items": [ + { + "key": "trigger_stop_at_boundaries", + "condition": { + "functionBody": "return model.trigger_stop_at_boundaries || model.stop_url || model.use_same_url_for_stop" + } + }, + { + "key": "use_same_url_for_stop", + "condition": { + "functionBody": "return model.use_same_url_for_stop || ((model.down_url || model.up_url) && !model.stop_url)" + } + } + ] + } + ] + }, + { + "title": "Position URL", + "description": "Update position by polling URL", + "type": "fieldset", + "expandable": true, + "items": [ + "position_url", + { + "key": "position_jsonata", + "condition": { + "functionBody": "return model.position_jsonata || model.position_url" + } + } + ] + }, + { + "title": "Position Webhook", + "description": "Update position on ad-hoc basis", + "type": "fieldset", + "expandable": true, + "items": [ + "webhook_port", + { + "type": "help", + "helpvalue": "Basic Authentication" + }, + "webhook_http_auth_user", + "webhook_http_auth_pass", + "webhook_https", + { + "key": "webhook_https_keyfile", + "condition": { + "functionBody": "return model.webhook_https_keyfile || model.webhook_https_certfile || model.webhook_https" + } + }, + { + "key": "webhook_https_certfile", + "condition": { + "functionBody": "return model.webhook_https_certfile || model.webhook_https_keyfile || model.webhook_https" + } + } + ] + }, + { + "title": "Motion Timing", + "description": "Calibrate blind movement times", + "type": "fieldset", + "expandable": true, + "items": [ + "motion_time", + "response_lag" + ] + }, + { + "type": "fieldset", + "expandable": true, + "title": "HomeKit Buttons", + "items": [ + { + "type": "flex", + "flex-flow": "row wrap", + "items": [ + { + "key": "show_toggle_button", + "condition": { + "functionBody": "return model.show_toggle_button || (model.up_url && model.down_url)" + } + }, + { + "key": "show_stop_button", + "condition": { + "functionBody": "return model.show_stop_button || model.use_same_url_for_stop || model.stop_url" + } + } + ] + } + ] + }, + { + "title": "Additional Options", + "type": "fieldset", + "expandable": true, + "items": [ + "verbose", + "unique_serial" + ] + } + ] } diff --git a/index.js b/index.js index c3472b3..5498ccf 100644 --- a/index.js +++ b/index.js @@ -44,7 +44,7 @@ function BlindsHTTPAccessory(log, config) { } } this.stopURL = config.stop_url || false; - this.httpMethod = config.http_method || { method: 'POST' }; + this.httpOptions = config.http_options || config.http_method || { method: 'POST' }; this.successCodes = config.success_codes || [200]; this.maxHttpAttempts = parseInt(config.max_http_attempts, 10) || 5; this.retryDelay = parseInt(config.retry_delay, 10) || 2000; @@ -401,7 +401,7 @@ BlindsHTTPAccessory.prototype.setTargetPosition = function (pos, callback) { this.httpRequest( exactPositionUrl || moveUrl, - this.httpMethod, + this.httpOptions, function (body, requestTime, err) { if (err) { this.service @@ -554,7 +554,7 @@ BlindsHTTPAccessory.prototype.sendStopRequest = function (targetService, on, cal this.httpRequest( this.stopURL, - this.httpMethod, + this.httpOptions, function (body, requestTime, err) { if (err) { this.log.warn('Stop request failed');