├── CNAME ├── .gitignore ├── _config.yml ├── _includes ├── indexes │ ├── functions.md │ ├── mqtt.md │ ├── http.md │ └── basic.md ├── toc-recipe.html ├── breadcrumbs.html ├── header.html ├── toc-footer.html └── footer.html ├── favicon.ico ├── node-red.png ├── images ├── node-red.png ├── basic │ ├── convert-xml.png │ ├── parse-csv.png │ ├── split-text.png │ ├── convert-json.png │ ├── convert-yaml.png │ ├── generate-csv.png │ ├── join-streams.png │ ├── operate-on-array.png │ ├── retry-on-error.png │ ├── route-on-context.png │ ├── trigger-at-time.png │ ├── trigger-on-error.png │ ├── trigger-on-start.png │ ├── trigger-timeout.png │ ├── route-on-property.png │ ├── copy-message-property.png │ ├── move-message-property.png │ ├── rate-limit-messages.png │ ├── report-by-exception.png │ ├── trigger-at-interval.png │ ├── trigger-placeholder.png │ ├── delete-message-property.png │ ├── rate-limit-message-stream.png │ ├── set-message-property-fixed.png │ └── map-between-different-number-ranges.png ├── mqtt │ ├── receive-json.png │ ├── connect-to-broker.png │ ├── publish-to-topic.png │ ├── set-publish-topic.png │ ├── subscribe-to-topic.png │ ├── publish-retained-message.png │ └── publish-to-topic-config.png └── http │ ├── set-query-string.png │ ├── set-request-url.png │ ├── work-with-cookies.png │ ├── get-binary-response.png │ ├── parse-json-response.png │ ├── serve-a-local-file.png │ ├── serve-json-content.png │ ├── set-request-header.png │ ├── simple-get-request.png │ ├── handle-url-parameters.png │ ├── create-an-http-endpoint.png │ ├── handle-query-parameters.png │ ├── post-form-data-to-a-flow.png │ ├── post-raw-data-to-a-flow.png │ ├── set-request-url-template.png │ ├── access-http-request-headers.png │ ├── include-data-from-another-flow.png │ └── simple-get-request-example-page.png ├── basic ├── index.md ├── delete-message-property.md ├── trigger-at-time.md ├── trigger-at-interval.md ├── copy-message-property.md ├── move-message-property.md ├── trigger-on-start.md ├── set-message-property-fixed.md ├── trigger-on-error.md ├── rate-limit-message-stream.md ├── report-by-exception.md ├── rate-limit-messages.md ├── map-between-different-number-ranges.md ├── trigger-timeout.md ├── convert-yaml.md ├── convert-json.md ├── parse-csv.md ├── retry-on-error.md ├── operate-on-array.md ├── route-on-property.md ├── split-text.md ├── join-streams.md ├── trigger-placeholder.md ├── convert-xml.md ├── route-on-context.md └── generate-csv.md ├── http ├── index.md ├── access-http-request-headers.md ├── get-binary-response.md ├── post-raw-data-to-a-flow.md ├── post-json-data-to-a-flow.md ├── serve-a-local-file.md ├── handle-query-parameters.md ├── simple-get-request.md ├── serve-json-content.md ├── parse-json-response.md ├── handle-url-parameters.md ├── set-request-header.md ├── post-form-data-to-a-flow.md ├── set-query-string.md ├── create-an-http-endpoint.md ├── set-request-url-template.md ├── include-data-from-another-flow.md ├── set-request-url.md └── work-with-cookies.md ├── mqtt ├── index.md ├── publish-to-topic.md ├── set-publish-topic.md ├── publish-retained-message.md ├── subscribe-to-topic.md ├── receive-json.md └── connect-to-broker.md ├── css ├── generate-style.sh └── style.min.css ├── README.md ├── index.md ├── _layouts ├── index.html └── default.html ├── styleguide.md └── LICENSE /CNAME: -------------------------------------------------------------------------------- 1 | cookbook.nodered.org 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | _site/ 3 | .sass-cache/ 4 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | name: Node-RED 2 | markdown: kramdown 3 | -------------------------------------------------------------------------------- /_includes/indexes/functions.md: -------------------------------------------------------------------------------- 1 | #### Function node 2 | 3 | - Create an HTTP Endpoint 4 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/favicon.ico -------------------------------------------------------------------------------- /node-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/node-red.png -------------------------------------------------------------------------------- /images/node-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/node-red.png -------------------------------------------------------------------------------- /basic/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: index 3 | title: Basic recipes 4 | --- 5 | 6 | {% include indexes/basic.md %} 7 | -------------------------------------------------------------------------------- /http/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: index 3 | title: HTTP recipes 4 | --- 5 | 6 | {% include indexes/http.md %} 7 | -------------------------------------------------------------------------------- /mqtt/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: index 3 | title: MQTT Recipes 4 | --- 5 | 6 | {% include indexes/mqtt.md %} 7 | -------------------------------------------------------------------------------- /images/basic/convert-xml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/convert-xml.png -------------------------------------------------------------------------------- /images/basic/parse-csv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/parse-csv.png -------------------------------------------------------------------------------- /images/basic/split-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/split-text.png -------------------------------------------------------------------------------- /images/mqtt/receive-json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/mqtt/receive-json.png -------------------------------------------------------------------------------- /images/basic/convert-json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/convert-json.png -------------------------------------------------------------------------------- /images/basic/convert-yaml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/convert-yaml.png -------------------------------------------------------------------------------- /images/basic/generate-csv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/generate-csv.png -------------------------------------------------------------------------------- /images/basic/join-streams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/join-streams.png -------------------------------------------------------------------------------- /images/basic/operate-on-array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/operate-on-array.png -------------------------------------------------------------------------------- /images/basic/retry-on-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/retry-on-error.png -------------------------------------------------------------------------------- /images/basic/route-on-context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/route-on-context.png -------------------------------------------------------------------------------- /images/basic/trigger-at-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/trigger-at-time.png -------------------------------------------------------------------------------- /images/basic/trigger-on-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/trigger-on-error.png -------------------------------------------------------------------------------- /images/basic/trigger-on-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/trigger-on-start.png -------------------------------------------------------------------------------- /images/basic/trigger-timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/trigger-timeout.png -------------------------------------------------------------------------------- /images/http/set-query-string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/set-query-string.png -------------------------------------------------------------------------------- /images/http/set-request-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/set-request-url.png -------------------------------------------------------------------------------- /images/http/work-with-cookies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/work-with-cookies.png -------------------------------------------------------------------------------- /images/mqtt/connect-to-broker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/mqtt/connect-to-broker.png -------------------------------------------------------------------------------- /images/mqtt/publish-to-topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/mqtt/publish-to-topic.png -------------------------------------------------------------------------------- /images/mqtt/set-publish-topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/mqtt/set-publish-topic.png -------------------------------------------------------------------------------- /images/basic/route-on-property.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/route-on-property.png -------------------------------------------------------------------------------- /images/http/get-binary-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/get-binary-response.png -------------------------------------------------------------------------------- /images/http/parse-json-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/parse-json-response.png -------------------------------------------------------------------------------- /images/http/serve-a-local-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/serve-a-local-file.png -------------------------------------------------------------------------------- /images/http/serve-json-content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/serve-json-content.png -------------------------------------------------------------------------------- /images/http/set-request-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/set-request-header.png -------------------------------------------------------------------------------- /images/http/simple-get-request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/simple-get-request.png -------------------------------------------------------------------------------- /images/mqtt/subscribe-to-topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/mqtt/subscribe-to-topic.png -------------------------------------------------------------------------------- /images/basic/copy-message-property.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/copy-message-property.png -------------------------------------------------------------------------------- /images/basic/move-message-property.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/move-message-property.png -------------------------------------------------------------------------------- /images/basic/rate-limit-messages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/rate-limit-messages.png -------------------------------------------------------------------------------- /images/basic/report-by-exception.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/report-by-exception.png -------------------------------------------------------------------------------- /images/basic/trigger-at-interval.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/trigger-at-interval.png -------------------------------------------------------------------------------- /images/basic/trigger-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/trigger-placeholder.png -------------------------------------------------------------------------------- /images/http/handle-url-parameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/handle-url-parameters.png -------------------------------------------------------------------------------- /images/basic/delete-message-property.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/delete-message-property.png -------------------------------------------------------------------------------- /images/http/create-an-http-endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/create-an-http-endpoint.png -------------------------------------------------------------------------------- /images/http/handle-query-parameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/handle-query-parameters.png -------------------------------------------------------------------------------- /images/http/post-form-data-to-a-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/post-form-data-to-a-flow.png -------------------------------------------------------------------------------- /images/http/post-raw-data-to-a-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/post-raw-data-to-a-flow.png -------------------------------------------------------------------------------- /images/http/set-request-url-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/set-request-url-template.png -------------------------------------------------------------------------------- /images/mqtt/publish-retained-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/mqtt/publish-retained-message.png -------------------------------------------------------------------------------- /images/mqtt/publish-to-topic-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/mqtt/publish-to-topic-config.png -------------------------------------------------------------------------------- /images/basic/rate-limit-message-stream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/rate-limit-message-stream.png -------------------------------------------------------------------------------- /images/basic/set-message-property-fixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/set-message-property-fixed.png -------------------------------------------------------------------------------- /images/http/access-http-request-headers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/access-http-request-headers.png -------------------------------------------------------------------------------- /images/http/include-data-from-another-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/include-data-from-another-flow.png -------------------------------------------------------------------------------- /images/http/simple-get-request-example-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/http/simple-get-request-example-page.png -------------------------------------------------------------------------------- /images/basic/map-between-different-number-ranges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-red/cookbook.nodered.org/master/images/basic/map-between-different-number-ranges.png -------------------------------------------------------------------------------- /css/generate-style.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Generates the stylesheet based on the main site css that assumed to be co-located 3 | 4 | echo > style.css 5 | for x in simplegrid.css style.css front.css docs.css blog.css syntax.css 6 | do 7 | cat "../../node-red.github.io/css/"$x >> style.css 8 | done 9 | scss -t compressed style.css > style.min.css 10 | rm style.css 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-red.github.io 2 | ================== 3 | 4 | [Node-RED Site](http://nodered.org) 5 | 6 | ### Contributing / Fixes 7 | 8 | For simple typos and single line fixes please just raise an issue pointing out 9 | our mistakes. If you need to raise a pull request please read our 10 | [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md) 11 | before doing so. 12 | -------------------------------------------------------------------------------- /_includes/indexes/mqtt.md: -------------------------------------------------------------------------------- 1 | #### MQTT 2 | 3 | - [Connect to an MQTT broker](/mqtt/connect-to-broker) 4 | - [Publish messages to a topic](/mqtt/publish-to-topic) 5 | - [Set the topic of a published message](/mqtt/set-publish-topic) 6 | - [Publish a retained message to a topic](/mqtt/publish-retained-message) 7 | - [Subscribe to a topic](/mqtt/subscribe-to-topic) 8 | - [Receive a parsed JSON message](/mqtt/receive-json) 9 | -------------------------------------------------------------------------------- /_includes/toc-recipe.html: -------------------------------------------------------------------------------- 1 | 8 | 18 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: index 3 | title: Node-RED Cookbook 4 | --- 5 | 6 | This is a collection of recipes for how to use Node-RED to solve many common 7 | programming tasks. 8 | 9 | Each recipe addresses a specific problem and shows by example how it can be solved 10 | using the capabilities of the platform. 11 | 12 | 13 | If you're interested in contributing to the cookbook you are more than welcome. 14 | Join the `#docs` channel on [slack](https://nodered.org/slack) and get involved. 15 | 16 | 17 | ## Table of Contents 18 | 19 | {% include indexes/basic.md %} 20 | 21 | {% include indexes/http.md %} 22 | 23 | {% include indexes/mqtt.md %} 24 | -------------------------------------------------------------------------------- /_includes/breadcrumbs.html: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /_layouts/index.html: -------------------------------------------------------------------------------- 1 | {% include header.html %} 2 |
3 | {% assign top_url = '/' %} 4 | {% assign top_slug = 'cookbook' %} 5 | {% include breadcrumbs.html %} 6 |
7 |
8 | {% include toc-recipe.html %} 9 | {% include toc-footer.html %} 10 |
11 |
12 |
13 |

{{ page.title }}

14 | {{ content }} 15 | 16 |
17 |
18 |
19 |
20 | {% include footer.html %} 21 | -------------------------------------------------------------------------------- /basic/delete-message-property.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Delete a message property 4 | slug: 5 | - label: messages 6 | url: /#messages 7 | - delete property 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to delete a message property. 13 | 14 | ### Solution 15 | 16 | Use the Change node to delete the property. 17 | 18 | #### Example 19 | 20 | ![](/images/basic/delete-message-property.png){:width="616px"} 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"91cd2fa9.e0a96","type":"inject","z":"535331d8.55c1f","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":140,"y":180,"wires":[["54ec03e4.5714bc"]]},{"id":"54ec03e4.5714bc","type":"change","z":"535331d8.55c1f","name":"","rules":[{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":180,"wires":[["321900de.3cbea"]]},{"id":"321900de.3cbea","type":"debug","z":"535331d8.55c1f","name":"","active":true,"console":"false","complete":"false","x":550,"y":180,"wires":[]}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | ### Discussion 30 | 31 | The Change node can be used to delete properties of a message. 32 | -------------------------------------------------------------------------------- /_includes/indexes/http.md: -------------------------------------------------------------------------------- 1 | #### HTTP endpoints 2 | 3 | - [Create an HTTP Endpoint](/http/create-an-http-endpoint) 4 | - [Handle query parameters passed to an HTTP endpoint](/http/handle-query-parameters) 5 | - [Handle url parameters in an HTTP endpoint](/http/handle-url-parameters) 6 | - [Access HTTP request headers](/http/access-http-request-headers) 7 | - [Include data captured in another flow](/http/include-data-from-another-flow) 8 | - [Serve JSON content](/http/serve-json-content) 9 | - [Serve a local file](/http/serve-a-local-file) 10 | - [Post raw data to a flow](/http/post-raw-data-to-a-flow) 11 | - [Post form data to a flow](/http/post-form-data-to-a-flow) 12 | - [Post JSON data to a flow](/http/post-json-data-to-a-flow) 13 | - [Work with cookies](/http/work-with-cookies) 14 | 15 | #### HTTP requests 16 | - [Simple GET request](/http/simple-get-request) 17 | - [Set the url of a request](/http/set-request-url) 18 | - [Set the url of a request using a template](/http/set-request-url-template) 19 | - [Set query string parameters](/http/set-query-string) 20 | - [Get a parsed JSON response](/http/parse-json-response) 21 | - [Get a binary response](/http/get-binary-response) 22 | - [Set a request header](/http/set-request-header) 23 | -------------------------------------------------------------------------------- /basic/trigger-at-time.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Trigger a flow at a specific time 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - trigger at time 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to trigger a flow at a specific time, such as at 4pm every weekday. 13 | 14 | ### Solution 15 | 16 | Use an Inject node configured to trigger at the desired 17 | time. 18 | 19 | #### Example 20 | 21 | ![](/images/basic/trigger-at-time.png){:width="530px"} 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"24579bcb.5c9814","type":"inject","z":"535331d8.55c1f","name":"","topic":"","payload":"It is 4pm on a weekday!","payloadType":"str","repeat":"","crontab":"00 16 * * 1,2,3,4,5","once":false,"x":190,"y":660,"wires":[["145b508a.f3325f"]]},{"id":"145b508a.f3325f","type":"debug","z":"535331d8.55c1f","name":"","active":true,"console":"false","complete":"false","x":410,"y":660,"wires":[]}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ### Discussion 31 | 32 | The Inject node can be configured to trigger at a specific 33 | time on specific days of the week. 34 | 35 | If multiple different times are required, multiple Inject 36 | nodes should be used. 37 | -------------------------------------------------------------------------------- /basic/trigger-at-interval.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Trigger a flow at regular intervals 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - trigger at interval 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to trigger a flow at regular intervals. For example, to periodically 13 | call an api to retrieve its current state. 14 | 15 | ### Solution 16 | 17 | Use an Inject node configured to repeat at the desired 18 | interval. 19 | 20 | To stop - you must re-configure the node and set Repeat to none. Click done to save the change and deploy. 21 | 22 | #### Example 23 | 24 | ![](/images/basic/trigger-at-interval.png){:width="530px"} 25 | 26 | {% raw %} 27 | ~~~json 28 | [{"id":"372cfc32.bcd244","type":"inject","z":"535331d8.55c1f","name":"","topic":"","payload":"","payloadType":"date","repeat":"5","crontab":"","once":false,"x":150,"y":600,"wires":[["6c63c499.ce3adc"]]},{"id":"6c63c499.ce3adc","type":"debug","z":"535331d8.55c1f","name":"","active":true,"console":"false","complete":"false","x":410,"y":600,"wires":[]}] 29 | ~~~ 30 | {: .flow} 31 | {% endraw %} 32 | 33 | ### Discussion 34 | 35 | The Inject node can be configured to repeat at a fixed 36 | interval. If desired, it can also be constrained to do so between certain times on 37 | certain days. 38 | -------------------------------------------------------------------------------- /basic/copy-message-property.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Set a message property to another property 4 | slug: 5 | - label: messages 6 | url: /#messages 7 | - copy property 8 | --- 9 | *** Not published *** 10 | 11 | ### Problem 12 | 13 | You want to copy a property of a message to another property. 14 | 15 | ### Solution 16 | 17 | Use the Change node to set the property of the message. 18 | 19 | #### Example 20 | 21 | ![](/images/basic/copy-message-property.png){:width="616px"} 22 | 23 | {% raw %} 24 | ~~~json 25 | [] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ### Discussion 31 | 32 | The Change node can be used to set properties of a message. 33 | 34 | The node supports setting various JavaScript types as well as some Node-RED specific 35 | 36 | - strings: `"hello world"` 37 | - numbers: `42` 38 | - boolean: `true`/`false` 39 | - timestamp: the current time, in milliseconds, since epoch (January 1st, 1970) 40 | - JSON: a JSON string that will be parsed to its Object representation 41 | 42 | It also supports setting a property to a value based on the value of context properties, 43 | other message properties or an JSONata expression. These are each explored more in the 44 | following recipes: 45 | 46 | - [Set a message property to a context value]() 47 | - [Set a message property to another message property]() 48 | -------------------------------------------------------------------------------- /mqtt/publish-to-topic.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Publish messages to a topic 4 | slug: 5 | - label: mqtt 6 | url: /#mqtt 7 | - publish 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to publish a message to an MQTT topic on a broker. 13 | 14 | ### Solution 15 | 16 | Use the MQTT Output node to publish messages to a topic. 17 | 18 | #### Example 19 | 20 | ![](/images/mqtt/publish-to-topic.png) 21 | 22 | ![](/images/mqtt/publish-to-topic-config.png) 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"9c138886.116928","type":"mqtt out","z":"eda2a949.74ea98","name":"","topic":"sensors/livingroom/temp","qos":"","retain":"","broker":"61de5090.0f5d9","x":430,"y":100,"wires":[]},{"id":"ff654e7f.32e9e","type":"inject","z":"eda2a949.74ea98","name":"temperature","topic":"","payload":"22","payloadType":"num","repeat":"","crontab":"","once":false,"x":230,"y":100,"wires":[["9c138886.116928"]]},{"id":"61de5090.0f5d9","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ### Discussion 32 | 33 | The MQTT Output node with an associated MQTT Config node connected to an MQTT broker can be used to publish messages to a pre-configured topic. 34 | -------------------------------------------------------------------------------- /basic/move-message-property.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Move a message property 4 | slug: 5 | - label: messages 6 | url: /#messages 7 | - move property 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to move a message property to a different property. 13 | 14 | ### Solution 15 | 16 | Use the Change node to move a property. 17 | 18 | #### Example 19 | 20 | ![](/images/basic/move-message-property.png){:width="616px"} 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"d11f7311.77c15","type":"inject","z":"535331d8.55c1f","name":"","topic":"Hello","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":160,"y":280,"wires":[["13c01487.eb13cb"]]},{"id":"13c01487.eb13cb","type":"change","z":"535331d8.55c1f","name":"","rules":[{"t":"move","p":"topic","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":280,"wires":[["89cc4fb1.9b208"]]},{"id":"89cc4fb1.9b208","type":"debug","z":"535331d8.55c1f","name":"","active":true,"console":"false","complete":"false","x":550,"y":280,"wires":[]}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | ### Discussion 30 | 31 | The Change node can be used to move a property of a message. 32 | 33 | It can be done as two separate actions in the Change node; 34 | first using a Set action to copy the property to its new location and then a Delete 35 | action to remove the original. 36 | 37 | Alternatively, the node supports a Move action that does it in one step. 38 | -------------------------------------------------------------------------------- /mqtt/set-publish-topic.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Set the topic of a published message 4 | slug: 5 | - label: mqtt 6 | url: /#mqtt 7 | - publish topic 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to set the topic of a published MQTT message dynamically. 13 | 14 | ### Solution 15 | 16 | Set the `topic` message property, before sending the message to an MQTT Output node. 17 | 18 | #### Example 19 | 20 | ![](/images/mqtt/set-publish-topic.png) 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"73abc692.bb3838","type":"mqtt out","z":"eda2a949.74ea98","name":"","topic":"","qos":"","retain":"","broker":"61de5090.0f5d9","x":410,"y":300,"wires":[]},{"id":"ef5a01ee.a940d","type":"inject","z":"eda2a949.74ea98","name":"kitchen temperature","topic":"sensors/kitchen/temperature","payload":"22","payloadType":"num","repeat":"","crontab":"","once":false,"x":250,"y":300,"wires":[["73abc692.bb3838"]]},{"id":"61de5090.0f5d9","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | In this example, the Inject node sets the `msg.topic`, 30 | but you don't always need to use an inject node to do this. 31 | 32 | ### Discussion 33 | 34 | Ensure the `Topic` field in the MQTT Output configuration dialog is left blank to use the `topic` message property. 35 | -------------------------------------------------------------------------------- /basic/trigger-on-start.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Trigger a flow whenever Node-RED starts 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - trigger on start 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to trigger a flow whenever Node-RED starts. 13 | 14 | This could be used to initialise context variables, or to send a notification 15 | that Node-RED has been restarted. 16 | 17 | ### Solution 18 | 19 | Use an Inject node configured to fire once on start. Double click the inject node to open it - scroll down and tick the checkbox 20 | 21 | ✅ Inject once after [0.1] seconds, then 22 | 23 | Click Done to save the change. 24 | 25 | #### Example 26 | 27 | ![](/images/basic/trigger-on-start.png){:width="530px"} 28 | 29 | {% raw %} 30 | ~~~json 31 | [{"id":"e60b12c1.93bb3","type":"inject","z":"535331d8.55c1f","name":"","topic":"","payload":"Started!","payloadType":"str","repeat":"","crontab":"","once":true,"x":140,"y":540,"wires":[["9b1d7727.56d0f8"]]},{"id":"9b1d7727.56d0f8","type":"debug","z":"535331d8.55c1f","name":"","active":true,"console":"false","complete":"false","x":410,"y":540,"wires":[]}] 32 | ~~~ 33 | {: .flow} 34 | {% endraw %} 35 | 36 | ### Discussion 37 | 38 | When configured to fire on start, the Inject node will 39 | be automatically triggered a few hundred milliseconds after it is deployed. This 40 | delay is used to help ensure the rest of the flows have been created and Started 41 | by this point. 42 | 43 | The node will trigger whenever it is deployed. 44 | -------------------------------------------------------------------------------- /mqtt/publish-retained-message.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Publish a retained message to a topic 4 | slug: 5 | - label: mqtt 6 | url: /#mqtt 7 | - retained 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to publish a retained message to an MQTT topic on a broker. 13 | 14 | ### Solution 15 | 16 | Set the `Retain` option to `true` in the MQTT Output node 17 | configuration dialog, or set the `msg.retain` message property to `true` in the 18 | message sent to the node. 19 | 20 | #### Example 21 | 22 | ![](/images/mqtt/publish-retained-message.png) 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"4a7dc819.3aa6f8","type":"mqtt out","z":"eda2a949.74ea98","name":"","topic":"sensors/livingroom/temp","qos":"","retain":"true","broker":"61de5090.0f5d9","x":430,"y":420,"wires":[]},{"id":"fb7b873.c391878","type":"inject","z":"eda2a949.74ea98","name":"temperature","topic":"","payload":"22","payloadType":"num","repeat":"","crontab":"","once":false,"x":230,"y":420,"wires":[["4a7dc819.3aa6f8"]]},{"id":"61de5090.0f5d9","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ### Discussion 32 | 33 | Once you have sent a retained message to a topic, all subscribers will receive 34 | that message when they subscribe. 35 | 36 | To clear a previously retained topic from the broker, send a blank message to 37 | that topic with the retain flag set. 38 | -------------------------------------------------------------------------------- /http/access-http-request-headers.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Access HTTP request headers 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - headers 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to access the HTTP headers sent in a request. 13 | 14 | ### Solution 15 | 16 | Use the `msg.req.headers` property of the message sent by the HTTP In 17 | node to access the headers. 18 | 19 | #### Example 20 | 21 | ![](/images/http/access-http-request-headers.png){:width="800px"} 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"c1460268.3eba","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-headers","method":"get","swaggerDoc":"","x":130,"y":380,"wires":[["24199456.dbe66c"]]},{"id":"24199456.dbe66c","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n \n \n

User agent: {{req.headers.user-agent}}

\n \n","x":310,"y":380,"wires":[["b3531892.4cace8"]]},{"id":"b3531892.4cace8","type":"http response","z":"3045204d.cfbae","name":"","x":450,"y":380,"wires":[]}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ~~~text 31 | [~]$ curl http://localhost:1880/hello-headers 32 | 33 | 34 | 35 |

User agent: curl/7.49.1

36 | 37 | 38 | ~~~ 39 | {: .shell} 40 | 41 | ### Discussion 42 | 43 | The `msg.req.headers` property is an object of key/value pairs for each request header. 44 | The header names are all lower-cased regardless of how they appear in the request. 45 | -------------------------------------------------------------------------------- /http/get-binary-response.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Get binary response 4 | slug: 5 | - label: http 6 | url: /#http-requests 7 | - binary 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to get a binary HTTP response from an HTTP request. 13 | 14 | ### Solution 15 | 16 | The HTTP Request node will return the body of a response in the `msg.payload` as a string by default. 17 | Change the `Return` configuration of this node to `a binary buffer` to return the response as a binary buffer in the `msg.payload`. 18 | 19 | #### Example 20 | 21 | ![](/images/http/get-binary-response.png){:width="556px"} 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"871ee927.0d69c8","type":"inject","z":"c9a81b70.8abed8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":240,"y":660,"wires":[["8ea4e52a.03d678"]]},{"id":"8ea4e52a.03d678","type":"http request","z":"c9a81b70.8abed8","name":"binary http request","method":"GET","ret":"bin","url":"http://localhost:1880/binary","tls":"","x":410,"y":660,"wires":[["70309d0c.4dc504"]]},{"id":"70309d0c.4dc504","type":"debug","z":"c9a81b70.8abed8","name":"","active":true,"console":"false","complete":"false","x":590,"y":660,"wires":[]}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | We have modified the flow from the [Set the URL of a Request URL recipe](set-request-url.html) by changing the 31 | HTTP Request node `Return` configuration to `a binary buffer`. The Debug node 32 | will display the payload as a binary buffer such as: 33 | 34 | {% raw %} 35 | ~~~text 36 | [ 80, 75, 3, 4, 20, 0, 6, 0, 8, 0 … ] 37 | ~~~ 38 | {% endraw %} 39 | -------------------------------------------------------------------------------- /http/post-raw-data-to-a-flow.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Post raw data to a flow 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - post data 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to post raw text data in to a flow. 13 | 14 | ### Solution 15 | 16 | Use the HTTP In node to listen for POST requests that 17 | have their `Content-Type` set to `text/plain` and access the posted data as `msg.payload`. 18 | 19 | #### Example 20 | 21 | ![](/images/http/post-raw-data-to-a-flow.png) 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"3e1c5107.c1e3ae","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-raw","method":"post","swaggerDoc":"","x":120,"y":920,"wires":[["cf679478.309868"]]},{"id":"cf679478.309868","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n \n \n

Hello {{ payload }}!

\n \n","x":290,"y":920,"wires":[["f3c1a3f0.0c3e6"]]},{"id":"f3c1a3f0.0c3e6","type":"http response","z":"3045204d.cfbae","name":"","x":430,"y":920,"wires":[]}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ~~~text 31 | [~]$ curl -X POST -d 'Nick' -H "Content-type: text/plain" http://localhost:1880/hello-raw 32 | 33 | 34 | 35 |

Hello Nick!

36 | 37 | 38 | ~~~ 39 | {: .shell} 40 | 41 | ### Discussion 42 | 43 | When the HTTP In node receives a request with the `Content-Type` 44 | header set to `text/plain` it makes the body available as `msg.payload`: 45 | 46 | ~~~javascript 47 | var name = msg.payload; 48 | ~~~ 49 | -------------------------------------------------------------------------------- /http/post-json-data-to-a-flow.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Post JSON data to a flow 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - post json 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to post JSON data in to a flow. 13 | 14 | ### Solution 15 | 16 | Use the HTTP In node to listen for POST requests that 17 | have their `Content-Type` set to `application/json` and access the parsed JSON as 18 | properties of `msg.payload`. 19 | 20 | #### Example 21 | 22 | ![](/images/http/post-form-data-to-a-flow.png) 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"5b98a8ac.a46758","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-form","method":"post","swaggerDoc":"","x":120,"y":820,"wires":[["bba61009.4459f"]]},{"id":"bba61009.4459f","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n \n \n

Hello {{ payload.name }}!

\n \n","x":290,"y":820,"wires":[["6ceb930a.93146c"]]},{"id":"6ceb930a.93146c","type":"http response","z":"3045204d.cfbae","name":"","x":430,"y":820,"wires":[]}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ~~~text 32 | [~]$ curl -X POST -d '{"name":"Nick"}' -H "Content-type: application/json" http://localhost:1880/hello-form 33 | 34 | 35 | 36 |

Hello Nick!

37 | 38 | 39 | ~~~ 40 | {: .shell} 41 | 42 | ### Discussion 43 | 44 | When the HTTP In node receives a request with the `Content-Type` 45 | header set to `application/json` it parses the body of the request and makes the data 46 | available under `msg.payload`: 47 | 48 | ~~~javascript 49 | var name = msg.payload.name; 50 | ~~~ 51 | -------------------------------------------------------------------------------- /basic/set-message-property-fixed.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Set a message property to a fixed value 4 | slug: 5 | - label: messages 6 | url: /#messages 7 | - set property 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to set a message property to a fixed value. 13 | 14 | ### Solution 15 | 16 | Use the Change node to set the property of the message. 17 | 18 | #### Example 19 | 20 | ![](/images/basic/set-message-property-fixed.png){:width="616px"} 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"d72dc4ce.89b368","type":"inject","z":"535331d8.55c1f","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":140,"y":80,"wires":[["78075f19.e0174"]]},{"id":"78075f19.e0174","type":"change","z":"535331d8.55c1f","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"Hello World!","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":80,"wires":[["78dc7c25.b90d54"]]},{"id":"78dc7c25.b90d54","type":"debug","z":"535331d8.55c1f","name":"","active":true,"console":"false","complete":"false","x":550,"y":80,"wires":[]}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | ### Discussion 30 | 31 | The Change node can be used to set properties of a message. 32 | 33 | The node supports setting various JavaScript types as well as some Node-RED specific types. 34 | 35 | - strings: `"hello world"` 36 | - numbers: `42` 37 | - boolean: `true`/`false` 38 | - timestamp: the current time, in milliseconds, since epoch (January 1st, 1970) 39 | - JSON: a JSON string that will be parsed to its Object representation 40 | - Buffer: a Node.js Buffer object 41 | 42 | It also supports setting a property to a value based on the value of context properties, 43 | other message properties or a JSONata expression. 44 | -------------------------------------------------------------------------------- /_includes/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {% if page.title %}{{ page.title }} : {% endif %}Node-RED 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 25 | 26 | 27 |
28 |
29 | 30 | 39 | 40 |
41 |
42 | -------------------------------------------------------------------------------- /basic/trigger-on-error.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Trigger a flow when a node throws an error 4 | slug: 5 | - label: error handling 6 | url: /#error-handling 7 | - trigger on error 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to trigger a flow when a node throws an error. 13 | 14 | ### Solution 15 | 16 | Use the Catch node to receive the error and trigger a flow. 17 | 18 | #### Example 19 | 20 | ![](/images/basic/trigger-on-error.png){:width="401px"} 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"2bd6810d.e22ece","type":"catch","z":"fc046f99.4be08","name":"","scope":["2c94a22c.91012e"],"uncaught":false,"x":130,"y":160,"wires":[["d16b9fac.8212a"]]},{"id":"2c94a22c.91012e","type":"function","z":"fc046f99.4be08","name":"Throw Error","func":"node.error(\"an example error\", msg); ","outputs":1,"noerr":0,"x":310,"y":100,"wires":[[]]},{"id":"d16b9fac.8212a","type":"debug","z":"fc046f99.4be08","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"error","targetType":"msg","x":300,"y":160,"wires":[]},{"id":"c5ee9670.5dbbd8","type":"inject","z":"fc046f99.4be08","name":"Trigger error","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":100,"wires":[["2c94a22c.91012e"]]}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | ### Discussion 30 | 31 | The Catch node can be configured to catch errors from 32 | specific nodes in the flow or from any node. This allows you to create different 33 | error handling flows for different nodes. 34 | 35 | The Catch node sends on the message that was logged with 36 | the error. It also sets `msg.error` with details of the error and which node triggered 37 | it. 38 | 39 | Note that this requires nodes to properly log their errors so that they can be caught. 40 | -------------------------------------------------------------------------------- /basic/rate-limit-message-stream.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Handle messages at a regular rate 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - rate limit 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to handle messages at a regular rate, ignoring messages that arrive too 13 | quickly. For example, you have a sensor sending data every second but you only 14 | want to handle an update every 5 seconds. The messages you handle must be the most 15 | recent. 16 | 17 | ### Solution 18 | 19 | Use a Delay node configured to rate limit the messages 20 | passing through it with the option to drop intermediate messages enabled. 21 | 22 | #### Example 23 | 24 | ![](/images/basic/rate-limit-message-stream.png){:width="601px"} 25 | 26 | {% raw %} 27 | ~~~json 28 | [{"id":"8a1bcd7d.f6b67","type":"inject","z":"ac14500e.2c57d","name":"Inject Array","topic":"","payload":"[0,1,2,3,4,5,6,7,8,9]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":1380,"wires":[["bd4bdd42.bd1b"]]},{"id":"bd4bdd42.bd1b","type":"delay","z":"ac14500e.2c57d","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"5","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":320,"y":1380,"wires":[["be20c513.237c78"]]},{"id":"be20c513.237c78","type":"debug","z":"ac14500e.2c57d","name":"Debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":510,"y":1380,"wires":[]}] 29 | ~~~ 30 | {: .flow} 31 | {% endraw %} 32 | 33 | ### Discussion 34 | 35 | The rate limiting mode of the Delay node can be used to 36 | change the rate of messages passing through it. With the option to drop intermediate 37 | messages enabled, it will discard any message that arrives within the rate limit 38 | interval. 39 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | {% include header.html %} 2 | 51 |
52 | {% assign top_url = '/' %} 53 | {% assign top_slug = 'cookbook' %} 54 | {% include breadcrumbs.html %} 55 |
56 |
57 | {% include toc-recipe.html %} 58 | {% include toc-footer.html %} 59 |
60 |
61 |
62 |

{{ page.title }}

63 | {{ content }} 64 | 65 |
66 |
67 |
68 |
69 | {% include footer.html %} 70 | -------------------------------------------------------------------------------- /mqtt/subscribe-to-topic.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Subscribe to a topic 4 | slug: 5 | - label: mqtt 6 | url: /#mqtt 7 | - subscribe 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to subscribe to messages on an MQTT topic. 13 | 14 | ### Solution 15 | 16 | Use the MQTT Input node to subscribe to the broker and 17 | receive messages published to matching topics. 18 | 19 | #### Example 20 | 21 | ![](/images/mqtt/subscribe-to-topic.png) 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"8024cb4.98c5238","type":"mqtt in","z":"eda2a949.74ea98","name":"","topic":"sensors/#","qos":"2","broker":"61de5090.0f5d9","x":240,"y":180,"wires":[["15d727dd.33e808"]]},{"id":"15d727dd.33e808","type":"debug","z":"eda2a949.74ea98","name":"","active":true,"console":"false","complete":"false","x":390,"y":180,"wires":[]},{"id":"61de5090.0f5d9","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ### Discussion 31 | 32 | The MQTT Input node must be hardcoded with the topic filter 33 | to use - it cannot be changed dynamically. 34 | 35 | One possible workaround is to set the topic to an environment variable such as 36 | `$(MY_TOPIC)`. When the Node-RED runtime starts it will substitute the environment 37 | variable value into that property of the node. This does allow the topic to be changed, although 38 | doing so does require a restart of Node-RED to pickup changes to the environment variable. 39 | 40 | You can also use MQTT wildcards, `+` for a single topic level or `#` for multiple. This allows 41 | you to receive multiple topics with a single node. The messages will be sent from 42 | the node with `msg.topic` set to the actual topic received. 43 | -------------------------------------------------------------------------------- /http/serve-a-local-file.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Serve a local file 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - serve file 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to create an HTTP endpoint that responds to GET requests with content 13 | from a local file, such an png image. 14 | 15 | ### Solution 16 | 17 | Use the File In node to load the required content and 18 | set the `Content-Type` to the appropriate value for the file type being returned. 19 | 20 | #### Example 21 | 22 | ![](/images/http/serve-a-local-file.png) 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"c7e341a0.381cc","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-file","method":"get","swaggerDoc":"","x":110,"y":720,"wires":[["2fb1c354.d04e3c"]]},{"id":"2fb1c354.d04e3c","type":"file in","z":"3045204d.cfbae","name":"","filename":"/tmp/node-red.png","format":"","x":290,"y":720,"wires":[["c9e28681.361d78"]]},{"id":"c9e28681.361d78","type":"change","z":"3045204d.cfbae","name":"Set Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.content-type","pt":"msg","to":"image/png","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":720,"wires":[["88974243.7768c"]]},{"id":"88974243.7768c","type":"http response","z":"3045204d.cfbae","name":"","x":610,"y":720,"wires":[]}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ~~~text 32 | [~]$ curl http://localhost:1880/hello-file > file.png 33 | ~~~ 34 | {: .shell} 35 | 36 | ### Discussion 37 | 38 | When loading a non-text file such as an image, the File In 39 | node must be configured to return a `Buffer` object. 40 | 41 | So that the receiver knows how to handle the file, the `Content-Type` header must 42 | be set to the appropriate mime type. The example above, which returns a `.png` file 43 | sets the `Content-Type` header to `image/png`. 44 | -------------------------------------------------------------------------------- /_includes/indexes/basic.md: -------------------------------------------------------------------------------- 1 | #### Messages 2 | 3 | - [Set a message property to a fixed value](/basic/set-message-property-fixed) 4 | - [Delete a message property](/basic/delete-message-property) 5 | - [Move a message property](/basic/move-message-property) 6 | - [Map a property between different numeric ranges](/basic/map-between-different-number-ranges) 7 | 8 | 9 | #### Flow control 10 | 11 | - [Trigger a flow whenever Node-RED starts](/basic/trigger-on-start) 12 | - [Trigger a flow at regular intervals](/basic/trigger-at-interval) 13 | - [Trigger a flow at a specific time](/basic/trigger-at-time) 14 | - [Route a message based on one of its properties](/basic/route-on-property) 15 | - [Route a message based on a context value](/basic/route-on-context) 16 | - [Perform an operation on each element in an array](/basic/operate-on-array) 17 | - [Trigger a flow if a message isn't received after a defined time](/basic/trigger-timeout) 18 | - [Send placeholder messages when a stream stops sending](/basic/trigger-placeholder) 19 | - [Slow down messages passing through a flow](/basic/rate-limit-messages) 20 | - [Handle messages at a regular rate](/basic/rate-limit-message-stream) 21 | - [Drop messages that have not changed value](/basic/report-by-exception) 22 | - [Create a single message from separate streams of messages](/basic/join-streams) 23 | 24 | #### Error handling 25 | 26 | - [Trigger a flow when a node throws an error](/basic/trigger-on-error) 27 | - [Automatically retry an action after an error](/basic/retry-on-error) 28 | 29 | #### Working with data formats 30 | 31 | - [Convert to/from JSON](/basic/convert-json) 32 | - [Convert to/from XML](/basic/convert-xml) 33 | - [Convert to/from YAML](/basic/convert-yaml) 34 | - [Generate CSV output](/basic/generate-csv) 35 | - [Parse CSV input](/basic/parse-csv) 36 | - [Extracting data from an HTML page](/http/simple-get-request) 37 | - [Split text into one message per line](/basic/split-text) 38 | -------------------------------------------------------------------------------- /basic/report-by-exception.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Drop messages that have not changed value 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - rbe 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to drop a message if the value of its payload has not changed since the 13 | last message. For example, you have a sensor sending the state of a switch at 14 | regular intervals and you only want to know when the value has changed. 15 | 16 | 17 | ### Solution 18 | 19 | Use the RBE node (Report By Exception) to block messages 20 | unless its value has changed. 21 | 22 | #### Example 23 | 24 | ![](/images/basic/report-by-exception.png){:width="618px"} 25 | 26 | {% raw %} 27 | ~~~json 28 | [{"id":"6079638d.df403c","type":"inject","z":"ac14500e.2c57d","name":"","topic":"","payload":"0","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":1500,"wires":[["87129503.c7b358"]]},{"id":"87129503.c7b358","type":"rbe","z":"ac14500e.2c57d","name":"report-by-exception","func":"deadband","gap":"","start":"","inout":"out","property":"payload","x":300,"y":1520,"wires":[["5e2ffc27.c61dd4"]]},{"id":"5e2ffc27.c61dd4","type":"debug","z":"ac14500e.2c57d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":510,"y":1520,"wires":[]},{"id":"2dc49f96.3070c","type":"inject","z":"ac14500e.2c57d","name":"","topic":"","payload":"1","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":1540,"wires":[["87129503.c7b358"]]}] 29 | ~~~ 30 | {: .flow} 31 | {% endraw %} 32 | 33 | ### Discussion 34 | 35 | The RBE can be used to drop messages unless their value 36 | has changed. This is useful for detecting changes. 37 | 38 | If the property being checked is a number, the node can also be configured with 39 | a threshold for how much the value must change for the message to be passed on. 40 | -------------------------------------------------------------------------------- /http/handle-query-parameters.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Handle query parameters passed to an HTTP endpoint 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - query parameters 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to access the query parameters passed to an HTTP endpoint, such as: 13 | 14 | http://example.com/hello-query?name=Nick 15 | 16 | ### Solution 17 | 18 | Use the `msg.req.query` property of the message sent by the HTTP In 19 | node to access the parameters. 20 | 21 | #### Example 22 | 23 | ![](/images/http/handle-query-parameters.png) 24 | 25 | {% raw %} 26 | ~~~json 27 | [{"id":"b34dd1af.4cb23","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n \n \n

Hello {{req.query.name}}!

\n \n","x":290,"y":180,"wires":[["b828f6a6.47d708"]]},{"id":"1052941d.efad6c","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-query","method":"get","swaggerDoc":"","x":120,"y":180,"wires":[["b34dd1af.4cb23"]]},{"id":"b828f6a6.47d708","type":"http response","z":"3045204d.cfbae","name":"","x":430,"y":180,"wires":[]}] 28 | ~~~ 29 | {: .flow} 30 | {% endraw %} 31 | 32 | ~~~text 33 | [~]$ curl http://localhost:1880/hello-query?name=Nick 34 | 35 | 36 | 37 |

Hello Nick!

38 | 39 | 40 | ~~~ 41 | {: .shell} 42 | 43 | ### Discussion 44 | 45 | The `msg.req.query` property is an object of key/value pairs for each query parameter. 46 | 47 | In the above example, a request to `/hello-query?name=Nick&colour=blue` results in the property 48 | containing: 49 | 50 | ~~~json 51 | { 52 | "name": "Nick", 53 | "colour": "blue" 54 | } 55 | ~~~ 56 | 57 | If there are multiple query parameters with the same name, they will be provided 58 | as an array. For example, `/hello-query?colour=blue&colour=red`: 59 | 60 | ~~~json 61 | { 62 | "colour": ["blue","red"] 63 | } 64 | ~~~ 65 | -------------------------------------------------------------------------------- /http/simple-get-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Simple GET request 4 | slug: 5 | - label: http 6 | url: /#http-requests 7 | - get 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to make a simple GET request to a web site and extract useful information. 13 | 14 | ### Solution 15 | 16 | Use the HTTP Request node to make an HTTP request and an 17 | HTML node to extract elements from the retrieved html document. 18 | 19 | #### Example 20 | 21 | ![](/images/http/simple-get-request.png) 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"d88dd470.0ac7b8","type":"inject","z":"18c99b30.cf9d35","name":"make request","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":130,"y":180,"wires":[["874a3d4e.9b666"]]},{"id":"874a3d4e.9b666","type":"http request","z":"18c99b30.cf9d35","name":"","method":"GET","ret":"txt","url":"https://nodered.org","tls":"","x":294.5,"y":180,"wires":[["90243cc1.87edc"]]},{"id":"7403c68f.21d7c8","type":"debug","z":"18c99b30.cf9d35","name":"","active":true,"console":"false","complete":"false","x":650,"y":180,"wires":[]},{"id":"90243cc1.87edc","type":"html","z":"18c99b30.cf9d35","name":"","property":"","tag":".node-red-latest-version","ret":"text","as":"single","x":471.5,"y":180,"wires":[["7403c68f.21d7c8"]]}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ### Discussion 31 | 32 | To find content in a web page, the Chrome browser’s ‘Inspect Element’ can be a 33 | useful tool. Using the browser, right click on a page element to see the tags, 34 | ids and classes applied to an element as shown. 35 | 36 | In this example we retrieve the latest version of Node-RED from [https://nodered.org](). 37 | Using the inspector we can see the version is located in a `` tag with the 38 | class `node-red-latest-version`. 39 | 40 | The HTML node can be configured with the CSS selector 41 | `.node-red-latest-version` to return a message for each matching element. 42 | 43 | ![](/images/http/simple-get-request-example-page.png) 44 | -------------------------------------------------------------------------------- /http/serve-json-content.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Serve JSON content 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - serve json 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to respond to an HTTP request with JSON data. 13 | 14 | ### Solution 15 | 16 | Set the `content-type` of the response to `application/json` using the `msg.headers` 17 | object. 18 | 19 | #### Example 20 | 21 | ![](/images/http/serve-json-content.png) 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"c8107088.37ef9","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-json","method":"get","swaggerDoc":"","x":120,"y":620,"wires":[["4e8237da.b17dc8"]]},{"id":"4e8237da.b17dc8","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{ \"Hello\": \"World\" }","x":290,"y":620,"wires":[["65401623.9abfe8"]]},{"id":"65401623.9abfe8","type":"change","z":"3045204d.cfbae","name":"Set Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.content-type","pt":"msg","to":"application/json","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":620,"wires":[["f7d3e35a.082c2"]]},{"id":"f7d3e35a.082c2","type":"http response","z":"3045204d.cfbae","name":"","x":610,"y":620,"wires":[]}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ~~~text 31 | [~]$ curl -i http://localhost:1880/hello-json 32 | HTTP/1.1 200 OK 33 | X-Powered-By: Express 34 | Access-Control-Allow-Origin: * 35 | Content-Type: application/json; charset=utf-8 36 | Content-Length: 20 37 | ETag: W/"14-jgfjeX8FTECC4q5nXp6n5g" 38 | Date: Sat, 26 Nov 2016 23:07:50 GMT 39 | Connection: keep-alive 40 | 41 | { "Hello": "World" } 42 | ~~~ 43 | {: .shell} 44 | 45 | ### Discussion 46 | 47 | The HTTP headers returned in the response can be set using the `msg.headers` 48 | property. It should be an object of key/value pairs for each header. 49 | 50 | To return well-formed JSON, the `Content-Type` header should be set to 51 | `application/json` so the receiver knows to handle it as JSON data. 52 | -------------------------------------------------------------------------------- /basic/rate-limit-messages.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Slow down messages passing through a flow 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - rate limit 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to slow down the messages passing through a flow. For example, 13 | you have a message containing an array of values that you 14 | [split into a stream of messages](/basic/operate-on-array) and want to process 15 | each message in that stream at a rate of one per second. 16 | 17 | ### Solution 18 | 19 | Use a Delay node configured to rate limit the messages 20 | passing through it. 21 | 22 | #### Example 23 | 24 | ![](/images/basic/rate-limit-messages.png){:width="615px"} 25 | 26 | {% raw %} 27 | ~~~json 28 | [{"id":"1fccc223.7ba87e","type":"inject","z":"ac14500e.2c57d","name":"Inject Array","topic":"","payload":"[0,1,2,3,4,5,6,7,8,9]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":1280,"wires":[["b2837466.e02a38"]]},{"id":"b2837466.e02a38","type":"split","z":"ac14500e.2c57d","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":250,"y":1280,"wires":[["bd97c8ed.a5c8d8"]]},{"id":"bd97c8ed.a5c8d8","type":"delay","z":"ac14500e.2c57d","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":390,"y":1280,"wires":[["bd66f03e.bdf0c"]]},{"id":"bd66f03e.bdf0c","type":"debug","z":"ac14500e.2c57d","name":"Debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":530,"y":1280,"wires":[]}] 29 | ~~~ 30 | {: .flow} 31 | {% endraw %} 32 | 33 | ### Discussion 34 | 35 | The rate limiting mode of the Delay node can be used to 36 | change the rate of messages passing through it. It is configured with the desired 37 | number of messages to pass through the node per time interval. It will evenly 38 | spread the delivery of messages across the time period. 39 | -------------------------------------------------------------------------------- /basic/map-between-different-number-ranges.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Map a property between different numeric ranges 4 | slug: 5 | - label: messages 6 | url: /#messages 7 | - map range 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to scale a number from one numeric range to another. For example, a 13 | sensor reading in the range 0 - 1023 should be mapped to a voltage range of 0 - 5. 14 | 15 | ### Solution 16 | 17 | Use the Range node to map between the defined ranges. 18 | 19 | #### Example 20 | 21 | ![](/images/basic/map-between-different-number-ranges.png){:width="616px"} 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"80dae67d.b4d8f8","type":"inject","z":"535331d8.55c1f","name":"","topic":"","payload":"0","payloadType":"num","repeat":"","crontab":"","once":false,"x":130,"y":380,"wires":[["81f13534.456348"]]},{"id":"81f13534.456348","type":"range","z":"535331d8.55c1f","minin":"0","maxin":"1023","minout":"0","maxout":"5","action":"clamp","round":false,"name":"","x":350,"y":420,"wires":[["e80b61d7.4b399"]]},{"id":"cb21de23.75a2f","type":"inject","z":"535331d8.55c1f","name":"","topic":"","payload":"512","payloadType":"num","repeat":"","crontab":"","once":false,"x":130,"y":420,"wires":[["81f13534.456348"]]},{"id":"342552de.255a1e","type":"inject","z":"535331d8.55c1f","name":"","topic":"","payload":"1023","payloadType":"num","repeat":"","crontab":"","once":false,"x":130,"y":460,"wires":[["81f13534.456348"]]},{"id":"e80b61d7.4b399","type":"debug","z":"535331d8.55c1f","name":"","active":true,"console":"false","complete":"false","x":550,"y":420,"wires":[]}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ### Discussion 31 | 32 | The Range node can be used to linearly scale between two 33 | different numeric ranges. 34 | 35 | By default, the result is not constrained to the range defined in the node. This means 36 | using the voltage example above, a value of 2046 would map to a result of 10. 37 | 38 | The node can be configured to constrain the result to the target range, or apply simple 39 | modulo arithmetic so the value wraps within the target range. 40 | -------------------------------------------------------------------------------- /mqtt/receive-json.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Receive a parsed JSON message 4 | slug: 5 | - label: mqtt 6 | url: /#mqtt 7 | - subscribe json 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to receive a parsed JSON message from an MQTT broker. 13 | 14 | ### Solution 15 | 16 | Use the MQTT Input node and a JSON node to receive a parsed JSON message. 17 | 18 | #### Example 19 | 20 | ![](/images/mqtt/receive-json.png) 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"8024cb4.98c5238","type":"mqtt in","z":"eda2a949.74ea98","name":"","topic":"sensors/#","qos":"2","broker":"61de5090.0f5d9","x":260,"y":580,"wires":[["b5098b7f.2361d8"]]},{"id":"15d727dd.33e808","type":"debug","z":"eda2a949.74ea98","name":"","active":true,"console":"false","complete":"false","x":530,"y":580,"wires":[]},{"id":"2aed678c.3de738","type":"mqtt out","z":"eda2a949.74ea98","name":"","topic":"sensors/livingroom/temp","qos":"","retain":"false","broker":"61de5090.0f5d9","x":310,"y":520,"wires":[]},{"id":"3b613a69.a247c6","type":"inject","z":"eda2a949.74ea98","name":"temp json","topic":"","payload":"{\"sensor_id\":1234,\"temperature\":13}","payloadType":"json","repeat":"","crontab":"","once":false,"x":120,"y":520,"wires":[["2aed678c.3de738"]]},{"id":"b5098b7f.2361d8","type":"json","z":"eda2a949.74ea98","name":"","pretty":false,"x":390,"y":580,"wires":[["15d727dd.33e808"]]},{"id":"61de5090.0f5d9","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | ### Discussion 30 | 31 | The payload of an MQTT Input node is a string unless it was detected as a binary buffer. To parse the JSON string and convert it to a JavaScript Object, use the JSON node. 32 | 33 | Newer versions of the MQTT node (Node-RED version 0.19+) now have a select option to choose the required output format so the JSON node may no longer be required. 34 | -------------------------------------------------------------------------------- /http/parse-json-response.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Get a parsed JSON Response 4 | slug: 5 | - label: http 6 | url: /#http-requests 7 | - parse json 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to return the JSON response of an HTTP request as a parsed Javascript object. 13 | 14 | ### Solution 15 | 16 | The HTTP Request node will return the body of a JSON response in the `msg.payload` as a string by default. 17 | Change the `Return` configuration of this node to `a parsed JSON object` to parse the JSON response in the `msg.payload` that 18 | can be easily accessed by downstream nodes. 19 | 20 | #### Example 21 | 22 | ![](/images/http/parse-json-response.png) 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"14c60a10.794df6","type":"http request","z":"c9a81b70.8abed8","name":"","method":"GET","ret":"obj","url":"https://jsonplaceholder.typicode.com/posts/{{post}}","tls":"","x":390,"y":500,"wires":[["b4ea8dd4.61a05"]]},{"id":"b4ea8dd4.61a05","type":"debug","z":"c9a81b70.8abed8","name":"","active":true,"console":"false","complete":"payload.title","x":570,"y":500,"wires":[]},{"id":"3479192a.04f016","type":"inject","z":"c9a81b70.8abed8","name":"post id","topic":"","payload":"2","payloadType":"str","repeat":"","crontab":"","once":false,"x":90,"y":500,"wires":[["e69250cf.368fd"]]},{"id":"e69250cf.368fd","type":"change","z":"c9a81b70.8abed8","name":"","rules":[{"t":"set","p":"post","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":230,"y":500,"wires":[["14c60a10.794df6"]]}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | We have reconfigured the flow from the [Set the URL of a Request URL recipe](set-request-url.html) 32 | by changing the HTTP Request node configuration. The Debug 33 | node has been modified to display only the `title` property of the parsed JSON response: 34 | 35 | {% raw %} 36 | ~~~text 37 | "qui est esse" 38 | ~~~ 39 | {% endraw %} 40 | 41 | ### Discussion 42 | 43 | If your HTTP request returns XML, the XML node can be used to parse Javascript objects from XML documents. 44 | -------------------------------------------------------------------------------- /http/handle-url-parameters.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Handle url parameters in an HTTP endpoint 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - url parameters 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to create a single HTTP endpoint that can handle requests where parts 13 | of the path are set per-request. 14 | 15 | For example, a single endpoint that can handle requests to both: 16 | 17 | http://example.com/hello-param/Nick 18 | http://example.com/hello-param/Dave 19 | 20 | 21 | ### Solution 22 | 23 | Use named path parameters in your HTTP In node's `URL` 24 | property and then access the specific value provided in a request using the 25 | `msg.req.params` property of the message. 26 | 27 | #### Flow 28 | 29 | ![](/images/http/handle-url-parameters.png) 30 | 31 | {% raw %} 32 | ~~~json 33 | [{"id":"ce53954b.31ac68","type":"http response","z":"3045204d.cfbae","name":"","x":490,"y":280,"wires":[]},{"id":"288a7c0.fd77584","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n \n \n

Hello {{req.params.name}}!

\n \n","x":350,"y":280,"wires":[["ce53954b.31ac68"]]},{"id":"7665c67d.899a38","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-param/:name","method":"get","swaggerDoc":"","x":150,"y":280,"wires":[["288a7c0.fd77584"]]}] 34 | ~~~ 35 | {: .flow} 36 | {% endraw %} 37 | 38 | #### Example 39 | ~~~text 40 | [~]$ curl http://localhost:1880/hello-param/Nick 41 | 42 | 43 | 44 |

Hello Nick!

45 | 46 | 47 | ~~~ 48 | {: .shell} 49 | 50 | ### Discussion 51 | 52 | Named path parameters in the `URL` property can be used to identify parts of the 53 | path that can vary between requests. 54 | 55 | The `msg.req.params` property is an object of key/value pairs for each path parameter. 56 | 57 | In the above example, the node is configured with a URL of `/hello-params/:name`, 58 | so a request to `/hello-param/Nick` results in the `msg.req.params` property containing: 59 | 60 | ~~~json 61 | { 62 | "name": "Nick" 63 | } 64 | ~~~ 65 | -------------------------------------------------------------------------------- /http/set-request-header.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Set a request header 4 | slug: 5 | - label: http 6 | url: /#http-requests 7 | - set header 8 | --- 9 | 10 | ### Problem 11 | 12 | You need to send an HTTP request with specific request headers. 13 | 14 | ### Solution 15 | 16 | Set the `msg.headers` field to the field value pairs of the request headers you would like to include in the 17 | message sent to the HTTP request node. 18 | 19 | #### Example 20 | 21 | ![](/images/http/set-request-header.png) 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"cb1dfcde.fd153","type":"function","z":"124f654c.7a7c6b","name":"set payload and headers","func":"msg.payload = \"data to post\";\nmsg.headers = {};\nmsg.headers['X-Auth-User'] = 'mike';\nmsg.headers['X-Auth-Key'] = 'fred-key';\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":275,"y":600,"wires":[["e44209e7.752698"]]},{"id":"dcf34aab.218928","type":"inject","z":"124f654c.7a7c6b","name":"","repeat":"","crontab":"","once":false,"topic":"","payload":"","payloadType":"date","x":85,"y":600,"wires":[["cb1dfcde.fd153"]]},{"id":"e44209e7.752698","type":"http request","z":"124f654c.7a7c6b","name":"post to HttpBin","method":"POST","ret":"obj","paytoqs":"ignore","url":"https://httpbin.org/post","tls":"","persist":false,"proxy":"","authType":"","x":485,"y":600,"wires":[["ee306582.f0dde8"]]},{"id":"ee306582.f0dde8","type":"debug","z":"124f654c.7a7c6b","name":"","active":true,"console":"false","complete":"false","x":655,"y":600,"wires":[]}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | In this example we set the `X-Auth-User` and `X-Auth-Key` request headers to call public HttpBin post test service. 31 | 32 | The code in the Function node below adds these additional message 33 | fields by adding a `msg.headers` object, and setting the header field/values in this object as shown. 34 | 35 | {% raw %} 36 | ~~~text 37 | msg.payload = "data to post"; 38 | msg.headers = {}; 39 | msg.headers['X-Auth-User'] = 'mike'; 40 | msg.headers['X-Auth-Key'] = 'fred-key'; 41 | return msg; 42 | ~~~ 43 | {: .javascript} 44 | {% endraw %} 45 | 46 | These can be seen by expanding the debug object headers twistie - as the test service echos back the request. 47 | -------------------------------------------------------------------------------- /basic/trigger-timeout.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Trigger a flow if a message isn’t received after a defined time 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - trigger timeout 8 | --- 9 | 10 | ### Problem 11 | 12 | You want a flow to be triggered if a message is not received after a defined time. 13 | For example, you expect to receive a sensor reading every 5 seconds and need to know 14 | if it fails to arrive. 15 | 16 | ### Solution 17 | 18 | Use the Trigger node to detect when a message has not 19 | arrived after a defined interval. 20 | 21 | #### Example 22 | 23 | ![](/images/basic/trigger-timeout.png){:width="555px"} 24 | 25 | {% raw %} 26 | ~~~json 27 | [{"id":"6ea53ad8.2362a4","type":"debug","z":"ac14500e.2c57d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":450,"y":1160,"wires":[]},{"id":"3da6946e.184a5c","type":"inject","z":"ac14500e.2c57d","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":1160,"wires":[["38caaff4.03f6d","6ea53ad8.2362a4"]]},{"id":"38caaff4.03f6d","type":"trigger","z":"ac14500e.2c57d","op1":"","op2":"timeout","op1type":"nul","op2type":"str","duration":"5","extend":true,"units":"s","reset":"","bytopic":"all","name":"Watchdog","x":270,"y":1200,"wires":[["ae477709.016088"]]},{"id":"ae477709.016088","type":"debug","z":"ac14500e.2c57d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":450,"y":1200,"wires":[]}] 28 | ~~~ 29 | {: .flow} 30 | {% endraw %} 31 | 32 | ### Discussion 33 | 34 | In the example flow, the top branch represents the normal flow of the messages. 35 | They also get passed to the Trigger node on a second 36 | branch of the flow. 37 | 38 | The Trigger node is configured to initially send nothing, 39 | then to wait for 5 seconds before sending a `"timeout"` message. The option to 40 | extend the delay if new messages arrive is also selected. This means as long as 41 | messages continue to arrive, the node will not do anything. Once 5 seconds passes 42 | after the last message to arrive, it will send on the `"timeout"` message. 43 | -------------------------------------------------------------------------------- /mqtt/connect-to-broker.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Connect to an MQTT Broker 4 | slug: 5 | - label: mqtt 6 | url: /#mqtt 7 | - connect 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to connect to an MQTT broker running locally. 13 | 14 | ### Solution 15 | 16 | Use the MQTT Input input or MQTT Output node 17 | and an associated MQTT Config node to connect to an MQTT broker. 18 | 19 | #### Example 20 | 21 | ![](/images/mqtt/connect-to-broker.png) 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"2c6873d2.992abc","type":"mqtt out","z":"eda2a949.74ea98","name":"","topic":"sensors/livingroom/temp","qos":"","retain":"","broker":"407a01e4.6b637","x":330,"y":80,"wires":[]},{"id":"d9beed59.94155","type":"inject","z":"eda2a949.74ea98","name":"","topic":"","payload":"22","payloadType":"num","repeat":"","crontab":"","once":false,"x":150,"y":80,"wires":[["2c6873d2.992abc"]]},{"id":"be80048.8f232f8","type":"mqtt in","z":"eda2a949.74ea98","name":"","topic":"sensors/livingroom/temp","qos":"2","broker":"407a01e4.6b637","x":170,"y":160,"wires":[["8640b8ff.f82ff8"]]},{"id":"8640b8ff.f82ff8","type":"debug","z":"eda2a949.74ea98","name":"","active":true,"console":"false","complete":"false","x":370,"y":160,"wires":[]},{"id":"407a01e4.6b637","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ### Discussion 31 | 32 | Many users will run an MQTT broker such as [mosquitto](http://mosquitto.org) on 33 | the same Raspberry Pi or PC that Node-RED is running on. Once you have an 34 | MQTT input or output node in your flow, you create an 35 | MQTT Config node by double clicking on the node, then clicking on the pencil button to the right of the `Add an MQTT broker...` dropdown. Assuming your broker is open, 36 | set the server host to `localhost` and leave the port set to `1883`. 37 | 38 | To connect to non-local, secured brokers, other MQTT Config 39 | node options will need to be set according to your broker's connectivity requirements. 40 | -------------------------------------------------------------------------------- /basic/convert-yaml.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Convert to/from YAML 4 | slug: 5 | - label: formats 6 | url: /#working-with-data-formats 7 | - yaml 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to convert a message property between a YAML string and the JavaScript object 13 | it represents. 14 | 15 | ### Solution 16 | 17 | The YAML node can be used to convert between the two 18 | formats. 19 | 20 | #### Example 21 | 22 | ![](/images/basic/convert-yaml.png){:width="682px"} 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"f231967.0251a68","type":"inject","z":"64133d39.bb0394","name":"YAML String","topic":"","payload":"{\"a\":1}","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":320,"wires":[["a0110756.ecfa48"]]},{"id":"8f8f31b7.1f916","type":"debug","z":"64133d39.bb0394","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":590,"y":320,"wires":[]},{"id":"5138ba3.c972444","type":"inject","z":"64133d39.bb0394","name":"Object","topic":"","payload":"{\"a\":1, \"b\":[1,2,3]}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":360,"wires":[["2fa653cc.60d3dc"]]},{"id":"50f2f4c.4a6e60c","type":"debug","z":"64133d39.bb0394","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":430,"y":360,"wires":[]},{"id":"a0110756.ecfa48","type":"template","z":"64133d39.bb0394","name":"","field":"payload","fieldType":"msg","format":"yaml","syntax":"plain","template":"a: 1\nb:\n - 1\n - 2\n - 3","output":"str","x":280,"y":320,"wires":[["104b80e2.51068f"]]},{"id":"2fa653cc.60d3dc","type":"yaml","z":"64133d39.bb0394","property":"payload","name":"","x":250,"y":360,"wires":[["50f2f4c.4a6e60c"]]},{"id":"104b80e2.51068f","type":"yaml","z":"64133d39.bb0394","property":"payload","name":"","x":430,"y":320,"wires":[["8f8f31b7.1f916"]]}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ### Discussion 32 | 33 | In the example, the first flow injects the YAML: 34 | 35 | ~~~yaml 36 | a: 1 37 | b: 38 | - 1 39 | - 2 40 | - 3 41 | ~~~ 42 | 43 | The YAML node then converts it to the equivalent JavaScript 44 | object. 45 | 46 | The second flow does the reverse, injecting the object `{ a: 1, b: [1,2,3] }` 47 | and converting it to YAML. 48 | -------------------------------------------------------------------------------- /basic/convert-json.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Convert to/from JSON 4 | slug: 5 | - label: formats 6 | url: /#working-with-data-formats 7 | - json 8 | 9 | --- 10 | 11 | ### Problem 12 | 13 | You want to convert a message property between a JSON string and the JavaScript object 14 | it represents. 15 | 16 | ### Solution 17 | 18 | The JSON node can be used to convert between the two 19 | formats. 20 | 21 | #### Example 22 | 23 | ![](/images/basic/convert-json.png){:width="534px"} 24 | 25 | {% raw %} 26 | ~~~json 27 | [{"id":"634256b7.2d6818","type":"inject","z":"64133d39.bb0394","name":"JSON String","topic":"","payload":"{\"a\":1}","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":80,"wires":[["a2fe0fc8.095e1"]]},{"id":"a2fe0fc8.095e1","type":"json","z":"64133d39.bb0394","name":"","property":"payload","action":"","pretty":false,"x":270,"y":80,"wires":[["9a4ce2b8.47698"]]},{"id":"9a4ce2b8.47698","type":"debug","z":"64133d39.bb0394","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":430,"y":80,"wires":[]},{"id":"80032e2.7c92cd","type":"inject","z":"64133d39.bb0394","name":"Object","topic":"","payload":"{\"a\":1}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":120,"wires":[["cd40a0f4.4f5ac"]]},{"id":"cd40a0f4.4f5ac","type":"json","z":"64133d39.bb0394","name":"","property":"payload","action":"","pretty":false,"x":270,"y":120,"wires":[["478b4106.4fd7c"]]},{"id":"478b4106.4fd7c","type":"debug","z":"64133d39.bb0394","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":430,"y":120,"wires":[]}] 28 | ~~~ 29 | {: .flow} 30 | {% endraw %} 31 | 32 | ### Discussion 33 | 34 | In the example, the first flow injects the JSON string `'{"a":1}'` which the 35 | JSON node converts to the equivalent JavaScript object. 36 | 37 | The second flow does the reverse, injecting the object `{ a: 1 }` and converting 38 | it to JSON. 39 | 40 | The JSON will, by default, detect what it is being given 41 | to convert. It can also be configured to ensure the property is a given type. For 42 | example if your flow could receive either JSON or an Object, the JSON 43 | node can be configured to ensure the property is an Object. 44 | -------------------------------------------------------------------------------- /http/post-form-data-to-a-flow.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Post form data to a flow 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - post form data 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to post form data in to a flow. 13 | 14 | ### Solution 15 | 16 | Use the HTTP In node to listen for POST requests that 17 | have their `Content-Type` set to `application/x-www-form-urlencoded` and access 18 | the form data as properties of `msg.payload`. 19 | 20 | #### Example 21 | 22 | ![](/images/http/post-form-data-to-a-flow.png) 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"5b98a8ac.a46758","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-form","method":"post","swaggerDoc":"","x":120,"y":820,"wires":[["bba61009.4459f"]]},{"id":"bba61009.4459f","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n \n \n

Hello {{ payload.name }}!

\n \n","x":290,"y":820,"wires":[["6ceb930a.93146c"]]},{"id":"6ceb930a.93146c","type":"http response","z":"3045204d.cfbae","name":"","x":430,"y":820,"wires":[]}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ~~~text 32 | [~]$ curl -X POST -d "name=Nick" http://localhost:1880/hello-form 33 | 34 | 35 | 36 |

Hello Nick!

37 | 38 | 39 | ~~~ 40 | {: .shell} 41 | 42 | ### Discussion 43 | 44 | HTML Forms can be used to send data from the browser back to a server. If 45 | configured to `POST` the data, the browser will encode the data held in the 46 | `
` using a `content-type` of `application/x-www-form-urlencoded`. 47 | 48 | For example, when a form that looks like this is submitted: 49 | 50 | ~~~html 51 | 52 | 53 | 54 |
55 | ~~~ 56 | 57 | it results in the request: 58 | 59 | ~~~text 60 | POST / HTTP/1.1 61 | Host: localhost:1880 62 | Content-Type: application/x-www-form-urlencoded 63 | Content-Length: 9 64 | 65 | name=Nick 66 | ~~~ 67 | 68 | When the HTTP In node receives such a request, it 69 | parses the body of the request and makes the form data available under 70 | `msg.payload`: 71 | 72 | ~~~javascript 73 | var name = msg.payload.name; 74 | ~~~ 75 | -------------------------------------------------------------------------------- /http/set-query-string.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Set the query string parameters in a URL 4 | slug: 5 | - label: http 6 | url: /#http-requests 7 | - set query string 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to set the query string parameters of a URL for an HTTP request. 13 | 14 | ### Solution 15 | 16 | Use the HTTP Request node's support for [mustache](http://mustache.github.io/mustache.5.html) to substitute query parameter strings in URLs directly. 17 | 18 | #### Example 19 | 20 | ![](/images/http/set-query-string.png) 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"e95c6faa.ab2e1","type":"http request","z":"c9a81b70.8abed8","name":"","method":"GET","ret":"txt","url":"https://query.yahooapis.com/v1/public/yql?q={{{query}}}&format=json","tls":"","x":470,"y":420,"wires":[["7cf30700.5bc978"]]},{"id":"7cf30700.5bc978","type":"debug","z":"c9a81b70.8abed8","name":"","active":true,"console":"false","complete":"payload","x":630,"y":420,"wires":[]},{"id":"637d3c55.eb3084","type":"inject","z":"c9a81b70.8abed8","name":"query parameter","topic":"","payload":"select astronomy.sunset from weather.forecast where woeid in (select woeid from geo.places(1) where text=\"maui, hi\")","payloadType":"str","repeat":"","crontab":"","once":false,"x":120,"y":420,"wires":[["b001d489.d8f818"]]},{"id":"b001d489.d8f818","type":"change","z":"c9a81b70.8abed8","name":"","rules":[{"t":"set","p":"query","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":420,"wires":[["e95c6faa.ab2e1"]]}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | The Inject node generates a query string that is to be sent in the URL. The Change node changes this to `msg.query` which is substituted in the mustache template in the HTTP Request node URL property configured as shown: 30 | 31 | {% raw %} 32 | ~~~text 33 | https://query.yahooapis.com/v1/public/yql?q={{{query}}}&format=json 34 | ~~~ 35 | {% endraw %} 36 | 37 | The returned JSON content is the sunset in Hawaii: 38 | 39 | {% raw %} 40 | ~~~text 41 | "{"query":{"count":1,"created":"2017-01-22T01:31:07Z","lang":"en-US","results":{"channel":{"astronomy":{"sunset":"6:9 pm"}}}}}" 42 | ~~~ 43 | {% endraw %} 44 | 45 | 46 | #### Discussion 47 | 48 | By default, mustache will escape any HTML entities in the values it substitutes. To ensure HTML escaping is not used in your URL use `{% raw %}{{{triple}}}{% endraw %}` braces. 49 | -------------------------------------------------------------------------------- /_includes/toc-footer.html: -------------------------------------------------------------------------------- 1 |   2 | 84 | -------------------------------------------------------------------------------- /basic/parse-csv.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Parse CSV input 4 | slug: 5 | - label: formats 6 | url: /#working-with-data-formats 7 | - csv 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to parse CSV data to work with the values it contains. 13 | 14 | ### Solution 15 | 16 | The CSV node can be used to parse CSV and generate 17 | JavaScript objects from it. 18 | 19 | 20 | #### Example 21 | 22 | ![](/images/basic/parse-csv.png){:width="660px"} 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"73e4e16.4d9742","type":"inject","z":"64133d39.bb0394","name":"Inject","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":780,"wires":[["2bef78fd.ae70f8"]]},{"id":"90ed51dc.dcc71","type":"csv","z":"64133d39.bb0394","name":"","sep":",","hdrin":true,"hdrout":false,"multi":"mult","ret":"\\n","temp":"","skip":"1","x":410,"y":780,"wires":[["9aace6e7.adc538"]]},{"id":"9aace6e7.adc538","type":"debug","z":"64133d39.bb0394","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":570,"y":780,"wires":[]},{"id":"2bef78fd.ae70f8","type":"template","z":"64133d39.bb0394","name":"CSV data","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"# This is some random data\na,b,c\n80,18,2\n52,36,10\n91,18,61\n32,47,65","output":"str","x":260,"y":780,"wires":[["90ed51dc.dcc71"]]}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ### Discussion 32 | 33 | In the example, the flow injects a payload containing CSV data: 34 | 35 | ```javascript 36 | # This is some random data 37 | a,b,c 38 | 80,18,2 39 | 52,36,10 40 | 91,18,61 41 | 32,47,65 42 | ``` 43 | 44 | The CSV has been configured to ignore the first line of 45 | the input so it ignores the initial comment line. It then uses the next line to 46 | get the column names, and the remaining rows for the data. 47 | 48 | 49 | In this particular example, the node has also been configured to send a single 50 | message with all of the data. This results in a message with the payload: 51 | 52 | ```javascript 53 | [ 54 | { a: 80, b: 18, c: 2}, 55 | { a: 52, b: 36, c: 10}, 56 | { a: 91, b: 18, c: 61}, 57 | { a: 32, b: 47, c: 65}, 58 | ] 59 | ``` 60 | 61 | It is also possible to configure the node to emit one message for each row of data. 62 | In this mode, the messages will also include the `msg.parts` property that allows 63 | them to be passed to a Join node to reassemble them back 64 | into a single array. 65 | -------------------------------------------------------------------------------- /http/create-an-http-endpoint.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Create an HTTP Endpoint 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - create endpoint 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to create an HTTP endpoint that responds to GET requests with some static 13 | content, such as an HTML page or CSS stylesheet. 14 | 15 | ### Solution 16 | 17 | Use the HTTP In node to listen for requests, a 18 | Template node to include the static content, and an 19 | HTTP Response node to reply to the request. 20 | 21 | #### Example 22 | 23 | ![](/images/http/create-an-http-endpoint.png) 24 | 25 | {% raw %} 26 | ~~~json 27 | [{"id":"59ff2a1.fa600d4","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello","method":"get","swaggerDoc":"","x":100,"y":80,"wires":[["54c1e70d.ab3e18"]]},{"id":"54c1e70d.ab3e18","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n \n \n

Hello World!

\n \n","x":250,"y":80,"wires":[["266c286f.d993d8"]]},{"id":"266c286f.d993d8","type":"http response","z":"3045204d.cfbae","name":"","x":390,"y":80,"wires":[]}] 28 | ~~~ 29 | {: .flow} 30 | {% endraw %} 31 | 32 | ~~~text 33 | [~]$ curl http://localhost:1880/hello 34 | 35 | 36 | 37 |

Hello World!

38 | 39 | 40 | ~~~ 41 | {: .shell} 42 | 43 | ### Discussion 44 | 45 | The HTTP In and HTTP Response 46 | pair of nodes are the starting point for all HTTP endpoints you create. 47 | 48 | Any flow that starts with an HTTP In node must have a 49 | path to an HTTP Response node otherwise requests will 50 | eventually timeout. 51 | 52 | The HTTP Response node uses the `payload` property of 53 | messages it receives as the body of the response. Other properties can be used to 54 | further customize the response - they are covered in other recipes. 55 | 56 | The Template node provides a convenient way to embed 57 | a body of content into a flow. It may be desirable to maintain such static content 58 | outside of the flow. 59 | 60 | If you have turned on http authentication then you may need add your userid and password 61 | to the curl command. e.g. 62 | 63 | ~~~text 64 | [~]$ curl -u userid:password http://localhost:1880/hello 65 | ~~~ -------------------------------------------------------------------------------- /http/set-request-url-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Set the URL of a request using a template 4 | slug: 5 | - label: http 6 | url: /#http-requests 7 | - set url 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to dynamically set the URL of an HTTP request where only parts of the url change between requests. 13 | 14 | ### Solution 15 | 16 | Configure the HTTP Request node to generate a URL dynamically using a [mustache](http://mustache.github.io/mustache.5.html) URL template. 17 | 18 | #### Example 19 | 20 | ![](/images/http/set-request-url-template.png) 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"41747a17.54ffd4","type":"http request","z":"c9a81b70.8abed8","name":"","method":"GET","ret":"txt","url":"https://jsonplaceholder.typicode.com/posts/{{post}}","tls":"","x":550,"y":480,"wires":[["d682318c.36823"]]},{"id":"d682318c.36823","type":"debug","z":"c9a81b70.8abed8","name":"","active":true,"console":"false","complete":"payload","x":710,"y":480,"wires":[]},{"id":"90bfea22.dd2b98","type":"inject","z":"c9a81b70.8abed8","name":"post id","topic":"","payload":"2","payloadType":"str","repeat":"","crontab":"","once":false,"x":250,"y":480,"wires":[["e67a0cc.596d4f"]]},{"id":"e67a0cc.596d4f","type":"change","z":"c9a81b70.8abed8","name":"","rules":[{"t":"set","p":"post","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":480,"wires":[["41747a17.54ffd4"]]}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | In this flow, the Inject node sends an id for a post we would like to request from an API. The Change node changes this to `msg.post`. The HTTP Request node generates a URL by substituting `msg.post` of the URL property configured as shown: 30 | 31 | {% raw %} 32 | ~~~text 33 | https://jsonplaceholder.typicode.com/posts/{{post}} 34 | ~~~ 35 | {% endraw %} 36 | 37 | The JSON output from this API in the debug panel will look as follows: 38 | 39 | {% raw %} 40 | ~~~text 41 | { 42 | "userId": 1, 43 | "id": 2, 44 | "title": "qui est esse", 45 | "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla" 46 | } 47 | ~~~ 48 | {% endraw %} 49 | 50 | ### Discussion 51 | 52 | By default, mustache will escape any HTML entities in the values it substitutes. To ensure HTML escaping is not used in your URL use `{% raw %}{{{triple}}}{% endraw %}` braces. 53 | -------------------------------------------------------------------------------- /basic/retry-on-error.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Automatically retry an action after an error 4 | slug: 5 | - label: error handling 6 | url: /#error-handling 7 | - retry 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to retry an action after an error is thrown. 13 | 14 | ### Solution 15 | 16 | Use the Catch node to receive the error and connect it 17 | back to the node that needs to retry the action. 18 | 19 | #### Example 20 | 21 | ![](/images/basic/retry-on-error.png){:width="610px"} 22 | 23 | {% raw %} 24 | ~~~json 25 | [{"id":"27e61f12.c1a15","type":"inject","z":"fc046f99.4be08","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":320,"wires":[["d7d08440.31b678"]]},{"id":"d7d08440.31b678","type":"function","z":"fc046f99.4be08","name":"Random error","func":"// Randomly throw an error rather than\n// pass on message.\nif (Math.random() < 0.5) {\n node.error(\"a random error\", msg);\n} else {\n return msg;\n}","outputs":1,"noerr":0,"x":320,"y":320,"wires":[["f22b1e9a.5d89b"]]},{"id":"f22b1e9a.5d89b","type":"debug","z":"fc046f99.4be08","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":510,"y":320,"wires":[]},{"id":"2166290d.98d736","type":"delay","z":"fc046f99.4be08","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":240,"y":380,"wires":[["d7d08440.31b678"]]},{"id":"139b836e.7950ed","type":"catch","z":"fc046f99.4be08","name":"","scope":["d7d08440.31b678"],"uncaught":false,"x":90,"y":380,"wires":[["2166290d.98d736","9c8ab214.0ecaa"]]},{"id":"9c8ab214.0ecaa","type":"debug","z":"fc046f99.4be08","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"error","targetType":"msg","x":240,"y":440,"wires":[]}] 26 | ~~~ 27 | {: .flow} 28 | {% endraw %} 29 | 30 | ### Discussion 31 | 32 | Some errors are transitory and an action simply needs to be retried in order to succeed. 33 | Alternatively, there may be some remedial action needed before retrying. 34 | 35 | In the example flow, a Function simulates a random 36 | error - there is a 50% chance it will throw an error rather than pass on the message. 37 | 38 | The Catch receives the error which passes the message 39 | back to the Function node to retry. It also includes 40 | a Delay node as, in some circumstances, it is suitable 41 | to wait for a short interval before retrying. 42 | -------------------------------------------------------------------------------- /http/include-data-from-another-flow.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Include data captured in another flow 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - context 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to respond to an HTTP request using data captured by another flow. 13 | 14 | ### Solution 15 | 16 | Store data using `flow context` so that it can be retrieved within the HTTP flow. 17 | 18 | #### Example 19 | 20 | ![](/images/http/include-data-from-another-flow.png) 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"92eaf6c0.6d1508","type":"inject","z":"3045204d.cfbae","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":100,"y":480,"wires":[["8055b557.7faa48"]]},{"id":"8055b557.7faa48","type":"change","z":"3045204d.cfbae","name":"Store time","rules":[{"t":"set","p":"timestamp","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":480,"wires":[[]]},{"id":"93bf2335.6c40e","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-data","method":"get","swaggerDoc":"","x":120,"y":520,"wires":[["9e3aa25e.61c56"]]},{"id":"9e3aa25e.61c56","type":"change","z":"3045204d.cfbae","name":"Copy time","rules":[{"t":"set","p":"timestamp","pt":"msg","to":"timestamp","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":520,"wires":[["f2c385a.f0d3c78"]]},{"id":"f2c385a.f0d3c78","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n \n \n

Time: {{ timestamp }}

\n \n","x":470,"y":520,"wires":[["def756a1.2108a8"]]},{"id":"def756a1.2108a8","type":"http response","z":"3045204d.cfbae","name":"","x":610,"y":520,"wires":[]}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | ~~~text 30 | [~]$ curl http://localhost:1880/hello-data 31 | 32 | 33 | 34 |

Time: 1480201022517

35 | 36 | 37 | ~~~ 38 | {: .shell} 39 | 40 | ### Discussion 41 | 42 | There are many different ways data can be stored and retrieved within a flow. For 43 | example, using an external database. 44 | 45 | Node-RED provides the `flow context` as a simple key/value store that is accessible 46 | to all nodes on the same tab. 47 | 48 | The example above stores a timestamp generated by an Inject 49 | node into `flow context` using a Change node. The flow 50 | that handles the HTTP request then uses another Change node 51 | to retrieve the value, attaching it to the message which is then passed to a 52 | Template node to generate the response. 53 | -------------------------------------------------------------------------------- /basic/operate-on-array.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Perform an operation on each element in an array 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - operate on array 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to perform an operation on every element in an array. For example, 13 | given an array of numbers, you want to round each value to the nearest integer. 14 | 15 | ### Solution 16 | 17 | The Split node can be used to send a message for every 18 | element in the array. It can be followed by the nodes needed to operate on the 19 | individual elements, followed by a Join node to recombine 20 | them back into a single array. 21 | 22 | #### Example 23 | 24 | ![](/images/basic/operate-on-array.png){:width="645px"} 25 | 26 | {% raw %} 27 | ~~~json 28 | [{"id":"3149f240.c0e25e","type":"inject","z":"ac14500e.2c57d","name":"Array of decimals","topic":"","payload":"[1.67,2.98,3.12,4.99,5.50]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":960,"wires":[["bd57baa6.00f998"]]},{"id":"bd57baa6.00f998","type":"split","z":"ac14500e.2c57d","name":"Split array","splt":"\\n","spltType":"str","arraySplt":"1","arraySpltType":"len","stream":false,"addname":"","x":200,"y":1020,"wires":[["7ab9e9ed.d514b8"]]},{"id":"7ab9e9ed.d514b8","type":"range","z":"ac14500e.2c57d","minin":"0","maxin":"10","minout":"0","maxout":"10","action":"scale","round":true,"property":"payload","name":"Round value","x":350,"y":1020,"wires":[["f26660ab.007b3"]]},{"id":"f26660ab.007b3","type":"join","z":"ac14500e.2c57d","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":490,"y":1020,"wires":[["f9b5abac.f13828"]]},{"id":"f9b5abac.f13828","type":"debug","z":"ac14500e.2c57d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":550,"y":1080,"wires":[]}] 29 | ~~~ 30 | {: .flow} 31 | {% endraw %} 32 | 33 | ### Discussion 34 | 35 | In other programming environments, this task would be accomplished by creating a loop 36 | over the elements of the array. 37 | 38 | In Node-RED, the way to achieve the same thing is to turn the single message containing 39 | the array into a stream of messages that can be processed individually and finally 40 | recombine them back into one message. 41 | 42 | The Split/Join node pair are 43 | commonly used together to achieve this. The Split node 44 | adds the `msg.parts` property to each message in the stream which allows the 45 | Join node to properly reassemble the original message. 46 | -------------------------------------------------------------------------------- /styleguide.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Cookbook Style Guide 4 | lcb: "{" 5 | --- 6 | 7 | The cookbook recipes are intended to be task-focused guides to solving specific 8 | problems. 9 | 10 | They should follow a standard structure. Lines beginning with `>` are comments 11 | that should be removed: 12 | 13 | ~~~~~markdown 14 | --- 15 | layout: default 16 | title: A short (<10 words) summary of the task addressed 17 | --- 18 | 19 | ### Problem 20 | > A description of the problem solved by this recipe. It should be phrased as: 21 | You want to do X. 22 | 23 | ### Solution 24 | > A description of the solution (the 'how') without going into too much detail (the 'why'). 25 | Use the Inject node to do X. 26 | 27 | #### Example 28 | > The recipe should provide an example of the solution to illustrate it in action. 29 | 30 | ![](/images/flow-image.png) 31 | 32 | > Paste a sample flow json that can be imported by the reader 33 | {{ page.lcb }}% raw %} 34 | ~~~json 35 | [{"id":"7c87b536.83784c","type":"inject","z":"55635136.aa9cb","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":100,"y":80,"wires":[["7de918a6.8216e8"]]}] 36 | ~~~ 37 | {: .flow} 38 | {{ page.lcb }}% endraw %} 39 | 40 | > If the recipe can be meaningfully demonstrated in action via static text, add it here. 41 | 42 | ### Discussion 43 | > A more detailed discussion of the solution and additional relevant information 44 | > that will help the reader apply it to their own scenario. 45 | The Inject node code can be configured to do X. This 46 | means it can... 47 | 48 | ~~~~~ 49 | 50 | The following styles should be used consistently. 51 | 52 | #### Message properties 53 | 54 | Message properties should be enclosed in back-ticks: 55 | 56 | ~~~~~markdown 57 | Use the `msg.payload` property to ... 58 | ~~~~~ 59 | 60 | #### Node types 61 | 62 | When a specific node type is mentioned, it should be styled as: 63 | 64 | ~~~~~markdown 65 | Use the HTTP In node to ... 66 | ~~~~~ 67 | 68 | #### Example flows 69 | 70 | ~~~~~markdown 71 | {{ page.lcb }}% raw %} 72 | ~~~json 73 | > insert flow json here. It should be minified and on a single line. 74 | ~~~ 75 | {: .flow} 76 | {{ page.lcb }}% endraw %} 77 | ~~~~~ 78 | 79 | #### Terminal output 80 | 81 | ~~~~~markdown 82 | ~~~text 83 | [~]$ date 84 | Mon 28 Nov 2016 10:55:48 GMT 85 | ~~~ 86 | {: .shell} 87 | ~~~~~ 88 | 89 | #### JavaScript samples 90 | 91 | ~~~~~markdown 92 | ~~~javascript 93 | var name = msg.payload.name; 94 | ~~~ 95 | ~~~~~ 96 | 97 | #### JSON samples 98 | 99 | ~~~~~markdown 100 | ~~~json 101 | { 102 | "name": "Nick" 103 | } 104 | ~~~ 105 | ~~~~~ 106 | -------------------------------------------------------------------------------- /basic/route-on-property.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Route a message based on one of its properties 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - route on property 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to route a message to different flows according to the value of the 13 | `msg.topic` property. For example, you have an MQTT node 14 | subscribed to multiple sensors and you want to pass the messages to different 15 | Dashboard ui_gauge nodes. 16 | 17 | ### Solution 18 | 19 | Use the Switch node to check the value of the property 20 | against different values corresponding to different outputs of the node. 21 | 22 | #### Example 23 | 24 | ![](/images/basic/route-on-property.png){:width="601px"} 25 | 26 | {% raw %} 27 | ~~~json 28 | [{"id":"3bc8e1d2.744abe","type":"switch","z":"ac14500e.2c57d","name":"Route ","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"temperature","vt":"str"},{"t":"eq","v":"humidity","vt":"str"},{"t":"eq","v":"pressure","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":330,"y":420,"wires":[["907bf3b8.def45"],["fe425938.926838"],["ec261304.52f73"]]},{"id":"be3da36c.1c142","type":"inject","z":"ac14500e.2c57d","name":"","topic":"temperature","payload":"27","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":380,"wires":[["3bc8e1d2.744abe"]]},{"id":"f271ceef.172b3","type":"inject","z":"ac14500e.2c57d","name":"","topic":"humidity","payload":"45","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":420,"wires":[["3bc8e1d2.744abe"]]},{"id":"907bf3b8.def45","type":"debug","z":"ac14500e.2c57d","name":"Temperature","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":510,"y":380,"wires":[]},{"id":"fe425938.926838","type":"debug","z":"ac14500e.2c57d","name":"Humidity","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":500,"y":420,"wires":[]},{"id":"ec261304.52f73","type":"debug","z":"ac14500e.2c57d","name":"Pressure","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":500,"y":460,"wires":[]},{"id":"fca957dd.9d8078","type":"inject","z":"ac14500e.2c57d","name":"","topic":"pressure","payload":"1001","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":460,"wires":[["3bc8e1d2.744abe"]]}] 29 | ~~~ 30 | {: .flow} 31 | {% endraw %} 32 | 33 | ### Discussion 34 | 35 | The Switch node will send on messages it receives on the 36 | outputs that corresponding to rules that match. 37 | 38 | It can be configured to send on all rules that match, or only on the first one that 39 | matches. 40 | -------------------------------------------------------------------------------- /basic/split-text.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Split text into one message per line 4 | slug: 5 | - label: formats 6 | url: /#working-with-data-formats 7 | - text 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to perform an operation on every line in a block text. For example, 13 | you want to add the line number to the beginning of each line. 14 | 15 | ### Solution 16 | 17 | The Split node can be used to split the message into 18 | one message per line. It can be followed by the nodes needed to operate on the 19 | individual lines of text, followed by a Join node to 20 | recombine them back into a single block of text. 21 | 22 | 23 | #### Example 24 | 25 | ![](/images/basic/split-text.png){:width="717px"} 26 | 27 | {% raw %} 28 | ~~~json 29 | [{"id":"df6514f0.029748","type":"inject","z":"64133d39.bb0394","name":"inject","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":"","x":110,"y":900,"wires":[["11f53f61.2f7be1"]]},{"id":"11f53f61.2f7be1","type":"template","z":"64133d39.bb0394","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"one\ntwo\nthree\nfour\nfive","x":240,"y":900,"wires":[["760c1d71.c29744"]]},{"id":"760c1d71.c29744","type":"split","z":"64133d39.bb0394","name":"","splt":"\\n","x":190,"y":960,"wires":[["3e427aac.9b9596"]]},{"id":"3e427aac.9b9596","type":"change","z":"64133d39.bb0394","name":"Prepend line number","rules":[{"t":"set","p":"payload","pt":"msg","to":"(parts.index+1) & \": \" & payload","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":960,"wires":[["d44d4767.945fd8"]]},{"id":"d44d4767.945fd8","type":"join","z":"64133d39.bb0394","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","timeout":"","count":"","x":530,"y":960,"wires":[["bfe3e43b.85fa88"]]},{"id":"bfe3e43b.85fa88","type":"debug","z":"64133d39.bb0394","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":650,"y":960,"wires":[]}] 30 | ~~~ 31 | {: .flow} 32 | {% endraw %} 33 | 34 | ### Discussion 35 | 36 | In the example, the Inject and Template 37 | nodes are used to inject a block of text with multiple lines. 38 | 39 | ~~~text 40 | one 41 | two 42 | three 43 | four 44 | five 45 | ~~~ 46 | 47 | The Split node's default behaviour when passed a string 48 | is to split it into one message per line. 49 | 50 | The Change node modifies each message payload using 51 | a JSONata expression: `(parts.index+1) & ": " & payload` - which uses `msg.parts.index` 52 | to get the line number and prepends it to the existing `msg.payload`. 53 | 54 | Finally the Join node reassembles the messages into 55 | a single block of text: 56 | 57 | ~~~text 58 | 1: one 59 | 2: two 60 | 3: three 61 | 4: four 62 | 5: five 63 | ~~~ 64 | -------------------------------------------------------------------------------- /http/set-request-url.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Set the URL of a request 4 | slug: 5 | - label: http 6 | url: /#http-requests 7 | - set url 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to set the URL of an HTTP request node dynamically. 13 | 14 | ### Solution 15 | 16 | Set the URL property of the HTTP Request node. 17 | 18 | #### Example 19 | 20 | ![](/images/http/set-request-url.png) 21 | 22 | {% raw %} 23 | ~~~json 24 | [{"id":"b36aa30.3a7276","type":"http request","z":"c9a81b70.8abed8","name":"","method":"GET","ret":"txt","url":"","x":470,"y":300,"wires":[["1ef9987c.956c78"]]},{"id":"11167f67.5d5031","type":"inject","z":"c9a81b70.8abed8","name":"cars on craigslist","topic":"","payload":"http://vancouver.craigslist.org/search/sss?format=rss&query=cars","payloadType":"str","repeat":"","crontab":"","once":false,"x":140,"y":300,"wires":[["70154cd4.de1444"]]},{"id":"70154cd4.de1444","type":"change","z":"c9a81b70.8abed8","name":"","rules":[{"t":"set","p":"url","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":300,"wires":[["b36aa30.3a7276"]]},{"id":"1ef9987c.956c78","type":"debug","z":"c9a81b70.8abed8","name":"","active":true,"console":"false","complete":"false","x":630,"y":300,"wires":[]}] 25 | ~~~ 26 | {: .flow} 27 | {% endraw %} 28 | 29 | The Inject node generates a string URL, and the Change node sets the msg.url property. In this flow the URL is set to: 30 | 31 | {% raw %} 32 | ~~~text 33 | http://vancouver.craigslist.org/search/sss?format=rss&query=cars 34 | ~~~ 35 | {% endraw %} 36 | 37 | To return an RSS feed for cars for sale in Vancouver on Craigslist. It returns something like the following XML content in the debug window: 38 | 39 | {% raw %} 40 | ~~~text 41 | 42 | 43 | 55 | 56 | 57 | craigslist vancouver, BC | for sale search "cars" 58 | https://vancouver.craigslist.ca/search/sss?query=cars 59 | 60 | en-us 61 | copyright 2017 craiglist 62 | robot@craigslist.org 63 | robot@craigslist.org 64 | https://vancouver... 65 | ~~~ 66 | {% endraw %} 67 | 68 | ### Discussion 69 | 70 | An XML node can be added after the HTTP Request to change the XML RSS content returned to a JavaScript object for easy access to the data. 71 | -------------------------------------------------------------------------------- /basic/join-streams.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Create a single message from separate streams of messages 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - join streams 8 | --- 9 | 10 | ### Problem 11 | 12 | You have messages arriving from different sources that you need to combine into 13 | a single message. 14 | 15 | For example, you have three different sensors publishing values and you want to 16 | insert them into a database as a single entry. 17 | 18 | ### Solution 19 | 20 | Give each stream a unique `msg.topic` value and use the Join 21 | node to group them into a single message. 22 | 23 | #### Example 24 | 25 | ![](/images/basic/join-streams.png){:width="571px"} 26 | 27 | {% raw %} 28 | ~~~json 29 | [{"id":"8ccddb9a.a55f38","type":"inject","z":"ac14500e.2c57d","name":"temperature","topic":"temperature","payload":"10","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":1760,"wires":[["47b769c5.cb0e28"]]},{"id":"47b769c5.cb0e28","type":"join","z":"ac14500e.2c57d","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"3","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":310,"y":1800,"wires":[["f9afb265.b11b7"]]},{"id":"f9afb265.b11b7","type":"debug","z":"ac14500e.2c57d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":470,"y":1800,"wires":[]},{"id":"2d269127.4f04ce","type":"inject","z":"ac14500e.2c57d","name":"humidity","topic":"humidity","payload":"","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":1800,"wires":[["47b769c5.cb0e28"]]},{"id":"d6fbe805.0e4628","type":"inject","z":"ac14500e.2c57d","name":"pressure","topic":"pressure","payload":"999","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":1840,"wires":[["47b769c5.cb0e28"]]}] 30 | ~~~ 31 | {: .flow} 32 | {% endraw %} 33 | 34 | ### Discussion 35 | 36 | In the example flow, each Inject node represents a 37 | different source of messages. They each set a unique `msg.topic` value so they 38 | can be identified later in the flow. 39 | 40 | The Join node has been configured in manual mode to 41 | create a key/value object using `msg.topic` as the key name. As we know there 42 | are three separate streams of messages to join, the node has been to configure to 43 | send on a message when it receives that number of parts. 44 | 45 | This means it will send on a message each time it receives at least one message 46 | from three different topics - using the most recent value from each topic. 47 | 48 | ```json 49 | { 50 | "temperature":10, 51 | "humidity":0, 52 | "pressure":999 53 | } 54 | ``` 55 | 56 | The node has further options to change its behaviour that have not been used in 57 | this recipe. For example, a timeout can be set to ensure it sends *something* 58 | in case one of the sensors stops sending values. If that is a concern, you may 59 | consider [this recipe](/basic/trigger-placeholder) for providing a placeholder 60 | value. 61 | -------------------------------------------------------------------------------- /basic/trigger-placeholder.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Send placeholder messages when a stream stops sending 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - trigger placeholder 8 | --- 9 | 10 | ### Problem 11 | 12 | You have a stream of messages coming from a sensor at regular intervals. If the 13 | sensor stops sending messages, you want to send placeholder messages at the same 14 | rate. 15 | 16 | For example, the sensor data may be feeding a Dashboard chart. If the sensor 17 | stops sending, the chart will stop updating. So placeholder messages are needed 18 | for the chart to update with a `0` value to highlight the sensor has stopped. 19 | 20 | ### Solution 21 | 22 | Use the Trigger node to detect when a message has not 23 | arrived after a defined interval and a second Trigger node 24 | to send the placeholder messages at a regular interval. 25 | 26 | #### Example 27 | 28 | ![](/images/basic/trigger-placeholder.png){:width="711px"} 29 | 30 | {% raw %} 31 | ~~~json 32 | [{"id":"9ccdf268.c96ff","type":"inject","z":"ac14500e.2c57d","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":1660,"wires":[["38950a5.28d15f6","2c532f67.0330e"]]},{"id":"38950a5.28d15f6","type":"debug","z":"ac14500e.2c57d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":610,"y":1660,"wires":[]},{"id":"2c532f67.0330e","type":"trigger","z":"ac14500e.2c57d","op1":"reset","op2":"true","op1type":"str","op2type":"bool","duration":"2","extend":true,"units":"s","reset":"","bytopic":"all","name":"","x":260,"y":1700,"wires":[["e4e42b96.97a338"]]},{"id":"e4e42b96.97a338","type":"trigger","z":"ac14500e.2c57d","op1":"0","op2":"0","op1type":"num","op2type":"str","duration":"-2","extend":false,"units":"s","reset":"reset","bytopic":"all","name":"","x":420,"y":1700,"wires":[["38950a5.28d15f6"]]}] 33 | ~~~ 34 | {: .flow} 35 | {% endraw %} 36 | 37 | ### Discussion 38 | 39 | In the example flow, the top branch represents the normal flow of the messages, 40 | from the Inject node to the Debug 41 | node. 42 | 43 | The messages also get passed to the first Trigger node 44 | on a second branch of the flow. That node is configured to initially send a payload 45 | of `"reset"`, then to wait for 2 seconds before sending a timeout message. The 46 | option to extend the delay if new messages arrive is also selected. This means 47 | as long as messages continue to arrive, the node will not do anything. Once 2 48 | seconds passes after the last message to arrive, it will send on the timeout message. 49 | 50 | The timeout message feeds into a second Trigger node. This 51 | node is configured to send on `0` every two seconds and feeds back into the top 52 | branch. The node is also configured to stop sending if it receives a `msg.payload` 53 | of `"reset"`. As this is the initial message sent by the first 54 | Trigger node when it receives a sensor message, this will 55 | cause the second Trigger node to be reset when the sensor 56 | resumes sending its own messages. 57 | -------------------------------------------------------------------------------- /basic/convert-xml.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Convert to/from XML 4 | slug: 5 | - label: formats 6 | url: /#working-with-data-formats 7 | - xml 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to convert a message property between an XML string and the JavaScript object 13 | it represents. 14 | 15 | ### Solution 16 | 17 | The XML node can be used to convert between the two 18 | formats. 19 | 20 | #### Example 21 | 22 | ![](/images/basic/convert-xml.png){:width="684px"} 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"1b546d47.9474e3","type":"inject","z":"64133d39.bb0394","name":"XML String","topic":"","payload":"{\"a\":1}","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":260,"wires":[["d72b2bfd.77d068"]]},{"id":"1adf407d.6c4fe","type":"debug","z":"64133d39.bb0394","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":590,"y":260,"wires":[]},{"id":"46638890.8ae758","type":"inject","z":"64133d39.bb0394","name":"Object","topic":"","payload":"{\"note\":{\"$\":{\"priority\":\"high\"},\"to\":[\"Nick\"],\"from\":[\"Dave\"],\"heading\":[\"Reminder\"],\"body\":[\"Update the website\"]}}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":300,"wires":[["dae1d291.de0d2"]]},{"id":"6fefca67.3669e4","type":"debug","z":"64133d39.bb0394","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":430,"y":300,"wires":[]},{"id":"d72b2bfd.77d068","type":"template","z":"64133d39.bb0394","name":"","field":"payload","fieldType":"msg","format":"text","syntax":"plain","template":"\n Nick\n Dave\n Reminder\n Update the website\n","output":"str","x":280,"y":260,"wires":[["1746464a.87aa4a"]]},{"id":"1746464a.87aa4a","type":"xml","z":"64133d39.bb0394","name":"","property":"payload","attr":"","chr":"","x":430,"y":260,"wires":[["1adf407d.6c4fe"]]},{"id":"dae1d291.de0d2","type":"xml","z":"64133d39.bb0394","name":"","property":"payload","attr":"","chr":"","x":250,"y":300,"wires":[["6fefca67.3669e4"]]}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ### Discussion 32 | 33 | In the example, the first flow injects the XML: 34 | 35 | ~~~xml 36 | 37 | Nick 38 | Dave 39 | Reminder 40 | Update the website 41 | 42 | ~~~ 43 | 44 | The XML node then converts it to the equivalent JavaScript 45 | object: 46 | 47 | ~~~json 48 | { 49 | "note": { 50 | "$": { 51 | "priority":"high" 52 | }, 53 | "to": ["Nick"], 54 | "from": ["Dave"], 55 | "heading": ["Reminder"], 56 | "body": ["Update the website"] 57 | } 58 | } 59 | ~~~ 60 | 61 | Note how the attributes of the `` tag have been added under the `$` property 62 | of the `note` object. 63 | 64 | The second flow does the reverse, injecting that object and converting it to XML. 65 | 66 | When a particular XML output format is required, it can be easier to start by 67 | injecting that into the XML node to see the necessary 68 | JavaScript object required to generate it when fed back the other way. 69 | -------------------------------------------------------------------------------- /basic/route-on-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Route or block a message based on a context value 4 | slug: 5 | - label: flow control 6 | url: /#flow-control 7 | - route on context 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to route a message to different flows, or stop it entirely, according to the current value 13 | of another variable. 14 | 15 | ### Solution 16 | 17 | Save the switching variable into a flow context variable, and then use a Switch node to check the value of that flow context 18 | property against different values corresponding to different outputs of the node. 19 | 20 | #### Example 21 | 22 | ![](/images/basic/route-on-context.png){:width="643px"} 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"a62d8cdf.1bd82","type":"inject","z":"9138b11f.f64d5","name":"Inject","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"2","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":585,"wires":[["b8bbbc41.f272"]]},{"id":"b8bbbc41.f272","type":"switch","z":"9138b11f.f64d5","name":"Context based routing","property":"state","propertyType":"flow","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"2","vt":"num"},{"t":"eq","v":"3","vt":"num"}],"checkall":"true","repair":false,"outputs":3,"x":350,"y":585,"wires":[["9aa9c6b2.18a8e8"],["6ba4ec46.476794"],["a2e806c8.ffa168"]]},{"id":"9aa9c6b2.18a8e8","type":"debug","z":"9138b11f.f64d5","name":"Output 1","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","x":590,"y":525,"wires":[]},{"id":"6ba4ec46.476794","type":"debug","z":"9138b11f.f64d5","name":"Output 2","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","x":590,"y":585,"wires":[]},{"id":"a2e806c8.ffa168","type":"debug","z":"9138b11f.f64d5","name":"Output 3","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","x":590,"y":645,"wires":[]},{"id":"8aabdb51.e8b538","type":"inject","z":"9138b11f.f64d5","name":"Set state 0 - turn off","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":180,"y":675,"wires":[["e46083e4.1f17b"]]},{"id":"d1722dee.48db4","type":"inject","z":"9138b11f.f64d5","name":"Set state 1 - send to output 1","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":210,"y":720,"wires":[["e46083e4.1f17b"]]},{"id":"4bdb08de.706328","type":"inject","z":"9138b11f.f64d5","name":"Set state 2 - send to output 2","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2","payloadType":"num","x":210,"y":765,"wires":[["e46083e4.1f17b"]]},{"id":"220ce0a6.cf81e","type":"inject","z":"9138b11f.f64d5","name":"Set state 3 - send to output 3","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3","payloadType":"num","x":210,"y":810,"wires":[["e46083e4.1f17b"]]},{"id":"e46083e4.1f17b","type":"change","z":"9138b11f.f64d5","name":"Set flow.state","rules":[{"t":"set","p":"state","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":720,"wires":[[]]}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ### Discussion 32 | 33 | In the example flow, the top flow represents the stream of messages that can be stopped or routed to one of the three outputs by the Switch node. 34 | 35 | The bottom flow provides a set of Inject nodes to change 36 | the current value of the `flow.state` context property. 37 | 38 | This can be used as a simple gate like function - based on the value of some other input, that you store into the `flow.state` context property. 39 | 40 | This shows how the routing in one flow can be changed by a separate flow. 41 | -------------------------------------------------------------------------------- /basic/generate-csv.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Generate CSV output 4 | slug: 5 | - label: formats 6 | url: /#working-with-data-formats 7 | - csv 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to generate valid CSV output from a message containing key/value pairs of 13 | data. 14 | 15 | ### Solution 16 | 17 | The CSV node can be used to generate well-formatted CSV 18 | strings. 19 | 20 | #### Example 21 | 22 | ![](/images/basic/generate-csv.png){:width="688px"} 23 | 24 | {% raw %} 25 | ~~~json 26 | [{"id":"457d9ad6.b737b4","type":"inject","z":"64133d39.bb0394","name":"single","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":640,"wires":[["1e05fafd.887b05"]]},{"id":"1e05fafd.887b05","type":"change","z":"64133d39.bb0394","name":"Generate single payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"{ \"a\":$floor(100*$random()),\"b\":$floor(100*$random()),\"c\":$floor(100*$random())}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":640,"wires":[["e9546682.b39898"]]},{"id":"e9546682.b39898","type":"csv","z":"64133d39.bb0394","name":"","sep":",","hdrin":"","hdrout":false,"multi":"one","ret":"\\n","temp":"a,b,c","skip":"0","x":450,"y":640,"wires":[["f83ad3b0.78d32"]]},{"id":"f83ad3b0.78d32","type":"debug","z":"64133d39.bb0394","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":590,"y":640,"wires":[]},{"id":"ae242f2c.d1c8a","type":"inject","z":"64133d39.bb0394","name":"array","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":700,"wires":[["7535f521.4a88bc"]]},{"id":"7535f521.4a88bc","type":"change","z":"64133d39.bb0394","name":"Generate array payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"[\t { \"a\":$floor(100*$random()),\"b\":$floor(100*$random()),\"c\":$floor(100*$random())},\t { \"a\":$floor(100*$random()),\"b\":$floor(100*$random()),\"c\":$floor(100*$random())},\t { \"a\":$floor(100*$random()),\"b\":$floor(100*$random()),\"c\":$floor(100*$random())},\t { \"a\":$floor(100*$random()),\"b\":$floor(100*$random()),\"c\":$floor(100*$random())}\t]","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":700,"wires":[["f4e0465f.ef0338"]]},{"id":"f4e0465f.ef0338","type":"csv","z":"64133d39.bb0394","name":"","sep":",","hdrin":"","hdrout":true,"multi":"one","ret":"\\n","temp":"a,b,c","skip":"0","x":450,"y":700,"wires":[["6eb67fdf.58626"]]},{"id":"6eb67fdf.58626","type":"debug","z":"64133d39.bb0394","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":590,"y":700,"wires":[]}] 27 | ~~~ 28 | {: .flow} 29 | {% endraw %} 30 | 31 | ### Discussion 32 | 33 | In the example, the first flow injects a payload containing a single object with three 34 | properties containing randomly generated values. 35 | 36 | ```javascript 37 | { 38 | a: 10, 39 | b: 20, 40 | c: 30 41 | } 42 | ``` 43 | 44 | The CSV has been configured with the desired column names 45 | and uses the corresponding object properties to fill in those columns. 46 | 47 | The resulting message contains the well-formatted CSV string for that single row 48 | of data - including a newline character at the end. 49 | 50 | ```javascript 51 | "10,20,30\n" 52 | ``` 53 | 54 | This is suitable for passing to a File Out node to 55 | append to an existing CSV file. 56 | 57 | The second flow injects an array of objects with randomly generated values: 58 | 59 | ```javascript 60 | [ 61 | { a: 80, b: 18, c: 2}, 62 | { a: 52, b: 36, c: 10}, 63 | { a: 91, b: 18, c: 61}, 64 | { a: 32, b: 47, c: 65}, 65 | ] 66 | ``` 67 | 68 | Again the CSV node has been configured with the column names to use. It has also 69 | been configured to include the column names as the first row of output. 70 | 71 | ```javascript 72 | a,b,c 73 | 80,18,2 74 | 52,36,10 75 | 91,18,61 76 | 32,47,65 77 | ``` 78 | -------------------------------------------------------------------------------- /_includes/footer.html: -------------------------------------------------------------------------------- 1 | 41 | 42 | 45 | 46 | 47 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /http/work-with-cookies.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Work with cookies 4 | slug: 5 | - label: http 6 | url: /#http-endpoints 7 | - cookies 8 | --- 9 | 10 | ### Problem 11 | 12 | You want to create an HTTP flow that uses cookies. 13 | 14 | ### Solution 15 | 16 | The messages sent by the HTTP In node include the 17 | `msg.req.cookies` property that lists the cookies set on the current request. 18 | 19 | The HTTP Response node will use the `msg.cookies` property 20 | in order to set or clear cookies. 21 | 22 | The HTTP Request node will accept an input property of `msg.cookies` containing the cookies to be sent with that request. 23 | #### Example 24 | 25 | ![](/images/http/work-with-cookies.png) 26 | 27 | {% raw %} 28 | ~~~json 29 | [{"id":"c362b989.954ae8","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-cookie","method":"get","swaggerDoc":"","x":130,"y":1020,"wires":[["21ddf71f.d00518"]]},{"id":"21ddf71f.d00518","type":"function","z":"3045204d.cfbae","name":"Format cookies","func":"msg.payload = JSON.stringify(msg.req.cookies,null,4);\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":1020,"wires":[["f3aa98c1.befc18"]]},{"id":"f3aa98c1.befc18","type":"template","z":"3045204d.cfbae","name":"page","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n \n \n

Cookies

\n

Add a cookieClear cookies

\n
{{ payload }}
\n \n","x":530,"y":1020,"wires":[["f52e2880.180968"]]},{"id":"f52e2880.180968","type":"http response","z":"3045204d.cfbae","name":"","x":750,"y":1020,"wires":[]},{"id":"9a2a9a4.0fc0768","type":"change","z":"3045204d.cfbae","name":"Redirect to /hello-cookie","rules":[{"t":"set","p":"statusCode","pt":"msg","to":"302","tot":"num"},{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.location","pt":"msg","to":"/hello-cookie","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":1080,"wires":[["f52e2880.180968"]]},{"id":"afefb90.53dcf48","type":"function","z":"3045204d.cfbae","name":"Add a cookie","func":"msg.cookies = { };\nmsg.cookies[\"demo-\"+(Math.floor(Math.random()*1000))] = Date.now();\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":1060,"wires":[["9a2a9a4.0fc0768"]]},{"id":"d5205a2c.db9018","type":"function","z":"3045204d.cfbae","name":"Clear cookies","func":"// Find demo cookies and clear them\nvar cookieNames = Object.keys(msg.req.cookies).filter(function(cookieName) { return /^demo-/.test(cookieName);});\nmsg.cookies = {};\n\ncookieNames.forEach(function(cookieName) {\n msg.cookies[cookieName] = null;\n});\n\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":1100,"wires":[["9a2a9a4.0fc0768"]]},{"id":"fda60c66.04975","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-cookie/add","method":"get","swaggerDoc":"","x":140,"y":1060,"wires":[["afefb90.53dcf48"]]},{"id":"35285a76.1f8636","type":"http in","z":"3045204d.cfbae","name":"","url":"/hello-cookie/clear","method":"get","swaggerDoc":"","x":140,"y":1100,"wires":[["d5205a2c.db9018"]]}] 30 | ~~~ 31 | {: .flow} 32 | {% endraw %} 33 | 34 | This example provides three HTTP endpoints: 35 | 36 | - `/hello-cookie` returns a page that lists the cookies currently set 37 | - `/hello-cookie/add` adds a new cookie and redirects back to `/hello-cookie` 38 | - `/hello-cookie/clear` clears all cookies created by the example and redirects back to `/hello-cookie` 39 | 40 | ### Discussion 41 | 42 | The `msg.req.cookies` property is an object of key/value pairs containing the cookies 43 | set on the current request. 44 | 45 | ~~~javascript 46 | var mySessionId = msg.req.cookies['sessionId']; 47 | ~~~ 48 | 49 | In order to set a cookie in the response, the `msg.cookies` property should be set 50 | to a similar key/value object. 51 | 52 | The value can be either a string to set the value of the cookie with default 53 | options, or it can be an object of options. 54 | 55 | The following example sets two cookies - one called `name` with a value of `Nick`, the other called `session` with a value of `1234` and an expiry set to 15 minutes. 56 | 57 | ~~~javascript 58 | msg.cookies = { 59 | name: 'nick', 60 | session: { 61 | value: '1234', 62 | maxAge: 900000 63 | } 64 | } 65 | ~~~ 66 | 67 | The valid options include: 68 | 69 | - `domain` - (String) domain name for the cookie 70 | - `expires` - (Date) expiry date in GMT. If not specified or set to 0, creates a session cookie 71 | - `maxAge` - (String) expiry date as relative to the current time in milliseconds 72 | - `path` - (String) path for the cookie. Defaults to / 73 | - `value` - (String) the value to use for the cookie 74 | 75 | To delete a cookie, set its value to null. 76 | 77 | #### URL Encoding of Cookies 78 | The HTTP Request node will accept a property of `encode : false` within the cookie which will avoid the value being URL Encoded when sent in the request 79 | 80 | ~~~javascript 81 | msg.cookies = { 82 | myCookie : { 83 | Path : "/", 84 | value : "ysjLVJA==", 85 | encode : false 86 | } 87 | } 88 | ~~~ 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /css/style.min.css: -------------------------------------------------------------------------------- 1 | *,*:after,*:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0px}[class*='col-']{float:left;padding-left:17px;padding-right:17px}[class*='col-']:last-of-type{padding-right:0px}[class*='col-']:first-of-type{padding-left:0px}.grid{width:100%;max-width:1155px;min-width:755px;margin:0 auto;overflow:hidden;padding:0px 75px 0 75px}.grid:after{content:"";display:table;clear:both}.col-1-1{width:100%}.col-2-3,.col-8-12{width:66.66%}.col-1-2,.col-6-12{width:50%}.col-1-3,.col-4-12{width:33.33%}.col-1-4,.col-3-12{width:25%}.col-3-4,.col-4-12{width:75%}.col-1-5{width:20%}.col-1-6,.col-2-12{width:16.667%}.col-1-7{width:14.28%}.col-1-8{width:12.5%}.col-1-9{width:11.1%}.col-1-10{width:10%}.col-1-11{width:9.09%}.col-1-12{width:8.33%}.col-11-12{width:91.66%}.col-10-12{width:83.333%}.col-9-12{width:75%}.col-5-12{width:41.66%}.col-7-12{width:58.33%}@media handheld, only screen and (max-width: 767px){.grid{width:100%;min-width:0;margin-left:0px;margin-right:0px;padding-left:15px !important;padding-right:15px !important}[class*='col-']{width:auto;float:none;margin-left:0px;margin-right:0px;margin-top:10px;margin-bottom:10px;padding-left:0px !important;padding-right:0px !important}}body{color:#555;background:#eee;font-family:'Open Sans', sans-serif;font-size:16px;line-height:1.6em}a{text-decoration:none}a:hover{text-decoration:underline}a.button{box-shadow:rgba(0,0,0,0.4) 5px 5px 20px;text-decoration:none;display:inline-block;padding:20px 60px;border-radius:5px;background:#aa6767;color:#eee}a.button:hover{background:#7F4545}h1,h2,h3,h4,h5{font-family:"Roboto Slab";font-weight:normal}h1{font-size:36px;font-weight:bold}h2{font-size:30px}h3{font-size:24px}h4{font-size:18px}dd{margin-bottom:10px}.header{font-family:"Roboto Slab";font-size:20px;line-height:50px;color:#999;padding:0px 10px;min-height:50px;background:#333}.header-content{width:100%;max-width:1155px;min-width:755px;margin:0 auto;background:#333;border-bottom:5px solid #333}.header-content .brand{display:inline-block;float:left;margin-top:3px}.header-content .brand a{display:block;font-size:20px;border-bottom:5px solid #333;padding:0 15px 0 15px;color:#eee}.header-content .logo{vertical-align:middle;height:20px}.header-content a{color:#999}.header-content ul{margin-left:170px;text-align:right;list-style-type:none;margin:-0;padding:0}.header-content li{display:inline-block;margin:0;padding:0}.header-content li a{display:block;font-size:16px;padding:0 15px 0 15px;border-bottom:5px solid #333}.header-content li.current a{color:#fff;border-bottom:5px solid #fff}.header a:hover{text-decoration:none;color:#eee}.header a:hover{border-bottom:5px solid #eee}.header .menu{position:absolute;top:7px;right:10px;display:none;width:55px;height:45px;color:#999;text-align:center}.footer{background:#333}.footer .grid{color:#eee;padding:20px 0;font-family:Arial}.footer a{color:#eee}.footer .content{height:auto;min-height:0;margin:20px auto}.footer .headline{margin:80px auto 20px auto}.footer{text-align:center}.ets-link{margin:10px 0px}.ets-globe{width:60px;vertical-align:middle}.hide{display:none}.title>.grid{overflow:visible}.title>.grid>.col-1-1{position:relative}.ribbon{position:absolute;left:-5px;top:-5px;z-index:1;width:400px;padding:30px;text-align:right}.ribbon>a{border-top-right-radius:10px;border-top-left-radius:10px;font-weight:bold;color:#e9e9e9;text-align:center;line-height:20px;transform:rotate(-45deg);width:400px;display:block;background:#c15555;box-shadow:0 3px 10px -5px #000;position:absolute;top:76px;left:-110px;font-size:22px;line-height:1.1em;padding:10px;font-family:"Roboto Slab";box-shadow:0 10px 10px -5px #7b7b7b}.ribbon>a:hover{text-decoration:none;color:#fff}.ribbon>a>span{font-size:18px}.ribbon>a::before{content:"";position:absolute;left:0px;top:100%;z-index:-1;border-left:8px solid #6F0808;border-right:8px solid transparent;border-bottom:8px solid transparent;border-top:8px solid #6F0808}.ribbon .edge{position:absolute;width:80px;height:380px;left:-15px;top:0px;box-shadow:-11px 0 22px -14px grey}.ribbon>a::after{content:"";position:absolute;right:0px;top:100%;z-index:-1;border-left:8px solid transparent;border-right:8px solid #6F0808;border-bottom:8px solid transparent;border-top:8px solid #6F0808}@media handheld, only screen and (max-width: 767px){.ribbon{margin:auto;transform:none;position:static;width:100%}.ribbon>a{width:100%;transform:none;position:static;border-radius:5px;box-shadow:none}.ribbon>a::after{display:none}.ribbon>a::before{display:none}.ribbon .edge{display:none}}@media handheld, only screen and (max-width: 755px){.header .menu{display:inline-block}.header-content{min-width:0}.header-content .brand{float:none}.header-content ul{display:none;margin:0;text-align:right}.header-content li{display:block;line-height:26px}.header-content li.current a{border:none}.header ul a:hover{border:none}}.title{background:url(../images/title-wave.png) no-repeat 0% 100% #8f0000;border-top:10px solid #a22222;text-align:center}.intro,.nodes{background:#eee}.intro{border-bottom:2px solid #a22222}.features{background:#fff}.links{background:#444}.platforms{background:#fff;border-top:2px solid #a22222;border-bottom:2px solid #a22222}.platforms img{width:100%;max-width:300px}.platforms h4{text-align:center;margin:5px 0}a{color:#aa6767}a:hover{color:#B77777}.openjs-logo{max-width:200px}.links .grid{font-size:16px;color:#aaa;padding-top:20px;padding-bottom:20px;font-family:Arial}.links .openjs{font-size:13px;line-height:1.4em}.links p{margin:0 0 10px 0}.links a{color:#eee}.links ul{margin:0;padding:0;padding-left:15px}.links .content{height:auto;min-height:0;margin:10px auto}.content{min-height:250px;padding:60px 0;margin:0}.blurb p{margin-top:0}.blurb h3,.nodes h3,.features h3,.community h3,.platforms h3{margin-top:0;margin-bottom:0.5em}.feature{max-width:485px;margin-left:auto;margin-right:auto;text-align:center}.feature img{max-width:445px;width:100%}.title .content{height:280px}.title h1{margin-top:20px;margin-bottom:10px;color:#f0f0f0;font-size:2.5em;font-weight:normal}.title h2{margin-top:0px;font-size:20px;font-family:"Open Sans", sans-serif;font-weight:normal;color:#c19e9e}.title p{color:#c19e9e}.title img{margin:auto;max-width:769px;width:100%}.nodes .content{height:auto;min-height:0;margin:40px auto}.nodes .col-1-2 .content{width:290px}.nodes .grid{padding-top:40px;padding-bottom:40px}.node-image{width:100px;vertical-align:top;display:inline-block}.node-block{vertical-align:top;display:inline-block;margin-left:10px}.headline h3{text-align:center}.nodes h4{font-family:"Arial";font-weight:normal;margin-top:3px;height:50px;font-size:16px}.helplink{text-align:center;font-size:14px}.helplink a{color:#aa6767}.community{background:#fff}.community ul{list-style-type:none;margin:0;padding:0}.community h4{text-align:center}.community .blog ul li a{display:inline-block;padding:10px 5px;width:100%;height:100%}.community .blog ul li{padding:0}.community .blog ul li:not(:last-child){border-bottom:1px dashed #999}.community .blog ul li span.date{float:right;font-size:0.8em}.community .blog ul li .box-link{border:none}.community .social ul li{display:inline-block;box-sizing:border-box;width:calc( 50% - 25px );height:120px;margin:10px;text-align:center}.community .social ul li a{display:block;width:100%;height:100%;padding-top:25px}.community .social ul li a:hover{text-decoration:none;background:#aa6767;color:#eee}.users{background:#fff;border-bottom:2px solid #a22222}.users h3{margin-top:0;text-align:center}.users ul{text-align:center;list-style-type:none;margin:0;padding:0}.users ul li{margin:10px;padding:10px;display:inline-block;width:200px;vertical-align:middle;background:white}.users ul li img{width:100%}h3 a.box-link{margin-left:30px}a.box-link{font-size:14px;display:inline-block;padding:3px 12px;border:1px solid #aa6767;border-radius:1px}a.box-link:hover{text-decoration:none;background:#aa5555;color:#eee}a.box-link:not(:first-child){margin-left:20px}a.box-link img{width:150px}.node-red-latest-version{color:#c19e9e}.docs-intro{background:#8f0000;border-top:10px solid #a22222}.docs-intro .content{height:100px}.docs-intro h1{text-align:center;font-size:2em;margin:80px 0 100px 0;padding:0;color:#f0f0f0;font-weight:normal}.doc-items{list-style-type:none;padding:0;margin:0 30px 0}.doc-items li{box-sizing:border-box;background:#eee;display:inline-block;width:calc(33% - 30px);margin:10px 10px 50px 10px}.doc-items li a.box-link{padding:15px 10px;width:100%;height:100%}.doc-items img{vertical-align:top;margin-top:-50px}.doc-items h2{margin:0;font-size:20px;color:#333}.doc-items li a.box-link:hover h2{color:#eee}.doc-item-info{padding:10px}.docs{background:#eee;font-size:16px}.docs .grid{background:#fff;padding:0 30px;min-height:730px}h1.docs-title{border-bottom:2px solid #B68181;padding-bottom:10px;margin-top:30px;margin-bottom:40px;font-weight:normal}ul.fixed-toc{position:fixed;top:-28px}ul.toc{overflow:hidden;list-style-type:none;margin:40px 0 20px 0;padding:0;font-size:14px;line-height:16px;z-index:1}ul.toc ul{list-style-type:none;padding:0}ul.toc li:not(.tocheader) a,ul.toc li span{padding:6px 10px;display:block;width:100%;height:100%;color:#666;border-left:8px solid #eee;-border-right:2px solid #B68181;-margin-bottom:3px}ul.toc li.tocheader>span a{display:block;width:100%;height:100%;color:#fff}ul.toc li.tocheader{margin-top:3px}ul.toc li:not(.tocheader).active a{border-left-color:#B68181;-border-right-color:#eee;background:#eee}ul.toc li:not(.tocheader) a:hover{border-left-color:#B68181}ul.toc li.tocheader>span{background:#B68181;border-left-color:#B68181;color:#fff}ul.toc li.tocsubheader span{background:none;border-left-color:#eee;color:#666;padding-left:20px}ul.toc li.tocheader li:not(.toctitle) a{padding-left:30px;font-size:14px}ul.toc li.tocheader li.toctitle a{font-size:15px;line-height:18px}.docs-content a{color:#aa4444}.docs-content img{max-width:100%}.docs-content pre{padding:10px;background:#564848;color:#eee;border-radius:3px}.docs-content pre code{background:inherit;color:inherit}pre,code,.code{font-family:'Ubuntu Mono', Monospace}.code{color:#533;background:#F3E7E7;padding:2px}code{color:#533;background:#F3E7E7;padding:2px 4px;border-radius:2px}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}pre>code{padding:0}h1{font-size:2em;line-height:1.3em;margin:1.5em 0 0.8em -0.3em}h2{font-size:1.953em;line-height:1.2em;margin:1.5em 0 0.8em -0.3em}h3{font-size:1.563em;margin:1.5em 0 0.8em -0.3em}h4{font-size:1.25em;margin:1.5em 0 0.8em -0.3em}h5{font-size:1.15em;margin:1.5em 0 0.8em -0.3em}.doc-callout{margin:10px 0px;background:#FFFFE0;padding:10px;border-radius:3px;font-size:0.85em;border:1px solid #BE9A9A}ul.toc li.toc-expander{display:none;position:absolute;width:35px;right:0;top:3px;height:37px;background:#B68181;text-align:center;line-height:37px;border-left:1px solid #fff;color:#eee;z-index:10}code.node{border-radius:6px;border:1px solid #B68181;background:#fff0f0;color:#555;white-space:nowrap}div.figure{max-width:100%}div.figure img{display:block;max-width:100%;height:auto;box-shadow:4px 4px 8px 0px #ddd}div.figure.align-left{float:left;width:auto;margin:5px 20px 5px 0}div.figure.align-right{float:right;width:auto;margin:5px 0 5px 20px}div.figure.align-center{margin:5px auto}div.figure p.caption{margin-top:3px;font-size:0.9em;font-style:italic;color:#999}div.figure pre{margin-bottom:0px}table.action-ref{width:auto;min-width:310px;float:right;font-size:0.9em;background:#ffeecc;margin-bottom:10px;margin-left:20px}table.action-ref.inline{float:none;margin-left:0}table.action-ref th{font-weight:bold;font-family:inherit;border-bottom:inherit;text-align:left;font-size:inherit;padding:0px 20px 0px 5px}table.action-ref td{padding:0px 5px;border:none}table.action-ref td:first-child{width:110px;border:none}table.action-ref code{background:none;font-size:1.1em}ul.multi-column-toc{column-count:3}@media handheld, only screen and (max-width: 755px){ul.toc:not(.open) li.tocheader>span{display:none}ul.toc:not(.open) li.tocheader li:not(.active){display:none}ul.toc:not(.open) li.tocheader li.active{padding-right:37px}ul.toc li.toc-expander{display:inline}ul.toc.open li.toc-expander div{transform:rotate(180deg)}.doc-items li{width:calc(50% - 30px)}ul.fixed-toc{position:relative;top:auto}}@media handheld, only screen and (max-width: 450px){.doc-items li{width:100%}ul.multi-column-toc{column-count:2}}table{border-collapse:collapse;border-spacing:0;width:100%;max-width:100%}th{font-family:"Roboto Slab";font-weight:normal;text-align:left;font-size:20px;padding:0 5px;border-bottom:2px solid #B68181}th:not(:first-child),td:not(:first-child){border-left:1px solid #F4DEDE}td{padding:6px 5px;border-bottom:1px solid #F4DEDE}td:first-child span.method{font-family:"Ubuntu Mono";font-size:0.9em;background:#F4DEDE;border:1px solid #F4DEDE;border-radius:3px;width:60px;text-align:center;display:inline-block;margin-right:10px}td:first-child{padding:6px 15px 6px 5px;width:1px;white-space:nowrap}th:first-child{padding:6px 15px 6px 5px;width:1px;white-space:nowrap}.breadcrumbs{min-height:0;height:50px}.breadcrumbs>div.grid{color:white;background:#aa4444;min-height:0;line-height:50px;font-size:20px;font-family:Roboto Slab}.breadcrumbs>div a{color:white;opacity:0.9;display:inline-block;height:45px;box-sizing:border-box;border-bottom:4px solid transparent}.breadcrumbs>div a:hover,.breadcrumbs>div a.active{text-decoration:none;border-color:white;opacity:1}.breadcrumbs>div>a:not(:first-child){margin-left:10px}.breadcrumbs>div>a{margin-right:10px}.post-header h2{margin-bottom:10px}.sidebar a.fa{margin:0 10px;font-size:20px}.sidebar h4{margin-top:0px;margin-bottom:5px}.sidebar p{margin:5px 0}.pagination{padding:25px 0;text-align:center}.page-number{display:inline-block;width:200px}.sidebar{margin-top:30px}.post-meta{color:#999}.post-footer{margin:30px auto;border:1px solid #999;padding:10px}.blog-posts .grid{background:#f3f3f3}.blog-posts .post-preview{margin:20px;top:-50px;border-color:#fff;width:calc(100% / 3 - 60px);height:300px}.blog-posts .post-preview .post-header{text-align:left}.blog-posts .post-preview .post-header .post-meta{text-align:center}.post-preview{background:white;position:relative;display:inline-block;margin:0 10px 10px;width:calc(100% / 3 - 30px);overflow:hidden;height:250px;text-overflow:ellipsis;border:1px solid #999;border-radius:3px}.post-preview .post-header{text-align:center}.post-preview .post-header img{max-width:100px}.post-preview>a{padding:10px 15px;position:absolute;top:0;left:0;right:0;bottom:0}.post-preview>a:hover{text-decoration:none}.post-preview:hover{border-color:#999;box-shadow:0px 2px 3px 0 #aaa}.post-preview .post-content{margin-top:5px;color:#666;font-size:0.9em;overflow:hidden;max-height:180px}.post-preview p{margin:0}.post-preview h2{margin:5px 0 5px;font-size:22px}.post-preview h3{margin:5px 0 5px;font-size:18px}.post-preview .post-link{position:absolute;bottom:0;left:0;right:0;height:30px;padding-right:10px;background:rgba(255,255,255,0.5);text-align:right}.post-preview .post-meta{font-size:0.9em}blockquote{border-left:10px solid #eee;padding-left:10px;font-style:italic;color:#888}.blog-posts-title{height:100px;background:url(../images/title-wave.png) no-repeat 0% 25% #8f0000;border-top:10px solid #a22222;text-align:center}.blog-posts .grid{overflow:visible}@media handheld, only screen and (max-width: 850px){.blog-posts .post-preview{width:calc(100% / 2 - 50px);height:200px}}@media handheld, only screen and (max-width: 767px){.blog-posts .post-preview{width:calc(100% - 40px);height:200px}}.highlight pre{background-color:#564848}.highlight .hll{background-color:#564848}.highlight .c{color:#75715e}.highlight .err{color:#ff58b1;background-color:#564848}.highlight .k{color:#66d9ef}.highlight .l{color:#ae81ff}.highlight .n{color:#f8f8f2}.highlight .o{color:#dc8f8f}.highlight .p{color:#f8f8f2}.highlight .cm{color:#afa98c}.highlight .cp{color:#afa98c}.highlight .c1{color:#afa98c}.highlight .cs{color:#afa98c}.highlight .ge{font-style:italic}.highlight .gs{font-weight:bold}.highlight .kc{color:#66d9ef}.highlight .kd{color:#66d9ef}.highlight .kn{color:#dc8f8f}.highlight .kp{color:#66d9ef}.highlight .kr{color:#66d9ef}.highlight .kt{color:#66d9ef}.highlight .ld{color:#e6db74}.highlight .m{color:#ae81ff}.highlight .s{color:#e6db74}.highlight .na{color:#a6e22e}.highlight .nb{color:#f8f8f2}.highlight .nc{color:#a6e22e}.highlight .no{color:#66d9ef}.highlight .nd{color:#a6e22e}.highlight .ni{color:#f8f8f2}.highlight .ne{color:#a6e22e}.highlight .nf{color:#a6e22e}.highlight .nl{color:#f8f8f2}.highlight .nn{color:#f8f8f2}.highlight .nx{color:#a6e22e}.highlight .py{color:#f8f8f2}.highlight .nt{color:#dc8f8f}.highlight .nv{color:#f8f8f2}.highlight .ow{color:#dc8f8f}.highlight .w{color:#f8f8f2}.highlight .mf{color:#ae81ff}.highlight .mh{color:#ae81ff}.highlight .mi{color:#ae81ff}.highlight .mo{color:#ae81ff}.highlight .sb{color:#e6db74}.highlight .sc{color:#e6db74}.highlight .sd{color:#e6db74}.highlight .s2{color:#e6db74}.highlight .se{color:#ae81ff}.highlight .sh{color:#e6db74}.highlight .si{color:#e6db74}.highlight .sx{color:#e6db74}.highlight .sr{color:#e6db74}.highlight .s1{color:#e6db74}.highlight .ss{color:#e6db74}.highlight .bp{color:#f8f8f2}.highlight .vc{color:#f8f8f2}.highlight .vg{color:#f8f8f2}.highlight .vi{color:#f8f8f2}.highlight .il{color:#ae81ff}.highlight .gu{color:#75715e}.highlight .gd{color:#dc8f8f}.highlight .gi{color:#a6e22e} 2 | --------------------------------------------------------------------------------