node-red-contrib-ruuvi-gateway 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Dirk-Jan Faber
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Ruuvi gateway node
2
+
3
+ ![Example flow](./documentation/images/flow.png)
4
+
5
+ The node uses the [poll mode](https://docs.ruuvi.com/gw-examples/polling-mode) to retrieve
6
+ the values from the [Ruuvi Gateway](https://ruuvi.com/gateway/) and gets triggered when receiving input.
7
+
8
+ ### Inputs
9
+
10
+ - `payload (string|number|json)` : the trigger to poll the Ruuvi gateway
11
+
12
+ The `msg.topic` is filled with the MAC address of the queried gateway.
13
+
14
+ ### Outputs
15
+
16
+ - `payload (json)` : the answer from the Ruuvi gateway
17
+
18
+ A typical answer contains a few top level fields:
19
+
20
+ - `timestamp` : the timestamp of the retrieval
21
+ - `tags` : an object, containing all of the tags that are within reach of the gateway
22
+ - `gw_mac` : the MAC address of the queried gateway
23
+
24
+ The `tags` contain an object for each found Ruuvi sensor, identified by its id. Typical fields that reside within this object are:
25
+ - `rssi` : the signal strenght
26
+ - `timestamp` : timestamp of the last received sensor reading
27
+ - `data` : raw field containing the data of the sensor
28
+ - `dataFormat` : identifier for the data format
29
+ - `temperature` : the temperature in degrees Celsius
30
+ - `humidity` : the humidity in percentage
31
+ - `pressure` : if available, the pressure. Seems to return `null`
32
+ - `axelX` ,`axelY` and `axelZ` - movement information
33
+ - `movementCounter` - how often the sensor was moved
34
+ - `voltage` - the battery voltage
35
+ - `txPower` - the transmit txPower
36
+ - `measurementSequenceNumber` - a sequence number
37
+ - `id` - the identification id for the sensor
38
+
39
+ ### Configuration
40
+
41
+ - `name (string)` : the name for the node
42
+ - `gateway (ruuvi config)` : the configured Ruuvi gateway
43
+ - `store in global context (boolean)` : whether or not to store the answer in the global context
44
+ - `verbose (boolean)` : show the used curl command in the debug tab
45
+
46
+ Configure the gateway to enable [poll mode](https://docs.ruuvi.com/gw-examples/polling-mode).
47
+ Then copy and paste the token into the configuration of a new Ruuvi gateway.
48
+
49
+ Also fill out the hostname or ip address for the Ruuvi gateway. The easiest way is to
50
+ enter `ruuvigatewayXXXX.local`, where the `XXXX` match the code, which is printed on the
51
+ backside of the Ruuvi gateway.
Binary file
@@ -0,0 +1,69 @@
1
+ [
2
+ {
3
+ "id": "276894333a56665b",
4
+ "type": "Ruuvi gateway",
5
+ "z": "985f6c11457dd04d",
6
+ "name": "",
7
+ "gateway": "bd209778320ddac5",
8
+ "x": 560,
9
+ "y": 360,
10
+ "wires": [
11
+ [
12
+ "91b67e458fe5c91f"
13
+ ]
14
+ ]
15
+ },
16
+ {
17
+ "id": "1b9c74cd503b2870",
18
+ "type": "inject",
19
+ "z": "985f6c11457dd04d",
20
+ "name": "",
21
+ "props": [
22
+ {
23
+ "p": "payload"
24
+ },
25
+ {
26
+ "p": "topic",
27
+ "vt": "str"
28
+ }
29
+ ],
30
+ "repeat": "",
31
+ "crontab": "",
32
+ "once": false,
33
+ "onceDelay": 0.1,
34
+ "topic": "",
35
+ "payload": "",
36
+ "payloadType": "date",
37
+ "x": 370,
38
+ "y": 360,
39
+ "wires": [
40
+ [
41
+ "276894333a56665b"
42
+ ]
43
+ ]
44
+ },
45
+ {
46
+ "id": "91b67e458fe5c91f",
47
+ "type": "debug",
48
+ "z": "985f6c11457dd04d",
49
+ "name": "Ruuvi debug",
50
+ "active": true,
51
+ "tosidebar": true,
52
+ "console": false,
53
+ "tostatus": false,
54
+ "complete": "true",
55
+ "targetType": "full",
56
+ "statusVal": "",
57
+ "statusType": "auto",
58
+ "x": 780,
59
+ "y": 360,
60
+ "wires": []
61
+ },
62
+ {
63
+ "id": "bd209778320ddac5",
64
+ "type": "config-ruuvi-gateway",
65
+ "name": "",
66
+ "host": "ruuvigatewayXXXX.local",
67
+ "token": ""
68
+ }
69
+ ]
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "node-red-contrib-ruuvi-gateway",
3
+ "version": "0.0.1",
4
+ "description": "Poll a Ruuvi gateway",
5
+ "main": "ruuvi-gateway.js",
6
+ "scripts": {
7
+ "test": "standard --fix"
8
+ },
9
+ "keywords": [
10
+ "node-red",
11
+ "ruuvi",
12
+ "gateway",
13
+ "victron"
14
+ ],
15
+ "author": "Dirk-Jan Faber <dfaber@victronenergy.com>",
16
+ "license": "MIT",
17
+ "node-red": {
18
+ "nodes": {
19
+ "config-ruuvi-gateway": "./src/nodes/config-ruuvi-gateway.js",
20
+ "ruuvi-gateway": "./src/nodes/ruuvi-gateway.js"
21
+ },
22
+ "version": ">=3.0.2"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/dirkjanfaber/node-red-contrib-ruuvi-gateway"
27
+ },
28
+ "dependencies": {
29
+ "axios": "^1.4.0",
30
+ "axios-curlirize": "^1.3.7"
31
+ },
32
+ "devDependencies": {
33
+ "standard": "^17.1.0"
34
+ },
35
+ "engines": {
36
+ "node": ">=14.17.4"
37
+ }
38
+ }
@@ -0,0 +1,33 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('config-ruuvi-gateway',{
3
+ category: 'config',
4
+ defaults: {
5
+ name: { value: "" },
6
+ host: { value: "", required: true },
7
+ token: { value: ""},
8
+ },
9
+ label: function() {
10
+ return ( this.name || 'Ruuvi gateway config')
11
+ }
12
+ });
13
+ </script>
14
+
15
+ <script type="text/html" data-template-name="config-ruuvi-gateway">
16
+ <div class="form-row">
17
+ <label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
18
+ <input type="text" id="node-config-input-name" placeholder="Name">
19
+ </div>
20
+ <div class="form-row">
21
+ <label for="node-config-input-host"><i class="fa fa-laptop"></i> Host / IP</label>
22
+ <input type="text" id="node-config-input-host" placeholder="Host / IP">
23
+ </div>
24
+ <div class="form-row">
25
+ <label for="node-config-input-token"><i class="fa fa-user-secret"></i> Token</label>
26
+ <input type="password" id="node-config-input-token" placeholder="password">
27
+ </div>
28
+ </script>
29
+
30
+ <script type="text/markdown" data-help-name="config-ruuvi-gateway">
31
+
32
+ todo
33
+ </script>
@@ -0,0 +1,10 @@
1
+ module.exports = function (RED) {
2
+ 'use strict'
3
+ function ConfigRuuviGateway (n) {
4
+ RED.nodes.createNode(this, n)
5
+ this.name = n.name
6
+ this.host = n.host
7
+ this.token = n.token
8
+ }
9
+ RED.nodes.registerType('config-ruuvi-gateway', ConfigRuuviGateway)
10
+ }
@@ -0,0 +1,55 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+
4
+ <svg
5
+ version="1.1"
6
+ id="Layer_1"
7
+ x="0px"
8
+ y="0px"
9
+ viewBox="0 0 721 475.1"
10
+ style="enable-background:new 0 0 721 475.1;"
11
+ xml:space="preserve"
12
+ sodipodi:docname="ruuvi-gateway.svg"
13
+ inkscape:version="1.3.2 (091e20e, 2023-11-25)"
14
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
15
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
16
+ xmlns="http://www.w3.org/2000/svg"
17
+ xmlns:svg="http://www.w3.org/2000/svg"><defs
18
+ id="defs9" /><sodipodi:namedview
19
+ id="namedview9"
20
+ pagecolor="#ffffff"
21
+ bordercolor="#000000"
22
+ borderopacity="0.25"
23
+ inkscape:showpageshadow="2"
24
+ inkscape:pageopacity="0.0"
25
+ inkscape:pagecheckerboard="0"
26
+ inkscape:deskcolor="#d1d1d1"
27
+ inkscape:zoom="0.70249294"
28
+ inkscape:cx="360.85772"
29
+ inkscape:cy="237.01306"
30
+ inkscape:window-width="1512"
31
+ inkscape:window-height="916"
32
+ inkscape:window-x="0"
33
+ inkscape:window-y="38"
34
+ inkscape:window-maximized="0"
35
+ inkscape:current-layer="Layer_1" />
36
+ <style
37
+ type="text/css"
38
+ id="style1">
39
+ .st0{opacity:0.5;}
40
+ .st1{fill:#7FD8CE;}
41
+ .st2{opacity:0.5;fill:#7FD8CE;enable-background:new ;}
42
+ </style>
43
+
44
+
45
+ <rect
46
+ x="719.8"
47
+ y="53.3"
48
+ class="st2"
49
+ width="1.2"
50
+ height="27"
51
+ id="rect9" />
52
+ <path
53
+ style="fill:#000000;stroke-width:1.42666"
54
+ d="m 112.36346,359.17815 c -4.55269,-3.72046 -5.19769,-7.77136 -3.84635,-24.15719 2.51586,-30.5065 19.6705,-49.25399 48.43783,-52.93537 16.95484,-2.16974 108.71717,-5.82619 146.21378,-5.82619 h 33.48678 v -10.90606 -10.90605 h 4.89891 c 3.96578,0 4.89891,-0.55396 4.89891,-2.90828 0,-2.87683 -0.93313,-2.90829 -86.26164,-2.90829 h -86.26162 l 0.95985,-8.81402 c 3.13282,-28.76771 16.44931,-58.55789 37.89317,-84.77038 12.00382,-14.67322 37.27248,-34.21977 56.01953,-43.33387 4.98629,-2.42413 9.06597,-4.99037 9.06597,-5.70275 0,-0.71238 -1.97101,-4.62315 -4.38002,-8.690615 -3.10932,-5.249866 -3.9458,-7.846489 -2.88331,-8.950318 1.06249,-1.103829 2.63776,0.440738 5.42978,5.323989 7.09808,12.414484 5.84238,11.761154 15.95094,8.299064 43.22952,-14.805695 93.41911,-14.652248 134.07564,0.40993 3.36856,1.24797 6.55055,2.26903 7.07111,2.26903 0.52054,0 3.0963,-3.85162 5.72392,-8.559152 2.91839,-5.228448 5.48275,-8.277986 6.58985,-7.836621 1.32961,0.530063 0.69016,2.760202 -2.40069,8.37262 -2.31718,4.207543 -4.21305,8.298563 -4.21305,9.091133 0,0.79258 4.05884,3.51694 9.01962,6.05413 26.23476,13.41778 54.19871,38.36677 69.43587,61.94957 12.51326,19.36698 21.16432,43.48475 24.38488,67.9811 l 0.90811,6.90716 h -86.16662 c -85.23345,0 -86.16659,0.0315 -86.16659,2.90829 0,2.35432 0.93314,2.90828 4.89891,2.90828 h 4.89891 v 10.68098 10.681 l 54.23792,0.90216 c 55.43415,0.92205 100.62301,2.88212 122.49579,5.31326 28.42386,3.15927 45.94443,20.24713 50.25535,49.01417 2.16474,14.4455 0.96855,23.54178 -3.63705,27.65747 l -3.75704,3.35738 H 357.76153 c -238.84237,0 -241.92346,-0.0361 -245.39807,-2.87556 z m 234.08986,-124.0387 v -9.12885 l -30.86695,-54.48982 c -16.97682,-29.9694 -31.78575,-55.95056 -32.90872,-57.73592 l -2.0418,-3.2461 -8.03215,3.68822 c -26.88686,12.34598 -53.88504,36.3254 -71.63571,63.6259 -9.92685,15.26745 -19.30561,40.93675 -21.74248,59.50825 l -0.90632,6.90717 h 84.06706 84.06707 z m 190.61812,4.03935 c -2.00754,-19.93407 -12.35205,-47.06233 -25.45367,-66.75182 -8.8775,-13.34134 -29.58393,-34.29385 -42.75083,-43.2589 -13.17158,-8.96822 -32.93337,-19.18934 -34.21826,-17.69825 -0.56811,0.65929 -15.29066,26.49495 -32.7168,57.41259 l -31.68386,56.21389 v 9.59743 9.59744 l 83.63138,-0.37497 83.63138,-0.37498 z M 351.73872,213.02798 c 0.43539,-0.38534 -58.09012,-104.08801 -59.7431,-105.86013 -1.50204,-1.61033 -5.72891,0.34105 -5.72891,2.64481 0,1.48187 46.59981,85.13342 56.91775,102.17332 1.51912,2.50881 2.79211,3.09933 5.09635,2.36409 1.68928,-0.539 3.24534,-1.13395 3.45791,-1.32209 z m 49.14912,-50.05725 c 16.25073,-28.7181 29.54678,-52.85323 29.54678,-53.63361 0,-2.16149 -5.32143,-3.13721 -6.78691,-1.24441 -1.72866,2.23274 -57.63418,101.13427 -58.51391,103.51597 -0.58602,1.58657 0.98365,2.59388 5.31081,3.40812 0.49307,0.0928 14.19252,-23.32796 30.44323,-52.04607 z m -11.49413,-2.90868 c 16.41386,-29.02301 29.84341,-53.37529 29.84341,-54.11619 0,-2.01747 -5.28126,-3.73616 -21.2605,-6.918876 -20.48562,-4.080282 -41.03931,-5.183921 -59.22159,-3.179926 -19.24704,2.121358 -39.84192,6.998502 -40.82818,9.668682 -0.878,2.377 58.42576,108.25199 60.3238,107.69609 0.71482,-0.20936 14.7292,-24.12675 31.14306,-53.14978 z"
55
+ id="path1" /></svg>
@@ -0,0 +1,86 @@
1
+ <script type="text/javascript">
2
+
3
+ RED.nodes.registerType('Ruuvi gateway',{
4
+ category: 'network',
5
+ color: '#d4efeb',
6
+ defaults: {
7
+ name: {value:""},
8
+ gateway: {value:"", type: "config-ruuvi-gateway"},
9
+ store_in_global_context: {value: false},
10
+ verbose: {value: false}
11
+ },
12
+ icon: "ruuvi-gateway.svg",
13
+ inputs:1,
14
+ outputs:1,
15
+ label: function() {
16
+ return (this.name||"Ruuvi gateway")
17
+ }
18
+ });
19
+ </script>
20
+
21
+ <script type="text/html" data-template-name="Ruuvi gateway">
22
+ <div class="form-row">
23
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
24
+ <input type="text" id="node-input-name" placeholder="Name">
25
+ </div>
26
+ <div class="form-row">
27
+ <label for="node-input-gateway"><i class="fa fa-dot-circle-o"></i> Gateway</label>
28
+ <input type="text" id="node-input-gateway" placeholder="Gateway">
29
+ </div>
30
+ <div class="form-row" style="margin-bottom:0px;">
31
+ <input type="checkbox" id="node-input-store_in_global_context" style="display:inline-block; margin-left:8px; width:auto; vertical-align:top;">
32
+ <label style="min-width:390px" for="node-input-store_in_global_context"> Store the response in the global context?</label>
33
+ </div>
34
+ <div class="form-row" style="margin-bottom:0px;">
35
+ <input type="checkbox" id="node-input-verbose" style="display:inline-block; margin-left:8px; width:auto; vertical-align:top;">
36
+ <label style="min-width:390px" for="node-input-verbose"> Verbose: show the used <em>url</em> in the debug tab?</label>
37
+ </div>
38
+ </script>
39
+
40
+ <script type="text/markdown" data-help-name="Ruuvi gateway">
41
+
42
+ Interface with the [Ruuvi gateway](https://ruuvi.com/gateway/).
43
+
44
+ ### Inputs
45
+
46
+ : payload (string|number|json) : the trigger to poll the Ruuvi gateway
47
+
48
+ ### Outputs
49
+
50
+ : payload (json) : the answer from the Ruuvi gateway
51
+
52
+ A typical answer contains a few top level fields:
53
+
54
+ - `timestamp` : the timestamp of the retrieval
55
+ - `tags` : an object, containing all of the tags that are within reach of the gateway
56
+ - `gw_mac` : the MAC address of the queried gateway
57
+
58
+ The `tags` contain an object for each found Ruuvi sensor, identified by its id. Typical fields that reside within this object are:
59
+ - `rssi` : the signal strenght
60
+ - `timestamp` : timestamp of the last received sensor reading
61
+ - `data` : raw field containing the data of the sensor
62
+ - `dataFormat` : identifier for the data format
63
+ - `temperature` : the temperature in degrees Celsius
64
+ - `humidity` : the humidity in percentage
65
+ - `pressure` : if available, the pressure. Seems to return `null`
66
+ - `axelX` ,`axelY` and `axelZ` - movement information
67
+ - `movementCounter` - how often the sensor was moved
68
+ - `voltage` - the battery voltage
69
+ - `txPower` - the transmit txPower
70
+ - `measurementSequenceNumber` - a sequence number
71
+ - `id` - the identification id for the sensor
72
+
73
+ ### Configuration
74
+
75
+ : name (string) : the name for the node
76
+ : gateway (ruuvi config) : the configured Ruuvi gateway
77
+ : store in global context (boolean) : whether or not to store the answer in the global context
78
+ : verbose (boolean) : show the used curl command in the debug tab
79
+
80
+ Configure the gateway to enable [poll mode](https://docs.ruuvi.com/gw-examples/polling-mode).
81
+ Then copy and paste the token into the configuration of a new Ruuvi gateway.
82
+
83
+ Also fill out the hostname or ip address for the Ruuvi gateway. The easiest way is to
84
+ enter `ruuvigatewayXXXX.local`, where the `XXXX` match the code, which is printed on the
85
+ backside of the Ruuvi gateway.
86
+ </script>
@@ -0,0 +1,48 @@
1
+ const axios = require('axios')
2
+ const curlirize = require('axios-curlirize')
3
+ const path = require('path')
4
+ const packageJson = require(path.join(__dirname, '../../', 'package.json'))
5
+
6
+ module.exports = function (RED) {
7
+ function RuuviGatewayNode (config) {
8
+ RED.nodes.createNode(this, config)
9
+ this.gateway = RED.nodes.getNode(config.gateway)
10
+ const node = this
11
+
12
+ node.on('input', function (msg) {
13
+ const url = msg.url || `http://${node.gateway.host}/history`
14
+ const headers = {
15
+ 'User-Agent': 'node-red-contrib-ruuvi-gateway/' + packageJson.version,
16
+ Authorization: `Bearer ${node.gateway.token}`
17
+ }
18
+
19
+ axios.get(url, { headers }).then(function (response) {
20
+ msg.payload = response.data.data
21
+ if (config.store_in_global_context === true) {
22
+ const globalContext = node.context().global
23
+ globalContext.set('ruuvi.' + response.data.data.gw_mac, response.data.data)
24
+ }
25
+ msg.topic = response.data.data.gw_mac
26
+ node.status({ fill: 'green', shape: 'ring', text: 'Ok' })
27
+ node.send(msg)
28
+ }).catch(function (error) {
29
+ if (error.response && error.message) {
30
+ node.status({ fill: 'red', shape: 'dot', text: error.message })
31
+ } else {
32
+ node.status({ fill: 'red', shape: 'dot', text: 'Error fetching Ruuvi data' })
33
+ }
34
+ })
35
+
36
+ if (config.verbose === true) {
37
+ curlirize(axios, (result, err) => {
38
+ node.warn(result.command)
39
+ })
40
+ }
41
+ })
42
+
43
+ node.on('close', function (done) {
44
+ done()
45
+ })
46
+ }
47
+ RED.nodes.registerType('Ruuvi gateway', RuuviGatewayNode)
48
+ }