@mschaeffler/node-red-bthome 1.4.3 → 1.6.0
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/README.md +78 -8
- package/bthome.js +4 -1
- package/examples/CSS.json +54 -0
- package/examples/bthome.json +84 -24
- package/examples/bthome.png +0 -0
- package/package.json +5 -3
- package/ws90.html +115 -0
- package/ws90.js +154 -0
package/README.md
CHANGED
|
@@ -12,6 +12,9 @@ At the moment these sensors are implemented and tested:
|
|
|
12
12
|
- Shelly BLU RC Button 4
|
|
13
13
|
- Shelly BLU Wall Switch 4
|
|
14
14
|
- Shelly BLU Motion
|
|
15
|
+
- Shelly BLU Distance
|
|
16
|
+
- Shelly BLU Remote
|
|
17
|
+
- Ecowitt WS90
|
|
15
18
|
|
|
16
19
|
## Capture of Raw Frames
|
|
17
20
|
|
|
@@ -29,14 +32,18 @@ This node can decrypt [encrypted messages](https://bthome.io/encryption/), if th
|
|
|
29
32
|
$ npm install @mschaeffler/node-red-bthome
|
|
30
33
|
```
|
|
31
34
|
|
|
32
|
-
##
|
|
35
|
+
## bthome Node
|
|
36
|
+
|
|
37
|
+
This node decodes the raw frames.
|
|
38
|
+
|
|
39
|
+
### Input
|
|
33
40
|
|
|
34
41
|
|msg. | type | description |
|
|
35
42
|
|:-------|:-------|:----------------------------------|
|
|
36
43
|
|payload | object | data from Shelly script|
|
|
37
44
|
|resync | boolean| if set to yes, allow all packed ids; to resync after some issue|
|
|
38
45
|
|
|
39
|
-
|
|
46
|
+
#### msg.payload
|
|
40
47
|
|
|
41
48
|
Only the first two values are needed, the others are optional.
|
|
42
49
|
|
|
@@ -59,27 +66,27 @@ This is an example of such a message payload:
|
|
|
59
66
|
}
|
|
60
67
|
```
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
### Outputs
|
|
63
70
|
|
|
64
71
|
There are two output ports:
|
|
65
72
|
1. one for meassurement values (states)
|
|
66
73
|
2. one for actions done with the devices (events)
|
|
67
74
|
|
|
68
|
-
|
|
75
|
+
#### State
|
|
69
76
|
|
|
70
77
|
|msg. | type | description |
|
|
71
78
|
|:-------|:-------|:----------------------------------|
|
|
72
79
|
|topic | string | `State-Prefix` + name of the device|
|
|
73
80
|
|payload | object | decoded state data|
|
|
74
81
|
|
|
75
|
-
|
|
82
|
+
#### Events
|
|
76
83
|
|
|
77
84
|
|msg. | type | description |
|
|
78
85
|
|:-------|:-------|:----------------------------------|
|
|
79
86
|
|topic | string | `Event-Prefix` + name of the device|
|
|
80
87
|
|payload | object | data of the decoded event|
|
|
81
88
|
|
|
82
|
-
|
|
89
|
+
### Parameters
|
|
83
90
|
|
|
84
91
|
|config | type | description |
|
|
85
92
|
|:------------|:-------|:----------------------------------|
|
|
@@ -90,7 +97,7 @@ There are two output ports:
|
|
|
90
97
|
|Context-Variable| string | name of the variable in flow context storage |
|
|
91
98
|
|Contextstore | string | context store to be used |
|
|
92
99
|
|
|
93
|
-
|
|
100
|
+
#### Device-Configuration
|
|
94
101
|
|
|
95
102
|
With this JSON string the installed [BT-Home](https://bthome.io) devices are configured:
|
|
96
103
|
```
|
|
@@ -109,7 +116,7 @@ An example for such a config from the unit tests:
|
|
|
109
116
|
}
|
|
110
117
|
```
|
|
111
118
|
|
|
112
|
-
|
|
119
|
+
#### Context storage
|
|
113
120
|
|
|
114
121
|
All recorded data can be stored in a flow context variable for
|
|
115
122
|
- initialisation
|
|
@@ -136,10 +143,73 @@ If content storage is active, statistical data is also stored in a variable with
|
|
|
136
143
|
{ ok:0, err:0, old:0, dup:0 }
|
|
137
144
|
```
|
|
138
145
|
|
|
146
|
+
## ws90 Node
|
|
147
|
+
|
|
148
|
+
This node does some additional calculations to get more usefull data from the WS90 weather station.
|
|
149
|
+
|
|
150
|
+
Similar calculations are done by the Ecowitt base stations.
|
|
151
|
+
|
|
152
|
+
### Input
|
|
153
|
+
|
|
154
|
+
|msg. | type | description |
|
|
155
|
+
|:-------|:-------|:----------------------------------|
|
|
156
|
+
|payload |object |data from `bthome` node. |
|
|
157
|
+
|
|
158
|
+
This is an example of such a message payload:
|
|
159
|
+
```
|
|
160
|
+
{
|
|
161
|
+
"lux": 3630,
|
|
162
|
+
"moisture": false,
|
|
163
|
+
"wind": [1.5,1.5],
|
|
164
|
+
"uv": 0,
|
|
165
|
+
"direction": 174,
|
|
166
|
+
"pressure": 948.2,
|
|
167
|
+
"dewpoint": -0.43,
|
|
168
|
+
"humidity": 99,
|
|
169
|
+
"temperature": -0.3,
|
|
170
|
+
"precipitation": 121.5
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Outputs
|
|
175
|
+
|
|
176
|
+
There are 12 output ports:
|
|
177
|
+
- temperature
|
|
178
|
+
- dew point
|
|
179
|
+
- humidity
|
|
180
|
+
- raining²
|
|
181
|
+
- rain yesterday
|
|
182
|
+
- rain today
|
|
183
|
+
- uv²
|
|
184
|
+
- air pressure
|
|
185
|
+
- direction
|
|
186
|
+
- wind²
|
|
187
|
+
- wind max
|
|
188
|
+
- illumination
|
|
189
|
+
|
|
190
|
+
²: this values have style classes for the `dashboard 2` attached.
|
|
191
|
+
|
|
192
|
+
#### style classes
|
|
193
|
+
This classes should be defined (for an example check the `ui-template` in the
|
|
194
|
+
[examples](https://github.com/m-schaeffler/node-red-my-nodes/raw/main/node-red-bthome/examples/CSS.json)):
|
|
195
|
+
- greenValue
|
|
196
|
+
- yellowValue
|
|
197
|
+
- redValue
|
|
198
|
+
- blueValue
|
|
199
|
+
|
|
200
|
+
### Parameters
|
|
201
|
+
|
|
202
|
+
|config | type | description |
|
|
203
|
+
|:------------|:-------|:----------------------------------|
|
|
204
|
+
|Contextstore | string | context store to be used. |
|
|
205
|
+
|reference height|number| height of the installation over the sea level, to caculate the absolute air pressure. |
|
|
206
|
+
|
|
139
207
|
## Example Flow
|
|
140
208
|
|
|
141
209
|
[example flow](https://github.com/m-schaeffler/node-red-my-nodes/raw/main/node-red-bthome/examples/bthome.json)
|
|
142
210
|
|
|
211
|
+
[CSS template](https://github.com/m-schaeffler/node-red-my-nodes/raw/main/node-red-bthome/examples/CSS.json)
|
|
212
|
+
|
|
143
213
|
## Author
|
|
144
214
|
|
|
145
215
|
[Mathias Schäffler](https://github.com/m-schaeffler)
|
package/bthome.js
CHANGED
|
@@ -189,7 +189,7 @@ module.exports = function(RED) {
|
|
|
189
189
|
setData( "lux", rawdata.getUInt24() / 100 );
|
|
190
190
|
break;
|
|
191
191
|
case 0x08:
|
|
192
|
-
setData( "dewpoint", rawdata.getInt16() /100 );
|
|
192
|
+
setData( "dewpoint", rawdata.getInt16() / 100 );
|
|
193
193
|
break;
|
|
194
194
|
case 0x0C:
|
|
195
195
|
if( node.batteryState )
|
|
@@ -203,6 +203,9 @@ module.exports = function(RED) {
|
|
|
203
203
|
delete item.data?.voltage;
|
|
204
204
|
}
|
|
205
205
|
break;
|
|
206
|
+
case 0x1E:
|
|
207
|
+
setData( "light", rawdata.getUInt8() );
|
|
208
|
+
break;
|
|
206
209
|
case 0x20:
|
|
207
210
|
setData( "moisture", Boolean( rawdata.getUInt8() ) );
|
|
208
211
|
break;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "fb58dd887406a7d3",
|
|
4
|
+
"type": "ui-template",
|
|
5
|
+
"z": "bf934c281a6f35c0",
|
|
6
|
+
"group": "",
|
|
7
|
+
"page": "",
|
|
8
|
+
"ui": "fd06eabec55c4e5d",
|
|
9
|
+
"name": "CSS classes 2.0",
|
|
10
|
+
"order": 0,
|
|
11
|
+
"width": 0,
|
|
12
|
+
"height": 0,
|
|
13
|
+
"head": "",
|
|
14
|
+
"format": ".red label.nrdb-ui-text-label, .red span, .redValue span.nrdb-ui-text-value, span.red {\n color: red !important;\n}\n.green label.nrdb-ui-text-label, .green span, .greenValue span.nrdb-ui-text-value, span.green {\n color: LimeGreen !important;\n}\n.darkgreen label.nrdb-ui-text-label, .darkgreen span, span.darkgreen {\n color: green !important;\n}\n.blue label.nrdb-ui-text-label, .blue span, .blueValue span.nrdb-ui-text-value, span.blue {\n color: aqua !important;\n}\n.yellow label.nrdb-ui-text-label, .yellow span, .yellowValue span.nrdb-ui-text-value, span.yellow {\n color: yellow !important;\n}\n.gray label.nrdb-ui-text-label, .gray span, .grayValue span.nrdb-ui-text-value, span.gray {\n color: gray !important;\n}\n.silver label.nrdb-ui-text-label, .silver span, .silverValue span.nrdb-ui-text-value, span.silver {\n color: #b0b0b0 !important;\n}",
|
|
15
|
+
"storeOutMessages": true,
|
|
16
|
+
"passthru": false,
|
|
17
|
+
"resendOnRefresh": true,
|
|
18
|
+
"templateScope": "site:style",
|
|
19
|
+
"className": "",
|
|
20
|
+
"x": 440,
|
|
21
|
+
"y": 640,
|
|
22
|
+
"wires": [
|
|
23
|
+
[]
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "fd06eabec55c4e5d",
|
|
28
|
+
"type": "ui-base",
|
|
29
|
+
"name": "Dashboard 2.0",
|
|
30
|
+
"path": "/dashboard",
|
|
31
|
+
"appIcon": "",
|
|
32
|
+
"includeClientData": false,
|
|
33
|
+
"acceptsClientConfig": [
|
|
34
|
+
"ui-notification",
|
|
35
|
+
"ui-control"
|
|
36
|
+
],
|
|
37
|
+
"showPathInSidebar": false,
|
|
38
|
+
"headerContent": "page",
|
|
39
|
+
"navigationStyle": "default",
|
|
40
|
+
"titleBarStyle": "fixed",
|
|
41
|
+
"showReconnectNotification": false,
|
|
42
|
+
"notificationDisplayTime": "5",
|
|
43
|
+
"showDisconnectNotification": false,
|
|
44
|
+
"allowInstall": true
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "7d327676499cd8a2",
|
|
48
|
+
"type": "global-config",
|
|
49
|
+
"env": [],
|
|
50
|
+
"modules": {
|
|
51
|
+
"@flowfuse/node-red-dashboard": "1.29.0"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
package/examples/bthome.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
|
-
"id": "
|
|
3
|
+
"id": "da2333b077f13c53",
|
|
4
4
|
"type": "mqtt in",
|
|
5
|
-
"z": "
|
|
5
|
+
"z": "1607080a6a66359a",
|
|
6
6
|
"name": "",
|
|
7
7
|
"topic": "node-red/bleraw",
|
|
8
8
|
"qos": "0",
|
|
@@ -12,39 +12,42 @@
|
|
|
12
12
|
"rap": true,
|
|
13
13
|
"rh": 0,
|
|
14
14
|
"inputs": 0,
|
|
15
|
-
"x":
|
|
16
|
-
"y":
|
|
15
|
+
"x": 260,
|
|
16
|
+
"y": 560,
|
|
17
17
|
"wires": [
|
|
18
18
|
[
|
|
19
|
-
"
|
|
19
|
+
"da23792a27f30483"
|
|
20
20
|
]
|
|
21
21
|
]
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
|
-
"id": "
|
|
24
|
+
"id": "da23792a27f30483",
|
|
25
25
|
"type": "bthome",
|
|
26
|
-
"z": "
|
|
26
|
+
"z": "1607080a6a66359a",
|
|
27
27
|
"name": "",
|
|
28
28
|
"devices": "{\"11:22:33:44:55:66\":{\"topic\":\"dev_unencrypted_1\"},\"00:01:02:03:04:05\":{\"topic\":\"dev_unencrypted_2\"},\"00:10:20:30:40:50\":{\"topic\":\"dev_encrypted_1\",\"key\":\"00112233445566778899AABBCCDDEEFF\"},\"00:00:00:00:00:00\":{\"topic\":\"dev_encrypted_2\",\"key\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]}}",
|
|
29
|
+
"counterMode": "none",
|
|
29
30
|
"statusPrefix": "Status",
|
|
30
31
|
"eventPrefix": "Event",
|
|
31
32
|
"contextVar": "bt-home",
|
|
32
33
|
"contextStore": "memoryOnly",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
34
|
+
"batteryState": false,
|
|
35
|
+
"x": 460,
|
|
36
|
+
"y": 540,
|
|
35
37
|
"wires": [
|
|
36
38
|
[
|
|
37
|
-
"
|
|
39
|
+
"45cc12931b5a0b52",
|
|
40
|
+
"02887e37eebfaae5"
|
|
38
41
|
],
|
|
39
42
|
[
|
|
40
|
-
"
|
|
43
|
+
"347a2db3d0351824"
|
|
41
44
|
]
|
|
42
45
|
]
|
|
43
46
|
},
|
|
44
47
|
{
|
|
45
|
-
"id": "
|
|
48
|
+
"id": "45cc12931b5a0b52",
|
|
46
49
|
"type": "debug",
|
|
47
|
-
"z": "
|
|
50
|
+
"z": "1607080a6a66359a",
|
|
48
51
|
"name": "debug 9",
|
|
49
52
|
"active": true,
|
|
50
53
|
"tosidebar": true,
|
|
@@ -53,14 +56,14 @@
|
|
|
53
56
|
"complete": "false",
|
|
54
57
|
"statusVal": "",
|
|
55
58
|
"statusType": "auto",
|
|
56
|
-
"x":
|
|
57
|
-
"y":
|
|
59
|
+
"x": 640,
|
|
60
|
+
"y": 520,
|
|
58
61
|
"wires": []
|
|
59
62
|
},
|
|
60
63
|
{
|
|
61
|
-
"id": "
|
|
64
|
+
"id": "347a2db3d0351824",
|
|
62
65
|
"type": "debug",
|
|
63
|
-
"z": "
|
|
66
|
+
"z": "1607080a6a66359a",
|
|
64
67
|
"name": "debug 10",
|
|
65
68
|
"active": true,
|
|
66
69
|
"tosidebar": true,
|
|
@@ -69,14 +72,14 @@
|
|
|
69
72
|
"complete": "false",
|
|
70
73
|
"statusVal": "",
|
|
71
74
|
"statusType": "auto",
|
|
72
|
-
"x":
|
|
73
|
-
"y":
|
|
75
|
+
"x": 640,
|
|
76
|
+
"y": 560,
|
|
74
77
|
"wires": []
|
|
75
78
|
},
|
|
76
79
|
{
|
|
77
|
-
"id": "
|
|
80
|
+
"id": "5ba4c641b627284d",
|
|
78
81
|
"type": "inject",
|
|
79
|
-
"z": "
|
|
82
|
+
"z": "1607080a6a66359a",
|
|
80
83
|
"name": "",
|
|
81
84
|
"props": [
|
|
82
85
|
{
|
|
@@ -94,14 +97,63 @@
|
|
|
94
97
|
"topic": "",
|
|
95
98
|
"payload": "{\"addr\":\"11:22:33:44:55:66\",\"rssi\":-85,\"time\":1745395033113,\"gateway\":\"Shelly Gateway\",\"data\":[68,0,164,1,100,46,56,69,43,255]}",
|
|
96
99
|
"payloadType": "json",
|
|
97
|
-
"x":
|
|
98
|
-
"y":
|
|
100
|
+
"x": 290,
|
|
101
|
+
"y": 520,
|
|
99
102
|
"wires": [
|
|
100
103
|
[
|
|
101
|
-
"
|
|
104
|
+
"da23792a27f30483"
|
|
102
105
|
]
|
|
103
106
|
]
|
|
104
107
|
},
|
|
108
|
+
{
|
|
109
|
+
"id": "02887e37eebfaae5",
|
|
110
|
+
"type": "switch",
|
|
111
|
+
"z": "1607080a6a66359a",
|
|
112
|
+
"name": "is WS90",
|
|
113
|
+
"property": "payload",
|
|
114
|
+
"propertyType": "msg",
|
|
115
|
+
"rules": [
|
|
116
|
+
{
|
|
117
|
+
"t": "hask",
|
|
118
|
+
"v": "dewpoint",
|
|
119
|
+
"vt": "str"
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"checkall": "false",
|
|
123
|
+
"repair": false,
|
|
124
|
+
"outputs": 1,
|
|
125
|
+
"x": 480,
|
|
126
|
+
"y": 360,
|
|
127
|
+
"wires": [
|
|
128
|
+
[
|
|
129
|
+
"57b7cd4b7d2e6f25"
|
|
130
|
+
]
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"id": "57b7cd4b7d2e6f25",
|
|
135
|
+
"type": "ws90",
|
|
136
|
+
"z": "1607080a6a66359a",
|
|
137
|
+
"name": "",
|
|
138
|
+
"contextStore": "memoryOnly",
|
|
139
|
+
"refheight": "500",
|
|
140
|
+
"x": 630,
|
|
141
|
+
"y": 360,
|
|
142
|
+
"wires": [
|
|
143
|
+
[],
|
|
144
|
+
[],
|
|
145
|
+
[],
|
|
146
|
+
[],
|
|
147
|
+
[],
|
|
148
|
+
[],
|
|
149
|
+
[],
|
|
150
|
+
[],
|
|
151
|
+
[],
|
|
152
|
+
[],
|
|
153
|
+
[],
|
|
154
|
+
[]
|
|
155
|
+
]
|
|
156
|
+
},
|
|
105
157
|
{
|
|
106
158
|
"id": "66b8b47e.0f87bc",
|
|
107
159
|
"type": "mqtt-broker",
|
|
@@ -133,5 +185,13 @@
|
|
|
133
185
|
"willMsg": {},
|
|
134
186
|
"userProps": "",
|
|
135
187
|
"sessionExpiry": ""
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"id": "888386dbbbc56d84",
|
|
191
|
+
"type": "global-config",
|
|
192
|
+
"env": [],
|
|
193
|
+
"modules": {
|
|
194
|
+
"@mschaeffler/node-red-bthome": "1.6.0"
|
|
195
|
+
}
|
|
136
196
|
}
|
|
137
197
|
]
|
package/examples/bthome.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mschaeffler/node-red-bthome",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A Node Red node to decrypt and decode BT-Home frames",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mathias Schäffler",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"BT-Home",
|
|
13
13
|
"bthome",
|
|
14
14
|
"Shelly BLU",
|
|
15
|
-
"BLE"
|
|
15
|
+
"BLE",
|
|
16
|
+
"WS90"
|
|
16
17
|
],
|
|
17
18
|
"bugs": {
|
|
18
19
|
"url": "https://github.com/m-schaeffler/node-red-my-nodes/tree/main/node-red-bthome/issues"
|
|
@@ -25,7 +26,8 @@
|
|
|
25
26
|
"node-red": {
|
|
26
27
|
"version": ">=4.0.0",
|
|
27
28
|
"nodes": {
|
|
28
|
-
"bthome": "bthome.js"
|
|
29
|
+
"bthome": "bthome.js",
|
|
30
|
+
"ws90": "ws90.js"
|
|
29
31
|
}
|
|
30
32
|
},
|
|
31
33
|
"engines": {
|
package/ws90.html
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('ws90',{
|
|
3
|
+
category: 'input',
|
|
4
|
+
color: '#DEB887',
|
|
5
|
+
defaults: {
|
|
6
|
+
name: {value:""},
|
|
7
|
+
contextStore:{value:"none",required:true},
|
|
8
|
+
refheight:{value:0,validate:RED.validators.number(),required:true}
|
|
9
|
+
},
|
|
10
|
+
inputs:1,
|
|
11
|
+
outputs:12,
|
|
12
|
+
icon: "font-awesome/fa-sun-o",
|
|
13
|
+
label: function() {
|
|
14
|
+
return this.name||"WS 90";
|
|
15
|
+
},
|
|
16
|
+
labelStyle: function() {
|
|
17
|
+
return this.name?"node_label_italic":"";
|
|
18
|
+
},
|
|
19
|
+
paletteLabel: "ws 90",
|
|
20
|
+
inputLabels: 'decoded bthome frame from ws90',
|
|
21
|
+
outputLabels: ['temperature','dew point','humidity','raining','rain yesterday','rain today','uv','air pressure','direction','wind','wind max','illumination'],
|
|
22
|
+
oneditprepare: function() {
|
|
23
|
+
const node = this;
|
|
24
|
+
// populate store names
|
|
25
|
+
$("#node-input-contextStore").empty();
|
|
26
|
+
$("#node-input-contextStore").append(`<option value="none">None</option>`);
|
|
27
|
+
const defaultStore = RED.settings.hasOwnProperty('context') ? RED.settings.context.default : '';
|
|
28
|
+
RED.settings.context.stores.forEach(function(item){
|
|
29
|
+
if( item ) {
|
|
30
|
+
const opt = $(`<option value="${item}">${item}${item===defaultStore?" (default)":""}</option>`);
|
|
31
|
+
$("#node-input-contextStore").append(opt);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const currentStore = node.contextStore || '';
|
|
35
|
+
// select the current option
|
|
36
|
+
$("#node-input-contextStore").val(currentStore);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<script type="text/x-red" data-template-name="ws90">
|
|
42
|
+
<div class="form-row">
|
|
43
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
44
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
45
|
+
</div>
|
|
46
|
+
<div class="form-row">
|
|
47
|
+
<label for="node-input-contextStore"><i class="fa fa-database"></i> Contextstore</label>
|
|
48
|
+
<select type="text" id="node-input-contextStore"></select>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="form-row">
|
|
51
|
+
<label for="node-input-refheight"><i class="fa fa-"></i> reference height</label>
|
|
52
|
+
<input type="text" id="node-input-refheight">
|
|
53
|
+
</div>
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<script type="text/x-red" data-help-name="ws90">
|
|
57
|
+
|
|
58
|
+
<p>A Node Red node to work with data from the WS90 weather station.</p>
|
|
59
|
+
|
|
60
|
+
<h3>Input</h3>
|
|
61
|
+
<dl class="message-properties">
|
|
62
|
+
<dt>payload <span class="property-type">object</span></dt>
|
|
63
|
+
<dd> data from <code>bthome</code> node.</dd>
|
|
64
|
+
</dl>
|
|
65
|
+
|
|
66
|
+
<p>This is an example of such a message payload:</p>
|
|
67
|
+
<code>{<br>
|
|
68
|
+
"lux": 3630,<br>
|
|
69
|
+
"moisture": false,<br>
|
|
70
|
+
"wind": [1.5,1.5],<br>
|
|
71
|
+
"uv": 0,<br>
|
|
72
|
+
"direction": 174,<br>
|
|
73
|
+
"pressure": 948.2,<br>
|
|
74
|
+
"dewpoint": -0.43,<br>
|
|
75
|
+
"humidity": 99,<br>
|
|
76
|
+
"temperature": -0.3,<br>
|
|
77
|
+
"precipitation": 121.5<br>
|
|
78
|
+
}</code>
|
|
79
|
+
|
|
80
|
+
<h3>Outputs</h3>
|
|
81
|
+
<p>There are 12 output ports:</p>
|
|
82
|
+
<ol>
|
|
83
|
+
<li>temperature</li>
|
|
84
|
+
<li>dew point</li>
|
|
85
|
+
<li>humidity</li>
|
|
86
|
+
<li>raining²</li>
|
|
87
|
+
<li>rain yesterday</li>
|
|
88
|
+
<li>rain today</li>
|
|
89
|
+
<li>uv²</li>
|
|
90
|
+
<li>air pressure</li>
|
|
91
|
+
<li>direction</li>
|
|
92
|
+
<li>wind²</li>
|
|
93
|
+
<li>wind max</li>
|
|
94
|
+
<li>illumination</li>
|
|
95
|
+
</ol>
|
|
96
|
+
<p>²: this values have style classes for the <code>dashboard 2</code> attached.</p>
|
|
97
|
+
|
|
98
|
+
<h4>style classes</h4>
|
|
99
|
+
<p>This classes should be defined (for an example check the <code>ui-template</code> in the <a href="https://github.com/m-schaeffler/node-red-my-nodes/raw/main/node-red-bthome/examples/CSS.json">examples</a>):<p>
|
|
100
|
+
<ul>
|
|
101
|
+
<li>greenValue</li>
|
|
102
|
+
<li>yellowValue</li>
|
|
103
|
+
<li>redValue</li>
|
|
104
|
+
<li>blueValue</li>
|
|
105
|
+
</ul>
|
|
106
|
+
|
|
107
|
+
<h3>Parameters</h3>
|
|
108
|
+
<dl class="message-properties">
|
|
109
|
+
<dt>Contextstore <span class="property-type">string</span></dt>
|
|
110
|
+
<dd> context store to be used.</dd>
|
|
111
|
+
<dt>reference height <span class="property-type">number</span></dt>
|
|
112
|
+
<dd> height of the installation over the sea level, to caculate the absolute air pressure.</dt>
|
|
113
|
+
</dl>
|
|
114
|
+
|
|
115
|
+
</script>
|
package/ws90.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
|
|
3
|
+
function Ws90Node(config) {
|
|
4
|
+
RED.nodes.createNode(this,config);
|
|
5
|
+
var node = this;
|
|
6
|
+
var context = this.context();
|
|
7
|
+
this.contextStore = config.contextStore ?? "none";
|
|
8
|
+
this.refheight = Number( config.refheight ?? 0 );
|
|
9
|
+
this.timebase = Number( config.timebase ?? 60 ) * 1000;
|
|
10
|
+
this.last = {};
|
|
11
|
+
this.storage = {Raining:false,RegenHeute:0,RegenGestern:0,WindMax:0};
|
|
12
|
+
node.status( "" );
|
|
13
|
+
if( node.contextStore !== "none" )
|
|
14
|
+
{
|
|
15
|
+
context.get( "storage", node.contextStore, function(err,value)
|
|
16
|
+
{
|
|
17
|
+
if( err )
|
|
18
|
+
{
|
|
19
|
+
node.error( err );
|
|
20
|
+
}
|
|
21
|
+
else
|
|
22
|
+
{
|
|
23
|
+
//console.log( "context read", value );
|
|
24
|
+
if( value !== undefined )
|
|
25
|
+
{
|
|
26
|
+
node.storage = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} );
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function ampel(value,v1,v2,s1="")
|
|
33
|
+
{
|
|
34
|
+
return value <= v1 ? s1 : ( value <= v2 ? "yellowValue" : "redValue" );
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function setStorage(topic,value)
|
|
38
|
+
{
|
|
39
|
+
node.storage[topic] = value;
|
|
40
|
+
if( node.contextStore !== "none" )
|
|
41
|
+
{
|
|
42
|
+
context.set( "storage", node.storage, node.contextStore );
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function genMessage(name,value,style=null)
|
|
47
|
+
{
|
|
48
|
+
if( node.last[name]?.value !== value || node.last[name]?.style !== style )
|
|
49
|
+
{
|
|
50
|
+
node.last[name] = { value:value, style:style };
|
|
51
|
+
let result = { topic: name, payload: value };
|
|
52
|
+
if( style !== null )
|
|
53
|
+
{
|
|
54
|
+
result.ui_update = { class: style };
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
else
|
|
59
|
+
{
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
node.on('input', function(msg,send,done) {
|
|
65
|
+
if( msg.invalid )
|
|
66
|
+
{
|
|
67
|
+
}
|
|
68
|
+
else if( msg.reset || msg.topic==="init" )
|
|
69
|
+
{
|
|
70
|
+
node.last = {};
|
|
71
|
+
}
|
|
72
|
+
else if( msg.newday || msg.topic==="newday" )
|
|
73
|
+
{
|
|
74
|
+
setStorage( "RegenGestern", node.storage.RegenHeute );
|
|
75
|
+
setStorage( "RegenHeute", 0 );
|
|
76
|
+
setStorage( "WindMax", 0 );
|
|
77
|
+
}
|
|
78
|
+
else if( typeof msg.payload !== "object" || msg.payload.temperature === undefined )
|
|
79
|
+
{
|
|
80
|
+
node.error( "invalid payload "+msg.payload );
|
|
81
|
+
}
|
|
82
|
+
else
|
|
83
|
+
{
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
|
|
86
|
+
// Luftdruck https://de.wikipedia.org/wiki/Barometrische_H%C3%B6henformel
|
|
87
|
+
//const E = temperature >= 9.1 ? 18.219 * ( 1.0463 - Math.exp( -0.0666 * temperature ) ) : 5.6402 * ( Math.exp( 0.06 * temperature ) - 0.0916 );
|
|
88
|
+
const p_H2O = ( 6.112 * Math.exp((17.62 * msg.payload.temperature) / ( msg.payload.temperature + 243.12)) * msg.payload.humidity) / 100; // Partialdruck Wasserdampf https://www.schweizer-fn.de/lueftung/feuchte/feuchte.php
|
|
89
|
+
const h = node.refheight; // Ortshöhe
|
|
90
|
+
const normdruck = msg.payload.pressure * Math.exp( 9.80665 / ( 287.05 * ( 273.15 + msg.payload.temperature + 0.12 * p_H2O + 0.0065 * h / 2 ) ) * h );
|
|
91
|
+
|
|
92
|
+
// Regen
|
|
93
|
+
let raining = node.storage.Raining;
|
|
94
|
+
function setRaining(value,timeout)
|
|
95
|
+
{
|
|
96
|
+
raining = value;
|
|
97
|
+
setStorage( "rainingDate", value ? now + timeout*node.timebase : 8640000000000000 );
|
|
98
|
+
if( node.storage.Raining !== value )
|
|
99
|
+
{
|
|
100
|
+
setStorage( "Raining", value );
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if( msg.payload.moisture && ! node.storage.moisture )
|
|
104
|
+
{
|
|
105
|
+
setRaining( true, 15 );
|
|
106
|
+
}
|
|
107
|
+
else if( now >= node.storage.rainingDate )
|
|
108
|
+
{
|
|
109
|
+
setRaining( false, 0 );
|
|
110
|
+
}
|
|
111
|
+
setStorage( "moisture", msg.payload.moisture );
|
|
112
|
+
|
|
113
|
+
// Regenmenge
|
|
114
|
+
if( msg.payload.precipitation > node.storage.Regen )
|
|
115
|
+
{
|
|
116
|
+
setStorage( "RegenHeute", node.storage.RegenHeute + msg.payload.precipitation - node.storage.Regen );
|
|
117
|
+
setRaining( true, 20 );
|
|
118
|
+
}
|
|
119
|
+
else if( msg.payload.precipitation < node.storage.Regen && msg.payload.precipitation <= 10 )
|
|
120
|
+
{
|
|
121
|
+
setStorage( "RegenHeute", node.storage.RegenHeute + msg.payload.precipitation );
|
|
122
|
+
setRaining( true, 20 );
|
|
123
|
+
}
|
|
124
|
+
setStorage( "Regen", msg.payload.precipitation );
|
|
125
|
+
|
|
126
|
+
// Wind
|
|
127
|
+
const wind = msg.payload.wind[1] * 3.6;
|
|
128
|
+
if( wind > node.storage.WindMax )
|
|
129
|
+
{
|
|
130
|
+
setStorage( "WindMax", wind );
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
node.status( Math.round( normdruck ) );
|
|
134
|
+
send( [
|
|
135
|
+
genMessage( "outside temperature", msg.payload.temperature ),
|
|
136
|
+
genMessage( "dew point", msg.payload.dewpoint ),
|
|
137
|
+
genMessage( "humidity", msg.payload.humidity ),
|
|
138
|
+
genMessage( "raining", raining ),
|
|
139
|
+
genMessage( "rain yesterday", node.storage.RegenGestern ),
|
|
140
|
+
genMessage( "rain today", node.storage.RegenHeute, msg.payload.moisture ? "blueValue" : "" ),
|
|
141
|
+
genMessage( "uv index", msg.payload.uv, ampel( msg.payload.uv, 2, 5, "greenValue" ) ),
|
|
142
|
+
genMessage( "air pressure", normdruck ),
|
|
143
|
+
genMessage( "wind direction", msg.payload.direction ),
|
|
144
|
+
genMessage( "wind", wind, ampel( wind, 25, 50 ) ),
|
|
145
|
+
genMessage( "wind_max", node.storage.WindMax ),
|
|
146
|
+
genMessage( "illumination", msg.payload.lux )
|
|
147
|
+
] );
|
|
148
|
+
}
|
|
149
|
+
done();
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
RED.nodes.registerType( "ws90", Ws90Node );
|
|
154
|
+
}
|