├── LICENSE
├── README.md
├── config_node.html
├── config_node.js
├── config_req.html
├── config_req.js
├── dataregister.json
├── example
├── example_flow1.png
└── example_flow2.png
├── fan.html
├── fan.js
├── graph.html
├── graph.js
├── hotwater.html
├── hotwater.js
├── indoor.html
├── indoor.js
├── input.html
├── input.js
├── language-DE.json
├── language-EN.json
├── language-SE.json
├── output.html
├── output.js
├── package.json
├── price.html
├── price.js
├── request.html
├── request.js
├── rmu.html
├── rmu.js
├── translate.json
├── weather.html
└── weather.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 anerdins
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # node-red-contrib-nibepi
2 | Node-RED plugins for NibePi interface for connecting to Nibe F series heatpump
3 |
Link to downloadable image: http://anerdins.se/NibePi/nibepi_1.1_clean.rar
4 | Download the NibePi clean image file. Burn it to a 16GB SD-card and load it to the Raspberry Pi with the RS485 transciever.
5 | The image file contains a Raspbian setup, MQTT Broker (no auth), Node-RED, preinstalled NibePi nodes for Node-RED with a read-only filesystem (writemode is possible from Node-RED)
6 | Please edit the wpa_supplicant.conf file on the boot partion with your country and wifi credentials.
7 | The Node-RED interface will be available on http://IP_ADDRESS:1880 or http://nibepi:1880
8 |
9 |
10 | If you already have Node-RED set up, install my Node-RED nodes with this command from inside the Node-RED folder.
11 |
12 | ```
13 | npm install --save anerdins/node-red-contrib-nibepi#master
14 | ```
15 |
16 | 
17 | 
18 |
19 | Example flow here:
20 |
21 | ```
22 | [
23 | {
24 | "id": "c2626870.6a52c8",
25 | "type": "tab",
26 | "label": "Example",
27 | "disabled": false,
28 | "info": ""
29 | },
30 | {
31 | "id": "c2a66540.4f6c28",
32 | "type": "nibe-config",
33 | "z": "",
34 | "mqtt": true,
35 | "mqtt_discovery": false,
36 | "mqtt_topic": "nibe/modbus/",
37 | "mqtt_host": "127.0.0.1",
38 | "mqtt_port": "1883",
39 | "mqtt_user": "",
40 | "mqtt_pass": "",
41 | "readonly": true,
42 | "connection_series": "fSeries",
43 | "connection": "serial",
44 | "serial_port": "/dev/ttyAMA0",
45 | "tcp_server": "",
46 | "tcp_port": ""
47 | },
48 | {
49 | "id": "df78f47a.a51968",
50 | "type": "mqtt-broker",
51 | "z": "",
52 | "name": "NibePi",
53 | "broker": "127.0.0.1",
54 | "port": "1883",
55 | "clientid": "",
56 | "usetls": false,
57 | "compatmode": true,
58 | "keepalive": "60",
59 | "cleansession": true,
60 | "birthTopic": "",
61 | "birthQos": "0",
62 | "birthPayload": "",
63 | "closeTopic": "",
64 | "closeQos": "0",
65 | "closePayload": "",
66 | "willTopic": "",
67 | "willQos": "0",
68 | "willPayload": ""
69 | },
70 | {
71 | "id": "8c65f770.eb8d08",
72 | "type": "nibe-request",
73 | "z": "c2626870.6a52c8",
74 | "server": "c2a66540.4f6c28",
75 | "name": "40004",
76 | "x": 310,
77 | "y": 700,
78 | "wires": [
79 | [
80 | "1827f654.dda3da"
81 | ],
82 | [
83 | "562698f4.9ee208"
84 | ]
85 | ]
86 | },
87 | {
88 | "id": "b25667f9.794ea8",
89 | "type": "nibe-input",
90 | "z": "c2626870.6a52c8",
91 | "server": "c2a66540.4f6c28",
92 | "name": "40004",
93 | "add": false,
94 | "x": 100,
95 | "y": 420,
96 | "wires": [
97 | [
98 | "bf0de12f.f51dc"
99 | ],
100 | [
101 | "626f648.4ede99c"
102 | ]
103 | ]
104 | },
105 | {
106 | "id": "9a818749.c99058",
107 | "type": "nibe-output",
108 | "z": "c2626870.6a52c8",
109 | "server": "c2a66540.4f6c28",
110 | "name": "47260",
111 | "x": 345,
112 | "y": 847,
113 | "wires": [
114 | [
115 | "dc6484bf.e07ab8"
116 | ]
117 | ]
118 | },
119 | {
120 | "id": "bf0de12f.f51dc",
121 | "type": "debug",
122 | "z": "c2626870.6a52c8",
123 | "name": "Get only updated values in msg.payload",
124 | "active": false,
125 | "tosidebar": true,
126 | "console": false,
127 | "tostatus": false,
128 | "complete": "true",
129 | "targetType": "full",
130 | "x": 392,
131 | "y": 420,
132 | "wires": []
133 | },
134 | {
135 | "id": "626f648.4ede99c",
136 | "type": "debug",
137 | "z": "c2626870.6a52c8",
138 | "name": "Always updates when messages comes msg.payload",
139 | "active": false,
140 | "tosidebar": true,
141 | "console": false,
142 | "tostatus": false,
143 | "complete": "true",
144 | "targetType": "full",
145 | "x": 432,
146 | "y": 462,
147 | "wires": []
148 | },
149 | {
150 | "id": "37fbfd1e.6971c2",
151 | "type": "inject",
152 | "z": "c2626870.6a52c8",
153 | "name": "",
154 | "topic": "",
155 | "payload": "Original message",
156 | "payloadType": "str",
157 | "repeat": "",
158 | "crontab": "",
159 | "once": false,
160 | "onceDelay": 0.1,
161 | "x": 123,
162 | "y": 700,
163 | "wires": [
164 | [
165 | "8c65f770.eb8d08"
166 | ]
167 | ]
168 | },
169 | {
170 | "id": "1827f654.dda3da",
171 | "type": "debug",
172 | "z": "c2626870.6a52c8",
173 | "name": "msg.40004 is updated with the payload msg.40004_raw contains all json info",
174 | "active": true,
175 | "tosidebar": true,
176 | "console": false,
177 | "tostatus": false,
178 | "complete": "true",
179 | "targetType": "full",
180 | "x": 738,
181 | "y": 700,
182 | "wires": []
183 | },
184 | {
185 | "id": "562698f4.9ee208",
186 | "type": "debug",
187 | "z": "c2626870.6a52c8",
188 | "name": "msg.payload is updated with the request.",
189 | "active": true,
190 | "tosidebar": true,
191 | "console": false,
192 | "tostatus": false,
193 | "complete": "true",
194 | "targetType": "full",
195 | "x": 630,
196 | "y": 735,
197 | "wires": []
198 | },
199 | {
200 | "id": "61f75660.8a4878",
201 | "type": "comment",
202 | "z": "c2626870.6a52c8",
203 | "name": "Input node, first output only updates when the value is changed, the second output always updates, msg.raw contains the whole JSON with more information",
204 | "info": "",
205 | "x": 570,
206 | "y": 364,
207 | "wires": []
208 | },
209 | {
210 | "id": "b212730b.d53a3",
211 | "type": "comment",
212 | "z": "c2626870.6a52c8",
213 | "name": "Request node, request a updated value from the heatpump, the first output will add the message to msg.40004 (example), the second output adds it to the msg.payload",
214 | "info": "",
215 | "x": 600,
216 | "y": 644,
217 | "wires": []
218 | },
219 | {
220 | "id": "31a8d9c5.6981b6",
221 | "type": "inject",
222 | "z": "c2626870.6a52c8",
223 | "name": "",
224 | "topic": "",
225 | "payload": "0",
226 | "payloadType": "num",
227 | "repeat": "",
228 | "crontab": "",
229 | "once": false,
230 | "onceDelay": 0.1,
231 | "x": 100,
232 | "y": 847,
233 | "wires": [
234 | [
235 | "9a818749.c99058"
236 | ]
237 | ]
238 | },
239 | {
240 | "id": "dc6484bf.e07ab8",
241 | "type": "debug",
242 | "z": "c2626870.6a52c8",
243 | "name": "Confirmation from the heatpump that the message has been ACKED and the new value is returned",
244 | "active": false,
245 | "tosidebar": true,
246 | "console": false,
247 | "tostatus": false,
248 | "complete": "true",
249 | "targetType": "full",
250 | "x": 824,
251 | "y": 847,
252 | "wires": []
253 | },
254 | {
255 | "id": "41f4ab59.2f0d34",
256 | "type": "comment",
257 | "z": "c2626870.6a52c8",
258 | "name": "Output node, sets the selected register to the value in msg.payload",
259 | "info": "",
260 | "x": 290,
261 | "y": 791,
262 | "wires": []
263 | },
264 | {
265 | "id": "ee2894e7.83c318",
266 | "type": "nibe-input",
267 | "z": "c2626870.6a52c8",
268 | "server": "c2a66540.4f6c28",
269 | "name": "",
270 | "add": false,
271 | "x": 110,
272 | "y": 553,
273 | "wires": [
274 | [
275 | "4000c971.6d68c8"
276 | ],
277 | [
278 | "249a6ce6.a39d24"
279 | ]
280 | ]
281 | },
282 | {
283 | "id": "167e9574.2f7b9b",
284 | "type": "comment",
285 | "z": "c2626870.6a52c8",
286 | "name": "Unnamed node gets all the updates from every register that has been added or in the LOG.SET",
287 | "info": "",
288 | "x": 380,
289 | "y": 504,
290 | "wires": []
291 | },
292 | {
293 | "id": "4000c971.6d68c8",
294 | "type": "debug",
295 | "z": "c2626870.6a52c8",
296 | "name": "Only updated values",
297 | "active": false,
298 | "tosidebar": true,
299 | "console": false,
300 | "tostatus": false,
301 | "complete": "true",
302 | "targetType": "full",
303 | "x": 374,
304 | "y": 546,
305 | "wires": []
306 | },
307 | {
308 | "id": "249a6ce6.a39d24",
309 | "type": "debug",
310 | "z": "c2626870.6a52c8",
311 | "name": "Every value, the same or not",
312 | "active": false,
313 | "tosidebar": true,
314 | "console": false,
315 | "tostatus": false,
316 | "complete": "true",
317 | "targetType": "full",
318 | "x": 394,
319 | "y": 588,
320 | "wires": []
321 | },
322 | {
323 | "id": "e2d6010c.b657b",
324 | "type": "nibe-output",
325 | "z": "c2626870.6a52c8",
326 | "server": "c2a66540.4f6c28",
327 | "name": "",
328 | "x": 365,
329 | "y": 959,
330 | "wires": [
331 | [
332 | "db4d7e3a.27941"
333 | ]
334 | ]
335 | },
336 | {
337 | "id": "db4d7e3a.27941",
338 | "type": "debug",
339 | "z": "c2626870.6a52c8",
340 | "name": "Confirmation from the heatpump that the message has been ACKED and the new value is returned",
341 | "active": true,
342 | "tosidebar": true,
343 | "console": false,
344 | "tostatus": false,
345 | "complete": "true",
346 | "targetType": "full",
347 | "x": 838,
348 | "y": 959,
349 | "wires": []
350 | },
351 | {
352 | "id": "8bd15965.08a3f8",
353 | "type": "inject",
354 | "z": "c2626870.6a52c8",
355 | "name": "",
356 | "topic": "47260",
357 | "payload": "2",
358 | "payloadType": "num",
359 | "repeat": "",
360 | "crontab": "",
361 | "once": false,
362 | "onceDelay": 0.1,
363 | "x": 110,
364 | "y": 959,
365 | "wires": [
366 | [
367 | "e2d6010c.b657b"
368 | ]
369 | ]
370 | },
371 | {
372 | "id": "210aa2e4.0c50fe",
373 | "type": "comment",
374 | "z": "c2626870.6a52c8",
375 | "name": "No name in output node takes register from msg.topic, and the value from msg.payload",
376 | "info": "",
377 | "x": 350,
378 | "y": 910,
379 | "wires": []
380 | },
381 | {
382 | "id": "c73554e.4583fa8",
383 | "type": "exec",
384 | "z": "c2626870.6a52c8",
385 | "command": "sudo mount -o remount,ro /",
386 | "addpay": false,
387 | "append": "",
388 | "useSpawn": "false",
389 | "timer": "",
390 | "oldrc": false,
391 | "name": "Set READ-ONLY filesystem",
392 | "x": 920,
393 | "y": 220,
394 | "wires": [
395 | [],
396 | [],
397 | []
398 | ]
399 | },
400 | {
401 | "id": "fc6019a2.379148",
402 | "type": "inject",
403 | "z": "c2626870.6a52c8",
404 | "name": "Start",
405 | "topic": "",
406 | "payload": "",
407 | "payloadType": "date",
408 | "repeat": "",
409 | "crontab": "",
410 | "once": false,
411 | "onceDelay": 0.1,
412 | "x": 710,
413 | "y": 220,
414 | "wires": [
415 | [
416 | "c73554e.4583fa8"
417 | ]
418 | ]
419 | },
420 | {
421 | "id": "b4a28289.aeca2",
422 | "type": "exec",
423 | "z": "c2626870.6a52c8",
424 | "command": "sudo mount -o remount,rw /",
425 | "addpay": false,
426 | "append": "",
427 | "useSpawn": "false",
428 | "timer": "",
429 | "oldrc": false,
430 | "name": "Set READ/WRITE filesystem",
431 | "x": 920,
432 | "y": 280,
433 | "wires": [
434 | [],
435 | [],
436 | []
437 | ]
438 | },
439 | {
440 | "id": "46a6b81d.816838",
441 | "type": "inject",
442 | "z": "c2626870.6a52c8",
443 | "name": "Start",
444 | "topic": "",
445 | "payload": "",
446 | "payloadType": "date",
447 | "repeat": "",
448 | "crontab": "",
449 | "once": false,
450 | "onceDelay": 0.1,
451 | "x": 710,
452 | "y": 280,
453 | "wires": [
454 | [
455 | "b4a28289.aeca2"
456 | ]
457 | ]
458 | },
459 | {
460 | "id": "5a2aae0a.fc23a",
461 | "type": "comment",
462 | "z": "c2626870.6a52c8",
463 | "name": "Double click a NibePi node and add a server, the MQTT broker is running in the background, if you want NibePi to publish values to it, enable it. Set serialport etc.",
464 | "info": "",
465 | "x": 570,
466 | "y": 80,
467 | "wires": []
468 | },
469 | {
470 | "id": "ca5fa578.ff4248",
471 | "type": "comment",
472 | "z": "c2626870.6a52c8",
473 | "name": "The MQTT Broker is running at 127.0.0.1:1883 with no auth.",
474 | "info": "",
475 | "x": 260,
476 | "y": 122,
477 | "wires": []
478 | },
479 | {
480 | "id": "c6bd5ead.c97e4",
481 | "type": "comment",
482 | "z": "c2626870.6a52c8",
483 | "name": "In the input node, you can easily add registers by checking the box, NibePi will request them regulary, the more registers added, the longer it will take for each message.",
484 | "info": "",
485 | "x": 590,
486 | "y": 164,
487 | "wires": []
488 | },
489 | {
490 | "id": "99fbe0b.f0e772",
491 | "type": "inject",
492 | "z": "c2626870.6a52c8",
493 | "name": "",
494 | "topic": "removeRegister",
495 | "payload": "47260",
496 | "payloadType": "num",
497 | "repeat": "",
498 | "crontab": "",
499 | "once": false,
500 | "onceDelay": 0.1,
501 | "x": 140,
502 | "y": 256,
503 | "wires": [
504 | [
505 | "df31bf9e.12a3f"
506 | ]
507 | ]
508 | },
509 | {
510 | "id": "1856e3af.d0724c",
511 | "type": "inject",
512 | "z": "c2626870.6a52c8",
513 | "name": "",
514 | "topic": "addRegister",
515 | "payload": "47260",
516 | "payloadType": "num",
517 | "repeat": "",
518 | "crontab": "",
519 | "once": false,
520 | "onceDelay": 0.1,
521 | "x": 130,
522 | "y": 298,
523 | "wires": [
524 | [
525 | "df31bf9e.12a3f"
526 | ]
527 | ]
528 | },
529 | {
530 | "id": "df31bf9e.12a3f",
531 | "type": "nibe-output",
532 | "z": "c2626870.6a52c8",
533 | "server": "c2a66540.4f6c28",
534 | "name": "",
535 | "x": 376,
536 | "y": 277,
537 | "wires": [
538 | []
539 | ]
540 | },
541 | {
542 | "id": "2cb52bd8.777394",
543 | "type": "comment",
544 | "z": "c2626870.6a52c8",
545 | "name": "Remove or add register with the output node, set the topic as in the example.",
546 | "info": "",
547 | "x": 310,
548 | "y": 214,
549 | "wires": []
550 | },
551 | {
552 | "id": "8e3d51e6.e5956",
553 | "type": "debug",
554 | "z": "c2626870.6a52c8",
555 | "name": "MQTT ALL",
556 | "active": false,
557 | "tosidebar": true,
558 | "console": false,
559 | "tostatus": false,
560 | "complete": "payload",
561 | "targetType": "msg",
562 | "x": 390,
563 | "y": 1060,
564 | "wires": []
565 | },
566 | {
567 | "id": "4265c026.fe34b",
568 | "type": "mqtt in",
569 | "z": "c2626870.6a52c8",
570 | "name": "MQTT IN : nibe/modbus/#",
571 | "topic": "nibe/modbus/#",
572 | "qos": "2",
573 | "datatype": "auto",
574 | "broker": "df78f47a.a51968",
575 | "x": 164,
576 | "y": 1060,
577 | "wires": [
578 | [
579 | "8e3d51e6.e5956"
580 | ]
581 | ]
582 | },
583 | {
584 | "id": "986b14a8.cf5b88",
585 | "type": "comment",
586 | "z": "c2626870.6a52c8",
587 | "name": "If MQTT is activated, messages will come here",
588 | "info": "",
589 | "x": 224,
590 | "y": 1011,
591 | "wires": []
592 | },
593 | {
594 | "id": "9cb59e8d.d998a",
595 | "type": "mqtt out",
596 | "z": "c2626870.6a52c8",
597 | "name": "MQTT Out",
598 | "topic": "",
599 | "qos": "",
600 | "retain": "false",
601 | "broker": "df78f47a.a51968",
602 | "x": 460,
603 | "y": 1305,
604 | "wires": []
605 | },
606 | {
607 | "id": "4efe2494.f25bcc",
608 | "type": "inject",
609 | "z": "c2626870.6a52c8",
610 | "name": "",
611 | "topic": "nibe/modbus/47260/set",
612 | "payload": "2",
613 | "payloadType": "num",
614 | "repeat": "",
615 | "crontab": "",
616 | "once": false,
617 | "onceDelay": 0.1,
618 | "x": 164,
619 | "y": 1263,
620 | "wires": [
621 | [
622 | "9cb59e8d.d998a"
623 | ]
624 | ]
625 | },
626 | {
627 | "id": "d0917bf.7880088",
628 | "type": "inject",
629 | "z": "c2626870.6a52c8",
630 | "name": "",
631 | "topic": "nibe/modbus/47260/set",
632 | "payload": "0",
633 | "payloadType": "num",
634 | "repeat": "",
635 | "crontab": "",
636 | "once": false,
637 | "onceDelay": 0.1,
638 | "x": 164,
639 | "y": 1312,
640 | "wires": [
641 | [
642 | "9cb59e8d.d998a"
643 | ]
644 | ]
645 | },
646 | {
647 | "id": "2489accf.d8f284",
648 | "type": "inject",
649 | "z": "c2626870.6a52c8",
650 | "name": "",
651 | "topic": "nibe/modbus/47260/get",
652 | "payload": "true",
653 | "payloadType": "bool",
654 | "repeat": "",
655 | "crontab": "",
656 | "once": false,
657 | "onceDelay": 0.1,
658 | "x": 174,
659 | "y": 1361,
660 | "wires": [
661 | [
662 | "9cb59e8d.d998a"
663 | ]
664 | ]
665 | },
666 | {
667 | "id": "5eca8eef.35781",
668 | "type": "mqtt in",
669 | "z": "c2626870.6a52c8",
670 | "name": "MQTT IN : nibe/modbus/47260",
671 | "topic": "nibe/modbus/47260",
672 | "qos": "2",
673 | "datatype": "auto",
674 | "broker": "df78f47a.a51968",
675 | "x": 184,
676 | "y": 1109,
677 | "wires": [
678 | [
679 | "d7003a71.6ac5a8"
680 | ]
681 | ]
682 | },
683 | {
684 | "id": "70b75416.77deac",
685 | "type": "mqtt in",
686 | "z": "c2626870.6a52c8",
687 | "name": "MQTT IN : nibe/modbus/47260/raw",
688 | "topic": "nibe/modbus/47260/raw",
689 | "qos": "2",
690 | "datatype": "auto",
691 | "broker": "df78f47a.a51968",
692 | "x": 194,
693 | "y": 1158,
694 | "wires": [
695 | [
696 | "e86dfd41.0bc2d"
697 | ]
698 | ]
699 | },
700 | {
701 | "id": "9f17fb63.355578",
702 | "type": "mqtt in",
703 | "z": "c2626870.6a52c8",
704 | "name": "MQTT IN : nibe/modbus/47260/json",
705 | "topic": "nibe/modbus/47260/json",
706 | "qos": "2",
707 | "datatype": "auto",
708 | "broker": "df78f47a.a51968",
709 | "x": 194,
710 | "y": 1207,
711 | "wires": [
712 | [
713 | "53dab2da.1afa0c"
714 | ]
715 | ]
716 | },
717 | {
718 | "id": "d7003a71.6ac5a8",
719 | "type": "debug",
720 | "z": "c2626870.6a52c8",
721 | "name": "MQTT",
722 | "active": false,
723 | "tosidebar": true,
724 | "console": false,
725 | "tostatus": false,
726 | "complete": "payload",
727 | "targetType": "msg",
728 | "x": 405,
729 | "y": 1109,
730 | "wires": []
731 | },
732 | {
733 | "id": "e86dfd41.0bc2d",
734 | "type": "debug",
735 | "z": "c2626870.6a52c8",
736 | "name": "MQTT RAW",
737 | "active": true,
738 | "tosidebar": true,
739 | "console": false,
740 | "tostatus": false,
741 | "complete": "payload",
742 | "targetType": "msg",
743 | "x": 432,
744 | "y": 1158,
745 | "wires": []
746 | },
747 | {
748 | "id": "53dab2da.1afa0c",
749 | "type": "debug",
750 | "z": "c2626870.6a52c8",
751 | "name": "MQTT JSON",
752 | "active": true,
753 | "tosidebar": true,
754 | "console": false,
755 | "tostatus": false,
756 | "complete": "payload",
757 | "targetType": "msg",
758 | "x": 432,
759 | "y": 1207,
760 | "wires": []
761 | },
762 | {
763 | "id": "6540a3be.8a232c",
764 | "type": "inject",
765 | "z": "c2626870.6a52c8",
766 | "name": "",
767 | "topic": "nibe/modbus/47260/add",
768 | "payload": "true",
769 | "payloadType": "bool",
770 | "repeat": "",
771 | "crontab": "",
772 | "once": false,
773 | "onceDelay": 0.1,
774 | "x": 174,
775 | "y": 1410,
776 | "wires": [
777 | [
778 | "9cb59e8d.d998a"
779 | ]
780 | ]
781 | },
782 | {
783 | "id": "c82d49fd.a65a48",
784 | "type": "inject",
785 | "z": "c2626870.6a52c8",
786 | "name": "",
787 | "topic": "nibe/modbus/47260/remove",
788 | "payload": "true",
789 | "payloadType": "bool",
790 | "repeat": "",
791 | "crontab": "",
792 | "once": false,
793 | "onceDelay": 0.1,
794 | "x": 184,
795 | "y": 1459,
796 | "wires": [
797 | [
798 | "9cb59e8d.d998a"
799 | ]
800 | ]
801 | },
802 | {
803 | "id": "998dbdec.aa7f6",
804 | "type": "exec",
805 | "z": "c2626870.6a52c8",
806 | "command": "sudo reboot",
807 | "addpay": false,
808 | "append": "",
809 | "useSpawn": "false",
810 | "timer": "",
811 | "oldrc": false,
812 | "name": "Reboot NibePi hardware",
813 | "x": 990,
814 | "y": 440,
815 | "wires": [
816 | [],
817 | [],
818 | []
819 | ]
820 | },
821 | {
822 | "id": "82316f38.199e2",
823 | "type": "inject",
824 | "z": "c2626870.6a52c8",
825 | "name": "Start",
826 | "topic": "",
827 | "payload": "",
828 | "payloadType": "date",
829 | "repeat": "",
830 | "crontab": "",
831 | "once": false,
832 | "onceDelay": 0.1,
833 | "x": 790,
834 | "y": 440,
835 | "wires": [
836 | [
837 | "998dbdec.aa7f6"
838 | ]
839 | ]
840 | },
841 | {
842 | "id": "a434e28.575d82",
843 | "type": "inject",
844 | "z": "c2626870.6a52c8",
845 | "name": "Start",
846 | "topic": "",
847 | "payload": "",
848 | "payloadType": "date",
849 | "repeat": "",
850 | "crontab": "",
851 | "once": false,
852 | "onceDelay": 0.1,
853 | "x": 790,
854 | "y": 500,
855 | "wires": [
856 | [
857 | "a69d43bb.f7373"
858 | ]
859 | ]
860 | },
861 | {
862 | "id": "a69d43bb.f7373",
863 | "type": "exec",
864 | "z": "c2626870.6a52c8",
865 | "command": "sudo shutdown now",
866 | "addpay": false,
867 | "append": "",
868 | "useSpawn": "false",
869 | "timer": "",
870 | "oldrc": false,
871 | "name": "Shutdown NibePi hardware",
872 | "x": 1000,
873 | "y": 500,
874 | "wires": [
875 | [],
876 | [],
877 | []
878 | ]
879 | },
880 | {
881 | "id": "88732c48.ab2f5",
882 | "type": "comment",
883 | "z": "c2626870.6a52c8",
884 | "name": "NibePi will automatically discover the heatpump and load the registers when the right serialport is selected",
885 | "info": "",
886 | "x": 400,
887 | "y": 40,
888 | "wires": []
889 | }
890 | ]
891 | ```
892 |
--------------------------------------------------------------------------------
/config_node.html:
--------------------------------------------------------------------------------
1 |
131 |
132 |
--------------------------------------------------------------------------------
/config_req.html:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
49 |
50 |
--------------------------------------------------------------------------------
/config_req.js:
--------------------------------------------------------------------------------
1 | module.exports = function(RED) {
2 | function nibeRequest(config) {
3 | RED.nodes.createNode(this,config);
4 | this.server = RED.nodes.getNode(config.server);
5 | let nibe = this.server.nibe;
6 | let translate = this.server.translate;
7 | function getInfo(data,node,msg={}) {
8 | let category = data.category;
9 | let parameter = data.parameter;
10 | let label = data.label;
11 | if(category!==undefined && category!=="" && parameter!==undefined && parameter!=="") {
12 | if(label===undefined || label=="") label = parameter;
13 | let config = nibe.getConfig();
14 | if(config[category]!==undefined && config[category][parameter]!==undefined) {
15 | node.status({ fill: 'green', shape: 'dot', text: `Received parameter ${parameter}` });
16 | // Check if parameter should be visible
17 | if(parameter!=="enable") {
18 | if(config[category].enable===true) {
19 | msg.enabled = true;
20 | } else {
21 | msg.enabled = false;
22 | }
23 | }
24 | // Labeling
25 | let language = "SE"; // default language
26 | if(config.system!==undefined && config.system.language!==undefined) {
27 | language = config.system.language;
28 | }
29 | if(translate!==undefined && translate.dash!==undefined && translate.dash[label]!==undefined && translate.dash[label][language]!==undefined) {
30 | label = translate.dash[label][language]
31 | }
32 | msg.label = label;
33 | msg.payload = config[category][parameter];
34 | node.send(msg);
35 | } else {
36 | node.status({ fill: 'red', shape: 'dot', text: `Could not found parameter` });
37 | node.send(msg);
38 | }
39 | }
40 | }
41 | if (this.server) {
42 | getInfo(config,this);
43 | this.on('input', function(msg) {
44 | if(msg.topic=="setConfig") {
45 | this.server.updateConfig(config.category,config.parameter,msg.payload);
46 | } else {
47 | getInfo(config,this,msg)
48 | }
49 | });
50 | if(config.category!==undefined && config.parameter!==undefined) {
51 | this.server.nibeData.on(`config_${config.category}`, (data) => {
52 | getInfo(config,this)
53 | })
54 | }
55 |
56 | } else {
57 | this.status({ fill: 'red', shape: 'dot', text: `No configuration node configured` });
58 | // No config node configured
59 | }
60 | }
61 | RED.nodes.registerType("nibe-config-req",nibeRequest);
62 | }
--------------------------------------------------------------------------------
/dataregister.json:
--------------------------------------------------------------------------------
1 | {
2 | "fSeries":{
3 | "inside":"40033",
4 | "inside_s1":"40033",
5 | "inside_s2":"40032",
6 | "outside":"40004",
7 | "heatcurve":"47007",
8 | "curveadjust":"47011",
9 | "cpr_tot_kwh":"43144",
10 | "cpr_hw_kwh":"43305",
11 | "curveadjust_s1":"47011",
12 | "curveadjust_s2":"47010",
13 | "heatcurve_s1":"47007",
14 | "heatcurve_s2":"47006",
15 | "setpoint_s1":"43009",
16 | "setpoint_s2":"43008",
17 | "supply_s1":"40008",
18 | "supply_s2":"40007",
19 | "inside_enable_s1":"47394",
20 | "inside_enable_s2":"47393",
21 | "inside_set_s1":"47398",
22 | "inside_set_s2":"47397",
23 | "inside_factor_s1":"47402",
24 | "inside_factor_s2":"47401",
25 | "outdoor_set":"47375",
26 | "dM":"43005",
27 | "dMstart":"47206",
28 | "dMadd":"48072",
29 | "exhaust":"40025",
30 | "hw_mode":"47041",
31 | "price_current":"41928",
32 | "price_level":"41929",
33 | "price_enable":"44908",
34 | "startHW":"48132",
35 | "startHW_rmu_s1":"10020",
36 | "startHW_rmu_s2":"10120",
37 | "startHW_rmu_s3":"10220",
38 | "startHW_rmu_s4":"10320",
39 | "fan_rmu_s1":"10010",
40 | "fan_rmu_s2":"10110",
41 | "fan_rmu_s3":"10210",
42 | "fan_rmu_s4":"10310",
43 | "fan_mode":"47260",
44 | "rmu_sensor_s1":"10060",
45 | "rmu_sensor_s2":"10160",
46 | "rmu_sensor_s3":"10260",
47 | "rmu_sensor_s4":"10360",
48 | "rmu_s1":"47365",
49 | "rmu_s2":"47366",
50 | "rmu_s3":"47367",
51 | "rmu_s4":"47368",
52 | "bt6":"40014",
53 | "bt7":"40013",
54 | "hw_start_0":"47045",
55 | "hw_start_1":"47044",
56 | "hw_start_2":"47043",
57 | "hw_stop_0":"47049",
58 | "hw_stop_1":"47048",
59 | "hw_stop_2":"47047",
60 | "bs1_flow":"40050",
61 | "fan_speed":"47265",
62 | "alarm":"45001",
63 | "alarm_reset":"45171",
64 | "vented":"40026",
65 | "cpr_set":"43182",
66 | "cpr_act":"43136",
67 | "defrost_time":"43066",
68 | "evaporator":"40020"
69 | },
70 | "sSeries": {
71 | "inside":"30026",
72 | "inside_s1":"30026",
73 | "inside_s2":"00000",
74 | "outside":"30001",
75 | "heatcurve":"40026",
76 | "curveadjust":"40030",
77 | "cpr_tot_kwh":"00000",
78 | "cpr_hw_kwh":"00000",
79 | "curveadjust_s1":"40030",
80 | "curveadjust_s2":"40029",
81 | "heatcurve_s1":"40026",
82 | "heatcurve_s2":"40025",
83 | "setpoint_s1":"31017",
84 | "setpoint_s2":"31016",
85 | "supply_s1":"30005",
86 | "supply_s2":"30004",
87 | "inside_enable_s1":"00000",
88 | "inside_enable_s2":"00000",
89 | "inside_set_s1":"00000",
90 | "inside_set_s2":"00000",
91 | "inside_factor_s1":"00000",
92 | "inside_factor_s2":"00000",
93 | "outdoor_set":"00000",
94 | "dM":"40018",
95 | "dMstart":"00000",
96 | "dMadd":"00000",
97 | "exhaust":"00000",
98 | "hw_mode":"00000",
99 | "price_current":"00000",
100 | "price_level":"00000",
101 | "price_enable":"00000",
102 | "startHW":"00000",
103 | "fan_mode":"00000",
104 | "bt6":"30009",
105 | "bt7":"30008",
106 | "hw_start_0":"00000",
107 | "hw_start_1":"00000",
108 | "hw_start_2":"00000",
109 | "hw_stop_0":"00000",
110 | "hw_stop_1":"00000",
111 | "hw_stop_2":"00000",
112 | "bs1_flow":"00000",
113 | "fan_speed":"00000",
114 | "alarm":"31975",
115 | "vented":"00000",
116 | "cpr_set":"00000",
117 | "cpr_act":"31046",
118 | "defrost_time":"00000",
119 | "evaporator":"00000"
120 | }
121 | }
--------------------------------------------------------------------------------
/example/example_flow1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/anerdins/node-red-contrib-nibepi/f3cf74d99c43250eb02dfb5533a92e777d50721e/example/example_flow1.png
--------------------------------------------------------------------------------
/example/example_flow2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/anerdins/node-red-contrib-nibepi/f3cf74d99c43250eb02dfb5533a92e777d50721e/example/example_flow2.png
--------------------------------------------------------------------------------
/fan.html:
--------------------------------------------------------------------------------
1 |
56 |
57 |
90 |
91 |
--------------------------------------------------------------------------------
/fan.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(RED) {
3 | function nibeFan(config) {
4 | RED.nodes.createNode(this,config);
5 | const server = RED.nodes.getNode(config.server);
6 | const startUp = () => {
7 | let system = config.system.replace('s','S');
8 | this.status({ fill: 'yellow', shape: 'dot', text: `System ${system}` });
9 | const arr = [
10 | /*{topic:"bs1_flow",source:"nibe"},
11 | {topic:"fan_speed",source:"nibe"},
12 | {topic:"alarm",source:"nibe"},
13 | {topic:"vented",source:"nibe"},
14 | {topic:"cpr_set",source:"nibe"},
15 | {topic:"cpr_act",source:"nibe"}*/
16 | ];
17 | let conf = server.nibe.getConfig();
18 | if(conf.fan===undefined) {
19 | conf.fan = {};
20 | server.nibe.setConfig(conf);
21 | }
22 | /*
23 | if(conf.home.inside_sensors===undefined) {
24 | conf.home.inside_sensors = [];
25 | server.nibe.setConfig(conf);
26 | }
27 | if(conf.fan.sensor===undefined || conf.fan.sensor=="Ingen") {
28 |
29 | } else {
30 | let index = conf.home.inside_sensors.findIndex(i => i.name == conf.fan.sensor);
31 | if(index!==-1) {
32 | var co2Sensor = Object.assign({}, conf.home.inside_sensors[index]);
33 | arr.push(co2Sensor);
34 | }
35 | }*/
36 | server.initiatePlugin(arr,'fan',config.system).then(data => {
37 | this.status({ fill: 'green', shape: 'dot', text: `System ${system}` });
38 | this.send({enabled:true});
39 | },(reject => {
40 | this.status({ fill: 'red', shape: 'dot', text: `System ${system}` });
41 | this.send({enabled:false});
42 | }));
43 | }
44 | this.on('input', function(msg) {
45 | let conf = server.nibe.getConfig();
46 | if(msg.topic=="update") {
47 | server.runFan();
48 | return;
49 | }
50 | if(msg.payload!==undefined && msg.topic!==undefined && msg.topic!=="") {
51 | let req = msg.topic.split('/');
52 | if(conf[req[0]]===undefined) conf[req[0]] = {};
53 | if(conf[req[0]][req[1]]!==msg.payload) {
54 | conf[req[0]][req[1]] = msg.payload;
55 | server.nibe.setConfig(conf);
56 | }
57 | startUp();
58 | }
59 |
60 | });
61 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
62 | startUp();
63 | } else {
64 | server.nibeData.on('ready', (data) => {
65 | startUp();
66 | })
67 | }
68 | server.nibeData.on(this.id, (data) => {
69 | if(data.changed===true) {
70 | config.system = data.system;
71 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
72 | startUp();
73 | }
74 | }
75 | })
76 | server.nibeData.on('pluginFan', (data) => {
77 | let co2 = data.co2Sensor;
78 | let low_co2_limit = data.low_co2_limit;
79 | let high_co2_limit = data.high_co2_limit;
80 | if(co2!==undefined && co2.data!==undefined && co2.data.data>0) {
81 | //this.send({topic:"CO2",payload:co2.data.data});
82 | }
83 | if(low_co2_limit!==undefined) {
84 | //this.send({topic:"CO2 Gränsvärde för sänkt flöde.",payload:low_co2_limit});
85 | }
86 | if(high_co2_limit!==undefined) {
87 | //this.send({topic:"CO2 Gränsvärde för ökat flöde.",payload:high_co2_limit});
88 | }
89 | if(data.filter_eff!==undefined) {
90 | this.send({topic:"Filtereffektivitet",payload:data.filter_eff});
91 | }
92 | this.send({topic:"Fläkthastighet",payload:data['fan_speed'].data});
93 | this.send({topic:"Luftflöde",payload:data['bs1_flow'].data});
94 | this.send({topic:"Luftflöde Börvärde",payload:data.setpoint});
95 | this.send({topic:"Kompressorfrekvens",payload:data['cpr_act'].data});
96 | this.send({topic:"Förångartemperatur",payload:data['evaporator'].raw_data});
97 | })
98 |
99 | this.on('close', function() {
100 | let system = config.system.replace('s','S');
101 | server.nibeData.removeAllListeners();
102 | this.status({ fill: 'yellow', shape: 'dot', text: `System ${system}` });
103 | });
104 | }
105 | RED.nodes.registerType("nibe-fan",nibeFan);
106 | }
--------------------------------------------------------------------------------
/graph.html:
--------------------------------------------------------------------------------
1 |
52 |
53 |
94 |
95 |
--------------------------------------------------------------------------------
/graph.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(RED) {
3 | function nibeGraph(config) {
4 | RED.nodes.createNode(this,config);
5 | const server = RED.nodes.getNode(config.server);
6 | let node = this;
7 | let timeFrame;
8 | let translate = server.translate.dash;
9 | function buildGraph(arr) {
10 | let timeNow = Date.now();
11 | if(timeFrame===undefined) timeFrame = (24*60*60*1000);
12 | let timeCut = timeNow-timeFrame;
13 | let savedData = server.savedData();
14 | //let savedGraph = Object.assign({}, ...server.savedGraph())
15 | let savedGraph = server.savedGraph();
16 | var array = [
17 | {
18 | "series":[],
19 | "data":[]
20 | }];
21 | for( var i = 0; i < arr.length; i++){
22 | let name;
23 | if(arr[i].name!==undefined && arr[i].name!=="") {
24 | name = arr[i].name;
25 | } else {
26 | if(savedData[arr[i].register]!==undefined) {
27 | name = savedData[arr[i].register].titel;
28 | }
29 | }
30 | if(name!==undefined) {
31 | let index = array[0].series.findIndex(n => n == name);
32 | if(index===-1) {
33 | if(savedGraph[arr[i].register]!==undefined && savedGraph[arr[i].register].length!==0) {
34 | if(arr[i].chart==="pie") {
35 | timeCut = timeNow-(timeFrame*24);
36 | }
37 | let cut = savedGraph[arr[i].register].findIndex(n => n.x >= timeCut);
38 | if(cut!==-1) {
39 | let newArr = savedGraph[arr[i].register].slice(cut,savedGraph[arr[i].register].length);
40 | if(newArr.length!==0) {
41 | if(newArr[0].x!==timeCut) {
42 | newArr.unshift({x:timeCut,y:newArr[0].y})
43 | }
44 | }
45 | if(newArr.length!==0) {
46 | if(arr[i].chart==="pie") {
47 | let value = 0;
48 | for( var j = 0; j < newArr.length; j++){
49 | value = value+newArr[j].y;
50 | }
51 | //array[0].series.push(name);
52 | if(array[0].series.length===0) array[0].series.push("")
53 | if(array[0].labels===undefined) array[0].labels = [];
54 | array[0].labels.push(name);
55 | if(array[0].data.length===0) {
56 | array[0].data.push([Math.round(value/newArr.length)])
57 | } else if(array[0].data.length===1) {
58 | array[0].data[0].push(Math.round(value/newArr.length))
59 | }
60 | } else {
61 | array[0].series.push(name);
62 | array[0].data.push(newArr);
63 | }
64 |
65 | }
66 | }
67 | }
68 | }
69 | }
70 | }
71 | if(array[0].series.length!==0) {
72 | return array;
73 | } else {
74 | return [];
75 | }
76 | }
77 | function updateData() {
78 | let systems = server.systems();
79 | let conf = server.config;
80 | if(conf.data===undefined || conf.data.graph===undefined) {
81 | conf.data = {graph:[]};
82 | server.nibe.setConfig(conf)
83 | }
84 | if(config.select=="datagraph") {
85 | let arr = [];
86 | for( var i = 0; i < conf.data.graph.length; i++){
87 | arr.push({register:conf.data.graph[i]});
88 | }
89 | node.send({graph:"datagraph",payload:buildGraph(arr)});
90 | } else if(config.select=="start_1") {
91 | let arr = buildGraph(
92 | [
93 | {name:"Utomhustemperatur",register:server.hP()['outside']},
94 | {name:"Inomhustemperatur",register:server.hP()['inside']},
95 | {name:"Kurvjustering S1",register:server.hP()['curveadjust_s1']},
96 | {name:"Börvärde S1",register:server.hP()['setpoint_s1']},
97 | {name:"Framledning S1",register:server.hP()['supply_s1']}
98 | ]
99 | );
100 | if(systems!==undefined && systems.s2===true) {
101 | arr.push({name:"Kurvjustering S2",register:server.hP()['curveadjust_s2']});
102 | arr.push({name:"Börvärde S2",register:server.hP()['setpoint_s2']});
103 | arr.push({name:"Framledning S2",register:server.hP()['supply_s2']});
104 | }
105 | node.send({graph:"start_1",payload:arr});
106 | } else if(config.select=="forecast_1") {
107 | let arr = [
108 | {name:"Utomhustemperatur",register:server.hP()['outside']},
109 | {name:"Inomhustemperatur",register:['weather_sensor_'+config.system]},
110 | {name:"Kurvjustering",register:['weather_offset_'+config.system]},
111 | {name:"Prognos",register:['weather_forecast_'+config.system]},
112 | {name:"Ojusterad Prognos",register:['weather_unfilterd_'+config.system]}
113 | ]
114 | node.send({graph:config.select,payload:buildGraph(arr)});
115 | } else if(config.select=="indoor_1") {
116 | let arr = [
117 | {name:"Utomhustemperatur",register:server.hP()['outside']},
118 | {name:"Inomhustemperatur",register:['indoor_sensor_'+config.system]},
119 | {name:"Inomhusbörvärde",register:server.hP()['inside_set_'+config.system]}
120 | ]
121 | if(conf.indoor['enable_'+config.system]===true) {
122 | arr.push({name:"Kurvjustering",register:['indoor_offset_'+config.system]});
123 | }
124 | node.send({graph:config.select,payload:buildGraph(arr)});
125 | } else if(config.select=="diagnostic_defrost") {
126 | if(conf.system.pump!==undefined && (conf.system.pump=="F730" || conf.system.pump=="F750")) {
127 | let arr = [
128 | {name:"Gångtid (m)",register:"cpr_runtime"},
129 | {name:"Avfrostningstid (m)",register:"defrosting"}
130 | ]
131 | node.send({graph:config.select,enabled:true,payload:buildGraph(arr)});
132 | } else {
133 | node.send({graph:config.select,enabled:false});
134 | }
135 | } else if(config.select=="efficiency_graph") {
136 | if(conf.system.pump!==undefined && (conf.system.pump=="F730" || conf.system.pump=="F750")) {
137 | let arr = [
138 | {name:"Gångtid (%)",register:"cpr_uptime",chart:"pie"},
139 | {name:"Avfrostningstid (%)",register:"cpr_downtime",chart:"pie"}
140 | ]
141 | let result = buildGraph(arr);
142 | if(result[0]===undefined) return;
143 | let cpr_efficiency = result[0].data[0][0];
144 | if(100-cpr_efficiency>50) {
145 | node.send([null,{topic:"Resultat",payload:`Värmepumpens effektivitet är mycket dålig, långa avfrostningstider.
146 | Trolig orsak är felaktigt placerad avluftsgivare eller igensatt filter med nedsmutsning av förångare.
147 | Kompressorn har en effektivitet på endast ${cpr_efficiency} % och hela ${100-cpr_efficiency} % avfrostningstid.
`}])
148 | } else if(100-cpr_efficiency>35) {
149 | node.send([null,{topic:"Resultat",payload:`Värmepumpens effektivitet är dålig, långa avfrostningstider.
150 | Trolig orsak är felaktigt placerad avluftsgivare eller igensatt filter med nedsmutsning av förångare.
151 | Kompressorn har en effektivitet på ${cpr_efficiency} % och ${100-cpr_efficiency} % avfrostningstid.
`}])
152 | } else if(100-cpr_efficiency>25) {
153 | node.send([null,{topic:"Resultat",payload:`Värmepumpens effektivitet är är bra men kunde vara bättre.
154 | Kontrollera luftfilter.
155 | Kompressorn har en effektivitet på ${cpr_efficiency} % och ${100-cpr_efficiency} % avfrostningstid.
`}])
156 | } else {
157 | node.send([null,{topic:"Resultat",payload:`Värmepumpens effektivitet är mycket bra.
158 | Kompressorn har en effektivitet på ${cpr_efficiency} % och endast ${100-cpr_efficiency} % avfrostningstid.
`}])
159 | }
160 | node.send({graph:config.select,enabled:true,payload:result});
161 | node.send({graph:config.select,enabled:true,payload:result});
162 | } else {
163 | node.send({graph:config.select,enabled:false});
164 | }
165 | } else if(config.select=="fan_1") {
166 | let arr = [
167 | {name:"Fläkthastighet",register:server.hP()['fan_speed']},
168 | {name:"Luftflöde",register:server.hP()['bs1_flow']},
169 | {name:"Luftflöde Börvärde",register:['fan_setpoint']},
170 | {name:"Kompressorfrekvens",register:server.hP()['cpr_act']},
171 | {name:"Förångartemperatur",register:server.hP()['evaporator']}
172 | ]
173 | if(conf.fan.enable_co2===true) {
174 | arr.push({name:"CO2",register:['fan_co2Sensor']});
175 | arr.push({name:"CO2 Gränsvärde för sänkt flöde",register:['fan_low_co2_limit']});
176 | arr.push({name:"CO2 Gränsvärde för ökat flöde",register:['fan_high_co2_limit']});
177 | }
178 | if(conf.fan.enable_filter===true) {
179 | arr.push({name:"Filtereffektivitet",register:['filter_eff']});
180 | }
181 | node.send({graph:config.select,payload:buildGraph(arr)});
182 | } else if(config.select=="hw_lux") {
183 | let arr = [
184 | {name:"BT7 Topp",register:server.hP()['bt7']},
185 | {name:"BT6 Laddning",register:server.hP()['bt6']},
186 | {name:"Startvärde",register:'hw_trigger_temp'}
187 | ]
188 | arr.push({name:"Stoppvärde",register:'hw_target_temp'});
189 | node.send({graph:config.select,payload:buildGraph(arr)});
190 | } else if(config.select=="hw_prio") {
191 | let arr = [
192 | {name:"BT7 Topp",register:server.hP()['bt7']},
193 | {name:"BT6 Laddning",register:server.hP()['bt6']},
194 | {name:"Startvärde",register:'hw_start_temp'},
195 | {name:"Stoppvärde",register:'hw_stop_temp'}
196 | ]
197 | node.send({graph:config.select,payload:buildGraph(arr)});
198 | } else if(config.select=="registers_selected") {
199 | node.status({ fill: 'green', shape: 'dot', text: `Selected registerlist` });
200 | let list = buildRegisterList();
201 | if(list.length>0) {
202 | node.send({payload:list});
203 | }
204 | }
205 | }
206 | node.on('input', function(msg) {
207 | if(msg.topic=="update") {
208 | updateData();
209 | } else if(msg.topic=="time") {
210 | timeFrame = msg.payload*60*60*1000;
211 | updateData();
212 | }
213 |
214 | });
215 | server.nibeData.on('updateGraph', () => {
216 | updateData();
217 | });
218 | server.nibeData.on(this.id, (data) => {
219 | if(data.changed===true) {
220 | config.system = data.system;
221 | }
222 | })
223 | function buildRegisterList() {
224 | let conf = server.config;
225 | let savedData = server.savedData();
226 | var list = [];
227 | let icons = {
228 | "":"fa-bars",
229 | "°C":"fa-thermometer-three-quarters",
230 | "A":"fa-flash",
231 | "Hz":"fa-bar-chart",
232 | "min":"fa-clock-o",
233 | "h":"fa-clock-o",
234 | "m":"fa-clock-o",
235 | "kWh":"fa-area-chart",
236 | "W":"fa-flash",
237 | "l/m":"fa-leaf",
238 | "%RH":"fa-cloud",
239 | "%":"fa-percent"
240 | }
241 | for( var n = 0; n < conf.registers.length; n++){
242 | if(savedData[conf.registers[n]]!==undefined) {
243 | let register = {};
244 | register.title = `${savedData[conf.registers[n]].register}, ${savedData[conf.registers[n]].titel}`;
245 | register.description = `${savedData[conf.registers[n]].data} ${savedData[conf.registers[n]].unit}`;
246 | register.register = savedData[conf.registers[n]].register;
247 | register.icon_name = icons[savedData[conf.registers[n]].unit];
248 | if(conf.data!==undefined && conf.data.graph!==undefined) {
249 | let index = conf.data.graph.findIndex(i => i == savedData[conf.registers[n]].register);
250 | if(index!==-1) {
251 | register.isChecked = true;
252 | } else {
253 | register.isChecked = false;
254 | }
255 | }
256 |
257 | if(savedData[conf.registers[n]].register=="45001") register.icon_name = "fa-warning";
258 | if(savedData[conf.registers[n]].register=="10001") register.icon_name = "fa-warning";
259 | list.push(register);
260 | }
261 | }
262 | list.sort((a, b) => (a.register > b.register) ? 1 : -1)
263 | return(list)
264 | }
265 | node.on('close', function() {
266 | let system = config.system.replace('s','S');
267 | server.nibeData.removeAllListeners();
268 | node.status({ fill: 'yellow', shape: 'dot', text: `` });
269 | });
270 | }
271 | RED.nodes.registerType("nibe-graph",nibeGraph);
272 | }
--------------------------------------------------------------------------------
/hotwater.html:
--------------------------------------------------------------------------------
1 |
63 |
64 |
84 |
85 |
--------------------------------------------------------------------------------
/hotwater.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(RED) {
3 | function nibeHotwater(config) {
4 | RED.nodes.createNode(this,config);
5 | const server = RED.nodes.getNode(config.server);
6 | const startUp = () => {
7 | const arr = [
8 | //{topic:"bt6",source:"nibe"},
9 | //{topic:"bt7",source:"nibe"},
10 | ];
11 | let conf = server.nibe.getConfig();
12 | if(conf.price===undefined) {
13 | conf.price = {};
14 | server.nibe.setConfig(conf);
15 | }
16 | server.initiatePlugin(arr,'hotwater').then(result => {
17 | this.status({ fill: 'green', shape: 'dot', text: `` });
18 | this.send({enabled:true});
19 | },(reject => {
20 | this.status({ fill: 'red', shape: 'dot', text: `` });
21 | this.send({enabled:false});
22 | }));
23 | }
24 |
25 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
26 | startUp();
27 | } else {
28 | server.nibeData.on('ready', (data) => {
29 | startUp();
30 | })
31 | }
32 | this.on('input', function(msg) {
33 | let conf = server.nibe.getConfig();
34 | if(msg.topic=="update") {
35 | server.hotwaterPlugin();
36 | return;
37 | }
38 | if(msg.payload!==undefined && msg.topic!==undefined && msg.topic!=="") {
39 | let req = msg.topic.split('/');
40 | if(conf[req[0]][req[1]+'_'+config.system]!==msg.payload) {
41 | conf[req[0]][req[1]+'_'+config.system] = msg.payload;
42 | server.nibe.setConfig(conf);
43 | startUp();
44 | }
45 | }
46 |
47 | });
48 | server.nibeData.on(this.id, (data) => {
49 | if(data.changed===true) {
50 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
51 | startUp();
52 | }
53 | }
54 | })
55 | server.nibeData.on('pluginHotwaterAutoLuxury', (value) => {
56 | if(value.bt7!==undefined && value.bt7.data>-3276) {
57 | this.send({topic:"BT7 Topp",payload:value.bt7.data})
58 | this.send({topic:"BT6 Laddning",payload:value.bt6.data})
59 | if(value.hwTriggerTemp!==undefined) this.send({topic:"Startvärde",payload:value.hwTriggerTemp})
60 | if(value.hwTargetValue!==undefined) {
61 | this.send({topic:"Stoppvärde",payload:value.hwTargetValue});
62 | } else {
63 | this.send({topic:"Stoppvärde",payload:value.bt6.data});
64 | }
65 | }
66 | })
67 | server.nibeData.on('pluginHotwaterPriority', (value) => {
68 | if(value.bt7!==undefined && value.bt7.data>-3276) {
69 | this.send([null,{topic:"BT7 Topp",payload:value.bt7.data}])
70 | this.send([null,{topic:"BT6 Laddning",payload:value.bt6.data}])
71 | this.send([null,{topic:"Startvärde",payload:value.hwStartTemp.data}])
72 | this.send([null,{topic:"Stoppvärde",payload:value.hwStopTemp.data}])
73 |
74 | }
75 | })
76 | this.on('close', function() {
77 | server.nibeData.removeAllListeners();
78 | this.status({ fill: 'yellow', shape: 'dot', text: `` });
79 | });
80 | }
81 | RED.nodes.registerType("nibe-hotwater",nibeHotwater);
82 | }
83 |
--------------------------------------------------------------------------------
/indoor.html:
--------------------------------------------------------------------------------
1 |
56 |
57 |
90 |
91 |
--------------------------------------------------------------------------------
/indoor.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(RED) {
3 | function nibeIndoor(config) {
4 | RED.nodes.createNode(this,config);
5 | let node = this;
6 | const server = RED.nodes.getNode(config.server);
7 | async function startUp() {
8 | let system = config.system.replace('s','S');
9 | let conf = server.nibe.getConfig();
10 | node.status({ fill: 'yellow', shape: 'dot', text: `System ${system}` });
11 | let arr = [
12 | //{topic:"inside_"+config.system,source:"nibe"},
13 | {topic:"inside_set_"+config.system,source:"nibe"},
14 | {topic:"inside_enable_"+config.system,source:"nibe"},
15 | {topic:"inside_factor_"+config.system,source:"nibe"},
16 | {topic:"exhaust",source:"nibe"},
17 | {topic:"outside",source:"nibe"}
18 | ];
19 | if(conf.system.pump!=="F370" && conf.system.pump!=="F470") {
20 | arr.push({topic:"dM",source:"nibe"});
21 | arr.push({topic:"dMstart",source:"nibe"})
22 | }
23 | if(conf.indoor===undefined) {
24 | conf.indoor = {};
25 | server.nibe.setConfig(conf);
26 | }
27 | if(conf.home.inside_sensors===undefined) {
28 | conf.home.inside_sensors = [];
29 | server.nibe.setConfig(conf);
30 | }
31 | if(conf.indoor['sensor_'+config.system]===undefined || conf.indoor['sensor_'+config.system]=="Ingen") {
32 | arr.push({topic:"inside_"+config.system,source:"nibe"});
33 | } else {
34 | let index = conf.home.inside_sensors.findIndex(i => i.name == conf.indoor['sensor_'+config.system]);
35 | if(index!==-1) {
36 | var insideSensor = Object.assign({}, conf.home.inside_sensors[index]);
37 |
38 |
39 | arr.push(insideSensor);
40 | }
41 | }
42 | let nibe_enabled = await server.nibe.reqData(server.hP()["inside_enable_"+config.system]).catch();
43 | if(nibe_enabled===undefined || nibe_enabled.data===undefined || nibe_enabled.data!==1 && conf.indoor['enable_'+config.system]!==true){ arr = [];}
44 | server.initiatePlugin(arr,'indoor',config.system).then(data => {
45 | node.status({ fill: 'green', shape: 'dot', text: `System ${system}` });
46 | node.send({enabled:true});
47 | },(reject => {
48 | node.status({ fill: 'red', shape: 'dot', text: `System ${system}` });
49 | node.send({enabled:false});
50 | }));
51 | }
52 | node.on('input', function(msg) {
53 | let conf = server.nibe.getConfig();
54 | if(msg.topic=="update") {
55 | server.updateData();
56 | return;
57 | }
58 | if(msg.payload!==undefined && msg.topic!==undefined && msg.topic!=="") {
59 | let req = msg.topic.split('/');
60 | if(conf[req[0]]===undefined) conf[req[0]] = {};
61 | if(conf[req[0]][req[1]+'_'+config.system]!==msg.payload) {
62 | conf[req[0]][req[1]+'_'+config.system] = msg.payload;
63 | server.nibe.setConfig(conf);
64 | }
65 | startUp();
66 | }
67 |
68 | });
69 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
70 | startUp();
71 | } else {
72 | server.nibeData.on('ready', (data) => {
73 | startUp();
74 | })
75 | }
76 | server.nibeData.on(node.id, (data) => {
77 | if(data.changed===true) {
78 | config.system = data.system;
79 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
80 | startUp();
81 | }
82 | }
83 | })
84 | server.nibeData.on('pluginIndoor', (data) => {
85 | if(data.system===config.system) {
86 | let outside = data['outside'];
87 | let inside = data.indoorSensor;
88 |
89 | if(inside===undefined) inside = data['inside_'+data.system];
90 | if(inside!==undefined && inside.data>-3276) {
91 | node.send({topic:"Inomhustemperatur",payload:inside.data});
92 | }
93 | if(data.indoorOffset!==undefined) {
94 | node.send({topic:"Kurvjustering",payload:data.indoorOffset});
95 | }
96 | node.send({topic:"Tid",payload:outside.timestamp});
97 | node.send({topic:"Avvikelse",payload:data.accuracy});
98 | }
99 | })
100 |
101 | node.on('close', function() {
102 | let system = config.system.replace('s','S');
103 | server.nibeData.removeAllListeners();
104 | node.status({ fill: 'yellow', shape: 'dot', text: `System ${system}` });
105 | });
106 | }
107 | RED.nodes.registerType("nibe-indoor",nibeIndoor);
108 | }
--------------------------------------------------------------------------------
/input.html:
--------------------------------------------------------------------------------
1 |
26 |
27 |
41 |
42 |
--------------------------------------------------------------------------------
/input.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(RED) {
3 | function nibeInput(config) {
4 | RED.nodes.createNode(this,config);
5 | const server = RED.nodes.getNode(config.server);
6 | const nibe = server.nibe;
7 | var savedError = {};
8 | if(config.add===true && config.name.toLowerCase()!="config" && config.name.toLowerCase()!="error") {
9 | nibe.addRegister(config.name);
10 | }
11 | let register = config.name.toLowerCase();
12 | if(server.hP()[config.name]!==undefined) {
13 | register = server.hP()[config.name]
14 | }
15 | server.nibeData.on('ready', data => {
16 | if(server.hP()[config.name]!==undefined) {
17 | register = server.hP()[config.name]
18 | }
19 | })
20 | var node = this;
21 | if(config.name=="") {
22 | server.nibeData.on('data', data => {
23 | let saved = node.context().get(data.register);
24 | if(data.error!==undefined) {
25 |
26 | } else {
27 | if(saved!=data.data) {
28 | node.send([{topic:data.register,payload:data.data},{topic:data.register,payload:data.data,raw:data}]);
29 | node.context().set(data.register, data.data);
30 | node.status({ fill: 'green', shape: 'dot', text: `${data.register}: ${data.data} ${data.unit}` });
31 | } else {
32 | node.send([null,{topic:data.register,payload:data.data,raw:data}]);
33 | }
34 | }
35 | })
36 | } else if(config.name.toLowerCase()=="error") {
37 | server.nibeData.on('fault', data => {
38 | if(savedError.from!==data.from || savedError.message!==data.message) {
39 | node.send({topic:data.from,payload:data.message});
40 | savedError = data;
41 | }
42 | node.send([null,{topic:data.from,payload:data.message}]);
43 | })
44 | } else {
45 | server.nibeData.on(register, data => {
46 | if(register===data.register) {
47 | let saved = node.context().get(data.register);
48 | if(data.error!==undefined) {
49 |
50 | } else {
51 | if(saved!=data.data) {
52 | node.send([{topic:data.register,payload:data.data,raw:data},{topic:data.register,payload:data.data,raw:data}]);
53 | node.context().set(data.register, data.data);
54 | node.status({ fill: 'green', shape: 'dot', text: `${data.data}${data.unit}` });
55 | } else {
56 | node.send([null,{topic:data.register,payload:data.data,raw:data}]);
57 | }
58 | }
59 |
60 | }
61 | })
62 | }
63 | /*nibe.data.on(register, data => {
64 | node.status({ fill: 'red', shape: 'dot', text: data });
65 | })*/
66 | if(config.name.toLowerCase()=="config") {
67 | server.nibeData.on('config', data => {
68 | node.send([{topic:"config",payload:data},null]);
69 | })
70 | server.nibe.getConfig();
71 | }
72 | }
73 | RED.nodes.registerType("nibe-input",nibeInput);
74 | }
--------------------------------------------------------------------------------
/language-DE.json:
--------------------------------------------------------------------------------
1 | {
2 | "starting":"Startet",
3 | "sys_not_connected":"nicht verbunden.",
4 | "sys_connected":"verbunden",
5 | "extra_sensor":"Zusätzlicher Sensor",
6 | "not_updated":"wurde noch nicht empfangen. Ignoriert.",
7 | "no_values":"hat noch keine Werte"
8 | }
--------------------------------------------------------------------------------
/language-EN.json:
--------------------------------------------------------------------------------
1 | {
2 | "starting":"Starting",
3 | "sys_not_connected":"not connected.",
4 | "sys_connected":"connected",
5 | "extra_sensor":"Extra sensor",
6 | "not_updated":"has not updated yet. Ignoring",
7 | "no_values":"has no values yet"
8 | }
--------------------------------------------------------------------------------
/language-SE.json:
--------------------------------------------------------------------------------
1 | {
2 | "starting":"Startar",
3 | "sys_not_connected":"ej anslutet",
4 | "sys_connected":"anslutet",
5 | "extra_sensor":"Extra givare",
6 | "not_updated":"har inte uppdaterats. Ignorerar.",
7 | "no_values":"har inga värden än."
8 | }
--------------------------------------------------------------------------------
/output.html:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
30 |
31 |
--------------------------------------------------------------------------------
/output.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(RED) {
3 | function nibeOutput(config) {
4 | RED.nodes.createNode(this,config);
5 | var timer = {};
6 | this.server = RED.nodes.getNode(config.server);
7 | const nibe = this.server.nibe;
8 | if (this.server) {
9 |
10 | this.on('input', function(msg) {
11 | let register = config.name;
12 | if(this.server.hP()[config.name]!==undefined) {
13 | register = this.server.hP()[config.name]
14 | }
15 | if(config.name===undefined || config.name=="") {
16 | register = msg.topic
17 | if(this.server.hP()[msg.topic]!==undefined) {
18 | register = this.server.hP()[msg.topic]
19 | }
20 | }
21 | if(msg.topic=="getConfig" || config.name=="getConfig") {
22 | nibe.getConfig();
23 | } else if(msg.topic=="setConfig" || config.name=="setConfig") {
24 | nibe.setConfig(msg.payload);
25 | } else if(msg.topic=="refreshConfig" || config.name=="refreshConfig") {
26 | nibe.refreshConfig();
27 | this.status({ fill: 'green', shape: 'dot', text: `Refreshing configuration` });
28 | timer[register] = setTimeout(() => {
29 | this.status({ fill: 'yellow', shape: 'dot', text: `` });
30 | }, 10000);
31 | } else if(msg.topic=="addSensor" || config.name=="addSensor") {
32 | nibe.addSensor(msg.payload);
33 | } else if(msg.topic=="removeSensor" || config.name=="removeSensor") {
34 | nibe.removeSensor(msg.payload);
35 | } else if(msg.topic=="addRegister" || config.name=="addRegister") {
36 | nibe.addRegister(msg.payload)
37 | } else if(msg.topic=="getRegister" || config.name=="getRegister") {
38 | nibe.getRegister();
39 | } else if(msg.topic=="removeRegister" || config.name=="removeRegister") {
40 | nibe.removeRegister(msg.payload);
41 | } else if(msg.topic=="saveGraph" || config.name=="saveGraph") {
42 | this.status({ fill: 'yellow', shape: 'dot', text: `Sparar grafer...` });
43 | this.server.saveGraph().then(result => {
44 | this.send({topic:msg.topic,payload:result});
45 | this.status({ fill: 'green', shape: 'dot', text: `${result}` });
46 | },(err => {
47 | this.send({topic:msg.topic,payload:err});
48 | this.status({ fill: 'red', shape: 'dot', text: `${err}` });
49 | }));
50 |
51 | } else if(config.name===undefined || config.name=="") {
52 | msg = {topic:msg.topic,payload:msg.payload};
53 | this.status({ fill: 'yellow', shape: 'dot', text: `${msg.payload}` });
54 | timer[register] = setTimeout(() => {
55 | this.status({ fill: 'red', shape: 'dot', text: `Timeout setting data` });
56 | }, 10000);
57 | nibe.setData(register,msg.payload,(err,result) => {
58 | if(err) return console.log(err);
59 | if(result===true) {
60 | if(timer[register]._called!==true) {
61 | this.send({topic:msg.topic,payload:msg.payload});
62 | this.status({ fill: 'green', shape: 'dot', text: `${msg.payload}` });
63 | setTimeout(() => {
64 | this.status({ fill: 'yellow', shape: 'dot', text: `` });
65 | }, 10000);
66 | clearTimeout(timer[register]);
67 | }
68 | } else {
69 | this.status({ fill: 'red', shape: 'dot', text: `Timeout setting data` });
70 | }
71 |
72 | });
73 | } else {
74 | msg = {topic:register,payload:msg.payload};
75 | this.status({ fill: 'yellow', shape: 'dot', text: `${msg.payload}` });
76 | timer[register] = setTimeout(() => {
77 | this.status({ fill: 'red', shape: 'dot', text: `Timeout setting data` });
78 | }, 10000);
79 | nibe.setData(register,msg.payload,(err,result) => {
80 | if(err) return console.log(err);
81 | if(result===true) {
82 | if(timer[register]._called!==true) {
83 | this.send({topic:register,payload:msg.payload});
84 | this.status({ fill: 'green', shape: 'dot', text: `${msg.payload}` });
85 | setTimeout(() => {
86 | this.status({ fill: 'yellow', shape: 'dot', text: `` });
87 | }, 10000);
88 | clearTimeout(timer[register]);
89 | }
90 | } else {
91 | this.status({ fill: 'red', shape: 'dot', text: `Timeout setting data` });
92 | }
93 |
94 | });
95 |
96 | }
97 | });
98 |
99 | } else {
100 | // No config node configured
101 | }
102 | }
103 | RED.nodes.registerType("nibe-output",nibeOutput);
104 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": {
3 | "name": "Fredrik Anerdin"
4 | },
5 | "dependencies": {
6 | "nibepi": "github:anerdins/nibepi#master",
7 | "node-cron": "^2.0.3",
8 | "suncalc": "^1.8.0",
9 | "node-red-dashboard": "~2.22.1",
10 | "node-red-node-ui-list": "~0.2.5"
11 | },
12 | "description": "Node-RED NibePi, communicate with your Nibe F series heatpump with RS485 adapter.",
13 | "keywords": [
14 | "node-red",
15 | "nibe",
16 | "nibepi"
17 | ],
18 | "license": "MIT",
19 | "main": "index.js",
20 | "name": "node-red-contrib-nibepi",
21 | "node-red": {
22 | "nodes": {
23 | "nibe-config": "config_node.js",
24 | "nibe-config-req": "config_req.js",
25 | "nibe-output": "output.js",
26 | "nibe-input": "input.js",
27 | "nibe-request": "request.js",
28 | "nibe-weather": "weather.js",
29 | "nibe-indoor": "indoor.js",
30 | "nibe-price": "price.js",
31 | "nibe-hotwater": "hotwater.js",
32 | "nibe-rmu": "rmu.js",
33 | "nibe-fan": "fan.js",
34 | "nibe-graph": "graph.js"
35 | }
36 | },
37 | "scripts": {
38 | "start": "node index.js",
39 | "test": "echo \"Error: no test specified\" && exit 1"
40 | },
41 | "version": "1.1.1"
42 | }
43 |
--------------------------------------------------------------------------------
/price.html:
--------------------------------------------------------------------------------
1 |
56 |
57 |
90 |
91 |
--------------------------------------------------------------------------------
/price.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(RED) {
3 | function nibePrice(config) {
4 | RED.nodes.createNode(this,config);
5 | const server = RED.nodes.getNode(config.server);
6 | const startUp = () => {
7 | let system = config.system.replace('s','S');
8 | let conf = server.nibe.getConfig();
9 | this.status({ fill: 'yellow', shape: 'dot', text: `System ${system}` });
10 | let arr = [
11 | {topic:"inside_set_"+config.system,source:"nibe"},
12 | {topic:"outside",source:"nibe"}
13 | ];
14 | if(conf.system.pump!=="F370" && conf.system.pump!=="F470") {
15 | arr.push({topic:"dM",source:"nibe"});
16 | arr.push({topic:"dMstart",source:"nibe"})
17 | }
18 |
19 | if(conf.price===undefined) {
20 | conf.price = {};
21 | server.nibe.setConfig(conf);
22 | }
23 | if(conf.home.inside_sensors===undefined) {
24 | conf.home.inside_sensors = [];
25 | server.nibe.setConfig(conf);
26 | }
27 |
28 | if(conf.price['sensor_'+config.system]===undefined || conf.price['sensor_'+config.system]=="Ingen") {
29 | arr.push({topic:"inside_"+config.system,source:"nibe"});
30 | } else {
31 | let index = conf.home.inside_sensors.findIndex(i => i.name == conf.price['sensor_'+config.system]);
32 | if(index!==-1) {
33 | var insideSensor = Object.assign({}, conf.home.inside_sensors[index]);
34 | //let insideSensor = conf.home.inside_sensors[index];
35 | arr.push(insideSensor);
36 | }
37 | }
38 | if(conf.price.enable!==true) arr = [];
39 | server.initiatePlugin(arr,'price',config.system).then(data => {
40 | this.status({ fill: 'green', shape: 'dot', text: `System ${system}` });
41 | this.send({enabled:true});
42 | },(reject => {
43 | this.status({ fill: 'red', shape: 'dot', text: `System ${system}` });
44 | this.send({enabled:false});
45 | }));
46 |
47 | }
48 | this.on('input', function(msg) {
49 | let conf = server.nibe.getConfig();
50 | if(msg.topic=="update") {
51 | let data = {system:config.system}
52 | server.updateData(data);
53 | return;
54 | } else if(msg.topic=="price/enable") {
55 | let req = msg.topic.split('/');
56 | if(conf[req[0]]===undefined) conf[req[0]] = {};
57 | if(conf[req[0]][req[1]]!==msg.payload) {
58 | conf[req[0]][req[1]] = msg.payload;
59 | server.nibe.setConfig(conf);
60 | }
61 | startUp();
62 | } else if(msg.payload!==undefined && msg.topic!==undefined && msg.topic!=="") {
63 | let req = msg.topic.split('/');
64 | if(conf[req[0]]===undefined) conf[req[0]] = {};
65 | if(conf[req[0]][req[1]+'_'+config.system]!==msg.payload) {
66 | conf[req[0]][req[1]+'_'+config.system] = msg.payload;
67 | server.nibe.setConfig(conf);
68 | }
69 | startUp();
70 | }
71 |
72 | });
73 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
74 | startUp();
75 | } else {
76 | server.nibeData.on('ready', (data) => {
77 | startUp();
78 | })
79 | }
80 | server.nibeData.on(this.id, (data) => {
81 | if(data.changed===true) {
82 | config.system = data.system;
83 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
84 | startUp();
85 | }
86 | }
87 | })
88 | server.nibeData.on('pluginPriceGraph', (data) => {
89 | if(data.system===config.system) {
90 | this.send({topic:"Graf",payload:[]});
91 | this.send({topic:"Graf",payload:data.values});
92 | }
93 |
94 | });
95 | server.nibeData.on('pluginPrice', (data) => {
96 | if(data.system===config.system) {
97 | this.send({topic:"Nuvarande Elprisnivå",payload:data.price_level.data});
98 | this.send({topic:"Nuvarande Elpris",payload:data.price_current.data});
99 | this.send([null,{topic:"test",payload:data}]);
100 | }
101 | })
102 |
103 | this.on('close', function() {
104 | let system = config.system.replace('s','S');
105 | server.nibeData.removeAllListeners();
106 | this.status({ fill: 'yellow', shape: 'dot', text: `System ${system}` });
107 | });
108 | }
109 | RED.nodes.registerType("nibe-price",nibePrice);
110 | }
--------------------------------------------------------------------------------
/request.html:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
30 |
31 |
--------------------------------------------------------------------------------
/request.js:
--------------------------------------------------------------------------------
1 | module.exports = function(RED) {
2 | function nibeRequest(config) {
3 | RED.nodes.createNode(this,config);
4 | var timer;
5 | var reqOn = false;
6 | this.server = RED.nodes.getNode(config.server);
7 | let nibe = this.server.nibe;
8 | const suncalc = this.server.suncalc;
9 | if (this.server) {
10 | this.on('input', function(msg) {
11 | let register = config.name;
12 | if(this.server.hP()[config.name]!==undefined) {
13 | register = this.server.hP()[config.name]
14 | }
15 | if(register.toLowerCase()=="astro") {
16 | timer = setTimeout(() => {
17 | this.status({ fill: 'yellow', shape: 'dot', text: `` });
18 | }, 5000);
19 | this.status({ fill: 'green', shape: 'dot', text: `Requesting astro data` });
20 | msg.astro = suncalc(msg)
21 | this.status({ fill: 'green', shape: 'dot', text: `` });
22 | this.send(msg);
23 | } else if(register.toLowerCase()=="config") {
24 | this.status({ fill: 'yellow', shape: 'dot', text: `Requesting configuration` });
25 | let config = nibe.getConfig();
26 | if(config!==undefined) {
27 | this.status({ fill: 'green', shape: 'dot', text: `Received configuration` });
28 | msg.config = config;
29 | this.send([msg,{topic:config.name,payload:msg.config}]);
30 | } else {
31 | this.send(msg);
32 | this.status({ fill: 'red', shape: 'dot', text: `Timeout requesting configuration` });
33 | }
34 |
35 | } else {
36 | if(reqOn===false) {
37 | reqOn = true;
38 | this.status({ fill: 'yellow', shape: 'dot', text: `Requesting data` });
39 | nibe.reqData(register).then(result => {
40 | this.status({ fill: 'green', shape: 'dot', text: `Value: ${result.data}${result.unit}` });
41 | clearTimeout(timer);
42 | msg[config.name+"_raw"] = result;
43 | msg[config.name] = result.data;
44 | this.send([msg,{topic:config.name,payload:result.data}])
45 | reqOn = false;
46 | setTimeout(() => {
47 | this.status({ fill: 'yellow', shape: 'dot', text: `` });
48 | }, 10000);
49 | },(reject => {
50 |
51 | }));
52 | timer = setTimeout(() => {
53 | reqOn = false;
54 | this.send(msg);
55 | this.status({ fill: 'red', shape: 'dot', text: `Timeout requesting data` });
56 | }, 30000,msg);
57 |
58 | }
59 |
60 | }
61 | });
62 | } else {
63 | // No config node configured
64 | }
65 | }
66 | RED.nodes.registerType("nibe-request",nibeRequest);
67 | }
--------------------------------------------------------------------------------
/rmu.html:
--------------------------------------------------------------------------------
1 |
55 |
56 |
75 |
76 |
--------------------------------------------------------------------------------
/rmu.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(RED) {
3 | function nibeRMU(config) {
4 | RED.nodes.createNode(this,config);
5 | const server = RED.nodes.getNode(config.server);
6 |
7 | const startUp = () => {
8 | let system = config.system.replace('s','S');
9 | this.status({ fill: 'yellow', shape: 'dot', text: `RMU 40 ${system}` });
10 | const arr = [
11 | ];
12 | let conf = server.nibe.getConfig();
13 | if(conf.rmu===undefined) {
14 | conf.rmu = {};
15 | server.nibe.setConfig(conf);
16 | }
17 | if(conf.home.inside_sensors===undefined) {
18 | conf.home.inside_sensors = [];
19 | server.nibe.setConfig(conf);
20 | }
21 | if(conf.rmu['sensor_'+config.system]===undefined || conf.rmu['sensor_'+config.system]=="Ingen") {
22 | arr.push({topic:"inside_"+config.system,source:"nibe"});
23 | } else {
24 | let index = conf.home.inside_sensors.findIndex(i => i.name == conf.rmu['sensor_'+config.system]);
25 | if(index!==-1) {
26 | var insideSensor = Object.assign({}, conf.home.inside_sensors[index]);
27 | //let insideSensor = conf.home.inside_sensors[index];
28 | arr.push(insideSensor);
29 | }
30 | }
31 | server.initiatePlugin(arr,'rmu',config.system).then(data => {
32 | this.status({ fill: 'green', shape: 'dot', text: `RMU 40 ${system}` });
33 | server.sendError('RMU 40',`RMU 40 ${system} ${server.text.sys_connected}`);
34 | this.send({enabled:true});
35 | },(reject => {
36 | this.status({ fill: 'red', shape: 'dot', text: `RMU 40 ${system}` });
37 | this.send({enabled:false});
38 | }));
39 | }
40 | this.send({enabled:false});
41 | this.on('input', function(msg) {
42 | let conf = server.nibe.getConfig();
43 | if(msg.topic=="update") {
44 | server.updateData();
45 | return;
46 | }
47 | if(msg.payload!==undefined && msg.topic!==undefined && msg.topic!=="") {
48 | let req = msg.topic.split('/');
49 | if(conf[req[0]]===undefined) conf[req[0]] = {};
50 | if(conf[req[0]][req[1]+'_'+config.system]!==msg.payload) {
51 | conf[req[0]][req[1]+'_'+config.system] = msg.payload;
52 | server.nibe.setConfig(conf);
53 | }
54 | startUp();
55 | }
56 |
57 | });
58 |
59 | if(server.checkRMU()===true) {
60 | startUp();
61 | } else {
62 | server.nibeData.once('rmu_ready', (data) => {
63 | startUp();
64 | })
65 | }
66 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
67 | startUp();
68 | } else {
69 | server.nibeData.on('ready', (data) => {
70 | startUp();
71 | })
72 | }
73 | server.nibeData.on(this.id, (data) => {
74 | if(data.changed===true) {
75 | config.system = data.system;
76 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
77 | startUp();
78 | }
79 | }
80 | })
81 | server.nibeData.on('pluginRMU', (data) => {
82 | if(data.system===config.system) {
83 | this.send({topic:"Inomhustemperatur",payload:data.rmuSensor.data});
84 | }
85 | })
86 |
87 | this.on('close', function() {
88 | let system = config.system.replace('s','S');
89 | server.nibeData.removeAllListeners();
90 | this.status({ fill: 'yellow', shape: 'dot', text: `RMU 40 ${system}` });
91 | });
92 | }
93 | RED.nodes.registerType("nibe-rmu",nibeRMU);
94 | }
--------------------------------------------------------------------------------
/translate.json:
--------------------------------------------------------------------------------
1 | {
2 | "dash":{
3 | "readonly": {
4 | "SE":"Read-only
NibePi kör med ett läsbart filsystem",
5 | "EN":"Read-only
NibePi runs on a read-only filesystem",
6 | "DE":"Schreibschutz
NibePi läuft auf einem schreibgeschützen Dateisystem"
7 | },"language": {
8 | "SE":"Språk",
9 | "EN":"Language",
10 | "DE":"Sprache"
11 | },"auto_update": {
12 | "SE":"Uppdatera grafer
Aktiverar att hämta registerdata och uppdatera NibePis interna grafer.",
13 | "EN":"Update graphs
If activated, this updates NibePi's internal diagrams based on register data.",
14 | "DE":"Diagramme aktualisieren
Aktivieren um NibePi's Diagramme auf Basis der Registerdaten zu aktualisieren."
15 | },"save_graph": {
16 | "SE":"Spara grafer
Sparar ner grafer till SD-kortet varje timma och vid omstart/uppdatering",
17 | "EN":"Save graphs
Saves graphs to the SD-card every hour and when restarting/updating",
18 | "DE":"Diagramme speichern
Speichert die Diagramme stündlich und bei Neustart und Update auf die SD-Karte."
19 | },"heatcurve_info": {
20 | "SE":"Ställ in värmepumpens värmekurva och kurvjustering
OBS. Ställer man in kurvjustering manuellt i värmepumpen kommer den att skrivas över av NibePi, ställ den därför här
",
21 | "EN":"Set the heatcurve and the curve adjustment here
If you set it directly in the heatpump, NibePi will overwrite it
",
22 | "DE":"Stelle die Heizkurve und Parrallelverschiebung hier ein
Direkt in der Wärmepumpe eingestellte Werte werden überschrieben.
"
23 | },"heatcurve_s1": {
24 | "SE":"Värmekurva S1
",
25 | "EN":"Heatcurve S1
",
26 | "DE":"Heizkurve S1
"
27 | },"heatcurve_s2": {
28 | "SE":"Värmekurva S2
",
29 | "EN":"Heatcurve S2
",
30 | "DE":"Heizkurve S2
"
31 | },"adjust_s1": {
32 | "SE":"Kurvjustering S1
",
33 | "EN":"Curve adjustment S1
",
34 | "DE":"Parrallelverschiebung S1
"
35 | },"adjust_s2": {
36 | "SE":"Kurvjustering S2
",
37 | "EN":"Curve adjustment S2
",
38 | "DE":"Parrallelverschiebung S2
"
39 | },"intro": {
40 | "SE":"NibePi
NibePi är IoT produkten som både gör värmepumpen smartare och effektivare. Med hjälp av MQTT så kan värmepumpen övervakas och styras direkt från det smarta hemmet.
Det finns flera inbyggda funktioner som kan förbättra komforten och ekonomin. Mer information finns under varje funktion.
NibePi stödjer alla värmepumpar i Nibes F serie, samt elpannor med tillhörande utedel i VVM serien.
Nibe F370,F470,F730,F750,F1145,F1155,F1245,F1255,F1345,F1355
VVM 225,310,320,325,500
SMO 20/40
",
41 | "EN":"NibePi
NibePi is an IoT appliance that makes your heatpump smarter and more efficient. Thanks to MQTT you can control your heatpump directly from your smart home system.
NibePi's integrated features can increase comfort and efficiency. You can find additional information directly in the menus for the invidivual features.
NibePi works with all F series heatpumps, as well as the outdoor units of the VVM series.
Nibe F370,F470,F730,F750,F1145,F1155,F1245,F1255,F1345,F1355
VVM 225,310,320,325,500
SMO 20/40
",
42 | "DE":"NibePi
NibePi ist ein IoT Gerät, dass deine Wärmepumpe intelligenter und effizenter macht. Dank MQTT kannst du deine Wärmepumpe direkt aus deinem SmartHome System steuern.
NibePi's integrierte Funktionen können den Komfort und die Effizienz der Wärmepumpe steigern. Du findest weitere Informationen in den Menüs der einzelnen Funktionen.
NibePi ist kompatibel mit allen Wärmepumpen der F-Serie und den Außengeräten der VVM Serie.
Nibe F370,F470,F730,F750,F1145,F1155,F1245,F1255,F1345,F1355
VVM 225,310,320,325,500
SMO 20/40
"
43 | },"information": {
44 | "SE":"Information
",
45 | "EN":"Informations
",
46 | "DE":"Informationen
"
47 | },"system": {
48 | "SE":"System
",
49 | "EN":"System
",
50 | "DE":"System
"
51 | },"rmu_label": {
52 | "SE":"RMU-40
",
53 | "EN":"RMU-40
",
54 | "DE":"RMU-40
"
55 | },"rmu_info": {
56 | "SE":"Virtuell RMU-40
Aktivera en virtuell RMU-40 för att kunna simulera en riktig inomhusgivare via MQTT
OBS. har du en riktig RMU-40 går det inte att simulera
",
57 | "EN":"Virtual RMU-40
Activate a virtual RMU-40 to integrate any room temperature sensor via MQTT
Attention: Does not work if a physical RMU-40 is connected!
",
58 | "DE":"Virtuelle RMU-40
Aktiviere eine virtuelle RMU-40 um einen beliebigen Raumtemperatursensor per MQTT einzubinden.
Hinweis: Funktioniert nicht, wenn eine RMU-40 mit der Wärmepumpe verbunden ist!
"
59 | },"rmu_s1": {
60 | "SE":"RMU-40 S1",
61 | "EN":"RMU-40 S1",
62 | "DE":"RMU-40 S1"
63 | },"rmu_s2": {
64 | "SE":"RMU-40 S2",
65 | "EN":"RMU-40 S2",
66 | "DE":"RMU-40 S2"
67 | },"rmu_s3": {
68 | "SE":"RMU-40 S3",
69 | "EN":"RMU-40 S3",
70 | "DE":"RMU-40 S3"
71 | },"samba_stop": {
72 | "SE":"Stoppa Samba",
73 | "EN":"Stop Samba",
74 | "DE":"Samba Stoppen"
75 | },"samba_start": {
76 | "SE":"Starta Samba",
77 | "EN":"Start Samba",
78 | "DE":"Samba starten"
79 | },"nodered_restart": {
80 | "SE":"Starta om Node-RED",
81 | "EN":"Restart Node-RED",
82 | "DE":"Node-RED neu starten"
83 | },"nodered_restart_info": {
84 | "SE":"Startar om Node-RED",
85 | "EN":"Trigger a restart of Node-RED.",
86 | "DE":"Neustart von Node-RED auslösen."
87 | },"hw_restart": {
88 | "SE":"Starta om hårdvaran",
89 | "EN":"Restart NibePi hardware",
90 | "DE":"NibePi hardware neu starten"
91 | },"hw_restart_info": {
92 | "SE":"Startar om NibePi hårdvara",
93 | "EN":"Triggers a restart of the NibePi hardware.",
94 | "DE":"Neustart der NibePi Hardware auslösen."
95 | },"hw_shutdown": {
96 | "SE":"Stäng av NibePi hårdvaran",
97 | "EN":"Shutdown NibePi",
98 | "DE":"NibePi herunterfahren"
99 | },"hw_shutdown_info": {
100 | "SE":"Stänger av NibePi hårdvaran",
101 | "EN":"Triggers a shutdown of the NibePi hardware.",
102 | "DE":"Herunterfahren der NibePi Hardware auslösen."
103 | },"mqtt_info": {
104 | "SE":"MQTT
Aktivera åtkomst till MQTT broker.
NibePi har en inbyggd broker på 127.0.0.1:1883
Det går även att använda en extern broker, ändra bara anslutningsdetaljerna.
Aktivera HA Discovery om Home Assistant automatiskt ska upptäcka aktiverade givare från NibePi",
105 | "EN":"MQTT
Activate the MQTT broker.
NibePi has an internal MQTT broker that runs on 127.0.0.1:1883, You can also use an external broker just by changing the connection details. Activate HA auto discovery, if Home Assistant should detect NibePi's sensors automatically.",
106 | "DE":"MQTT
Aktiviert den MQTT broker.
NibePi hat einen internen MQTT Broker, der auf 127.0.0.1:1883 läuft.
Wenn deaktiviert, kann hier ein externer Broker angegeben werden.
Aktiviere HA Auto Discovery, wenn HomeAssistant die NibePi Sensoren automatisch erkennen soll."
107 | },"plejd_info": {
108 | "SE":"Plejd
Aktivera Plejd MQTT Gateway.
NibePi kan simulera en Plejd MQTT Gateway via ett projekt som heter plejd2mqtt.
Home Assistant/OpenHAB kan automatiskt upptäcka Plejdenheterna via MQTT Discovery
Ange den MQTT Broker nedan som gatewayen ska ansluta till.
NibePi har en inbyggd broker på 127.0.0.1:1883
"
109 | },"plejd_login": {
110 | "SE":"Ange Inloggningsinformation till Plejds molntjänst för att få kontakt med enheterna
OBS. Vid ändringar behöver gatewayen startas om
"
111 | },"cloud_name": {
112 | "SE":"Anläggning",
113 | "EN":"Place"
114 | },"cloud_user": {
115 | "SE":"E-post",
116 | "EN":"E-mail"
117 | },"cloud_pass": {
118 | "SE":"Lösenord",
119 | "EN":"Password"
120 | },"plejd_enable": {
121 | "SE":"Aktivera Plejd Gateway",
122 | "EN":"Enable Plejd Gateway"
123 | },"plejd_disable": {
124 | "SE":"Inaktivera Plejd Gateway",
125 | "EN":"Disable Plejd Gateway"
126 | },"plejd_restart": {
127 | "SE":"Starta om Plejd Gateway",
128 | "EN":"Restart Plejd Gateway"
129 | },"plejd_restart_info": {
130 | "SE":"Startar om Plejd Gateway"
131 | },"lat": {
132 | "SE":"Latitud",
133 | "EN":"Latitude",
134 | "DE":"Breitengrad"
135 | },"lon": {
136 | "SE":"Longitud",
137 | "EN":"Longitude",
138 | "DE":"Längengrad"
139 | },"home_size": {
140 | "SE":"Fastighetens storlek",
141 | "EN":"Building size (sqm)",
142 | "DE":"Gebäudegröße (m²)"
143 | },"update_info": {
144 | "SE":"Uppdateringar
Välj vilken version av NibePi som uppdateringar ska visas för.Det finns flera olika utgåvor, Stable release är den senaste tillgängliga versionen.
Snapshot versionen är experimentell och används för att prova fram lösningar på olika problem som uppstår för användare
Efter färdig uppdatering startar NibePi om och Modbus kommunikationslarm kan genereras.",
145 | "EN":"
Updates
Choose update channel.There are several update channels available: Stable releases are the latest relased versions.
Snapshot releases are experimental versions which can contain not thoroughly tested fixes for specific problems. These versions can be unstable.
NibePi restarts after the update. This can lead to a Modbus alarm being raised by the heatpump.",
146 | "DE":"
Updates
Update Kanal auswählen.Es stehen mehrere Update Kanäle zur Verfügung: Stable Versionen sind die jeweils neusten veröffentlichten Versionen.
Snapshot Versionen können nicht intensiv getestete Patches enthalten um speziefische Probleme zu beheben. Diese Versionen können eventuell instabil sein.
Nach dem Update führt NibePi einen Neustart durch. Dies kann zu einem Modbus Alarm an der Wärmepumpe führen."
147 | },"update_check": {
148 | "SE":"Kolla efter Uppdateringar",
149 | "EN":"Check for updates",
150 | "DE":"Auf Updates prüfen"
151 | },"update_run": {
152 | "SE":"Uppdatera",
153 | "EN":"Update",
154 | "DE":"Aktualisieren"
155 | },"extra_sensor_info": {
156 | "SE":"
Extra givare
Här kan du lägga till extra givare som kan användas i funktionerna
Det går att välja givare från register i värmepumpen eller via ett MQTT Topic",
157 | "EN":"Additional sensors
Here you can add additional sensors to be used in the different features.
You can select a sensor from a Modbus register or from MQTT topic",
158 | "DE":"Zusätzliche Sensoren
Hier kannst du zusätzliche Sensoren hinzufügen, die in den verschiedenen Funktionen genutzt werden können.
Du kannst einen Sensor entweder über ein Modbus Register hinzufügen oder über ein MQTT Topic."
159 | },"sensor_timeout": {
160 | "SE":"Timeout tid för MQTT givare
Givaren måste uppdateras inom detta interval annars ignoreras den. OBS \"0\" inaktiverar timeout.",
161 | "EN":"Timeout for MQTT sensors.
The sensor values must be updated within this time, otherwise they are ignored. Note: \"0\" deactivates the timeout.",
162 | "DE":"Timeout für MQTT Sensoren.
Sensorwerte müssen innerhalb dieser Zeit aktualisiert werden, ansonsten werden sie ignoriert. Hinweis: \"0\" deaktiviert den Timeout."
163 | },"extra_sensor_remove": {
164 | "SE":"Ta bort
Ta bort en extra givare genom att markera den i listan",
165 | "EN":"Remove
Remove an additional sensor by selecting it from the list.",
166 | "DE":"Entfernen
Entfernt einen zusätzlichen Sensor durch Auswählen in der Liste."
167 | },"loging_info": {
168 | "SE":"Loggning
Loggning sker till \"/tmp/nibepi.log\"
I SSH kör kommando \"tail -f /tmp/nibepi.log\"",
169 | "EN":"Logging
Log will be written to \"/tmp/nibepi.log\"
In SSH session, run \"tail -f /tmp/nibepi.log\" to read the log.",
170 | "DE":"Protkollierung
Protokoll wird nach \"/tmp/nibepi.log\" geschrieben.
Zum Lesen des Protokolls in einer SSH Verbindung \"tail -f /tmp/nibepi.log\" ausführen."
171 | },"forecast_s1_info": {
172 | "SE":"Prognosreglering klimatsystem 1
Styr värmeproduktionen efter prognostemperatur istället.
Rekommenderas framför allt för golvvärme.",
173 | "EN":"Forecast control climate system 1
Controls heat production based on temperature forecast. Recommended for floor heating systems.",
174 | "DE":"Prognoseregelung Klimasystem 1
Regelt die Wärmeerzeugung auf Basis der Temperaturvorhersage.
Empfohlen für Fußbodenheizungen."
175 | },"forecast_s2_info": {
176 | "SE":"Prognosreglering klimatsystem 2
Styr värmeproduktionen efter prognostemperatur istället.
Rekommenderas framför allt för golvvärme.",
177 | "EN":"Forecast control climate system 2
Controls heat production based on temperature forecast. Recommended for floor heating systems.",
178 | "DE":"Prognoseregelung Klimasystem 2
Regelt die Wärmeerzeugung auf Basis der Temperaturvorhersage.
Empfohlen für Fußbodenheizungen."
179 | },"forecast_update_info": {
180 | "SE":"Begär ny väderdata och uppdatera grafen med nya värden.",
181 | "EN":"Fetch new weather data and update the graph with new values.",
182 | "DE":"Neue Wetterdaten anfordern und Diagramm mit neuen Werten aktualisieren."
183 | },"forecast_sensor_s1": {
184 | "SE":"Välj inomhusgivare S1",
185 | "EN":"Select indoor sensor S1",
186 | "DE":"Innenraum-Temperatursensor S1"
187 | },"enable": {
188 | "SE":"Aktivera",
189 | "EN":"Activate",
190 | "DE":"Aktivieren"
191 | },"forecast_time": {
192 | "SE":"Prognostid",
193 | "EN":"Forecast time",
194 | "DE":"Vorhersagezeit"
195 | },"forecast_correction": {
196 | "SE":"Inställningar
Prognoskorrigering jämför nuvarande utomhustemperatur mot nuvarande prognostemperatur.
Samma avvikelse appliceras även på framtida prognostemperatur.",
197 | "EN":"Settings
Forecast correction compares the current outdoor temperature to the current forecast temperature. The same deviation is then also applied to future forecast temperatures.",
198 | "DE":"Einstellungen
Die Progrnosekorrektur vergleicht die aktuelle Außentemperatur mit der aktuellen Prognosetemperatur.
Die gleiche Abweichung wird auch auf zukünftige, prognostierte Temperaturen angewendet."
199 | },"forecast_adjust": {
200 | "SE":"Aktivera Prognoskorrigering",
201 | "EN":"Activate forecast correction",
202 | "DE":"Prognosekorrektur aktivieren"
203 | },"sunfactor_info": {
204 | "SE":"Solfaktor
Vid mycket solinstrålning så går det att ställa en solfaktor.
Värdet som valts läggs till på prognostemperaturen.",
205 | "EN":"Solar factor
In case of high solar radiation, it is possible to set a solar factor.
This value is added to the forecast temperature.",
206 | "DE":"Sonnenfaktor
Bei starker Sonneneinstrahlung kann ein Sonnenfaktor angegeben werden.
Dieser Wert wird zur prognostizierten Temperatur addiert."
207 | },"wind_direction_info": {
208 | "SE":"Riktning: Norr: -1, Söder: -2, Väst: -3, Öst: -4",
209 | "EN":"Direction: North: -1, South: -2, West: -3, East: -4",
210 | "DE":"Richtung: Nord: -1, Süd: -2, West: -3, Ost: -4"
211 | },"windfactor_info": {
212 | "SE":"Vindfaktor
Om fastigheten är utsatt för mycket vind kan man ta med detta i prognosen och kompensera med höjt börvärde.
Justeringsfaktorn är ställbart från varje väderstreck.",
213 | "EN":"Wind factor
If the building is exposed to high winds, a wind factor can be set to compensate with a higher setpoint.
The adjustment factor can be set for each cardinal direction individually.",
214 | "DE":"Windfaktor
Wenn das Gebäude starkem Wind ausgesetzt ist, kann dies durch einen Windfaktor in die Prognose einbezogen und mit einem höheren Sollwert kompensiert werden.
Der Windfaktor kann für jede Himmelsrichtung gesondert angegeben werden."
215 | },"wind_factor_n": {
216 | "SE":"Norr",
217 | "EN":"North",
218 | "DE":"Nord"
219 | },"wind_factor_s": {
220 | "SE":"Söder",
221 | "EN":"South",
222 | "DE":"Süd"
223 | },"wind_factor_e": {
224 | "SE":"Öst",
225 | "EN":"East",
226 | "DE":"Ost"
227 | },"wind_factor_w": {
228 | "SE":"Väst",
229 | "EN":"West",
230 | "DE":"West"
231 | },"clear": {
232 | "SE":"Klart väder",
233 | "EN":"Clear weather",
234 | "DE":"Klares Wetter"
235 | },"mostly_clear": {
236 | "SE":"Mestadels klart väder",
237 | "EN":"Mostly clear weather",
238 | "DE":"Überwiegend klares Wetter"
239 | },"half_clear": {
240 | "SE":"Halvklart väder",
241 | "EN":"Partially clear weather",
242 | "DE":"Teilweise klares Wetter"
243 | },"indoor_info_s1": {
244 | "SE":"Inomhusreglering S1
Inomhusreglering rekommenderas främst för högtempererade system (Radiatorer), det går att använda med lågtempererade system med mindre faktor
OBS golvvärme i betong anses väldigt trögt och en faktor över 2 är aldrig att rekommendera",
245 | "EN":"Indoor control S1
Indoor control is recommended for high temperature systems (radiators). With a lower factor, it can be used for low temperature systems.
Note: Floor heating in concrete is considered very inert and a factor above 2 is not recommended.",
246 | "DE":"Innentemperatur-Regelung S1
Die Innentemperatur-Regelung wird hauptsächlich für Hochtemperatursysteme (Heizkörper) empfohlen. Mit einem geringen Faktor, kann sie mit Niedertemperatursystemen verwendet werden.
Hinweis: Fußbodenheizungen in Estrich sind sehr träge Systeme und ein Faktor über 2 wird nicht empfohlen."
247 | },"indoor_info_s2": {
248 | "SE":"Inomhusreglering S2
Inomhusreglering rekommenderas främst för högtempererade system (Radiatorer), det går att använda med lågtempererade system med mindre faktor
OBS golvvärme i betong anses väldigt trögt och en faktor över 2 är aldrig att rekommendera",
249 | "EN":"Indoor temperature control S2
Indoor control is recommended for high temperature systems (radiators). With a lower factor, it can be used for low temperature systems.
Note: Floor heating in concrete is considered very inert and a factor above 2 is not recommended.",
250 | "DE":"Innentemperatur-Regelung S2
Die Innentemperatur-Regelung wird hauptsächlich für Hochtemperatursysteme (Heizkörper) empfohlen. Mit einem geringen Faktor, kann sie mit Niedertemperatursystemen verwendet werden.
Hinweis: Fußbodenheizungen in Estrich sind sehr träge Systeme und ein Faktor über 2 wird nicht empfohlen."
251 | },"indoor_options_label": {
252 | "SE":"Inomhusreglering",
253 | "EN":"Indoor temperature control",
254 | "DE":"Innentemperatur-Regelung"
255 | },"indoor_options_off": {
256 | "SE":"Avaktiverat",
257 | "EN":"Deactivated",
258 | "DE":"Deaktiviert"
259 | },"indoor_options_nibe": {
260 | "SE":"Nibes Inomhusreglering",
261 | "EN":"Nibes Indoor temperature control",
262 | "DE":"Nibes Innentemperatur-Regelung"
263 | },"indoor_options_nibepi": {
264 | "SE":"NibePis Inomhusreglering",
265 | "EN":"NibePis Indoor temperature control",
266 | "DE":"NibePis Innentemperatur-Regelung"
267 | },"indoor_set": {
268 | "SE":"Inomhus börvärde
Ställ rumsbörvärdet här. Samma värde visas/ändras i värmepumpen",
269 | "EN":"Indoor temperature set value
Set the room temperature setpoint here. The same value is shown/changed in the heatpump",
270 | "DE":"Raumtemperatur Sollwert
Stelle hier den Sollwert für die Raumtemperatur ein. Der selbe Wert wird in der Wärmepumpe angezeigt/geändert"
271 | },"indoor_diff": {
272 | "SE":"Avvikelse
Ungefärlig uträkning för hur mycket temperaturen avviker från börvärdet.",
273 | "EN":"Deviation
Approximate calculation of how much the room temperature deviates from the setpoint.",
274 | "DE":"Regelabweichung
Ungefähre Berechnung, um wie viel die Raumtemperatur vom Sollwert abweicht."
275 | },"indoor_temp": {
276 | "SE":"Inomhustemperatur",
277 | "EN":"Indoor temperature",
278 | "DE":"Raumtemperatur"
279 | },"indoor_curveadjust": {
280 | "SE":"Kurvjustering
Aktuell kurvjustering vid NibePis reglering",
281 | "EN":"Curve adjustment
Current curve adjustment according to NibePis control",
282 | "DE":"Kurvenanpassung
Aktuelle Kurvenanpassung entsprechend NibePi's Regelung"
283 | },"indoor_factor": {
284 | "SE":"Faktor för rumsgivare
Ställ in hur mycket rumsgivaren ska påverka framledningstemperaturen.",
285 | "EN":"Room sensor factor
Define how much the room temperature sensor affects the supply temperature.",
286 | "DE":"Raumtemperatur-Faktor
Lege fest, wie start der Raumtemperatursensor die Vorlauftemperatur beeinflusst."
287 | },"indoor_info_sensor": {
288 | "SE":"Välj en temperaturgivare som ska användas i grafen och för ekvationen
OBS. Endast för reglering via NibePi
",
289 | "EN":"Select a temperature sensor to be used in the graph and the control alogrithm.
Note: Only for control via NibePi
",
290 | "DE":"Wähle einen Temperatursensor, der für das Diagramm und die Berechnung verwendet werden soll.
Hinweis: Nur bei Steuerung via NibePi
"
291 | },"indoor_sensor_label": {
292 | "SE":"Välj rumsgivare",
293 | "EN":"Select temperature sensor",
294 | "DE":"Temperatursensor wählen"
295 | },"indoor_settings_info": {
296 | "SE":"Inställningar
Övergripande inställningar för inomhusregleringen",
297 | "EN":"Settings
General settings for the room temperature control",
298 | "DE":"Einstellungen
Allgemeine Einstellungen für die Raumtemperatur-Regelung"
299 | },"indoor_settings_outside": {
300 | "SE":"Utomhustemperatur blockering
Välj vid vilken utomhustemperatur som värmen ska blockeras",
301 | "EN":"Heating stop
Select at which outside temperature the heating should be stopped.",
302 | "DE":"Heizungsstop
Einstellung bei welcher Außentemperatur die Heizung stoppen soll."
303 | },"indoor_data_dm": {
304 | "SE":"Gradminuter",
305 | "EN":"Degree minutes",
306 | "DE":"Gradminuten"
307 | },"indoor_outside": {
308 | "SE":"Utomhustemperatur",
309 | "EN":"Outside temperature",
310 | "DE":"Außentemperatur"
311 | },"last_updated": {
312 | "SE":"Senast uppdaterad",
313 | "EN":"Last update",
314 | "DE":"Zuletzt aktualisiert"
315 | },"indoor_data_info": {
316 | "SE":"Data
",
317 | "EN":"Data
",
318 | "DE":"Daten
"
319 | },"dm_reset_enable": {
320 | "SE":"Gradminut återställning
När inomhusbörvärdet är uppnått så återställs gradminuter till startvärdet
Ställ in hur många grader över inomhusbörvärdet det ska aktiveras vid",
321 | "EN":"Degree minute reset
When the indoor temperature setpoint is reached, the degree minutes will be reset to the start value.
Define at which temperature above the setpoint this should happen.",
322 | "DE":"Gradminuten reset
Bei Erreichen der Innenraum-Solltemperatur werden die Gradminuten auf den Anfangswert zurückgesetzt.
Stelle ein, bei wieviel Grad über dem Sollwert das passieren soll."
323 | },"dm_reset_enable_stop": {
324 | "SE":"Gradminut återställning
När inomhusbörvärdet är uppnått så återställs gradminuter till startvärdet
Ställ in hur många grader över inomhusbörvärdet det ska aktiveras vid",
325 | "EN":"Stop heat production
When the indoor temperature set point is reached, the degree minutes will be reset to the start value.
Define at which temperature above the setpoint this should happen.",
326 | "DE":"Wärmeerzeugung stoppen
Bei Erreichen der Innenraum-Solltemperatur werden die Gradminuten auf den Anfangswert zurückgesetzt.
Stelle ein, bei wieviel Grad über dem Sollwert das passieren soll."
327 | },"diff": {
328 | "SE":"Differens
",
329 | "EN":"Difference
",
330 | "DE":"Differenz
"
331 | },"co2_limit": {
332 | "SE":"Gränsvärde CO2",
333 | "EN":"CO2 limit",
334 | "DE":"CO2 Grenzwert"
335 | },"airflow": {
336 | "SE":"Luftflöde",
337 | "EN":"Airflow",
338 | "DE":"Luftstrom"
339 | },"low_cpr_freq": {
340 | "SE":"Kompressorfrekvens",
341 | "EN":"Compressor frequency",
342 | "DE":"Kompressorfrequenz"
343 | },"enable_co2": {
344 | "SE":"Aktivera CO2 givare",
345 | "EN":"Activate CO2 sensor",
346 | "DE":"CO2 Sensor aktivieren"
347 | },"filter_value": {
348 | "SE":"Kalibrera",
349 | "EN":"Calibrate",
350 | "DE":"Kalibrieren"
351 | },"fan_efficiency": {
352 | "SE":"Effektivitet",
353 | "EN":"Efficiency",
354 | "DE":"Effizienz"
355 | },"fan_low_info": {
356 | "SE":"Sänk luftflöde
Sänk luftflödet vid låg kompressorfrekvens
Ange under vilken kompressorfrekvens det aktiveras, samt vilket luftflöde som ska hållas, om CO2 givare är aktiverad välj ett värde som nivån måste understiga",
357 | "EN":"Reduce airflow
Decrease airflow at low compressor frequencies
Specify below which compressor frequency it is activated, and which airflow to maintain, if CO2 sensor is activated select a value that the level must be below",
358 | "DE":"Luftstrom reduzieren b>
Luftstrom bei niedriger Kompressorfrequenz verringern
Gib an, unter welcher Kompressorfrequenz die Funktion aktiviert ist und welcher Luftstrom beibehalten werden soll, wenn CO2-Sensor aktiviert ist Wählen Sie einen Wert, unter dem der Füllstand liegen muss. "
359 | },"fan_co2_force_info": {
360 | "SE":"CO2 Forcering av luftflöde
Vid högt CO2 värde kan ventilationen forceras, ange vilket värde som ska överstigas och vilket luftflöde som ska hållas",
361 | "EN":"CO2 Force airflow
At high CO2 levels, ventilation can be forced. Specify which value is to be exceeded and which airflow is to be maintained",
362 | "DE":"CO2 Luftstrom erzwingen
Bei hohem CO2-Wert kann die Belüftung erzwungen werden. Gib an, welcher Wert überschritten werden muss und welcher Luftstrom aufrecht erhalten werden soll."
363 | },"fan_filter_info": {
364 | "SE":"Filterövervakning
Aktivera övervakning av filter, vid rengöring av filter behöver anläggningen kalibreras första gången
Under uppbyggnad...",
365 | "EN":"Filter monitoring
Activate filter monitoring. When you clean the filter, the system has to be re-calibrated.
Under development ...",
366 | "DE":"Filterüberwachung
Aktivierung der Filterüberwachung. Wenn du den Filter reinigst, muss das System neu kalibriert werden.
In Entwicklung ..."
367 | },"fan_degree_info": {
368 | "SE":"Forcering vid låga gradminuter
För att öka effektiviteten på kompressorn så kan luftflödet öka när gradminuterna börjar närma sig start för tillsats.",
369 | "EN":"Force airflow at low degree minutes
To increase efficiency of the compressor, the airflow can be increased when the degree minutes start reaching the start value for additional heating, thus preventing that.",
370 | "DE":"Erzwingen bei niedrigen Gradminuten
Um die Effizienz des Kompressors zu erhöhen, kann der Luftstrom angehoben werden, wenn die Gradminuten positive Werte erreichen."
371 | },"fan_sensor_label": {
372 | "SE":"Välj CO2 givare",
373 | "EN":"Select CO2 sensor",
374 | "DE":"CO2 sensor auswählen"
375 | },"fan_last_cal": {
376 | "SE":"Senaste kalibrering",
377 | "EN":"Last calibration",
378 | "DE":"Letzte Kalibrierung"
379 | },"fan_info": {
380 | "SE":"Automatiskt luftflöde
Justerar automatiskt fläkthastigheten och kompenserar för smutsigt filter eller andra tillfälliga variationer i luftflödet.
OBS. Ingen justering utförs under avfrostning/forcering/låg förångartemperatur. Detta för att undvika ökning av luftflödet medans påisning pågår",
381 | "EN":"Automatic airflow
Automatically adjusts the fan speed and compensates for dirty filters or other temporary variations in the airflow.
NOTE: No adjustment is made during defrosting/forcing/low evaporator temperature. This is to avoid increasing the airflow while icing is in progress",
382 | "DE":"Automatischer Luftstrom
Passt die Lüfterdrehzahl automatisch an und gleicht verschmutzte Filter oder andere vorübergehende Schwankungen des Luftstroms aus.
HINWEIS: Es wird keine Einstellung vorgenommen während des Abtauens/Forcierens/niedriger Verdampfertemperatur. Dies soll verhindern, dass der Luftstrom während der Vereisung erhöht wird."
383 | },"fan_size": {
384 | "SE":"Rekommenderat luftflöde
Det minsta rekommenderade flödet enligt angiven yta",
385 | "EN":"Recommended airflow
The recommended minimum airflow for the given area. ",
386 | "DE":"Empfohlener Luftstrom
Der empfohlene Mindestluftstrom für den angegeben Bereich. "
387 | },"log_enable": {
388 | "SE":"Loggning på
Aktiverar loggning, OBS loggning sparas endast i arbetsminnet",
389 | "EN":"Activate logging
Activates the logging. Note: Logs are only stored in RAM",
390 | "DE":"Protokollierung aktivieren
Aktiviert die Protokollierung. Hinweis: Logdateien werden nur im Arbeitsspeicher abgelegt."
391 | },"log_info": {
392 | "SE":"INFO",
393 | "EN":"INFO",
394 | "DE":"INFO"
395 | },"log_error": {
396 | "SE":"ERROR",
397 | "EN":"ERROR",
398 | "DE":"ERROR"
399 | },"log_debug": {
400 | "SE":"DEBUG",
401 | "EN":"DEBUG",
402 | "DE":"DEBUG"
403 | },"log_core": {
404 | "SE":"CORE",
405 | "EN":"CORE",
406 | "DE":"CORE"
407 | },"log_fan": {
408 | "SE":"Automatiskt Luftflöde",
409 | "EN":"Automatic airflow",
410 | "DE":"Automatischer Luftstrom"
411 | },"log_diag": {
412 | "SE":"Diagnostik",
413 | "EN":"Diagnostics",
414 | "DE":"Diagnostik"
415 | },"log_weather": {
416 | "SE":"Prognosreglering",
417 | "EN":"Forecast control",
418 | "DE":"Prognoseregelung"
419 | },"price_very_high": {
420 | "SE":"Väldigt högt pris",
421 | "EN":"Very high price",
422 | "DE":"Sehr hoher Preis"
423 | },"price_high": {
424 | "SE":"Högt pris",
425 | "EN":"High price",
426 | "DE":"Hoher Preis"
427 | },"price_normal": {
428 | "SE":"Normalt pris",
429 | "EN":"Normal price",
430 | "DE":"Normaler Preis"
431 | },"price_low": {
432 | "SE":"Lågt pris",
433 | "EN":"Low price",
434 | "DE":"Niedriger Preis"
435 | },"price_very_low": {
436 | "SE":"Väldigt lågt pris",
437 | "EN":"Very low price",
438 | "DE":"Sehr niedriger Preis"
439 | },"price_nibe_label": {
440 | "SE":"Nibes Elprisreglering
För att kunna använda sig av Nibe Uplinks elprisnivåer behöver det aktiveras i värmepumpen först",
441 | "EN":"Nibe electricity price adaption
To be able to use the electricity price adaption, it has to be activated in the heatpump first.",
442 | "DE":"Nibe Strompreisregelung
Um die Strompreisregelung zu verwenden, muss sie erst in der Wärmepumpe eingeschaltet werden."
443 | },"price_nibepi_label": {
444 | "SE":"NibePis Elprisreglering
Aktiverar NibePis elprisreglering och alla parametrar ställs in här",
445 | "EN":"NibePi electricity price adaption
Activates NibePis electricity price adaption and all parameters will be managed from here.",
446 | "DE":"NibePis Strompreisregelung
Aktiviert NibePi's Strompreisregelung und alle Parameter werden dann hier eingestellt."
447 | },"economy": {
448 | "SE":"Ekonomi",
449 | "EN":"Economy",
450 | "DE":"Sparmodus"
451 | },"normal": {
452 | "SE":"Normal",
453 | "EN":"Normal",
454 | "DE":"Normal"
455 | },"luxury": {
456 | "SE":"Lyx",
457 | "EN":"Luxury",
458 | "DE":"Luxus"
459 | },"tooltip": {
460 | "SE":"Välj",
461 | "EN":"Select",
462 | "DE":"Auswählen"
463 | },"price_info": {
464 | "SE":"Information
NibePi kan reglera efter elprisnivån. Uppgifter kan hämtas från antingen Nibe Uplink eller Tibber.
För att säkerställa komfort tas alltid inomhustemperaturen i beaktning vid reglering",
465 | "EN":"Information
NibePi can control the heat production based on electricity price. Energy prices come either from NibeUplink or Tibber.
To guarantee comfort, room temperature is always taken into account.",
466 | "DE":"Information
NibePi kann die Wärmeerzeugung nach dem aktuellen Strompreis regeln. Die Strompreise werden von NibeUplink oder Tibber bezogen.
Um den Komfort zu erhalten, wird die aktuelle Raumtemperatur mit in Betracht gezogen."
467 | },"price_now": {
468 | "SE":"Nuvarande Elpris",
469 | "EN":"Current electricity price",
470 | "DE":"Aktueller Strompreis"
471 | },"price_now_level": {
472 | "SE":"Nuvarande Elprisnivå",
473 | "EN":"Current electricity level",
474 | "DE":"Aktuelles Strompreisniveau"
475 | },"unknown": {
476 | "SE":"Okänt",
477 | "EN":"Unknown",
478 | "DE":"Unbekannt"
479 | },"currency": {
480 | "SE":"öre",
481 | "EN":"öre",
482 | "DE":"öre"
483 | },"price_hw_settings": {
484 | "SE":"Inställningar för varmvatten
Välj vilken varmvatten inställning det ska vara vid olika elprisnivåer",
485 | "EN":"Hot water settings
Select hot water settings for different electricity price levels.",
486 | "DE":"Wamrwassereinstellungen
Wähle die Wamrwassereinstellungen für die unterschiedlichen Strompreisniveaus."
487 | },"price_settings": {
488 | "SE":"Inställningar
Välj vilken reglering som ska gälla. Med styrning av NibePi så välj datakälla. För integrering mot Tibber, använd din personliga token som finns att hämta från https://developer.tibber.com/",
489 | "EN":"Settings
Select, which control mechanism should be used. With NibePi control, select the data source. For integration with Tibber, please use your personal access token, which can be obtained from https://developer.tibber.com/",
490 | "DE":"Einstellungen
Wähle aus, welcher Regelmechanismus verwendet werden soll. Für NibePi Regeleung, bitte die Datenquelle wählen. Für die Verwendung von Tibber muss ein persönliches Acces-Token verwendet werden. Dieses kist hier erhältlich: https://developer.tibber.com/"
491 | },"price_enable_hw": {
492 | "SE":"Aktivera varmvattenreglering",
493 | "EN":"Enable hot water control",
494 | "DE":"Warmwasserregelung aktivieren"
495 | },"price_smart1_info": {
496 | "SE":"OBS. För att kunna använda prisnivåer från Nibe Uplink måste Nibes SmartPrice vara aktiverat i värmepumpen manuellt under meny 4.1.6 Smart Price Adaption.
Alla åtgärder ska däremot nollställas för att NibePi ska kunna styra.",
497 | "EN":"Note: To be able to use price levels from NibeUplink, SmartPrice needs to be enabled in the heatpump from menu 4.1.6 Smart Price Adaption.
All settings need to be reset to default for NibePi to be able to control the system.",
498 | "DE":"Hinweis: Um die Preisniveaus von NibeUplink zu verwenden, muss SmartPrice im Menü 4.1.6 Smart Price Adaption der Wärmepumpe aktiviert werden.
Alle Einstellungen müssen jedoch auf Standardwerte zurückgesetzt werden, damit NibePi das System steuern kann."
499 | },"price_sensor_info": {
500 | "SE":"Begränsa elprisregleringens påverkan med hjälp av en rumsgivare.
Ställ in en differens för den lägsta temperaturen som tillåts vid högt elpris för reglering mot ditt rumsbörvärde.",
501 | "EN":"Limit the influence of the price adaption by means of a room temperature sensor.
Set the lowest temperature allowed at high electricity prices.",
502 | "DE":"Begrenzung der Auswirkung des Strompreises
Definiere eine Temperaturdifferenz die auch bei hohem Strompreis nicht unterschritten werden darf."
503 | },"price_s1_info": {
504 | "SE":"Elprisreglering S1
Välj hur mycket kurvjusteringen ska justeras vid olika prisnivåer.
Reglering mot rumsgivare rekommenderas starkt, ibland förekommer det längre perioder av samma låga eller höga pris",
505 | "EN":"Electricity price adaption S1
Select the curve adaption for different price levels.
Regulation against room temperature sensors is strongly recommended since there might be longer periods of high prices.",
506 | "DE":"Strompreisregelung S1
Stelle die Kurvenanpassung für die unterschiedlichen Strompreisniveaus ein.
Regelung gegen einen Temperatursensor wird strengstens empfohlen, da es durchaus lange Phasen mit hohen Strompreisen geben kann."
507 | },"price_s2_info": {
508 | "SE":"Elprisreglering S2
Välj hur mycket kurvjusteringen ska justeras vid olika prisnivåer.
Reglering mot rumsgivare rekommenderas starkt, ibland förekommer det längre perioder av samma låga eller höga pris",
509 | "EN":"Electricity price adaption S2
Select the curve adaption for different price levels.
Regulation against room temperature sensors is strongly recommended since there might be longer periods of high prices.",
510 | "DE":"Strompreisregelung S2
Stelle die Kurvenanpassung für die unterschiedlichen Strompreisniveaus ein.
Regelung gegen einen Temperatursensor wird strengstens empfohlen, da es durchaus lange Phasen mit hohen Strompreisen geben kann."
511 | },"indoor_sensor": {
512 | "SE":"Rumsgivare",
513 | "EN":"Room sensor",
514 | "DE":"Raumtemperatursensor"
515 | },"difference": {
516 | "SE":"Differens",
517 | "EN":"Difference",
518 | "DE":"Abweichung"
519 | },"price_s1_graph": {
520 | "SE":"Graf S1
",
521 | "EN":"Graph S1
",
522 | "DE":"Diagramm S1
"
523 | },"price_s2_graph": {
524 | "SE":"Graf S2
",
525 | "EN":"Graph S2
",
526 | "DE":"Diagramm S2
"
527 | },"time": {
528 | "SE":"Tid",
529 | "EN":"Time",
530 | "DE":"Zeit"
531 | },"start_heat_info": {
532 | "SE":"Värmeinformation
",
533 | "EN":"Heating information
",
534 | "DE":"Wärme-Informationen
"
535 | },"start_info": {
536 | "SE":"Husinformation
För att använda Prognosreglering behöver husets koordinater anges.
Fastighetens storlek anges i kvm (Uppvärmd yta) och används för att beräkna rätt flöde för frånluftsvärmepumpar med automatiskt luftflöde
",
537 | "EN":"House information
To use Forecast regulation, the house's coordinates need to be specified.
The size of the building is given in sqm (heated area) and is used to calculate the correct flow for exhaust air heatpumps with automatic airflow
",
538 | "DE":"Hausinformationen
Um die Prognoseregelung verwenden zu können, müssen die Koordinaten des Hauses angegeben werden.
Die Größe des Gebäudes wird in m² angegeben (beheizte Fläche) und wird verwendet, um den korrekten Durchfluss für Abluftwärmepumpen mit automatischem Luftstrom zu berechnen
"
539 | },"cpr_info": {
540 | "SE":"Kompressorförbrukning
Kompressorns förbrukning för värme och varmvatten
OBS. Notera att detta kan skilja sig avsevärt från en extern elmätare som mäter hela värmepumpens förbrukning
",
541 | "EN":"Compressor consumption
Compressor consumption for heating and hot water
NOTE: This can differ significantly from an external electricity meter that measures the entire heat pump consumption
",
542 | "DE":"Kompressorverbrauch
Kompressorverbrauch für Wärme und Warmwasser
HINWEIS: Beachte, dass dies erheblich von einem externen Stromzähler abweichen kann, der den gesamten Wärmepumpenverbrauch misst
"
543 | },"register_info": {
544 | "SE":"Registerhantering
Välj vilka register som ska hämtas från värmepumpen
Register aktiverade med LOG.SET är förvalda och går ej ta bort.
Alla valda register syns till höger och det senaste aktuella hämtade värdet visas.
OBS. Ju fler register som väljs kommer göra att registerna uppdateras mindre ofta. Detta eftersom att värmepumpen endast kan hantera ett visst antal meddelanden i sekunden.
",
545 | "EN":"Register management
Select which registers are to be retrieved from the heatpump
Registers activated in the LOG.SET file are preselected and cannot be deleted
All selected registers are visible on the right and the last retrieved value is displayed.
NOTE: The more registers you select, the less often the registers will be updated. This is due to the fact that the heatpump can only process a certain amount registers per second.
",
546 | "DE":"Registerverwaltung
Wähle aus, welche Register von der Wärmepumpe abgerufen werden sollen.
In der LOG.SET Datei aktivierte Register sind vorausgewählt und können nicht gelöscht werden
Alle ausgewählten Register werden rechts angezeigt und der zuletzt abgerufene Wert wird angezeigt.
HINWEIS: Je mehr Register ausgewählt werden, desto seltener werden die Register aktualisiert. Dies liegt daran, dass die Wärmepumpe nur eine bestimmte Anzahl von Registern pro Sekunde verarbeiten kann.
"
547 | },"data_info": {
548 | "SE":"Datahantering
Alla valda register visas här under, inkl. nuvarande värde.
För att visa data i graf nedan, markera register i denna lista.
Historik sparas alltid för valda register, och finns tillgängliga att visas i grafen nedan.
För att historiken ska sparas efter omstart behöver funktionen \"Spara grafer\" aktiveras under Inställingar
",
549 | "EN":"Data management
All selected registers are shown below, including current value.
To view data in graph below, select registers in this list.
Historical values are plotted in the graph below.
The data can be saved permanently using the function \"Save graphs\" in the menu item \"Installation\".
",
550 | "DE":"Datenverwaltung
Alle ausgewählten Register werden unten angezeigt, einschließlich des aktuellen Werts.
Um Daten in der folgenden Grafik anzuzeigen, wählen Register in dieser Liste aus.
Der Verlauf wird immer für ausgewählte Register gezeichnet. Über Funktion \"Grafiken speichern\" im Menüpunkt \"Installation\" können die Daten dauerhaft gespeichert werden.
"
551 | },"remove_register": {
552 | "SE":"Ta bort register",
553 | "EN":"Delete register",
554 | "DE":"Register löschen"
555 | },"hw_auto_lux_info": {
556 | "SE":"Automatisk tillfällig lyx
Vid stort uttag av varmvatten så kan tillfällig lyx startas automatiskt.
Injustering sker nedan med två värden, det ena är differens som bestämmer hur mycket botten på varmvattentanken ska sjunka i temperatur innan funktionen aktiveras
Tidfaktor anger hur lång bak i tiden differensen ska gälla. Se exempel under grafen",
557 | "EN":"Automatic temporary luxury
When a big amount of hot water is used, temporary luxury mode can be set automatically.
Adjustment is done below with two values, one is difference that determines how much the bottom of the hot water tank should drop in temperature before the function is activated
Time factor indicates how far back in time the difference should be considered. See example under the graph",
558 | "DE":"Automatischer temporärer Luxus
Bei Entnahme großer Mengen an Warmwasser, kann der temporäre Luxus-Modus automatisch gestartet werden.
Die Einstellung erfolgt unten mittels zwei Werten. Zum einen über die Differenz, die bestimmt, um wie viel die Temperatur am Bodens des Warmwasserspeichers sinken muss, bevor die Funktion aktiviert wird.
Zum Anderen über den Zeitfaktor, der angibt wie lange die Differenz zurück betrachtet werden soll. Siehe Beispiel unter der Grafik"
559 | },"hw_priority_info": {
560 | "SE":"Varmvatten prioritet
Om tappvarmvattnet (BT7) understiger den minsta inställda nivån så startar varmvattenproduktion direkt.
Denna funktion hjälper till att prioritera varmvattnet under vinterperioden där värmen ofta prioriteras",
561 | "EN":"Hot water priority
If the domestic hot water (BT7) is below the minimum set level, hot water production starts immediately.
This function helps to prioritize hot water during the winter period where heat is often prioritized",
562 | "DE":"Warmwasser-Priorität
Wenn das Brauchwarmwasser (BT7) unter dem eingestellten Mindestniveau liegt, beginnt die Heißwasserproduktion sofort.
Diese Funktion hilft bei der Priorisierung von heißem Wasser während der Winterperiode, in der Wärme häufig priorisiert wird."
563 | },"hw_auto_lux_info2": {
564 | "SE":"Exempel för badkar
För att kalibrera in funktionen så ställ grafen att visa 1 timma, det ger en detaljerad graf.
Notera vad klockan är och börja tappa upp badkaret, fyll upp badkaret rejält och kolla grafen efteråt. Du kommer se en ljusblå linje som dyker ner mot en orange linje som också dyker. Detta är samma linje men den nedre linjen är förskjuten med tidfaktorn, och avståndet mellan linjerna i höjdled är differensen. Om linjerna möts kommer funktionen att triggas och en grön linje kommer visa sig, när den ljusblå linjen har överstigit denna så stängs funktionen av.
Om linjerna precis missar varandra under tappningen så behöver tidfaktorn justeras till ett högre värde, om den ljusblåa linjen aldrig når ner till den orangea så behöver differensen sänkas.
Om funktionen triggas för enklare saker som dusch eller diskning så ska man höja differensen lite.
OBS. Grundinställningarna är i många fall tillräckliga och kan endast behöva justeras lite i tid för att fånga upp trögheten i olika system",
565 | "EN":"Example for a bathtub
To calibrate the function, set the graph to show 1 hour, it gives a detailed graph.
Note what time it is and start filling the bathtub. Check the graph afterwards. You will see a pale blue line that goes down to an orange line that is also falling. This is the same line but the bottom line is offset by the time factor, and the distance between the lines in height is the temperature difference. When both lines meet, the function will be triggered and a green line will appear, when the pale blue line has exceeded this green line, the function will be turned off.
If the lines miss eachother during filling of the tub, the time factor needs to be adjusted to a higher value. If the pale blue line never reaches the orange line, then the difference needs to be lowered.
If the function is triggered for smaller amounts of water, such as showering or washing dishes, you should increase the difference a little.
NOTE: The basic settings sufficient in most cases and may only need to be adjusted a little in time to catch up inertia in different systems",
566 | "DE":"Beispiel für eine Badewanne
Um die Funktion zu kalibrieren, stelle das Diagramm so ein, dass 1 Stunde angezeigt wird. Es wird ein detailliertes Diagramm angezeigt.
Notiere die Uhrzeit und Beginne mit dem Füllen der Badewanne. Fülle die Badewanne komplett auf. Du siehst eine hellblaue Linie, die bis zu einer orangefarbenen Linie abfällt, welche ebenfalls einen abfallenden Verlauf hat. Dies ist dieselbe Linie, aber die untere Linie ist um den Zeitfaktor versetzt und der Abstand zwischen den Linien in der Höhe ist der Temperaturunterschied. Wenn sich die Linien treffen, wird die Funktion ausgelöst wird und eine grüne Linie erscheint. Wenn die hellblaue Linie diese überschritten hat, wird die Funktion ausgeschaltet.
Wenn sich die Linien beim Füllen der Wanne verfehlen, muss der Zeitfaktor auf einen höheren Wert eingestellt werden. Wenn die hellblaue Linie niemals die orangene erreicht, muss die Temperaturdifferenz verringert werden.
Wenn die Funktion für kleinere Wassermengen wie Duschen oder Geschirrspülen ausgelöst wird, sollte die Temperaturdifferenz ein Wenig erhöht werden.
HINWEIS: Die Grundeinstellungen sind in meißten Fällen ausreichend und müssen möglicherweise nur minimal angepasst werden, um die Trägheit unterschiedlicher Systeme zu kompensieren."
567 | },"hw_diff": {
568 | "SE":"Differens",
569 | "EN":"Temperature difference",
570 | "DE":"Temperaturunterschied"
571 | },"hw_time": {
572 | "SE":"Tidfaktor",
573 | "EN":"Time factor",
574 | "DE":"Zeitfaktor"
575 | },"result": {
576 | "SE":"Resultat",
577 | "EN":"Result",
578 | "DE":"Ergebnis"
579 | },"register_add": {
580 | "SE":"Lägg till register",
581 | "EN":"Add register"
582 | },"register_remove": {
583 | "SE":"Ta bort register",
584 | "EN":"Remove register"
585 | },"testmode": {
586 | "SE":"Testläge
Aktiverar testläge, det är då möjligt att hämta data från register som inte finns med i registerlistan.",
587 | "EN":"Testmode
Activates testmode, experimental feature which makes it possible to get data that are not in the registerlist."
588 | },"advanced_2": {
589 | "SE":"Anslutning
Välj vilken typ av anslutning värmepumpen är ansluten till.
Efter ändring så behöver Node-RED startas om.
",
590 | "EN":"Connection
Select the connection to the heatpump.
After changing connection details, Node-RED needs to be restarted.
"
591 | },"con_serialport": {
592 | "SE":"Serieport",
593 | "EN":"Serialport"
594 | },"con_nibegw": {
595 | "SE":"NibeGW IP-adress",
596 | "EN":"NibeGW IP-address"
597 | },"con_type": {
598 | "SE":"Anslutningsmetod",
599 | "EN":"Connection type"
600 | },"con_series": {
601 | "SE":"Värmepumpserie",
602 | "EN":"Heatpump series"
603 | },"tcp_server": {
604 | "SE":"Modbus-TCP/IP Server",
605 | "EN":"Modbus-TCP/IP Host"
606 | },"tcp_port": {
607 | "SE":"Modbus-TCP/IP Port",
608 | "EN":"Modbus-TCP/IP Port"
609 | },"tcp_pump": {
610 | "SE":"Modell",
611 | "EN":"Model"
612 | },"serial": {
613 | "SE":"Serieport",
614 | "EN":"Serialport"
615 | },"modbus": {
616 | "SE":"Modbus TCP/IP",
617 | "EN":"Modbus TCP/IP"
618 | },"nibegw": {
619 | "SE":"NibeGW",
620 | "EN":"NibeGW"
621 | },"fSeries": {
622 | "SE":"F-Serien",
623 | "EN":"F-Series",
624 | "DE":"F-Serie"
625 | },"sSeries": {
626 | "SE":"S-Serien",
627 | "EN":"S-Series",
628 | "DE":"S-Serie"
629 | }
630 |
631 | }
632 | }
633 |
--------------------------------------------------------------------------------
/weather.html:
--------------------------------------------------------------------------------
1 |
78 |
79 |
130 |
131 |
--------------------------------------------------------------------------------
/weather.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function(RED) {
3 | function nibeWeather(config) {
4 | RED.nodes.createNode(this,config);
5 | const server = RED.nodes.getNode(config.server);
6 | let system = config.system.replace('s','S');
7 | const startUp = () => {
8 | this.status({ fill: 'yellow', shape: 'dot', text: `System ${system}` });
9 | let conf = server.nibe.getConfig();
10 | if(conf.weather===undefined) {
11 | conf.weather = {};
12 | server.nibe.setConfig(conf);
13 | }
14 | let arr = [
15 | {topic:"outside",source:"nibe"},
16 | {topic:"heatcurve_"+config.system,source:"nibe"}
17 | ];
18 | if(conf.home.inside_sensors===undefined) {
19 | conf.home.inside_sensors = [];
20 | server.nibe.setConfig(conf);
21 | }
22 |
23 | if(conf.weather['sensor_'+config.system]===undefined || conf.weather['sensor_'+config.system]=="Ingen") {
24 | arr.push({topic:"inside_"+config.system,source:"nibe"});
25 | } else {
26 | let index = conf.home.inside_sensors.findIndex(i => i.name == conf.weather['sensor_'+config.system]);
27 | if(index!==-1) {
28 | var insideSensor = Object.assign({}, conf.home.inside_sensors[index]);
29 | //let insideSensor = conf.home.inside_sensors[index];
30 | arr.push(insideSensor);
31 | }
32 | }
33 | if(conf.weather['enable_'+config.system]!==true) arr = [];
34 | server.initiatePlugin(arr,'weather',config.system).then(result => {
35 | this.status({ fill: 'green', shape: 'dot', text: `System ${system}` });
36 | this.send({enabled:true});
37 | },(reject => {
38 | this.status({ fill: 'red', shape: 'dot', text: `System ${system}` });
39 | this.send({enabled:false});
40 | }));
41 |
42 | }
43 |
44 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
45 | startUp();
46 | } else {
47 | server.nibeData.on('ready', (data) => {
48 | startUp();
49 | })
50 | }
51 | this.on('input', function(msg) {
52 | let conf = server.nibe.getConfig();
53 | if(msg.topic=="update") {
54 | server.updateData(true);
55 | return;
56 | }
57 | if(msg.payload!==undefined && msg.topic!==undefined && msg.topic!=="") {
58 | let req = msg.topic.split('/');
59 | if(conf[req[0]][req[1]+'_'+config.system]!==msg.payload) {
60 | conf[req[0]][req[1]+'_'+config.system] = msg.payload;
61 | server.nibe.setConfig(conf);
62 | startUp();
63 | }
64 | }
65 |
66 | });
67 | server.nibeData.on(this.id, (data) => {
68 | if(data.changed===true) {
69 | config.system = data.system;
70 | if(server.nibe.core!==undefined && server.nibe.core.connected!==undefined && server.nibe.core.connected===true) {
71 | startUp();
72 | }
73 | }
74 | })
75 | server.nibeData.on('pluginWeather', (data) => {
76 | if(data.system===config.system) {
77 | let outside = data['outside'];
78 | //server.nibeData.emit('forecast_1',arr,config.system);
79 | this.send({topic:"Utomhustemperatur",payload:outside.data});
80 | this.send({topic:"Kurvjustering",payload:data.weatherOffset});
81 | //if(data.predictedNow!==undefined) this.send({topic:"Nuvarande prognos",payload:data.predictedNow.payload,timestamp:data.predictedNow.timestamp});
82 | if(data.predictedLater!==undefined) this.send({topic:"Prognos",payload:data.predictedLater.payload,timestamp:data.predictedLater.timestamp});
83 | if(data.unfiltredTemp!==undefined) this.send({topic:"Ojusterad Prognos",payload:data.unfiltredTemp.payload,timestamp:data.unfiltredTemp.timestamp});
84 | this.send([null,{topic:"Vindgraf",payload:data.windGraph}]);
85 | }
86 | })
87 | this.on('close', function() {
88 | server.nibeData.removeAllListeners();
89 | this.status({ fill: 'yellow', shape: 'dot', text: `System ${system}` });
90 | });
91 | }
92 | RED.nodes.registerType("nibe-weather",nibeWeather);
93 | }
94 |
--------------------------------------------------------------------------------