Integration Guide for Node-RED, with examples (#2098)

* Create Node-RED.md

* Update Node-RED.md

* Add files via upload

* Update Node-RED.md

* Update Node-RED.md

* Add files via upload

* Update Node-RED.md

* Update Node-RED.md

* Add files via upload

* Update Node-RED.md

* Update Node-RED.md

* Update Node-RED.md

* Update Node-RED.md

* Update Node-RED.md

* Update Node-RED.md

* Update Node-RED.md

* Update Node-RED.md

* Update Node-RED.md

* Add files via upload

* Update Node-RED.md

* Create Node-RED.md

* Add files via upload

* Update Node-RED.md

* Update Node-RED.md

* Add files via upload

* Update Node-RED.md

* Update Node-RED.md

improved the directions for adding node-red modules

* Add sidebar link

* Format markdown file

* Fix file link

* Move assets into separate directory

* Optimize images

* fix spelling error

"hamburber" sounds like something Donald Trump would write. I fixed the spelling error. :)

Co-authored-by: Adrian Kumpf <8999358+adriankumpf@users.noreply.github.com>
This commit is contained in:
Peter Boothby
2021-11-08 17:05:36 +01:00
committed by GitHub
co-authored by Adrian Kumpf
parent 451b23634c
commit 1f8bbef39a
7 changed files with 816 additions and 1 deletions
+135
View File
@@ -0,0 +1,135 @@
---
title: Node-RED Integration
sidebar_label: Node-RED
---
# Overview
From the Node-RED website:
> Node-RED is a programming tool for wiring together hardware devices, APIs and online services in new and interesting ways.
>
> It provides a browser-based editor that makes it easy to wire together flows using the wide range of nodes in the palette that can be deployed to its runtime in a single-click.
The high-level logic "flow" is coded by wiring "nodes" in the user interface. Low-level logic can be coded in JavaScript. Visit the [Node-RED website](https://nodered.org) for a good introduction on its homepage.
| **Node-RED UI** |
| :---------------------------------------------------: |
| ![Node-RED Flow example](./Node-RED/example-flow.png) |
This integration guide assumes that Teslamate is deployed on docker and that Node-RED will not be exposed to the internet. Of course, you can install Node-RED outside of Docker and on another server, it that suits. The integration with Teslamate is entirely via MQTT. Notifications included in the examples are sent using available Node-RED modules for email and Telegram. Other notifications (e.g. Slack) are relatively simple by replacing the final node in the Notifications flow. One could also integrate with public tools such as IFTT. Or get really creative and integrate with Alexa or Google Assistant to control the car by voice. :)
Creation of the Telegram bot is not covered in this guide; there is plenty of documentation on the net explaining how to do this (links are provided in the _Requirements_ section below). However, the configuration of the Telgram node in Node-RED is described below.
Included are Node-RED flows with two examples:
- A simple Node-RED dashboard with Car Status and Charge Status panels
- Notification logic for state changes, entering/exiting geofences and time remaining to charge.
| **Node-RED Dashboard** |
| :-----------------------------------------------------: |
| ![Node-RED Dashboard example](./Node-RED/dashboard.png) |
| **Example Telegram Notifications** |
| :---------------------------------------------------: |
| ![Node-RED Telegram example](./Node-RED/Telegram.png) |
# Requirements
- Teslmate environment, preferably installed in Docker (if you are new to Docker, see Installing Docker and Docker Compose)
- Access by Node-RED to the internet to send notifications
- A mobile device with [Telegram](https://telegram.org/) client installed or use Telegram's browser interface
- your own Telegram Bot, see [Creating a new telegram bot](https://core.telegram.org/bots#6-botfather)
- your own Telegram chat id, see [Get your Telegram chat ID](https://docs.influxdata.com/kapacitor/v1.5/event_handlers/telegram/#get-your-telegram-chat-id)
# Installing Node-RED
Visit the Node-RED [Getting Started](https://nodered.org/docs/getting-started/) page for the various deployment options. If you are using Docker, the following section should suffice to get running.
## Docker Entries
Add the following parameters to your `docker-compose.yml` file. It's assumed that your timezone in set in the .env file's TM_TZ environment variable.
```yaml title="docker-compose.yml"
services:
node-red:
image: nodered/node-red:latest
restart: always
environment:
- TZ=${TM_TZ}
volumes:
- node-red-data:/data
ports:
- "1880:1880"
volumes:
node-red-data:
```
Build and start the docker container with `docker-compose up`. To run the containers in the background, add the `-d` flag:
```
docker-compose up -d
```
# Node-RED Configuration
There are two flows in the example exports provided. The first flow creates a simple dashboard with some of the MQTT values. The second flow sends notifications to Telegram.
The flow names are "Car Dashboard" and "Notifications".
## Required Modules
After bringing up the Node-RED container the first time, save the following shell script to a file (e.g. named _add-nr-modules.sh_, then run `bash ./add-nr-modules.sh`) to install modules required for the example flows:
```bash title="add-nr-modules.sh"
:
MODULES="node-red-contrib-calc
node-red-contrib-simpletime
node-red-dashboard
node-red-node-email
node-red-contrib-telegrambot
node-red-node-ui-table"
set -x
for MODULE in $MODULES
do
docker-compose exec -T node-red npm install --no-audit --no-update-notifier --no-fund --save --save-prefix=~ --production $MODULE
done
docker-compose stop node-red
docker-compose start node-red
```
## Import Flows
- Download the example JSON file [Node-RED-Teslamate-flows.json](./Node-RED/Teslamate-flows.json.example)
- Go to Node-RED's hamburger menu in the upper right corner and select `Import`
- Use the `select file to import` button on the pop-up to upload the JSON file.
## MQTT Configuration
If you are using the standard MQTT docker configuration as per the Teslamate installation guide, after you import the flows Node-RED should automatically connect. Otherwise, open the `teslamate/cars/1/#` node in the Car Dashboard flow, select the pencil icon next to the `Server` field in the panel, then edit the MQTT server's parameters. Click Update, Done and Deploy.
## Telegram Configuration
To enter your Telegram Bot's parameters:
- Edit the `Status Messages` Telegram node in the Notifications flow, then select the pencil icon next to the `Bot` name.
- Replace the `Bot-Name` field with the name of your Telegram bot
- Fill the `Token` field with the value that you copied from setting up your bot.
| **Telegram configuration pop-up** |
| :------------------------------------------------------------------------: |
| ![Node-RED Telegram configuration example](./Node-RED/telegram-wizard.png) |
- Click `Update`, then `Done`
- Edit the `Format messages` node in the Notifications flow
- Replace the value for `chatId` with the chatId that you obtained setting up the bot.
- Click `Done`
- Click `Deploy` to re-deploy your flows. "Connected" should now display beneath the `Status Messages` node.
## Email configuration
If you want to quickly try out this implementation without Telegram, you can simply remove the connection between the `Format Messages` and `Status Messages` nodes, then connect `Format Messages` with the `email` node. Edit the email node to set your parameters to send emails. After you re-deploy you should get at least one message as per the note below.
## Notes
- When you re-deploy your flows, a _Tesla entered Geofence ..._ message and a _Tesla Driver is present:_ notification may be sent. Some might consider this a bug. Or a test that the notification channel is working. :)
Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

@@ -0,0 +1,676 @@
[{
"id": "868e4cd7.9fc578",
"type": "tab",
"label": "Car Dashboards",
"disabled": false,
"info": ""
}, {
"id": "88633c3e.1b273",
"type": "tab",
"label": "Notifications",
"disabled": false,
"info": ""
}, {
"id": "f017ed81.199d2",
"type": "ui_tab",
"name": "Tesla",
"icon": "dashboard",
"order": 1,
"disabled": false,
"hidden": false
}, {
"id": "2cca3d1a.be10ba",
"type": "ui_group",
"name": "Charge Status",
"tab": "f017ed81.199d2",
"order": 2,
"disp": true,
"width": "6",
"collapse": false
}, {
"id": "51eafba7.ffc054",
"type": "ui_group",
"name": "Car Status",
"tab": "f017ed81.199d2",
"order": 1,
"disp": true,
"width": "8",
"collapse": false
}, {
"id": "ed12f72a.03fad",
"type": "telegram bot",
"botname": "pmb_tesla_bot",
"usernames": "",
"chatids": "",
"baseapiurl": "",
"updatemode": "polling",
"pollinterval": "300",
"usesocks": false,
"sockshost": "",
"socksport": "6667",
"socksusername": "anonymous",
"sockspassword": "",
"bothost": "",
"botpath": "",
"localbotport": "8443",
"publicbotport": "8443",
"privatekey": "",
"certificate": "",
"useselfsignedcertificate": false,
"sslterminated": false,
"verboselogging": false,
"info": "159696647614"
}, {
"id": "acbce132.6eef4",
"type": "mqtt-broker",
"name": "mosquitto",
"broker": "mosquitto",
"port": "1883",
"clientid": "",
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"sessionExpiry": ""
}, {
"id": "405404f2.d8a094",
"type": "ui_base",
"theme": {
"name": "theme-light",
"lightTheme": {
"default": "#0094CE",
"baseColor": "#0094CE",
"baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
"edited": true,
"reset": false
},
"darkTheme": {
"default": "#097479",
"baseColor": "#097479",
"baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
"edited": false
},
"customTheme": {
"name": "Untitled Theme 1",
"default": "#4B7930",
"baseColor": "#4B7930",
"baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
},
"themeState": {
"base-color": {
"default": "#0094CE",
"value": "#0094CE",
"edited": false
},
"page-titlebar-backgroundColor": {
"value": "#0094CE",
"edited": false
},
"page-backgroundColor": {
"value": "#fafafa",
"edited": false
},
"page-sidebar-backgroundColor": {
"value": "#ffffff",
"edited": false
},
"group-textColor": {
"value": "#1bbfff",
"edited": false
},
"group-borderColor": {
"value": "#ffffff",
"edited": false
},
"group-backgroundColor": {
"value": "#ffffff",
"edited": false
},
"widget-textColor": {
"value": "#111111",
"edited": false
},
"widget-backgroundColor": {
"value": "#0094ce",
"edited": false
},
"widget-borderColor": {
"value": "#ffffff",
"edited": false
},
"base-font": {
"value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
}
},
"angularTheme": {
"primary": "indigo",
"accents": "blue",
"warn": "red",
"background": "grey",
"palette": "light"
}
},
"site": {
"name": "Node-RED Dashboard",
"hideToolbar": "false",
"allowSwipe": "false",
"lockMenu": "false",
"allowTempTheme": "true",
"dateFormat": "DD/MM/YYYY",
"sizes": {
"sx": 48,
"sy": 48,
"gx": 6,
"gy": 6,
"cx": 6,
"cy": 6,
"px": 0,
"py": 0
}
}
}, {
"id": "94fe8f4d.0557d",
"type": "function",
"z": "868e4cd7.9fc578",
"name": "make time remaining string",
"func": "var t = global.get('state');\nvar remaining = msg.payload.split('.');\nvar minutes = Math.ceil(60 * Number(\".\" + remaining[1]));\nif(t[0] != 'charging'){\n msg.payload = \"Not charging\";\n} else if(remaining[0] > 0){\n msg.payload = `${remaining[0]} hours ${minutes} minutes`\n} else {\n msg.payload = `${minutes} minutes`\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 740,
"y": 220,
"wires": [["efe44f3.124a93"]]
}, {
"id": "d09c3311.862ff",
"type": "function",
"z": "868e4cd7.9fc578",
"name": "get time",
"func": "var d = new Date();\nmsg.payload = d;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 180,
"y": 380,
"wires": [["7b0d22a9.903e24"]]
}, {
"id": "2e575816.e13368",
"type": "link in",
"z": "868e4cd7.9fc578",
"name": "update-time",
"links": ["8491f8d1.1365f8"],
"x": 85,
"y": 381,
"wires": [["d09c3311.862ff"]]
}, {
"id": "8491f8d1.1365f8",
"type": "link out",
"z": "868e4cd7.9fc578",
"name": "topic-stored",
"links": ["2e575816.e13368", "cb1580bf.27615"],
"x": 395,
"y": 240,
"wires": []
}, {
"id": "22bcecd8.fac734",
"type": "switch",
"z": "868e4cd7.9fc578",
"name": "",
"property": "topic",
"propertyType": "msg",
"rules": [{
"t": "eq",
"v": "charge_limit_soc",
"vt": "str"
}, {
"t": "eq",
"v": "usable_battery_level",
"vt": "str"
}, {
"t": "cont",
"v": "battery_range_km",
"vt": "str"
}, {
"t": "eq",
"v": "time_to_full_charge",
"vt": "str"
}, {
"t": "else"
}, {
"t": "regex",
"v": "charger_*",
"vt": "str",
"case": false
}
],
"checkall": "true",
"repair": false,
"outputs": 6,
"x": 490,
"y": 140,
"wires": [["dda683ce.b5166"], ["3db79d48.c4c322"], ["cc9cc37a.5f4a78"], ["94fe8f4d.0557d", "4e84bee.72facc"], ["5599954f.8ecf84"], ["cc9cc37a.5f4a78"]]
}, {
"id": "4123d5c1.d02dcc",
"type": "mqtt in",
"z": "868e4cd7.9fc578",
"name": "",
"topic": "teslamate/cars/1/#",
"qos": "2",
"datatype": "auto",
"broker": "acbce132.6eef4",
"nl": false,
"rap": false,
"x": 120,
"y": 140,
"wires": [["892aaba0.903858"]]
}, {
"id": "892aaba0.903858",
"type": "function",
"z": "868e4cd7.9fc578",
"name": "store topic",
"func": "var short_topic = msg.topic.substring(17);\n\n// create object to be used by Grafana data source\nvar tmp = [];\ntmp.push(msg.payload);\ntmp.push(parseInt(new Date().getTime()));\nglobal.set(short_topic,tmp);\nmsg.topic = short_topic;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 310,
"y": 140,
"wires": [["22bcecd8.fac734", "8491f8d1.1365f8"]]
}, {
"id": "ce57ad0f.660c1",
"type": "switch",
"z": "868e4cd7.9fc578",
"name": "",
"property": "topic",
"propertyType": "msg",
"rules": [{
"t": "regex",
"v": "est_*",
"vt": "str",
"case": false
}, {
"t": "regex",
"v": "ideal_*",
"vt": "str",
"case": false
}, {
"t": "regex",
"v": "charger_actual_*",
"vt": "str",
"case": false
}, {
"t": "regex",
"v": "charger_power*",
"vt": "str",
"case": false
}
],
"checkall": "true",
"repair": false,
"outputs": 4,
"x": 872,
"y": 178,
"wires": [["ad15c026.1d1d6"], ["6ab267e6.5e4b78"], ["2ec008a.2fb84f8"], ["23ca2a1b.ce5fce"]]
}, {
"id": "5599954f.8ecf84",
"type": "function",
"z": "868e4cd7.9fc578",
"name": "fill state table",
"func": "var interesting = [\n 'state',\n 'odometer',\n 'locked',\n 'shift_state',\n 'is_user_present'\n ];\nvar tmp = [];\nvar keys = global.keys().sort();\nfor (var key in keys ) {\n //if(!interesting.includes(keys[key]))\n // next;\n var t = global.get(keys[key]);\n var row = {\n \"key\": keys[key],\n \"value\": t[0]\n }\n tmp.push(row);\n} \nmsg.payload = tmp;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 690,
"y": 275,
"wires": [["366025db.df598a"]]
}, {
"id": "4e84bee.72facc",
"type": "link out",
"z": "868e4cd7.9fc578",
"name": "Charge time remaining",
"links": ["cb1580bf.27615"],
"x": 815,
"y": 360,
"wires": []
}, {
"id": "5bf1c557.464144",
"type": "function",
"z": "88633c3e.1b273",
"name": "state changes",
"func": "var new_state = msg.payload;\nvar previous_state = \"\";\n\nnode.warn(msg.topic + \": \" + msg.payload);\n\nswitch(msg.topic){\n\tcase 'state':\n\t\tprevious_state = flow.get('state');\n\t\tflow.set('state',new_state);\n if(new_state != 'driving' && previous_state != 'driving') \n return;\n\t\tif(previous_state != new_state)\n\t\t\tmsg.topic = `Tesla state changed from *${previous_state}* to *${new_state}*`\n\t\telse\n\t\t\treturn;\n\t\tbreak;\n/*\tcase 'shift_state':\n\t if(msg.payload === \"\") return;\n msg.topic = `Tesla changed gear to ${msg.payload}`\n\t\tbreak;\n*/\n case 'is_user_present':\n\t\tprevious_state = flow.get('is_user_present');\n\t\tnew_state = msg.payload;\n\t\tflow.set('is_user_present',new_state);\n\n\t\tif(previous_state != new_state)\n\t\t\tmsg.topic = `Tesla driver is present: *${new_state}*`\n\t\telse return;\n\t\tbreak;\n case 'time_to_full_charge':\n\t\tvar previous = flow.get('time_to_full_charge');\n if(flow.get('state') != 'charging' || \n previous.timeleft === msg.payload){\n node.warn('returned from 1 ' + flow.get('state'))\n return;\n }\n var now = Math.round(Date.now()/1000);\n\t\tvar current = { timestamp: now, timeleft: msg.payload};\n var remaining = msg.payload.split('.');\n var minutes = Math.ceil(60 * Number(\".\" + remaining[1]));\n \n // If more than 1 hour remaining, print max 1 time per 15 minutes\n if(remaining[0] > 0 && (now - previous.timestamp)/60 <= 15)\n return;\n // If less than 1 hour remaining, print max 1 time per 5 minutes\n if(remaining[0] == 0 && (now - previous.timestamp)/60 < 5)\n return;\n \n var how_long = \"\";\n if(remaining[0] > 0){\n how_long = `${remaining[0]} hours ${minutes} minutes`\n } else {\n how_long = `${minutes} minutes`\n }\n\t\tflow.set('time_to_full_charge',current);\n\t\tmsg.topic = `Time to full charge: *${how_long}*`;\n\t\tbreak;\n\tdefault:\n\t\treturn;\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "// Code added here will be run once\n// whenever the node is deployed.\nflow.set('state','initial');\nflow.set('is_user_present','initial');\nflow.set('time_to_full_charge',{timestamp:0,timeleft:0});\n",
"finalize": "",
"libs": [],
"x": 340,
"y": 60,
"wires": [["8094b1d4.32f968"]]
}, {
"id": "cb1580bf.27615",
"type": "link in",
"z": "88633c3e.1b273",
"name": "process state changes",
"links": ["8491f8d1.1365f8", "4e84bee.72facc"],
"x": 175,
"y": 80,
"wires": [["5bf1c557.464144", "72d2c21.f2a2e3c"]]
}, {
"id": "72d2c21.f2a2e3c",
"type": "function",
"z": "88633c3e.1b273",
"name": "geofence",
"func": "/* show when car enters and leaves geofences */\nif(msg.topic != 'geofence') return;\n/* temporarly disable */\n//return;\n\nvar previous_fence = flow.get('geofence');\nvar new_fence = msg.payload;\nflow.set('geofence',new_fence);\nmsg.topic = '';\n\nif(previous_fence !== new_fence){\n if(previous_fence !== \"\")\n msg.topic = `Tesla left Geofence *${previous_fence}*`;\n if(new_fence !== \"\"){\n if(previous_fence !== \"\")\n msg.topic += ' and ';\n else\n msg.topic += 'Tesla ';\n msg.topic += `entered Geofence *${new_fence}*`;\n }\n} else return null;\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "// Code added here will be run once\n// whenever the node is deployed.\nflow.set('geofence','');",
"finalize": "",
"libs": [],
"x": 320,
"y": 100,
"wires": [["8094b1d4.32f968"]]
}, {
"id": "1da8704b.f78728",
"type": "inject",
"z": "88633c3e.1b273",
"name": "",
"props": [{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "Test from *Node\\-RED*",
"payloadType": "str",
"x": 210,
"y": 980,
"wires": [["a9c28815.0191c"]]
}, {
"id": "a9c28815.0191c",
"type": "function",
"z": "88633c3e.1b273",
"name": "create Bot message",
"func": "msg.payload = {\n chatId: '1596966476',\n type: 'message', \n content: msg.payload\n}\nmsg.payload.options = {parse_mode : \"MarkdownV2\"};\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 440,
"y": 980,
"wires": [["df6e0dc2.ac982"]]
}, {
"id": "8094b1d4.32f968",
"type": "function",
"z": "88633c3e.1b273",
"name": "Format message",
"func": "msg.payload = {\n chatId: '1596966476',\n type: 'message',\n content: msg.topic\n}\nmsg.payload.options = {parse_mode : \"MarkdownV2\"};\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 550,
"y": 80,
"wires": [["eb20a524.af3ba"]]
}, {
"id": "cc9cc37a.5f4a78",
"type": "calculator",
"z": "868e4cd7.9fc578",
"name": "",
"inputMsgField": "payload",
"outputMsgField": "payload",
"operation": "round",
"constant": "",
"round": false,
"decimals": 0,
"x": 700,
"y": 178,
"wires": [["ce57ad0f.660c1"]]
}, {
"id": "7b0d22a9.903e24",
"type": "simpletime",
"z": "868e4cd7.9fc578",
"name": "",
"mydate": true,
"myymd": true,
"myyear": true,
"mymonth": true,
"mymonthn": true,
"mydom": true,
"mydoy": true,
"myday": true,
"myhourpm": true,
"myhour": true,
"mytime": true,
"mytimes": true,
"myminute": true,
"myminutes": true,
"mysecond": true,
"mymillis": true,
"myepoch": true,
"myrawdate": true,
"mypm": true,
"x": 332,
"y": 380,
"wires": [["37af1984.d722ae"]]
}, {
"id": "366025db.df598a",
"type": "ui_table",
"z": "868e4cd7.9fc578",
"group": "51eafba7.ffc054",
"name": "State Table",
"order": 3,
"width": "8",
"height": "16",
"columns": [{
"field": "key",
"title": "Parameter",
"width": "50%",
"align": "left",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
}, {
"field": "value",
"title": "Value",
"width": "45%",
"align": "right",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
}
],
"outputs": 0,
"cts": false,
"x": 890,
"y": 275,
"wires": []
}, {
"id": "efe44f3.124a93",
"type": "ui_text",
"z": "868e4cd7.9fc578",
"group": "2cca3d1a.be10ba",
"order": 1,
"width": 0,
"height": 0,
"name": "Time to Full Charge",
"label": "Time to Full Charge",
"format": "{{msg.payload}}",
"layout": "row-spread",
"x": 1110,
"y": 260,
"wires": []
}, {
"id": "ad15c026.1d1d6",
"type": "ui_text",
"z": "868e4cd7.9fc578",
"group": "2cca3d1a.be10ba",
"order": 7,
"width": 0,
"height": 0,
"name": "",
"label": "Estimated battery range",
"format": "{{msg.payload}}",
"layout": "row-spread",
"x": 1070,
"y": 100,
"wires": []
}, {
"id": "6ab267e6.5e4b78",
"type": "ui_text",
"z": "868e4cd7.9fc578",
"group": "2cca3d1a.be10ba",
"order": 6,
"width": 0,
"height": 0,
"name": "",
"label": "Ideal battery range",
"format": "{{msg.payload}}",
"layout": "row-spread",
"x": 1050,
"y": 140,
"wires": []
}, {
"id": "37af1984.d722ae",
"type": "ui_text",
"z": "868e4cd7.9fc578",
"group": "51eafba7.ffc054",
"order": 1,
"width": 0,
"height": 0,
"name": "",
"label": "Last update time",
"format": "{{msg.mydate}}, {{msg.mytime}}",
"layout": "row-spread",
"x": 500,
"y": 379,
"wires": []
}, {
"id": "2ec008a.2fb84f8",
"type": "ui_text",
"z": "868e4cd7.9fc578",
"group": "2cca3d1a.be10ba",
"order": 4,
"width": 0,
"height": 0,
"name": "charger_actual_current",
"label": "Charging current",
"format": "{{msg.payload}} amps",
"layout": "row-spread",
"x": 1070,
"y": 180,
"wires": []
}, {
"id": "23ca2a1b.ce5fce",
"type": "ui_text",
"z": "868e4cd7.9fc578",
"group": "2cca3d1a.be10ba",
"order": 5,
"width": 0,
"height": 0,
"name": "charger_power",
"label": "Charging power",
"format": "{{msg.payload}} kw",
"layout": "row-spread",
"x": 1050,
"y": 220,
"wires": []
}, {
"id": "3db79d48.c4c322",
"type": "ui_gauge",
"z": "868e4cd7.9fc578",
"name": "Battery Level",
"group": "2cca3d1a.be10ba",
"order": 3,
"width": "0",
"height": "0",
"gtype": "gage",
"title": "Battery Level",
"label": "%",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": ["#ffff09", "#00ff00", "#ff8000"],
"seg1": "20",
"seg2": "80",
"x": 690,
"y": 133,
"wires": []
}, {
"id": "dda683ce.b5166",
"type": "ui_gauge",
"z": "868e4cd7.9fc578",
"name": "",
"group": "2cca3d1a.be10ba",
"order": 2,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Charge Limit",
"label": "%",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": ["#000000", "#000000", "#000000"],
"seg1": "",
"seg2": "",
"x": 691,
"y": 91,
"wires": []
}, {
"id": "dea66ad4.245ca",
"type": "e-mail",
"z": "88633c3e.1b273",
"server": "mail.bloggs.com",
"port": "465",
"secure": true,
"tls": true,
"name": "joe@bloggs.com",
"dname": "Email",
"x": 710,
"y": 140,
"wires": []
}, {
"id": "df6e0dc2.ac982",
"type": "telegram sender",
"z": "88633c3e.1b273",
"name": "Test sender",
"bot": "ed12f72a.03fad",
"haserroroutput": false,
"outputs": 1,
"x": 630,
"y": 980,
"wires": [[]]
}, {
"id": "eb20a524.af3ba",
"type": "telegram sender",
"z": "88633c3e.1b273",
"name": "Status messages",
"bot": "ed12f72a.03fad",
"haserroroutput": false,
"outputs": 1,
"x": 750,
"y": 80,
"wires": [[]]
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

+5 -1
View File
@@ -29,7 +29,11 @@ module.exports = {
{
type: "category",
label: "Integrations",
items: ["integrations/home_assistant", "integrations/mqtt"],
items: [
"integrations/home_assistant",
"integrations/mqtt",
"integrations/Node-RED",
],
},
{
type: "category",