├── hivemq-mqtt-web-client └── config.js ├── ruuvitag-demo-diagram.png ├── ruuvitag-demo-grafana.png ├── ruuvitag-demo-node-red.png ├── mosquitto └── config │ └── mosquitto.conf ├── .gitignore ├── grafana ├── provisioning │ ├── dashboards │ │ └── local.yml │ └── datasources │ │ └── influxdb.yml └── dashboards │ └── ruuvitag.json ├── bt-mqtt-gateway └── config.yaml ├── LICENSE ├── docker-compose.yml ├── docker-compose-epaper.yml ├── README.md ├── ruuvitag-demo-diagram.dot ├── telegraf └── telegraf.conf └── node-red ├── settings.js └── flows.json /hivemq-mqtt-web-client/config.js: -------------------------------------------------------------------------------- 1 | websocketserver = 'localhost'; 2 | websocketport = 9001; 3 | -------------------------------------------------------------------------------- /ruuvitag-demo-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenvervloesem/ruuvitag-demo/HEAD/ruuvitag-demo-diagram.png -------------------------------------------------------------------------------- /ruuvitag-demo-grafana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenvervloesem/ruuvitag-demo/HEAD/ruuvitag-demo-grafana.png -------------------------------------------------------------------------------- /ruuvitag-demo-node-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenvervloesem/ruuvitag-demo/HEAD/ruuvitag-demo-node-red.png -------------------------------------------------------------------------------- /mosquitto/config/mosquitto.conf: -------------------------------------------------------------------------------- 1 | port 1883 2 | listener 9001 3 | protocol websockets 4 | persistence true 5 | persistence_location /mosquitto/data/ 6 | log_dest file /mosquitto/log/mosquitto.log 7 | allow_anonymous true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | grafana/var/grafana.db 2 | grafana/var/plugins 3 | grafana/var/png 4 | influxdb/ 5 | node-red/.config.json 6 | node-red/.config.json.backup 7 | node-red/.flows.json.backup 8 | node-red/.sessions.json 9 | node-red/flows_cred.json 10 | node-red/package.json 11 | node-red/settings.js 12 | -------------------------------------------------------------------------------- /grafana/provisioning/dashboards/local.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'default' 5 | orgId: 1 6 | folder: '' 7 | type: file 8 | disableDeletion: false 9 | updateIntervalSeconds: 10 # How often Grafana scans for changed dashboards 10 | options: 11 | path: /var/lib/grafana/dashboards 12 | -------------------------------------------------------------------------------- /grafana/provisioning/datasources/influxdb.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: "InfluxDB" 5 | orgId: 1 6 | id: 1 7 | type: "influxdb" 8 | access: "proxy" 9 | typeLogoUrl: "public/app/plugins/datasource/influxdb/img/influxdb_logo.svg" 10 | url: "http://influxdb:8086" 11 | password: "" 12 | user: "" 13 | database: "telegraf" 14 | basicAuth: false 15 | isDefault: true 16 | readOnly: false 17 | -------------------------------------------------------------------------------- /bt-mqtt-gateway/config.yaml: -------------------------------------------------------------------------------- 1 | mqtt: 2 | host: localhost 3 | port: 1883 4 | topic_prefix: bt-mqtt-gateway 5 | client_id: bt-mqtt-gateway 6 | availability_topic: availability 7 | 8 | manager: 9 | command_timeout: 30 10 | workers: 11 | ruuvitag: 12 | args: 13 | devices: 14 | tag1: F9:DA:D2:0D:62:24 15 | tag2: C8:03:24:74:7E:0E 16 | tag3: C5:98:17:63:C3:E3 17 | tag4: D8:58:5E:B8:ED:DF 18 | topic_prefix: ruuvitag 19 | update_interval: 1 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Koen Vervloesem 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | mosquitto: 5 | image: eclipse-mosquitto 6 | container_name: mosquitto 7 | restart: always 8 | volumes: 9 | - ./mosquitto:/mosquitto 10 | ports: 11 | - "1883:1883" 12 | - "9001:9001" 13 | hivemq-mqtt-web-client: 14 | image: koenvervloesem/hivemq-mqtt-web-client 15 | container_name: hivemq-mqtt-web-client 16 | restart: always 17 | volumes: 18 | - ./hivemq-mqtt-web-client/config.js:/var/www/html/config.js 19 | ports: 20 | - "8080:80" 21 | depends_on: 22 | - mosquitto 23 | bt-mqtt-gateway: 24 | image: zewelor/bt-mqtt-gateway 25 | container_name: bt-mqtt-gateway 26 | restart: always 27 | volumes: 28 | - ./bt-mqtt-gateway/config.yaml:/config.yaml 29 | # These capabilities are needed for Bluetooth 30 | cap_add: 31 | - NET_ADMIN 32 | - SYS_ADMIN 33 | - SYS_RESOURCE 34 | # The Docker host should have working Bluetooth 35 | network_mode: host 36 | depends_on: 37 | - mosquitto 38 | node-red: 39 | image: nodered/node-red 40 | container_name: node-red 41 | restart: always 42 | volumes: 43 | - ./node-red:/data 44 | ports: 45 | - "1880:1880" 46 | environment: 47 | - TZ=Europe/Brussels 48 | # Install node-red-dashboard before starting Node-RED 49 | entrypoint: /bin/bash 50 | command: ["-c", "npm install node-red-dashboard && npm start -- --userDir /data"] 51 | depends_on: 52 | - mosquitto 53 | telegraf: 54 | image: telegraf 55 | container_name: telegraf 56 | restart: always 57 | volumes: 58 | - ./telegraf:/etc/telegraf 59 | depends_on: 60 | - influxdb 61 | - mosquitto 62 | influxdb: 63 | image: influxdb 64 | container_name: influxdb 65 | restart: always 66 | volumes: 67 | - ./influxdb/var:/var/lib/influxdb 68 | ports: 69 | - "8086:8086" 70 | grafana: 71 | # Use :master until Grafana 6.5 is released because it has a fix for ARM 72 | # See https://github.com/grafana/grafana/issues/19585 73 | image: grafana/grafana:master 74 | container_name: grafana 75 | restart: always 76 | volumes: 77 | - ./grafana/dashboards:/var/lib/grafana/dashboards 78 | - ./grafana/provisioning:/etc/grafana/provisioning 79 | ports: 80 | - "3000:3000" 81 | user: "472" 82 | depends_on: 83 | - influxdb 84 | -------------------------------------------------------------------------------- /docker-compose-epaper.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | mosquitto: 5 | image: eclipse-mosquitto 6 | container_name: mosquitto 7 | restart: always 8 | volumes: 9 | - ./mosquitto:/mosquitto 10 | ports: 11 | - "1883:1883" 12 | - "9001:9001" 13 | hivemq-mqtt-web-client: 14 | image: koenvervloesem/hivemq-mqtt-web-client 15 | container_name: hivemq-mqtt-web-client 16 | restart: always 17 | volumes: 18 | - ./hivemq-mqtt-web-client/config.js:/var/www/html/config.js 19 | ports: 20 | - "8080:80" 21 | depends_on: 22 | - mosquitto 23 | bt-mqtt-gateway: 24 | image: zewelor/bt-mqtt-gateway 25 | container_name: bt-mqtt-gateway 26 | restart: always 27 | volumes: 28 | - ./bt-mqtt-gateway:/config 29 | # These capabilities are needed for Bluetooth 30 | cap_add: 31 | - NET_ADMIN 32 | - SYS_ADMIN 33 | # The Docker host should have working Bluetooth 34 | network_mode: host 35 | depends_on: 36 | - mosquitto 37 | ruuvitag-epaper: 38 | image: ruuvitag-epaper 39 | container_name: ruuvitag-epaper 40 | restart: always 41 | # Needed for proper timezone handling 42 | volumes: 43 | - /etc/localtime:/etc/localtime 44 | network_mode: host 45 | depends_on: 46 | - mosquitto 47 | # Should be privileged to get GPIO and SPI access 48 | privileged: true 49 | node-red: 50 | image: nodered/node-red 51 | container_name: node-red 52 | restart: always 53 | volumes: 54 | - ./node-red:/data 55 | ports: 56 | - "1880:1880" 57 | environment: 58 | - TZ=Europe/Brussels 59 | # Install node-red-dashboard before starting Node-RED 60 | entrypoint: /bin/bash 61 | command: ["-c", "npm install node-red-dashboard && npm start -- --userDir /data"] 62 | depends_on: 63 | - mosquitto 64 | telegraf: 65 | image: telegraf 66 | container_name: telegraf 67 | restart: always 68 | volumes: 69 | - ./telegraf:/etc/telegraf 70 | depends_on: 71 | - influxdb 72 | - mosquitto 73 | influxdb: 74 | image: influxdb 75 | container_name: influxdb 76 | restart: always 77 | volumes: 78 | - ./influxdb/var:/var/lib/influxdb 79 | ports: 80 | - "8086:8086" 81 | grafana: 82 | # Use :master until Grafana 6.5 is released because it has a fix for ARM 83 | # See https://github.com/grafana/grafana/issues/19585 84 | image: grafana/grafana:master 85 | container_name: grafana 86 | restart: always 87 | volumes: 88 | - ./grafana/dashboards:/var/lib/grafana/dashboards 89 | - ./grafana/provisioning:/etc/grafana/provisioning 90 | ports: 91 | - "3000:3000" 92 | user: "472" 93 | depends_on: 94 | - influxdb 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RuuviTag Demo 2 | 3 | This is a demo of reading Bluetooth Low Energy sensor measurements of RuuviTag environmental sensors and feeding them to MQTT, a database and dashboards. 4 | 5 | This project is not affiliated to the [Ruuvi](https://ruuvi.com/) company in any way. 6 | 7 | ## Screenshots 8 | 9 | Grafana dashboard: 10 | 11 | ![Grafana dashboard](ruuvitag-demo-grafana.png) 12 | 13 | Node-RED dashboard: 14 | 15 | ![Node-RED dashboard](ruuvitag-demo-node-red.png) 16 | 17 | ## System requirements 18 | You need a Linux system with Bluetooth Low Energy (BLE) adapter, so at least Bluetooth 4.0. 19 | 20 | The demo uses Docker, so it has to be installed. 21 | 22 | This demo has been tested on: 23 | 24 | * Raspbian Buster Lite (on a Raspberry Pi 3B) 25 | * Ubuntu Desktop 19.10 26 | 27 | All instructions assume the first configuration. It should run on other Linux systems with minor adjustments, though. 28 | 29 | ### Checking your Bluetooth adapter 30 | Your system should have a Bluetooth Low Energy adapter, as is available in all recent Raspberry Pi models. You can verify this with: 31 | 32 | ```shell 33 | hciconfig -a 34 | ``` 35 | 36 | This should show a device **hci0** as **UP RUNNING** and the **LMP Version** should be at least 4.0. 37 | 38 | ### Installing Docker and Docker Compose 39 | Docker can be installed with: 40 | 41 | ```shel 42 | curl -sSL https://get.docker.com | sh 43 | ``` 44 | 45 | And give the `pi` user access to Docker by adding it to the `docker` group: 46 | 47 | ```shell 48 | sudo usermod pi -aG docker 49 | ``` 50 | 51 | Log out and then log in again, so the group permissions are applied to your session. 52 | 53 | Then install Python's pip package manager: 54 | 55 | ```shell 56 | sudo apt install python3-pip 57 | ``` 58 | 59 | And install Docker Compose: 60 | 61 | ```shell 62 | sudo pip3 install docker-compose 63 | ``` 64 | 65 | ## Installation 66 | Clone the repository (you may have to `sudo apt install git` first) and enter the directory: 67 | 68 | ```shell 69 | git clone https://github.com/koenvervloesem/ruuvitag-demo.git 70 | cd ruuvitag-demo 71 | ``` 72 | 73 | Change the owner of the `grafana` directory: 74 | 75 | ```shell 76 | sudo chown -R 472:472 grafana 77 | ``` 78 | 79 | ## Configuration 80 | Add the MAC addresses of your RuuviTag sensors to the `bt-mqtt-gateway/config.yaml` file. You can find these by scanning for Bluetooth Low Energy devices in your neighborhood: 81 | 82 | ```shell 83 | sudo hcitool lescan 84 | ``` 85 | 86 | Or you can run the Ruuvi Station app [on Android](https://github.com/ruuvi/com.ruuvi.station) or [on iOS](https://github.com/ruuvi/com.ruuvi.station.ios) and have a look at the MAC address in the tag settings of each RuuviTag. 87 | 88 | The Node-RED flow and Grafana dashboard suppose that you have four tags, called `tag1`, `tag2`, `tag3` and `tag4`. So I suggest that initially you leave these names in `bt-mqtt-gateway/config.yaml`. After starting up the demo, you can always change the configuration. 89 | 90 | ## Starting the demo 91 | Starting the demo is easy, as it's using Docker Compose: 92 | 93 | ```shell 94 | docker-compose up -d 95 | ``` 96 | 97 | This starts seven Docker containers: 98 | 99 | * [bt-mqtt-gateway](https://github.com/zewelor/bt-mqtt-gateway): Reads RuuviTag sensor measurements using Bluetooth Low Energy and forwards them to a MQTT broker. 100 | * [Mosquitto](https://mosquitto.org/): Receives the MQTT messages from bt-mqtt-gateway and relays them to anyone who is interested. 101 | * [HiveMQ MQTT Web Client](https://github.com/hivemq/hivemq-mqtt-web-client): Connects to Mosquitto and shows you the MQTT messages in your web browser using WebSockets. 102 | * [Node-RED](https://nodered.org/): Subscribes to the MQTT messages from Mosquitto and shows the values in a dashboard. 103 | * [Telegraf](https://www.influxdata.com/time-series-platform/telegraf/): Collects MQTT messages from Mosquitto and sends the values to InfluxDB. 104 | * [InfluxDB](https://www.influxdata.com/): Stores all the values of the RuuviTag measurements in a time-series database. 105 | * [Grafana](https://grafana.com/): Shows the values of the InfluxDB database in a dashboard. 106 | 107 | You have access to: 108 | 109 | * The MQTT web client on http://localhost:8080 110 | * The Node-RED flow on http://localhost:1880 111 | * The Node-RED dashboard on http://localhost:1880/ui 112 | * The Grafana dashboard on http://localhost:3000 113 | 114 | ## Extra demo: e-Paper HAT 115 | If you have a Waveshare 2.7 inch three-colour e-Paper HAT, you can use this demo in combination with the [RuuviTag ePaper](https://github.com/koenvervloesem/ruuvitag-epaper) project on a Raspberry Pi: 116 | 117 | * Build the Docker container of that project. 118 | * Start this container together with the containers of the RuuviTag Demo: `docker-compose up -f docker-compose-epaper.yml up -d`. 119 | 120 | This shows the temperature and humidity measurements of the four configured RuuviTag sensors on the display, as well as the date, time and IP address. The latter is a nice way to know which IP address you have to log into to access the dashboards. 121 | 122 | ## Security 123 | This is purely a demo of how you can process RuuviTag sensor measurements, so there are no special security measures such as encryption, and minimal authentication and user permissions. Only use this demo for evaluation purposes. 124 | 125 | The following default passwords are configured after installation: 126 | 127 | * Node-RED dashboard: username **admin** and password **password**. To change this, run `docker exec -ti node-red /usr/local/bin/node -e "console.log(require('bcryptjs').hashSync(process.argv[1], 8));" your-password-here` with your new password instead of `your-password-here` and paste the output string in the line `password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",` after the line `username: "admin",` in the file `node-red/settings.js`. After this, restart Node-RED with `docker-compose restart node-red`. 128 | * Grafana: username **admin** and password **admin**. After the first login, you're asked to choose another password. 129 | 130 | If you want to know more about securing Mosquitto and Node-RED, please consult my book [Control Your Home with Raspberry Pi: Secure, Modular, Open-Source and Self-Sufficient](https://koen.vervloesem.eu/books/control-your-home-with-raspberry-pi/) and the accompanying GitHub repository [koenvervloesem/raspberry-pi-home-automation](https://github.com/koenvervloesem/raspberry-pi-home-automation). 131 | 132 | ## Stopping the demo 133 | If you want to stop the demo, just run: 134 | 135 | ```shell 136 | docker-compose down 137 | ``` 138 | 139 | If you run the extra demo with the e-Paper HAT, stop the demo like this: 140 | 141 | ```shell 142 | docker-compose -f docker-compose-epaper.yml down 143 | ``` 144 | 145 | ## Architecture 146 | 147 | This is the architecture of the demo: 148 | 149 | ![Demo architecture diagram](ruuvitag-demo-diagram.png) 150 | 151 | This diagram shows two instances of `bt-mqtt-gateway`. You can use as many as you want, installed on Raspberry Pis or other devices positioned in multiple places for the best Bluetooth coverage of all sensors. 152 | 153 | While the demo installs all components on one device, you can distribute them over various devices. For instance, bt-mqtt-gateway on various receiver devices, ruuvitag-epaper on a Raspberry Pi with the Waveshare e-Paper HAT, HiveMQ MQTT Web Client on a developer laptop, and Mosquitto, Node-RED and Telegraf/InfluxDB/Grafana on a Linux server. 154 | 155 | ## License 156 | This program is provided by [Koen Vervloesem](mailto:koen@vervloesem.eu) as open source software with the MIT license. See the LICENSE file for more information. 157 | -------------------------------------------------------------------------------- /ruuvitag-demo-diagram.dot: -------------------------------------------------------------------------------- 1 | digraph demo { 2 | compound=true 3 | node [shape=folder style=filled fillcolor="#438dd5" 4 | fontcolor=white fontsize=12 fontname="Helvetica Bold"] 5 | graph [ranksep=1.5 rankdir=LR] 6 | edge [style=solid] 7 | 8 | // RuuviTags 9 | "RuuviTag 1" [shape=doublecircle fillcolor=white fontcolor=black] 10 | "RuuviTag 4" [shape=doublecircle fillcolor=white fontcolor=black] 11 | "RuuviTag 3" [shape=doublecircle fillcolor=white fontcolor=black] 12 | "RuuviTag 2" [shape=doublecircle fillcolor=white fontcolor=black] 13 | 14 | // First bt-mqtt-gateway receiving RuuviTag broadcasts 15 | "RuuviTag 1" -> "ruuvitag-sensor1" [ 16 | lhead="cluster_bt-mqtt-gateway1" 17 | style=dashed 18 | labeldistance=5 19 | labelangle=0 20 | label=< 21 | 22 | 23 | 24 | 25 |
BLE
26 | > 27 | ] 28 | 29 | "RuuviTag 2" -> "ruuvitag-sensor1" [ 30 | lhead="cluster_bt-mqtt-gateway1" 31 | style=dashed 32 | labeldistance=5 33 | labelangle=0 34 | label=< 35 | 36 | 37 | 38 | 39 |
BLE
40 | > 41 | ] 42 | 43 | "RuuviTag 3" -> "ruuvitag-sensor1" [ 44 | lhead="cluster_bt-mqtt-gateway1" 45 | style=dashed 46 | labeldistance=5 47 | labelangle=0 48 | label=< 49 | 50 | 51 | 52 | 53 |
BLE
54 | > 55 | ] 56 | 57 | "RuuviTag 4" -> "ruuvitag-sensor1" [ 58 | lhead="cluster_bt-mqtt-gateway1" 59 | style=dashed 60 | labeldistance=5 61 | labelangle=0 62 | label=< 63 | 64 | 65 | 66 | 67 |
BLE
68 | > 69 | ] 70 | 71 | subgraph "cluster_bt-mqtt-gateway1" { 72 | label="bt-mqtt-gateway" 73 | style=filled 74 | fillcolor="#438dd5" 75 | fontcolor=white 76 | "ruuvitag-sensor1" [label="ruuvitag-sensor" shape=tab fillcolor="#399ba3"] 77 | } 78 | 79 | // Second bt-mqtt-gateway receiving RuuviTag broadcasts 80 | "RuuviTag 1" -> "ruuvitag-sensor2" [ 81 | lhead="cluster_bt-mqtt-gateway2" 82 | style=dashed 83 | labeldistance=5 84 | labelangle=0 85 | label=< 86 | 87 | 88 | 89 | 90 |
BLE
91 | > 92 | ] 93 | 94 | "RuuviTag 2" -> "ruuvitag-sensor2" [ 95 | lhead="cluster_bt-mqtt-gateway2" 96 | style=dashed 97 | labeldistance=5 98 | labelangle=0 99 | label=< 100 | 101 | 102 | 103 | 104 |
BLE
105 | > 106 | ] 107 | 108 | "RuuviTag 3" -> "ruuvitag-sensor2" [ 109 | lhead="cluster_bt-mqtt-gateway2" 110 | style=dashed 111 | labeldistance=5 112 | labelangle=0 113 | label=< 114 | 115 | 116 | 117 | 118 |
BLE
119 | > 120 | ] 121 | 122 | "RuuviTag 4" -> "ruuvitag-sensor2" [ 123 | lhead="cluster_bt-mqtt-gateway2" 124 | style=dashed 125 | labeldistance=5 126 | labelangle=0 127 | label=< 128 | 129 | 130 | 131 | 132 |
BLE
133 | > 134 | ] 135 | 136 | subgraph "cluster_bt-mqtt-gateway2" { 137 | label="bt-mqtt-gateway" 138 | style=filled 139 | fillcolor="#438dd5" 140 | fontcolor=white 141 | "ruuvitag-sensor2" [label="ruuvitag-sensor" shape=tab fillcolor="#399ba3"] 142 | } 143 | 144 | // Mosquitto 145 | "ruuvitag-sensor1" -> Mosquitto [ 146 | ltail="cluster_bt-mqtt-gateway1" 147 | labeldistance=5 148 | labelangle=0 149 | label=< 150 | 151 | 152 | 153 | 154 |
MQTT
155 | > 156 | ] 157 | 158 | "ruuvitag-sensor2" -> Mosquitto [ 159 | ltail="cluster_bt-mqtt-gateway2" 160 | labeldistance=5 161 | labelangle=0 162 | label=< 163 | 164 | 165 | 166 | 167 |
MQTT
168 | > 169 | ] 170 | 171 | Mosquitto [shape=rect] 172 | 173 | // HiveMQT MQTT Web Client 174 | Mosquitto -> hivemq_web_client [ 175 | lhead=cluster_hivemq 176 | labeldistance=5 177 | labelangle=0 178 | label=< 179 | 180 | 181 | 182 | 183 |
WebSockets
184 | > 185 | ] 186 | 187 | subgraph cluster_hivemq { 188 | label="HiveMQ MQTT Web Client" 189 | style=filled 190 | fillcolor="#438dd5" 191 | fontcolor=white 192 | hivemq_web_client [label="Web client" fillcolor="#fcb338" fontcolor=black] 193 | } 194 | 195 | // ruuvitag-epaper 196 | Mosquitto -> epaper [ 197 | lhead=cluster_epaper 198 | labeldistance=5 199 | labelangle=0 200 | label=< 201 | 202 | 203 | 204 | 205 |
MQTT
206 | > 207 | ] 208 | 209 | subgraph cluster_epaper{ 210 | label="ruuvitag-epaper" 211 | style=filled 212 | fillcolor="#438dd5" 213 | fontcolor=white 214 | "Paho MQTT" [shape=tab fillcolor="#399ba3"] 215 | "epd-library-python" [shape=tab fillcolor="#399ba3"] 216 | epaper [label="Waveshare e-Paper HAT" fillcolor="#fcb338" fontcolor=black] 217 | } 218 | 219 | // Node-RED 220 | Mosquitto -> "Node-RED_flow" [ 221 | lhead="cluster_Node-RED" 222 | labeldistance=5 223 | labelangle=0 224 | label=< 225 | 226 | 227 | 228 | 229 |
MQTT
230 | > 231 | ] 232 | 233 | subgraph "cluster_Node-RED" { 234 | label="Node-RED" 235 | style=filled 236 | fillcolor="#438dd5" 237 | fontcolor=white 238 | "Node-RED_flow" [label="Flow" fillcolor="#fcb338" fontcolor=black] 239 | "Node-RED_dashboard" [label="Dashboard" fillcolor="#fcb338" fontcolor=black] 240 | } 241 | 242 | // Grafana 243 | Mosquitto -> Telegraf [ 244 | labeldistance=5 245 | labelangle=0 246 | label=< 247 | 248 | 249 | 250 | 251 |
MQTT
252 | > 253 | ] 254 | 255 | Telegraf [shape=rect] 256 | 257 | Telegraf -> InfluxDB [ 258 | labeldistance=5 259 | labelangle=0 260 | label=< 261 | 262 | 263 | 264 | 265 |
HTTP
266 | > 267 | ] 268 | 269 | InfluxDB [shape=cylinder] 270 | 271 | InfluxDB -> Grafana_dashboard [ 272 | lhead=cluster_Grafana 273 | labeldistance=5 274 | labelangle=0 275 | label=< 276 | 277 | 278 | 279 | 280 |
HTTP
281 | > 282 | ] 283 | 284 | subgraph cluster_Grafana { 285 | label="Grafana" 286 | style=filled 287 | fillcolor="#438dd5" 288 | fontcolor=white 289 | Grafana_dashboard [label="Dashboard" fillcolor="#fcb338" fontcolor=black] 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /telegraf/telegraf.conf: -------------------------------------------------------------------------------- 1 | # Configuration for telegraf agent 2 | [agent] 3 | ## Default data collection interval for all inputs 4 | interval = "10s" 5 | ## Rounds collection interval to 'interval' 6 | ## ie, if interval="10s" then always collect on :00, :10, :20, etc. 7 | round_interval = true 8 | 9 | ## Telegraf will send metrics to outputs in batches of at most 10 | ## metric_batch_size metrics. 11 | ## This controls the size of writes that Telegraf sends to output plugins. 12 | metric_batch_size = 1000 13 | 14 | ## Maximum number of unwritten metrics per output. 15 | metric_buffer_limit = 10000 16 | 17 | ## Collection jitter is used to jitter the collection by a random amount. 18 | ## Each plugin will sleep for a random time within jitter before collecting. 19 | ## This can be used to avoid many plugins querying things like sysfs at the 20 | ## same time, which can have a measurable effect on the system. 21 | collection_jitter = "0s" 22 | 23 | ## Default flushing interval for all outputs. Maximum flush_interval will be 24 | ## flush_interval + flush_jitter 25 | flush_interval = "10s" 26 | ## Jitter the flush interval by a random amount. This is primarily to avoid 27 | ## large write spikes for users running a large number of telegraf instances. 28 | ## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s 29 | flush_jitter = "0s" 30 | 31 | ## By default or when set to "0s", precision will be set to the same 32 | ## timestamp order as the collection interval, with the maximum being 1s. 33 | ## ie, when interval = "10s", precision will be "1s" 34 | ## when interval = "250ms", precision will be "1ms" 35 | ## Precision will NOT be used for service inputs. It is up to each individual 36 | ## service input to set the timestamp at the appropriate precision. 37 | ## Valid time units are "ns", "us" (or "µs"), "ms", "s". 38 | precision = "" 39 | 40 | ## Log at debug level. 41 | # debug = false 42 | ## Log only error level messages. 43 | # quiet = false 44 | 45 | ## Log file name, the empty string means to log to stderr. 46 | # logfile = "" 47 | 48 | ## The logfile will be rotated after the time interval specified. When set 49 | ## to 0 no time based rotation is performed. Logs are rotated only when 50 | ## written to, if there is no log activity rotation may be delayed. 51 | # logfile_rotation_interval = "0d" 52 | 53 | ## The logfile will be rotated when it becomes larger than the specified 54 | ## size. When set to 0 no size based rotation is performed. 55 | # logfile_rotation_max_size = "0MB" 56 | 57 | ## Maximum number of rotated archives to keep, any older logs are deleted. 58 | ## If set to -1, no archives are removed. 59 | # logfile_rotation_max_archives = 5 60 | 61 | ## Override default hostname, if empty use os.Hostname() 62 | hostname = "" 63 | ## If set to true, do no set the "host" tag in the telegraf agent. 64 | omit_hostname = false 65 | 66 | 67 | ############################################################################### 68 | # OUTPUT PLUGINS # 69 | ############################################################################### 70 | 71 | 72 | # Configuration for sending metrics to InfluxDB 73 | [[outputs.influxdb]] 74 | ## The full HTTP or UDP URL for your InfluxDB instance. 75 | ## 76 | ## Multiple URLs can be specified for a single cluster, only ONE of the 77 | ## urls will be written to each interval. 78 | # urls = ["unix:///var/run/influxdb.sock"] 79 | # urls = ["udp://127.0.0.1:8089"] 80 | # urls = ["http://127.0.0.1:8086"] 81 | urls = ["http://influxdb:8086"] 82 | 83 | ## The target database for metrics; will be created as needed. 84 | ## For UDP url endpoint database needs to be configured on server side. 85 | database = "telegraf" 86 | 87 | ## The value of this tag will be used to determine the database. If this 88 | ## tag is not set the 'database' option is used as the default. 89 | # database_tag = "" 90 | 91 | ## If true, the database tag will not be added to the metric. 92 | # exclude_database_tag = false 93 | 94 | ## If true, no CREATE DATABASE queries will be sent. Set to true when using 95 | ## Telegraf with a user without permissions to create databases or when the 96 | ## database already exists. 97 | # skip_database_creation = false 98 | 99 | ## Name of existing retention policy to write to. Empty string writes to 100 | ## the default retention policy. Only takes effect when using HTTP. 101 | # retention_policy = "" 102 | 103 | ## Write consistency (clusters only), can be: "any", "one", "quorum", "all". 104 | ## Only takes effect when using HTTP. 105 | # write_consistency = "any" 106 | 107 | ## Timeout for HTTP messages. 108 | # timeout = "5s" 109 | 110 | ## HTTP Basic Auth 111 | # username = "telegraf" 112 | # password = "metricsmetricsmetricsmetrics" 113 | 114 | ## HTTP User-Agent 115 | # user_agent = "telegraf" 116 | 117 | ## UDP payload size is the maximum packet size to send. 118 | # udp_payload = "512B" 119 | 120 | ## Optional TLS Config for use on HTTP connections. 121 | # tls_ca = "/etc/telegraf/ca.pem" 122 | # tls_cert = "/etc/telegraf/cert.pem" 123 | # tls_key = "/etc/telegraf/key.pem" 124 | ## Use TLS but skip chain & host verification 125 | # insecure_skip_verify = false 126 | 127 | ## HTTP Proxy override, if unset values the standard proxy environment 128 | ## variables are consulted to determine which proxy, if any, should be used. 129 | # http_proxy = "http://corporate.proxy:3128" 130 | 131 | ## Additional HTTP headers 132 | # http_headers = {"X-Special-Header" = "Special-Value"} 133 | 134 | ## HTTP Content-Encoding for write request body, can be set to "gzip" to 135 | ## compress body or "identity" to apply no encoding. 136 | # content_encoding = "identity" 137 | 138 | ## When true, Telegraf will output unsigned integers as unsigned values, 139 | ## i.e.: "42u". You will need a version of InfluxDB supporting unsigned 140 | ## integer values. Enabling this option will result in field type errors if 141 | ## existing data has been written. 142 | # influx_uint_support = false 143 | 144 | 145 | ############################################################################### 146 | # SERVICE INPUT PLUGINS # 147 | ############################################################################### 148 | 149 | 150 | # Read metrics from MQTT topic(s) 151 | [[inputs.mqtt_consumer]] 152 | ## MQTT broker URLs to be used. The format should be scheme://host:port, 153 | ## schema can be tcp, ssl, or ws. 154 | servers = ["tcp://mosquitto:1883"] 155 | 156 | ## Topics that will be subscribed to. 157 | topics = [ 158 | "bt-mqtt-gateway/ruuvitag/#", 159 | ] 160 | 161 | ## The message topic will be stored in a tag specified by this value. If set 162 | ## to the empty string no topic tag will be created. 163 | topic_tag = "topic" 164 | 165 | ## QoS policy for messages 166 | ## 0 = at most once 167 | ## 1 = at least once 168 | ## 2 = exactly once 169 | ## 170 | ## When using a QoS of 1 or 2, you should enable persistent_session to allow 171 | ## resuming unacknowledged messages. 172 | # qos = 0 173 | 174 | ## Connection timeout for initial connection in seconds 175 | # connection_timeout = "30s" 176 | 177 | ## Maximum messages to read from the broker that have not been written by an 178 | ## output. For best throughput set based on the number of metrics within 179 | ## each message and the size of the output's metric_batch_size. 180 | ## 181 | ## For example, if each message from the queue contains 10 metrics and the 182 | ## output metric_batch_size is 1000, setting this to 100 will ensure that a 183 | ## full batch is collected and the write is triggered immediately without 184 | ## waiting until the next flush_interval. 185 | # max_undelivered_messages = 1000 186 | 187 | ## Persistent session disables clearing of the client session on connection. 188 | ## In order for this option to work you must also set client_id to identity 189 | ## the client. To receive messages that arrived while the client is offline, 190 | ## also set the qos option to 1 or 2 and don't forget to also set the QoS when 191 | ## publishing. 192 | # persistent_session = false 193 | 194 | ## If unset, a random client ID will be generated. 195 | client_id = "telegraf" 196 | 197 | ## Username and password to connect MQTT server. 198 | # username = "telegraf" 199 | # password = "metricsmetricsmetricsmetrics" 200 | 201 | ## Optional TLS Config 202 | # tls_ca = "/etc/telegraf/ca.pem" 203 | # tls_cert = "/etc/telegraf/cert.pem" 204 | # tls_key = "/etc/telegraf/key.pem" 205 | ## Use TLS but skip chain & host verification 206 | # insecure_skip_verify = false 207 | 208 | ## Data format to consume. 209 | ## Each data format has its own unique set of configuration options, read 210 | ## more about them here: 211 | ## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md 212 | data_format = "value" 213 | data_type = "float" 214 | 215 | name_override = "ruuvitag" 216 | -------------------------------------------------------------------------------- /node-red/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright JS Foundation and other contributors, http://js.foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | // The `https` setting requires the `fs` module. Uncomment the following 18 | // to make it available: 19 | //var fs = require("fs"); 20 | 21 | module.exports = { 22 | // the tcp port that the Node-RED web server is listening on 23 | uiPort: process.env.PORT || 1880, 24 | 25 | // By default, the Node-RED UI accepts connections on all IPv4 interfaces. 26 | // To listen on all IPv6 addresses, set uiHost to "::", 27 | // The following property can be used to listen on a specific interface. For 28 | // example, the following would only allow connections from the local machine. 29 | //uiHost: "127.0.0.1", 30 | 31 | // Retry time in milliseconds for MQTT connections 32 | mqttReconnectTime: 15000, 33 | 34 | // Retry time in milliseconds for Serial port connections 35 | serialReconnectTime: 15000, 36 | 37 | // Retry time in milliseconds for TCP socket connections 38 | //socketReconnectTime: 10000, 39 | 40 | // Timeout in milliseconds for TCP server socket connections 41 | // defaults to no timeout 42 | //socketTimeout: 120000, 43 | 44 | // Maximum number of messages to wait in queue while attempting to connect to TCP socket 45 | // defaults to 1000 46 | //tcpMsgQueueSize: 2000, 47 | 48 | // Timeout in milliseconds for HTTP request connections 49 | // defaults to 120 seconds 50 | //httpRequestTimeout: 120000, 51 | 52 | // The maximum length, in characters, of any message sent to the debug sidebar tab 53 | debugMaxLength: 1000, 54 | 55 | // The maximum number of messages nodes will buffer internally as part of their 56 | // operation. This applies across a range of nodes that operate on message sequences. 57 | // defaults to no limit. A value of 0 also means no limit is applied. 58 | //nodeMessageBufferMaxLength: 0, 59 | 60 | // To disable the option for using local files for storing keys and certificates in the TLS configuration 61 | // node, set this to true 62 | //tlsConfigDisableLocalFiles: true, 63 | 64 | // Colourise the console output of the debug node 65 | //debugUseColors: true, 66 | 67 | // The file containing the flows. If not set, it defaults to flows_.json 68 | //flowFile: 'flows.json', 69 | 70 | // To enabled pretty-printing of the flow within the flow file, set the following 71 | // property to true: 72 | //flowFilePretty: true, 73 | 74 | // By default, credentials are encrypted in storage using a generated key. To 75 | // specify your own secret, set the following property. 76 | // If you want to disable encryption of credentials, set this property to false. 77 | // Note: once you set this property, do not change it - doing so will prevent 78 | // node-red from being able to decrypt your existing credentials and they will be 79 | // lost. 80 | //credentialSecret: "a-secret-key", 81 | 82 | // By default, all user data is stored in a directory called `.node-red` under 83 | // the user's home directory. To use a different location, the following 84 | // property can be used 85 | //userDir: '/home/nol/.node-red/', 86 | 87 | // Node-RED scans the `nodes` directory in the userDir to find local node files. 88 | // The following property can be used to specify an additional directory to scan. 89 | //nodesDir: '/home/nol/.node-red/nodes', 90 | 91 | // By default, the Node-RED UI is available at http://localhost:1880/ 92 | // The following property can be used to specify a different root path. 93 | // If set to false, this is disabled. 94 | //httpAdminRoot: '/admin', 95 | 96 | // Some nodes, such as HTTP In, can be used to listen for incoming http requests. 97 | // By default, these are served relative to '/'. The following property 98 | // can be used to specifiy a different root path. If set to false, this is 99 | // disabled. 100 | //httpNodeRoot: '/red-nodes', 101 | 102 | // The following property can be used in place of 'httpAdminRoot' and 'httpNodeRoot', 103 | // to apply the same root to both parts. 104 | //httpRoot: '/red', 105 | 106 | // When httpAdminRoot is used to move the UI to a different root path, the 107 | // following property can be used to identify a directory of static content 108 | // that should be served at http://localhost:1880/. 109 | //httpStatic: '/home/nol/node-red-static/', 110 | 111 | // The maximum size of HTTP request that will be accepted by the runtime api. 112 | // Default: 5mb 113 | //apiMaxLength: '5mb', 114 | 115 | // If you installed the optional node-red-dashboard you can set it's path 116 | // relative to httpRoot 117 | //ui: { path: "ui" }, 118 | 119 | // Securing Node-RED 120 | // ----------------- 121 | // To password protect the Node-RED editor and admin API, the following 122 | // property can be used. See http://nodered.org/docs/security.html for details. 123 | adminAuth: { 124 | type: "credentials", 125 | users: [{ 126 | username: "admin", 127 | password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.", 128 | permissions: "*" 129 | }] 130 | }, 131 | 132 | // To password protect the node-defined HTTP endpoints (httpNodeRoot), or 133 | // the static content (httpStatic), the following properties can be used. 134 | // The pass field is a bcrypt hash of the password. 135 | // See http://nodered.org/docs/security.html#generating-the-password-hash 136 | //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, 137 | //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, 138 | 139 | // The following property can be used to enable HTTPS 140 | // See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener 141 | // for details on its contents. 142 | // See the comment at the top of this file on how to load the `fs` module used by 143 | // this setting. 144 | // 145 | //https: { 146 | // key: fs.readFileSync('privatekey.pem'), 147 | // cert: fs.readFileSync('certificate.pem') 148 | //}, 149 | 150 | // The following property can be used to cause insecure HTTP connections to 151 | // be redirected to HTTPS. 152 | //requireHttps: true, 153 | 154 | // The following property can be used to disable the editor. The admin API 155 | // is not affected by this option. To disable both the editor and the admin 156 | // API, use either the httpRoot or httpAdminRoot properties 157 | //disableEditor: false, 158 | 159 | // The following property can be used to configure cross-origin resource sharing 160 | // in the HTTP nodes. 161 | // See https://github.com/troygoode/node-cors#configuration-options for 162 | // details on its contents. The following is a basic permissive set of options: 163 | //httpNodeCors: { 164 | // origin: "*", 165 | // methods: "GET,PUT,POST,DELETE" 166 | //}, 167 | 168 | // If you need to set an http proxy please set an environment variable 169 | // called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system. 170 | // For example - http_proxy=http://myproxy.com:8080 171 | // (Setting it here will have no effect) 172 | // You may also specify no_proxy (or NO_PROXY) to supply a comma separated 173 | // list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk 174 | 175 | // The following property can be used to add a custom middleware function 176 | // in front of all http in nodes. This allows custom authentication to be 177 | // applied to all http in nodes, or any other sort of common request processing. 178 | //httpNodeMiddleware: function(req,res,next) { 179 | // // Handle/reject the request, or pass it on to the http in node by calling next(); 180 | // // Optionally skip our rawBodyParser by setting this to true; 181 | // //req.skipRawBodyParser = true; 182 | // next(); 183 | //}, 184 | 185 | // The following property can be used to pass custom options to the Express.js 186 | // server used by Node-RED. For a full list of available options, refer 187 | // to http://expressjs.com/en/api.html#app.settings.table 188 | //httpServerOptions: { }, 189 | 190 | // The following property can be used to verify websocket connection attempts. 191 | // This allows, for example, the HTTP request headers to be checked to ensure 192 | // they include valid authentication information. 193 | //webSocketNodeVerifyClient: function(info) { 194 | // // 'info' has three properties: 195 | // // - origin : the value in the Origin header 196 | // // - req : the HTTP request 197 | // // - secure : true if req.connection.authorized or req.connection.encrypted is set 198 | // // 199 | // // The function should return true if the connection should be accepted, false otherwise. 200 | // // 201 | // // Alternatively, if this function is defined to accept a second argument, callback, 202 | // // it can be used to verify the client asynchronously. 203 | // // The callback takes three arguments: 204 | // // - result : boolean, whether to accept the connection or not 205 | // // - code : if result is false, the HTTP error status to return 206 | // // - reason: if result is false, the HTTP reason string to return 207 | //}, 208 | 209 | // The following property can be used to seed Global Context with predefined 210 | // values. This allows extra node modules to be made available with the 211 | // Function node. 212 | // For example, 213 | // functionGlobalContext: { os:require('os') } 214 | // can be accessed in a function block as: 215 | // global.get("os") 216 | functionGlobalContext: { 217 | // os:require('os'), 218 | // jfive:require("johnny-five"), 219 | // j5board:require("johnny-five").Board({repl:false}) 220 | }, 221 | // `global.keys()` returns a list of all properties set in global context. 222 | // This allows them to be displayed in the Context Sidebar within the editor. 223 | // In some circumstances it is not desirable to expose them to the editor. The 224 | // following property can be used to hide any property set in `functionGlobalContext` 225 | // from being list by `global.keys()`. 226 | // By default, the property is set to false to avoid accidental exposure of 227 | // their values. Setting this to true will cause the keys to be listed. 228 | exportGlobalContextKeys: false, 229 | 230 | 231 | // Context Storage 232 | // The following property can be used to enable context storage. The configuration 233 | // provided here will enable file-based context that flushes to disk every 30 seconds. 234 | // Refer to the documentation for further options: https://nodered.org/docs/api/context/ 235 | // 236 | //contextStorage: { 237 | // default: { 238 | // module:"localfilesystem" 239 | // }, 240 | //}, 241 | 242 | // The following property can be used to order the categories in the editor 243 | // palette. If a node's category is not in the list, the category will get 244 | // added to the end of the palette. 245 | // If not set, the following default order is used: 246 | //paletteCategories: ['subflows','flow','input','output','function','parser','social','mobile','storage','analysis','advanced'], 247 | 248 | // Configure the logging output 249 | logging: { 250 | // Only console logging is currently supported 251 | console: { 252 | // Level of logging to be recorded. Options are: 253 | // fatal - only those errors which make the application unusable should be recorded 254 | // error - record errors which are deemed fatal for a particular request + fatal errors 255 | // warn - record problems which are non fatal + errors + fatal errors 256 | // info - record information about the general running of the application + warn + error + fatal errors 257 | // debug - record information which is more verbose than info + info + warn + error + fatal errors 258 | // trace - record very detailed logging + debug + info + warn + error + fatal errors 259 | // off - turn off all logging (doesn't affect metrics or audit) 260 | level: "info", 261 | // Whether or not to include metric events in the log output 262 | metrics: false, 263 | // Whether or not to include audit events in the log output 264 | audit: false 265 | } 266 | }, 267 | 268 | // Customising the editor 269 | editorTheme: { 270 | projects: { 271 | // To enable the Projects feature, set this value to true 272 | enabled: false 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /node-red/flows.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "eaed8fa5.46ccc8", 4 | "type": "tab", 5 | "label": "RuuviTag Demo", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "d1ce6ba9.c90f2", 11 | "type": "mqtt-broker", 12 | "z": "", 13 | "name": "mosquitto", 14 | "broker": "mosquitto", 15 | "port": "1883", 16 | "clientid": "node-red", 17 | "usetls": false, 18 | "compatmode": false, 19 | "keepalive": "60", 20 | "cleansession": true, 21 | "birthTopic": "", 22 | "birthQos": "0", 23 | "birthPayload": "", 24 | "closeTopic": "", 25 | "closeQos": "0", 26 | "closePayload": "", 27 | "willTopic": "", 28 | "willQos": "0", 29 | "willPayload": "" 30 | }, 31 | { 32 | "id": "58b18a79.6e4324", 33 | "type": "ui_tab", 34 | "z": "", 35 | "name": "RuuviTag demo", 36 | "icon": "dashboard", 37 | "disabled": false, 38 | "hidden": false 39 | }, 40 | { 41 | "id": "4f008f1d.e39768", 42 | "type": "ui_group", 43 | "z": "", 44 | "name": "RuuviTag 1", 45 | "tab": "58b18a79.6e4324", 46 | "disp": true, 47 | "width": "6", 48 | "collapse": false 49 | }, 50 | { 51 | "id": "2b14eefe.3ed422", 52 | "type": "ui_base", 53 | "theme": { 54 | "name": "theme-light", 55 | "lightTheme": { 56 | "default": "#0094CE", 57 | "baseColor": "#0094CE", 58 | "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", 59 | "edited": true, 60 | "reset": false 61 | }, 62 | "darkTheme": { 63 | "default": "#097479", 64 | "baseColor": "#097479", 65 | "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", 66 | "edited": false 67 | }, 68 | "customTheme": { 69 | "name": "Untitled Theme 1", 70 | "default": "#4B7930", 71 | "baseColor": "#4B7930", 72 | "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" 73 | }, 74 | "themeState": { 75 | "base-color": { 76 | "default": "#0094CE", 77 | "value": "#0094CE", 78 | "edited": false 79 | }, 80 | "page-titlebar-backgroundColor": { 81 | "value": "#0094CE", 82 | "edited": false 83 | }, 84 | "page-backgroundColor": { 85 | "value": "#fafafa", 86 | "edited": false 87 | }, 88 | "page-sidebar-backgroundColor": { 89 | "value": "#ffffff", 90 | "edited": false 91 | }, 92 | "group-textColor": { 93 | "value": "#1bbfff", 94 | "edited": false 95 | }, 96 | "group-borderColor": { 97 | "value": "#ffffff", 98 | "edited": false 99 | }, 100 | "group-backgroundColor": { 101 | "value": "#ffffff", 102 | "edited": false 103 | }, 104 | "widget-textColor": { 105 | "value": "#111111", 106 | "edited": false 107 | }, 108 | "widget-backgroundColor": { 109 | "value": "#0094ce", 110 | "edited": false 111 | }, 112 | "widget-borderColor": { 113 | "value": "#ffffff", 114 | "edited": false 115 | }, 116 | "base-font": { 117 | "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" 118 | } 119 | }, 120 | "angularTheme": { 121 | "primary": "indigo", 122 | "accents": "blue", 123 | "warn": "red", 124 | "background": "grey" 125 | } 126 | }, 127 | "site": { 128 | "name": "Node-RED Dashboard", 129 | "hideToolbar": "false", 130 | "allowSwipe": "false", 131 | "lockMenu": "false", 132 | "allowTempTheme": "true", 133 | "dateFormat": "DD/MM/YYYY", 134 | "sizes": { 135 | "sx": 48, 136 | "sy": 48, 137 | "gx": 6, 138 | "gy": 6, 139 | "cx": 6, 140 | "cy": 6, 141 | "px": 0, 142 | "py": 0 143 | } 144 | } 145 | }, 146 | { 147 | "id": "7e2d42c6.4b1d84", 148 | "type": "ui_group", 149 | "z": "", 150 | "name": "RuuviTag 2", 151 | "tab": "58b18a79.6e4324", 152 | "disp": true, 153 | "width": "6", 154 | "collapse": false 155 | }, 156 | { 157 | "id": "c13387c.ae43bf8", 158 | "type": "ui_group", 159 | "z": "", 160 | "name": "RuuviTag 3", 161 | "tab": "58b18a79.6e4324", 162 | "disp": true, 163 | "width": "6", 164 | "collapse": false 165 | }, 166 | { 167 | "id": "30b8e062.37b95", 168 | "type": "ui_group", 169 | "z": "", 170 | "name": "RuuviTag 4", 171 | "tab": "58b18a79.6e4324", 172 | "disp": true, 173 | "width": "6", 174 | "collapse": false 175 | }, 176 | { 177 | "id": "f1499505.163d08", 178 | "type": "mqtt in", 179 | "z": "eaed8fa5.46ccc8", 180 | "name": "", 181 | "topic": "bt-mqtt-gateway/ruuvitag/tag1/temperature", 182 | "qos": "2", 183 | "datatype": "auto", 184 | "broker": "d1ce6ba9.c90f2", 185 | "x": 350, 186 | "y": 40, 187 | "wires": [ 188 | [ 189 | "3ed4e927.0d7556" 190 | ] 191 | ] 192 | }, 193 | { 194 | "id": "3ed4e927.0d7556", 195 | "type": "ui_gauge", 196 | "z": "eaed8fa5.46ccc8", 197 | "name": "RuuviTag 1 temperature", 198 | "group": "4f008f1d.e39768", 199 | "order": 0, 200 | "width": 0, 201 | "height": 0, 202 | "gtype": "gage", 203 | "title": "Temperature", 204 | "label": "\u00b0C", 205 | "format": "{{value}}", 206 | "min": "-40", 207 | "max": "85", 208 | "colors": [ 209 | "#00b500", 210 | "#e6e600", 211 | "#ca3838" 212 | ], 213 | "seg1": "", 214 | "seg2": "", 215 | "x": 910, 216 | "y": 40, 217 | "wires": [] 218 | }, 219 | { 220 | "id": "e95f5697.571e", 221 | "type": "mqtt in", 222 | "z": "eaed8fa5.46ccc8", 223 | "name": "", 224 | "topic": "bt-mqtt-gateway/ruuvitag/tag2/temperature", 225 | "qos": "2", 226 | "datatype": "auto", 227 | "broker": "d1ce6ba9.c90f2", 228 | "x": 350, 229 | "y": 440, 230 | "wires": [ 231 | [ 232 | "863e1b87.45c8c" 233 | ] 234 | ] 235 | }, 236 | { 237 | "id": "863e1b87.45c8c", 238 | "type": "ui_gauge", 239 | "z": "eaed8fa5.46ccc8", 240 | "name": "RuuviTag 2 temperature", 241 | "group": "7e2d42c6.4b1d84", 242 | "order": 0, 243 | "width": 0, 244 | "height": 0, 245 | "gtype": "gage", 246 | "title": "Temperature", 247 | "label": "\u00b0C", 248 | "format": "{{value}}", 249 | "min": "-40", 250 | "max": "85", 251 | "colors": [ 252 | "#00b500", 253 | "#e6e600", 254 | "#ca3838" 255 | ], 256 | "seg1": "", 257 | "seg2": "", 258 | "x": 930, 259 | "y": 440, 260 | "wires": [] 261 | }, 262 | { 263 | "id": "c1d1a418.33d548", 264 | "type": "mqtt in", 265 | "z": "eaed8fa5.46ccc8", 266 | "name": "", 267 | "topic": "bt-mqtt-gateway/ruuvitag/tag3/temperature", 268 | "qos": "2", 269 | "datatype": "auto", 270 | "broker": "d1ce6ba9.c90f2", 271 | "x": 350, 272 | "y": 880, 273 | "wires": [ 274 | [ 275 | "c86ed7a7.4c4cc8" 276 | ] 277 | ] 278 | }, 279 | { 280 | "id": "c86ed7a7.4c4cc8", 281 | "type": "ui_gauge", 282 | "z": "eaed8fa5.46ccc8", 283 | "name": "RuuviTag 3 temperature", 284 | "group": "c13387c.ae43bf8", 285 | "order": 0, 286 | "width": 0, 287 | "height": 0, 288 | "gtype": "gage", 289 | "title": "Temperature", 290 | "label": "\u00b0C", 291 | "format": "{{value}}", 292 | "min": "-40", 293 | "max": "85", 294 | "colors": [ 295 | "#00b500", 296 | "#e6e600", 297 | "#ca3838" 298 | ], 299 | "seg1": "", 300 | "seg2": "", 301 | "x": 930, 302 | "y": 880, 303 | "wires": [] 304 | }, 305 | { 306 | "id": "f47cc3f8.8913b", 307 | "type": "mqtt in", 308 | "z": "eaed8fa5.46ccc8", 309 | "name": "", 310 | "topic": "bt-mqtt-gateway/ruuvitag/tag1/humidity", 311 | "qos": "2", 312 | "datatype": "auto", 313 | "broker": "d1ce6ba9.c90f2", 314 | "x": 330, 315 | "y": 100, 316 | "wires": [ 317 | [ 318 | "5c188f09.7ed5b8" 319 | ] 320 | ] 321 | }, 322 | { 323 | "id": "5c188f09.7ed5b8", 324 | "type": "ui_gauge", 325 | "z": "eaed8fa5.46ccc8", 326 | "name": "RuuviTag 1 humidity", 327 | "group": "4f008f1d.e39768", 328 | "order": 0, 329 | "width": 0, 330 | "height": 0, 331 | "gtype": "gage", 332 | "title": "Humidity", 333 | "label": "%", 334 | "format": "{{value}}", 335 | "min": 0, 336 | "max": "100", 337 | "colors": [ 338 | "#00b500", 339 | "#e6e600", 340 | "#ca3838" 341 | ], 342 | "seg1": "", 343 | "seg2": "", 344 | "x": 900, 345 | "y": 100, 346 | "wires": [] 347 | }, 348 | { 349 | "id": "ffc8e29a.853b78", 350 | "type": "mqtt in", 351 | "z": "eaed8fa5.46ccc8", 352 | "name": "", 353 | "topic": "bt-mqtt-gateway/ruuvitag/tag2/humidity", 354 | "qos": "2", 355 | "datatype": "auto", 356 | "broker": "d1ce6ba9.c90f2", 357 | "x": 330, 358 | "y": 497, 359 | "wires": [ 360 | [ 361 | "9ccc8cf6.004788" 362 | ] 363 | ] 364 | }, 365 | { 366 | "id": "9ccc8cf6.004788", 367 | "type": "ui_gauge", 368 | "z": "eaed8fa5.46ccc8", 369 | "name": "RuuviTag 2 humidity", 370 | "group": "7e2d42c6.4b1d84", 371 | "order": 0, 372 | "width": 0, 373 | "height": 0, 374 | "gtype": "gage", 375 | "title": "Humidity", 376 | "label": "%", 377 | "format": "{{value}}", 378 | "min": 0, 379 | "max": "100", 380 | "colors": [ 381 | "#00b500", 382 | "#e6e600", 383 | "#ca3838" 384 | ], 385 | "seg1": "", 386 | "seg2": "", 387 | "x": 920, 388 | "y": 500, 389 | "wires": [] 390 | }, 391 | { 392 | "id": "3ef2487b.065738", 393 | "type": "mqtt in", 394 | "z": "eaed8fa5.46ccc8", 395 | "name": "", 396 | "topic": "bt-mqtt-gateway/ruuvitag/tag3/humidity", 397 | "qos": "2", 398 | "datatype": "auto", 399 | "broker": "d1ce6ba9.c90f2", 400 | "x": 330, 401 | "y": 940, 402 | "wires": [ 403 | [ 404 | "a5468b5a.6e6cd8" 405 | ] 406 | ] 407 | }, 408 | { 409 | "id": "a5468b5a.6e6cd8", 410 | "type": "ui_gauge", 411 | "z": "eaed8fa5.46ccc8", 412 | "name": "RuuviTag 3 humidity", 413 | "group": "c13387c.ae43bf8", 414 | "order": 0, 415 | "width": 0, 416 | "height": 0, 417 | "gtype": "gage", 418 | "title": "Humidity", 419 | "label": "%", 420 | "format": "{{value}}", 421 | "min": 0, 422 | "max": "100", 423 | "colors": [ 424 | "#00b500", 425 | "#e6e600", 426 | "#ca3838" 427 | ], 428 | "seg1": "", 429 | "seg2": "", 430 | "x": 920, 431 | "y": 940, 432 | "wires": [] 433 | }, 434 | { 435 | "id": "d75e46c0.9b16", 436 | "type": "mqtt in", 437 | "z": "eaed8fa5.46ccc8", 438 | "name": "", 439 | "topic": "bt-mqtt-gateway/ruuvitag/tag1/acceleration_z", 440 | "qos": "2", 441 | "datatype": "auto", 442 | "broker": "d1ce6ba9.c90f2", 443 | "x": 350, 444 | "y": 280, 445 | "wires": [ 446 | [ 447 | "b6dfc13d.c02ce" 448 | ] 449 | ] 450 | }, 451 | { 452 | "id": "b6dfc13d.c02ce", 453 | "type": "switch", 454 | "z": "eaed8fa5.46ccc8", 455 | "name": "", 456 | "property": "payload", 457 | "propertyType": "msg", 458 | "rules": [ 459 | { 460 | "t": "gt", 461 | "v": "50", 462 | "vt": "num" 463 | }, 464 | { 465 | "t": "lt", 466 | "v": "-50", 467 | "vt": "num" 468 | }, 469 | { 470 | "t": "btwn", 471 | "v": "-50", 472 | "vt": "num", 473 | "v2": "50", 474 | "v2t": "num" 475 | } 476 | ], 477 | "checkall": "true", 478 | "repair": false, 479 | "outputs": 3, 480 | "x": 670, 481 | "y": 280, 482 | "wires": [ 483 | [ 484 | "a2d62a5b.33809" 485 | ], 486 | [ 487 | "30946de0.a6ddc2" 488 | ], 489 | [ 490 | "3cbc8c7f.56a264" 491 | ] 492 | ] 493 | }, 494 | { 495 | "id": "a2d62a5b.33809", 496 | "type": "change", 497 | "z": "eaed8fa5.46ccc8", 498 | "name": "", 499 | "rules": [ 500 | { 501 | "t": "set", 502 | "p": "payload", 503 | "pt": "msg", 504 | "to": "Right side up", 505 | "tot": "str" 506 | } 507 | ], 508 | "action": "", 509 | "property": "", 510 | "from": "", 511 | "to": "", 512 | "reg": false, 513 | "x": 930, 514 | "y": 220, 515 | "wires": [ 516 | [ 517 | "d98a3ee6.b96258" 518 | ] 519 | ] 520 | }, 521 | { 522 | "id": "30946de0.a6ddc2", 523 | "type": "change", 524 | "z": "eaed8fa5.46ccc8", 525 | "name": "", 526 | "rules": [ 527 | { 528 | "t": "set", 529 | "p": "payload", 530 | "pt": "msg", 531 | "to": "Upside down", 532 | "tot": "str" 533 | } 534 | ], 535 | "action": "", 536 | "property": "", 537 | "from": "", 538 | "to": "", 539 | "reg": false, 540 | "x": 930, 541 | "y": 280, 542 | "wires": [ 543 | [ 544 | "d98a3ee6.b96258" 545 | ] 546 | ] 547 | }, 548 | { 549 | "id": "3cbc8c7f.56a264", 550 | "type": "change", 551 | "z": "eaed8fa5.46ccc8", 552 | "name": "", 553 | "rules": [ 554 | { 555 | "t": "set", 556 | "p": "payload", 557 | "pt": "msg", 558 | "to": "On its side", 559 | "tot": "str" 560 | } 561 | ], 562 | "action": "", 563 | "property": "", 564 | "from": "", 565 | "to": "", 566 | "reg": false, 567 | "x": 930, 568 | "y": 340, 569 | "wires": [ 570 | [ 571 | "d98a3ee6.b96258" 572 | ] 573 | ] 574 | }, 575 | { 576 | "id": "d98a3ee6.b96258", 577 | "type": "ui_text", 578 | "z": "eaed8fa5.46ccc8", 579 | "group": "4f008f1d.e39768", 580 | "order": 2, 581 | "width": 0, 582 | "height": 0, 583 | "name": "", 584 | "label": "Orientation", 585 | "format": "{{msg.payload}}", 586 | "layout": "row-spread", 587 | "x": 1220, 588 | "y": 280, 589 | "wires": [] 590 | }, 591 | { 592 | "id": "2d4ac8e9.1ad118", 593 | "type": "mqtt in", 594 | "z": "eaed8fa5.46ccc8", 595 | "name": "", 596 | "topic": "bt-mqtt-gateway/ruuvitag/tag3/acceleration_z", 597 | "qos": "2", 598 | "datatype": "auto", 599 | "broker": "d1ce6ba9.c90f2", 600 | "x": 350, 601 | "y": 1140, 602 | "wires": [ 603 | [ 604 | "540a5152.08973" 605 | ] 606 | ] 607 | }, 608 | { 609 | "id": "540a5152.08973", 610 | "type": "switch", 611 | "z": "eaed8fa5.46ccc8", 612 | "name": "", 613 | "property": "payload", 614 | "propertyType": "msg", 615 | "rules": [ 616 | { 617 | "t": "gt", 618 | "v": "50", 619 | "vt": "num" 620 | }, 621 | { 622 | "t": "lt", 623 | "v": "-50", 624 | "vt": "num" 625 | }, 626 | { 627 | "t": "btwn", 628 | "v": "-50", 629 | "vt": "num", 630 | "v2": "50", 631 | "v2t": "num" 632 | } 633 | ], 634 | "checkall": "true", 635 | "repair": false, 636 | "outputs": 3, 637 | "x": 670, 638 | "y": 1140, 639 | "wires": [ 640 | [ 641 | "c807a1cb.f58a98" 642 | ], 643 | [ 644 | "659f934e.708a44" 645 | ], 646 | [ 647 | "84b4252c.842c9" 648 | ] 649 | ] 650 | }, 651 | { 652 | "id": "c807a1cb.f58a98", 653 | "type": "change", 654 | "z": "eaed8fa5.46ccc8", 655 | "name": "", 656 | "rules": [ 657 | { 658 | "t": "set", 659 | "p": "payload", 660 | "pt": "msg", 661 | "to": "Right side up", 662 | "tot": "str" 663 | } 664 | ], 665 | "action": "", 666 | "property": "", 667 | "from": "", 668 | "to": "", 669 | "reg": false, 670 | "x": 930, 671 | "y": 1080, 672 | "wires": [ 673 | [ 674 | "85e4d15e.9b3ae" 675 | ] 676 | ] 677 | }, 678 | { 679 | "id": "659f934e.708a44", 680 | "type": "change", 681 | "z": "eaed8fa5.46ccc8", 682 | "name": "", 683 | "rules": [ 684 | { 685 | "t": "set", 686 | "p": "payload", 687 | "pt": "msg", 688 | "to": "Upside down", 689 | "tot": "str" 690 | } 691 | ], 692 | "action": "", 693 | "property": "", 694 | "from": "", 695 | "to": "", 696 | "reg": false, 697 | "x": 930, 698 | "y": 1140, 699 | "wires": [ 700 | [ 701 | "85e4d15e.9b3ae" 702 | ] 703 | ] 704 | }, 705 | { 706 | "id": "84b4252c.842c9", 707 | "type": "change", 708 | "z": "eaed8fa5.46ccc8", 709 | "name": "", 710 | "rules": [ 711 | { 712 | "t": "set", 713 | "p": "payload", 714 | "pt": "msg", 715 | "to": "On its side", 716 | "tot": "str" 717 | } 718 | ], 719 | "action": "", 720 | "property": "", 721 | "from": "", 722 | "to": "", 723 | "reg": false, 724 | "x": 930, 725 | "y": 1200, 726 | "wires": [ 727 | [ 728 | "85e4d15e.9b3ae" 729 | ] 730 | ] 731 | }, 732 | { 733 | "id": "85e4d15e.9b3ae", 734 | "type": "ui_text", 735 | "z": "eaed8fa5.46ccc8", 736 | "group": "c13387c.ae43bf8", 737 | "order": 2, 738 | "width": 0, 739 | "height": 0, 740 | "name": "", 741 | "label": "Orientation", 742 | "format": "{{msg.payload}}", 743 | "layout": "row-spread", 744 | "x": 1220, 745 | "y": 1140, 746 | "wires": [] 747 | }, 748 | { 749 | "id": "77c9b0e4.3f5b38", 750 | "type": "mqtt in", 751 | "z": "eaed8fa5.46ccc8", 752 | "name": "", 753 | "topic": "bt-mqtt-gateway/ruuvitag/tag2/acceleration_z", 754 | "qos": "2", 755 | "datatype": "auto", 756 | "broker": "d1ce6ba9.c90f2", 757 | "x": 350, 758 | "y": 700, 759 | "wires": [ 760 | [ 761 | "f48c70aa.d1c998" 762 | ] 763 | ] 764 | }, 765 | { 766 | "id": "f48c70aa.d1c998", 767 | "type": "switch", 768 | "z": "eaed8fa5.46ccc8", 769 | "name": "", 770 | "property": "payload", 771 | "propertyType": "msg", 772 | "rules": [ 773 | { 774 | "t": "gt", 775 | "v": "50", 776 | "vt": "num" 777 | }, 778 | { 779 | "t": "lt", 780 | "v": "-50", 781 | "vt": "num" 782 | }, 783 | { 784 | "t": "btwn", 785 | "v": "-50", 786 | "vt": "num", 787 | "v2": "50", 788 | "v2t": "num" 789 | } 790 | ], 791 | "checkall": "true", 792 | "repair": false, 793 | "outputs": 3, 794 | "x": 670, 795 | "y": 700, 796 | "wires": [ 797 | [ 798 | "ee1dbad1.0a15d" 799 | ], 800 | [ 801 | "5d72c5a8.79d204" 802 | ], 803 | [ 804 | "7031411.60caec" 805 | ] 806 | ] 807 | }, 808 | { 809 | "id": "ee1dbad1.0a15d", 810 | "type": "change", 811 | "z": "eaed8fa5.46ccc8", 812 | "name": "", 813 | "rules": [ 814 | { 815 | "t": "set", 816 | "p": "payload", 817 | "pt": "msg", 818 | "to": "Right side up", 819 | "tot": "str" 820 | } 821 | ], 822 | "action": "", 823 | "property": "", 824 | "from": "", 825 | "to": "", 826 | "reg": false, 827 | "x": 930, 828 | "y": 640, 829 | "wires": [ 830 | [ 831 | "d7fd4e74.22752" 832 | ] 833 | ] 834 | }, 835 | { 836 | "id": "5d72c5a8.79d204", 837 | "type": "change", 838 | "z": "eaed8fa5.46ccc8", 839 | "name": "", 840 | "rules": [ 841 | { 842 | "t": "set", 843 | "p": "payload", 844 | "pt": "msg", 845 | "to": "Upside down", 846 | "tot": "str" 847 | } 848 | ], 849 | "action": "", 850 | "property": "", 851 | "from": "", 852 | "to": "", 853 | "reg": false, 854 | "x": 930, 855 | "y": 700, 856 | "wires": [ 857 | [ 858 | "d7fd4e74.22752" 859 | ] 860 | ] 861 | }, 862 | { 863 | "id": "7031411.60caec", 864 | "type": "change", 865 | "z": "eaed8fa5.46ccc8", 866 | "name": "", 867 | "rules": [ 868 | { 869 | "t": "set", 870 | "p": "payload", 871 | "pt": "msg", 872 | "to": "On its side", 873 | "tot": "str" 874 | } 875 | ], 876 | "action": "", 877 | "property": "", 878 | "from": "", 879 | "to": "", 880 | "reg": false, 881 | "x": 930, 882 | "y": 760, 883 | "wires": [ 884 | [ 885 | "d7fd4e74.22752" 886 | ] 887 | ] 888 | }, 889 | { 890 | "id": "d7fd4e74.22752", 891 | "type": "ui_text", 892 | "z": "eaed8fa5.46ccc8", 893 | "group": "7e2d42c6.4b1d84", 894 | "order": 2, 895 | "width": 0, 896 | "height": 0, 897 | "name": "", 898 | "label": "Orientation", 899 | "format": "{{msg.payload}}", 900 | "layout": "row-spread", 901 | "x": 1220, 902 | "y": 700, 903 | "wires": [] 904 | }, 905 | { 906 | "id": "ddc35c81.3d6f5", 907 | "type": "mqtt in", 908 | "z": "eaed8fa5.46ccc8", 909 | "name": "", 910 | "topic": "bt-mqtt-gateway/ruuvitag/tag1/pressure", 911 | "qos": "2", 912 | "datatype": "auto", 913 | "broker": "d1ce6ba9.c90f2", 914 | "x": 330, 915 | "y": 160, 916 | "wires": [ 917 | [ 918 | "83769d57.833558" 919 | ] 920 | ] 921 | }, 922 | { 923 | "id": "83769d57.833558", 924 | "type": "ui_gauge", 925 | "z": "eaed8fa5.46ccc8", 926 | "name": "RuuviTag 1 pressure", 927 | "group": "4f008f1d.e39768", 928 | "order": 0, 929 | "width": 0, 930 | "height": 0, 931 | "gtype": "gage", 932 | "title": "Pressure", 933 | "label": "hPa", 934 | "format": "{{value}}", 935 | "min": "300", 936 | "max": "1100", 937 | "colors": [ 938 | "#00b500", 939 | "#e6e600", 940 | "#ca3838" 941 | ], 942 | "seg1": "", 943 | "seg2": "", 944 | "x": 900, 945 | "y": 160, 946 | "wires": [] 947 | }, 948 | { 949 | "id": "9aeaf7b.0047188", 950 | "type": "mqtt in", 951 | "z": "eaed8fa5.46ccc8", 952 | "name": "", 953 | "topic": "bt-mqtt-gateway/ruuvitag/tag2/pressure", 954 | "qos": "2", 955 | "datatype": "auto", 956 | "broker": "d1ce6ba9.c90f2", 957 | "x": 330, 958 | "y": 560, 959 | "wires": [ 960 | [ 961 | "904d0c9b.8cb66" 962 | ] 963 | ] 964 | }, 965 | { 966 | "id": "904d0c9b.8cb66", 967 | "type": "ui_gauge", 968 | "z": "eaed8fa5.46ccc8", 969 | "name": "RuuviTag 2 pressure", 970 | "group": "7e2d42c6.4b1d84", 971 | "order": 0, 972 | "width": 0, 973 | "height": 0, 974 | "gtype": "gage", 975 | "title": "Pressure", 976 | "label": "hPa", 977 | "format": "{{value}}", 978 | "min": "300", 979 | "max": "1100", 980 | "colors": [ 981 | "#00b500", 982 | "#e6e600", 983 | "#ca3838" 984 | ], 985 | "seg1": "", 986 | "seg2": "", 987 | "x": 920, 988 | "y": 560, 989 | "wires": [] 990 | }, 991 | { 992 | "id": "6c5a860d.edc83", 993 | "type": "mqtt in", 994 | "z": "eaed8fa5.46ccc8", 995 | "name": "", 996 | "topic": "bt-mqtt-gateway/ruuvitag/tag3/pressure", 997 | "qos": "2", 998 | "datatype": "auto", 999 | "broker": "d1ce6ba9.c90f2", 1000 | "x": 330, 1001 | "y": 1020, 1002 | "wires": [ 1003 | [ 1004 | "af08bcda.b4f22" 1005 | ] 1006 | ] 1007 | }, 1008 | { 1009 | "id": "af08bcda.b4f22", 1010 | "type": "ui_gauge", 1011 | "z": "eaed8fa5.46ccc8", 1012 | "name": "RuuviTag 3 pressure", 1013 | "group": "c13387c.ae43bf8", 1014 | "order": 0, 1015 | "width": 0, 1016 | "height": 0, 1017 | "gtype": "gage", 1018 | "title": "Pressure", 1019 | "label": "hPa", 1020 | "format": "{{value}}", 1021 | "min": "300", 1022 | "max": "1100", 1023 | "colors": [ 1024 | "#00b500", 1025 | "#e6e600", 1026 | "#ca3838" 1027 | ], 1028 | "seg1": "", 1029 | "seg2": "", 1030 | "x": 920, 1031 | "y": 1020, 1032 | "wires": [] 1033 | }, 1034 | { 1035 | "id": "b51ce146.ddb3f", 1036 | "type": "mqtt in", 1037 | "z": "eaed8fa5.46ccc8", 1038 | "name": "", 1039 | "topic": "bt-mqtt-gateway/ruuvitag/tag4/temperature", 1040 | "qos": "2", 1041 | "datatype": "auto", 1042 | "broker": "d1ce6ba9.c90f2", 1043 | "x": 350, 1044 | "y": 1320, 1045 | "wires": [ 1046 | [ 1047 | "bb064d50.922058" 1048 | ] 1049 | ] 1050 | }, 1051 | { 1052 | "id": "bb064d50.922058", 1053 | "type": "ui_gauge", 1054 | "z": "eaed8fa5.46ccc8", 1055 | "name": "RuuviTag 4 temperature", 1056 | "group": "30b8e062.37b95", 1057 | "order": 0, 1058 | "width": 0, 1059 | "height": 0, 1060 | "gtype": "gage", 1061 | "title": "Temperature", 1062 | "label": "\u00b0C", 1063 | "format": "{{value}}", 1064 | "min": "-40", 1065 | "max": "85", 1066 | "colors": [ 1067 | "#00b500", 1068 | "#e6e600", 1069 | "#ca3838" 1070 | ], 1071 | "seg1": "", 1072 | "seg2": "", 1073 | "x": 930, 1074 | "y": 1320, 1075 | "wires": [] 1076 | }, 1077 | { 1078 | "id": "e788725d.5a66a8", 1079 | "type": "mqtt in", 1080 | "z": "eaed8fa5.46ccc8", 1081 | "name": "", 1082 | "topic": "bt-mqtt-gateway/ruuvitag/tag4/humidity", 1083 | "qos": "2", 1084 | "datatype": "auto", 1085 | "broker": "d1ce6ba9.c90f2", 1086 | "x": 330, 1087 | "y": 1380, 1088 | "wires": [ 1089 | [ 1090 | "b486c770.b4c848" 1091 | ] 1092 | ] 1093 | }, 1094 | { 1095 | "id": "b486c770.b4c848", 1096 | "type": "ui_gauge", 1097 | "z": "eaed8fa5.46ccc8", 1098 | "name": "RuuviTag 4 humidity", 1099 | "group": "30b8e062.37b95", 1100 | "order": 0, 1101 | "width": 0, 1102 | "height": 0, 1103 | "gtype": "gage", 1104 | "title": "Humidity", 1105 | "label": "%", 1106 | "format": "{{value}}", 1107 | "min": 0, 1108 | "max": "100", 1109 | "colors": [ 1110 | "#00b500", 1111 | "#e6e600", 1112 | "#ca3838" 1113 | ], 1114 | "seg1": "", 1115 | "seg2": "", 1116 | "x": 920, 1117 | "y": 1380, 1118 | "wires": [] 1119 | }, 1120 | { 1121 | "id": "37a091af.b60cbe", 1122 | "type": "mqtt in", 1123 | "z": "eaed8fa5.46ccc8", 1124 | "name": "", 1125 | "topic": "bt-mqtt-gateway/ruuvitag/tag4/acceleration_z", 1126 | "qos": "2", 1127 | "datatype": "auto", 1128 | "broker": "d1ce6ba9.c90f2", 1129 | "x": 350, 1130 | "y": 1580, 1131 | "wires": [ 1132 | [ 1133 | "59b69629.903d6" 1134 | ] 1135 | ] 1136 | }, 1137 | { 1138 | "id": "59b69629.903d6", 1139 | "type": "switch", 1140 | "z": "eaed8fa5.46ccc8", 1141 | "name": "", 1142 | "property": "payload", 1143 | "propertyType": "msg", 1144 | "rules": [ 1145 | { 1146 | "t": "gt", 1147 | "v": "50", 1148 | "vt": "num" 1149 | }, 1150 | { 1151 | "t": "lt", 1152 | "v": "-50", 1153 | "vt": "num" 1154 | }, 1155 | { 1156 | "t": "btwn", 1157 | "v": "-50", 1158 | "vt": "num", 1159 | "v2": "50", 1160 | "v2t": "num" 1161 | } 1162 | ], 1163 | "checkall": "true", 1164 | "repair": false, 1165 | "outputs": 3, 1166 | "x": 670, 1167 | "y": 1580, 1168 | "wires": [ 1169 | [ 1170 | "cb8a25b6.0233a" 1171 | ], 1172 | [ 1173 | "73a39f82.39aab8" 1174 | ], 1175 | [ 1176 | "c4599fee.99bf18" 1177 | ] 1178 | ] 1179 | }, 1180 | { 1181 | "id": "cb8a25b6.0233a", 1182 | "type": "change", 1183 | "z": "eaed8fa5.46ccc8", 1184 | "name": "", 1185 | "rules": [ 1186 | { 1187 | "t": "set", 1188 | "p": "payload", 1189 | "pt": "msg", 1190 | "to": "Right side up", 1191 | "tot": "str" 1192 | } 1193 | ], 1194 | "action": "", 1195 | "property": "", 1196 | "from": "", 1197 | "to": "", 1198 | "reg": false, 1199 | "x": 930, 1200 | "y": 1520, 1201 | "wires": [ 1202 | [ 1203 | "10ec976c.4b2d19" 1204 | ] 1205 | ] 1206 | }, 1207 | { 1208 | "id": "73a39f82.39aab8", 1209 | "type": "change", 1210 | "z": "eaed8fa5.46ccc8", 1211 | "name": "", 1212 | "rules": [ 1213 | { 1214 | "t": "set", 1215 | "p": "payload", 1216 | "pt": "msg", 1217 | "to": "Upside down", 1218 | "tot": "str" 1219 | } 1220 | ], 1221 | "action": "", 1222 | "property": "", 1223 | "from": "", 1224 | "to": "", 1225 | "reg": false, 1226 | "x": 930, 1227 | "y": 1580, 1228 | "wires": [ 1229 | [ 1230 | "10ec976c.4b2d19" 1231 | ] 1232 | ] 1233 | }, 1234 | { 1235 | "id": "c4599fee.99bf18", 1236 | "type": "change", 1237 | "z": "eaed8fa5.46ccc8", 1238 | "name": "", 1239 | "rules": [ 1240 | { 1241 | "t": "set", 1242 | "p": "payload", 1243 | "pt": "msg", 1244 | "to": "On its side", 1245 | "tot": "str" 1246 | } 1247 | ], 1248 | "action": "", 1249 | "property": "", 1250 | "from": "", 1251 | "to": "", 1252 | "reg": false, 1253 | "x": 930, 1254 | "y": 1640, 1255 | "wires": [ 1256 | [ 1257 | "10ec976c.4b2d19" 1258 | ] 1259 | ] 1260 | }, 1261 | { 1262 | "id": "10ec976c.4b2d19", 1263 | "type": "ui_text", 1264 | "z": "eaed8fa5.46ccc8", 1265 | "group": "30b8e062.37b95", 1266 | "order": 2, 1267 | "width": 0, 1268 | "height": 0, 1269 | "name": "", 1270 | "label": "Orientation", 1271 | "format": "{{msg.payload}}", 1272 | "layout": "row-spread", 1273 | "x": 1220, 1274 | "y": 1580, 1275 | "wires": [] 1276 | }, 1277 | { 1278 | "id": "78eab4ae.36b62c", 1279 | "type": "mqtt in", 1280 | "z": "eaed8fa5.46ccc8", 1281 | "name": "", 1282 | "topic": "bt-mqtt-gateway/ruuvitag/tag4/pressure", 1283 | "qos": "2", 1284 | "datatype": "auto", 1285 | "broker": "d1ce6ba9.c90f2", 1286 | "x": 330, 1287 | "y": 1460, 1288 | "wires": [ 1289 | [ 1290 | "13f812ff.c0fb35" 1291 | ] 1292 | ] 1293 | }, 1294 | { 1295 | "id": "13f812ff.c0fb35", 1296 | "type": "ui_gauge", 1297 | "z": "eaed8fa5.46ccc8", 1298 | "name": "RuuviTag 4 pressure", 1299 | "group": "30b8e062.37b95", 1300 | "order": 0, 1301 | "width": 0, 1302 | "height": 0, 1303 | "gtype": "gage", 1304 | "title": "Pressure", 1305 | "label": "hPa", 1306 | "format": "{{value}}", 1307 | "min": "300", 1308 | "max": "1100", 1309 | "colors": [ 1310 | "#00b500", 1311 | "#e6e600", 1312 | "#ca3838" 1313 | ], 1314 | "seg1": "", 1315 | "seg2": "", 1316 | "x": 920, 1317 | "y": 1460, 1318 | "wires": [] 1319 | } 1320 | ] 1321 | -------------------------------------------------------------------------------- /grafana/dashboards/ruuvitag.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": "-- Grafana --", 7 | "enable": true, 8 | "hide": true, 9 | "iconColor": "rgba(0, 211, 255, 1)", 10 | "name": "Annotations & Alerts", 11 | "type": "dashboard" 12 | } 13 | ] 14 | }, 15 | "editable": true, 16 | "gnetId": null, 17 | "graphTooltip": 0, 18 | "links": [], 19 | "panels": [ 20 | { 21 | "aliasColors": {}, 22 | "bars": false, 23 | "dashLength": 10, 24 | "dashes": false, 25 | "datasource": "InfluxDB", 26 | "fill": 1, 27 | "fillGradient": 0, 28 | "gridPos": { 29 | "h": 8, 30 | "w": 12, 31 | "x": 0, 32 | "y": 0 33 | }, 34 | "hiddenSeries": false, 35 | "id": 2, 36 | "legend": { 37 | "avg": false, 38 | "current": false, 39 | "max": false, 40 | "min": false, 41 | "show": true, 42 | "total": false, 43 | "values": false 44 | }, 45 | "lines": true, 46 | "linewidth": 1, 47 | "nullPointMode": "null", 48 | "options": { 49 | "dataLinks": [] 50 | }, 51 | "percentage": false, 52 | "pointradius": 2, 53 | "points": false, 54 | "renderer": "flot", 55 | "seriesOverrides": [], 56 | "spaceLength": 10, 57 | "stack": false, 58 | "steppedLine": false, 59 | "targets": [ 60 | { 61 | "alias": "RuuviTag 1", 62 | "groupBy": [ 63 | { 64 | "params": [ 65 | "$__interval" 66 | ], 67 | "type": "time" 68 | }, 69 | { 70 | "params": [ 71 | "null" 72 | ], 73 | "type": "fill" 74 | } 75 | ], 76 | "measurement": "ruuvitag", 77 | "orderByTime": "ASC", 78 | "policy": "default", 79 | "refId": "A", 80 | "resultFormat": "time_series", 81 | "select": [ 82 | [ 83 | { 84 | "params": [ 85 | "value" 86 | ], 87 | "type": "field" 88 | }, 89 | { 90 | "params": [], 91 | "type": "mean" 92 | } 93 | ] 94 | ], 95 | "tags": [ 96 | { 97 | "key": "topic", 98 | "operator": "=", 99 | "value": "bt-mqtt-gateway/ruuvitag/tag1/temperature" 100 | } 101 | ] 102 | }, 103 | { 104 | "alias": "RuuviTag 2", 105 | "groupBy": [ 106 | { 107 | "params": [ 108 | "$__interval" 109 | ], 110 | "type": "time" 111 | }, 112 | { 113 | "params": [ 114 | "null" 115 | ], 116 | "type": "fill" 117 | } 118 | ], 119 | "measurement": "ruuvitag", 120 | "orderByTime": "ASC", 121 | "policy": "default", 122 | "refId": "B", 123 | "resultFormat": "time_series", 124 | "select": [ 125 | [ 126 | { 127 | "params": [ 128 | "value" 129 | ], 130 | "type": "field" 131 | }, 132 | { 133 | "params": [], 134 | "type": "mean" 135 | } 136 | ] 137 | ], 138 | "tags": [ 139 | { 140 | "key": "topic", 141 | "operator": "=", 142 | "value": "bt-mqtt-gateway/ruuvitag/tag2/temperature" 143 | } 144 | ] 145 | }, 146 | { 147 | "alias": "RuuviTag 3", 148 | "groupBy": [ 149 | { 150 | "params": [ 151 | "$__interval" 152 | ], 153 | "type": "time" 154 | }, 155 | { 156 | "params": [ 157 | "null" 158 | ], 159 | "type": "fill" 160 | } 161 | ], 162 | "measurement": "ruuvitag", 163 | "orderByTime": "ASC", 164 | "policy": "default", 165 | "refId": "C", 166 | "resultFormat": "time_series", 167 | "select": [ 168 | [ 169 | { 170 | "params": [ 171 | "value" 172 | ], 173 | "type": "field" 174 | }, 175 | { 176 | "params": [], 177 | "type": "mean" 178 | } 179 | ] 180 | ], 181 | "tags": [ 182 | { 183 | "key": "topic", 184 | "operator": "=", 185 | "value": "bt-mqtt-gateway/ruuvitag/tag3/temperature" 186 | } 187 | ] 188 | } 189 | ], 190 | "thresholds": [], 191 | "timeFrom": null, 192 | "timeRegions": [], 193 | "timeShift": null, 194 | "title": "Temperature", 195 | "tooltip": { 196 | "shared": true, 197 | "sort": 0, 198 | "value_type": "individual" 199 | }, 200 | "type": "graph", 201 | "xaxis": { 202 | "buckets": null, 203 | "mode": "time", 204 | "name": null, 205 | "show": true, 206 | "values": [] 207 | }, 208 | "yaxes": [ 209 | { 210 | "format": "celsius", 211 | "label": null, 212 | "logBase": 1, 213 | "max": null, 214 | "min": null, 215 | "show": true 216 | }, 217 | { 218 | "format": "short", 219 | "label": null, 220 | "logBase": 1, 221 | "max": null, 222 | "min": null, 223 | "show": true 224 | } 225 | ], 226 | "yaxis": { 227 | "align": false, 228 | "alignLevel": null 229 | } 230 | }, 231 | { 232 | "aliasColors": {}, 233 | "bars": false, 234 | "dashLength": 10, 235 | "dashes": false, 236 | "datasource": "InfluxDB", 237 | "fill": 1, 238 | "fillGradient": 0, 239 | "gridPos": { 240 | "h": 8, 241 | "w": 12, 242 | "x": 12, 243 | "y": 0 244 | }, 245 | "hiddenSeries": false, 246 | "id": 4, 247 | "legend": { 248 | "avg": false, 249 | "current": false, 250 | "max": false, 251 | "min": false, 252 | "show": true, 253 | "total": false, 254 | "values": false 255 | }, 256 | "lines": true, 257 | "linewidth": 1, 258 | "nullPointMode": "null", 259 | "options": { 260 | "dataLinks": [] 261 | }, 262 | "percentage": false, 263 | "pointradius": 2, 264 | "points": false, 265 | "renderer": "flot", 266 | "seriesOverrides": [], 267 | "spaceLength": 10, 268 | "stack": false, 269 | "steppedLine": false, 270 | "targets": [ 271 | { 272 | "alias": "RuuviTag 1", 273 | "groupBy": [ 274 | { 275 | "params": [ 276 | "$__interval" 277 | ], 278 | "type": "time" 279 | }, 280 | { 281 | "params": [ 282 | "null" 283 | ], 284 | "type": "fill" 285 | } 286 | ], 287 | "measurement": "ruuvitag", 288 | "orderByTime": "ASC", 289 | "policy": "default", 290 | "refId": "A", 291 | "resultFormat": "time_series", 292 | "select": [ 293 | [ 294 | { 295 | "params": [ 296 | "value" 297 | ], 298 | "type": "field" 299 | }, 300 | { 301 | "params": [], 302 | "type": "mean" 303 | } 304 | ] 305 | ], 306 | "tags": [ 307 | { 308 | "key": "topic", 309 | "operator": "=", 310 | "value": "bt-mqtt-gateway/ruuvitag/tag1/humidity" 311 | } 312 | ] 313 | }, 314 | { 315 | "alias": "RuuviTag 2", 316 | "groupBy": [ 317 | { 318 | "params": [ 319 | "$__interval" 320 | ], 321 | "type": "time" 322 | }, 323 | { 324 | "params": [ 325 | "null" 326 | ], 327 | "type": "fill" 328 | } 329 | ], 330 | "measurement": "ruuvitag", 331 | "orderByTime": "ASC", 332 | "policy": "default", 333 | "refId": "B", 334 | "resultFormat": "time_series", 335 | "select": [ 336 | [ 337 | { 338 | "params": [ 339 | "value" 340 | ], 341 | "type": "field" 342 | }, 343 | { 344 | "params": [], 345 | "type": "mean" 346 | } 347 | ] 348 | ], 349 | "tags": [ 350 | { 351 | "key": "topic", 352 | "operator": "=", 353 | "value": "bt-mqtt-gateway/ruuvitag/tag2/humidity" 354 | } 355 | ] 356 | }, 357 | { 358 | "alias": "RuuviTag 3", 359 | "groupBy": [ 360 | { 361 | "params": [ 362 | "$__interval" 363 | ], 364 | "type": "time" 365 | }, 366 | { 367 | "params": [ 368 | "null" 369 | ], 370 | "type": "fill" 371 | } 372 | ], 373 | "measurement": "ruuvitag", 374 | "orderByTime": "ASC", 375 | "policy": "default", 376 | "refId": "C", 377 | "resultFormat": "time_series", 378 | "select": [ 379 | [ 380 | { 381 | "params": [ 382 | "value" 383 | ], 384 | "type": "field" 385 | }, 386 | { 387 | "params": [], 388 | "type": "mean" 389 | } 390 | ] 391 | ], 392 | "tags": [ 393 | { 394 | "key": "topic", 395 | "operator": "=", 396 | "value": "bt-mqtt-gateway/ruuvitag/tag3/humidity" 397 | } 398 | ] 399 | } 400 | ], 401 | "thresholds": [], 402 | "timeFrom": null, 403 | "timeRegions": [], 404 | "timeShift": null, 405 | "title": "Humidity", 406 | "tooltip": { 407 | "shared": true, 408 | "sort": 0, 409 | "value_type": "individual" 410 | }, 411 | "type": "graph", 412 | "xaxis": { 413 | "buckets": null, 414 | "mode": "time", 415 | "name": null, 416 | "show": true, 417 | "values": [] 418 | }, 419 | "yaxes": [ 420 | { 421 | "format": "humidity", 422 | "label": null, 423 | "logBase": 1, 424 | "max": null, 425 | "min": null, 426 | "show": true 427 | }, 428 | { 429 | "format": "short", 430 | "label": null, 431 | "logBase": 1, 432 | "max": null, 433 | "min": null, 434 | "show": true 435 | } 436 | ], 437 | "yaxis": { 438 | "align": false, 439 | "alignLevel": null 440 | } 441 | }, 442 | { 443 | "cacheTimeout": null, 444 | "datasource": null, 445 | "gridPos": { 446 | "h": 5, 447 | "w": 3, 448 | "x": 0, 449 | "y": 8 450 | }, 451 | "id": 6, 452 | "links": [], 453 | "options": { 454 | "fieldOptions": { 455 | "calcs": [ 456 | "lastNotNull" 457 | ], 458 | "defaults": { 459 | "decimals": 1, 460 | "mappings": [ 461 | { 462 | "id": 0, 463 | "op": "=", 464 | "text": "N/A", 465 | "type": 1, 466 | "value": "null" 467 | } 468 | ], 469 | "max": 85, 470 | "min": -40, 471 | "nullValueMode": "connected", 472 | "thresholds": [ 473 | { 474 | "color": "green", 475 | "value": null 476 | }, 477 | { 478 | "color": "red", 479 | "value": 80 480 | } 481 | ], 482 | "unit": "celsius" 483 | }, 484 | "override": {}, 485 | "values": false 486 | }, 487 | "orientation": "horizontal", 488 | "showThresholdLabels": false, 489 | "showThresholdMarkers": true 490 | }, 491 | "pluginVersion": "6.5.0-pre", 492 | "targets": [ 493 | { 494 | "groupBy": [ 495 | { 496 | "params": [ 497 | "$__interval" 498 | ], 499 | "type": "time" 500 | }, 501 | { 502 | "params": [ 503 | "null" 504 | ], 505 | "type": "fill" 506 | } 507 | ], 508 | "measurement": "ruuvitag", 509 | "orderByTime": "ASC", 510 | "policy": "default", 511 | "refId": "A", 512 | "resultFormat": "time_series", 513 | "select": [ 514 | [ 515 | { 516 | "params": [ 517 | "value" 518 | ], 519 | "type": "field" 520 | }, 521 | { 522 | "params": [], 523 | "type": "mean" 524 | } 525 | ] 526 | ], 527 | "tags": [ 528 | { 529 | "key": "topic", 530 | "operator": "=", 531 | "value": "bt-mqtt-gateway/ruuvitag/tag1/temperature" 532 | } 533 | ] 534 | } 535 | ], 536 | "timeFrom": null, 537 | "timeShift": null, 538 | "title": "Temperature 1", 539 | "type": "gauge" 540 | }, 541 | { 542 | "datasource": null, 543 | "gridPos": { 544 | "h": 5, 545 | "w": 3, 546 | "x": 3, 547 | "y": 8 548 | }, 549 | "id": 8, 550 | "options": { 551 | "fieldOptions": { 552 | "calcs": [ 553 | "lastNotNull" 554 | ], 555 | "defaults": { 556 | "decimals": 1, 557 | "mappings": [], 558 | "max": 85, 559 | "min": -40, 560 | "thresholds": [ 561 | { 562 | "color": "green", 563 | "value": null 564 | }, 565 | { 566 | "color": "red", 567 | "value": 80 568 | } 569 | ], 570 | "unit": "celsius" 571 | }, 572 | "override": {}, 573 | "values": false 574 | }, 575 | "orientation": "auto", 576 | "showThresholdLabels": false, 577 | "showThresholdMarkers": true 578 | }, 579 | "pluginVersion": "6.5.0-pre", 580 | "targets": [ 581 | { 582 | "groupBy": [ 583 | { 584 | "params": [ 585 | "$__interval" 586 | ], 587 | "type": "time" 588 | }, 589 | { 590 | "params": [ 591 | "null" 592 | ], 593 | "type": "fill" 594 | } 595 | ], 596 | "measurement": "ruuvitag", 597 | "orderByTime": "ASC", 598 | "policy": "default", 599 | "refId": "A", 600 | "resultFormat": "time_series", 601 | "select": [ 602 | [ 603 | { 604 | "params": [ 605 | "value" 606 | ], 607 | "type": "field" 608 | }, 609 | { 610 | "params": [], 611 | "type": "mean" 612 | } 613 | ] 614 | ], 615 | "tags": [ 616 | { 617 | "key": "topic", 618 | "operator": "=", 619 | "value": "bt-mqtt-gateway/ruuvitag/tag2/temperature" 620 | } 621 | ] 622 | } 623 | ], 624 | "timeFrom": null, 625 | "timeShift": null, 626 | "title": "Temperature 2", 627 | "type": "gauge" 628 | }, 629 | { 630 | "datasource": null, 631 | "gridPos": { 632 | "h": 5, 633 | "w": 3, 634 | "x": 6, 635 | "y": 8 636 | }, 637 | "id": 10, 638 | "options": { 639 | "fieldOptions": { 640 | "calcs": [ 641 | "lastNotNull" 642 | ], 643 | "defaults": { 644 | "decimals": 1, 645 | "mappings": [], 646 | "max": 85, 647 | "min": -40, 648 | "thresholds": [ 649 | { 650 | "color": "green", 651 | "value": null 652 | }, 653 | { 654 | "color": "red", 655 | "value": 80 656 | } 657 | ], 658 | "unit": "celsius" 659 | }, 660 | "override": {}, 661 | "values": false 662 | }, 663 | "orientation": "auto", 664 | "showThresholdLabels": false, 665 | "showThresholdMarkers": true 666 | }, 667 | "pluginVersion": "6.5.0-pre", 668 | "targets": [ 669 | { 670 | "groupBy": [ 671 | { 672 | "params": [ 673 | "$__interval" 674 | ], 675 | "type": "time" 676 | }, 677 | { 678 | "params": [ 679 | "null" 680 | ], 681 | "type": "fill" 682 | } 683 | ], 684 | "measurement": "ruuvitag", 685 | "orderByTime": "ASC", 686 | "policy": "default", 687 | "refId": "A", 688 | "resultFormat": "time_series", 689 | "select": [ 690 | [ 691 | { 692 | "params": [ 693 | "value" 694 | ], 695 | "type": "field" 696 | }, 697 | { 698 | "params": [], 699 | "type": "mean" 700 | } 701 | ] 702 | ], 703 | "tags": [ 704 | { 705 | "key": "topic", 706 | "operator": "=", 707 | "value": "bt-mqtt-gateway/ruuvitag/tag3/temperature" 708 | } 709 | ] 710 | } 711 | ], 712 | "timeFrom": null, 713 | "timeShift": null, 714 | "title": "Temperature 3", 715 | "type": "gauge" 716 | }, 717 | { 718 | "cacheTimeout": null, 719 | "datasource": null, 720 | "gridPos": { 721 | "h": 5, 722 | "w": 3, 723 | "x": 9, 724 | "y": 8 725 | }, 726 | "id": 11, 727 | "links": [], 728 | "options": { 729 | "fieldOptions": { 730 | "calcs": [ 731 | "lastNotNull" 732 | ], 733 | "defaults": { 734 | "decimals": 1, 735 | "mappings": [ 736 | { 737 | "id": 0, 738 | "op": "=", 739 | "text": "N/A", 740 | "type": 1, 741 | "value": "null" 742 | } 743 | ], 744 | "max": 85, 745 | "min": -40, 746 | "nullValueMode": "connected", 747 | "thresholds": [ 748 | { 749 | "color": "green", 750 | "value": null 751 | }, 752 | { 753 | "color": "red", 754 | "value": 80 755 | } 756 | ], 757 | "unit": "celsius" 758 | }, 759 | "override": {}, 760 | "values": false 761 | }, 762 | "orientation": "horizontal", 763 | "showThresholdLabels": false, 764 | "showThresholdMarkers": true 765 | }, 766 | "pluginVersion": "6.5.0-pre", 767 | "targets": [ 768 | { 769 | "groupBy": [ 770 | { 771 | "params": [ 772 | "$__interval" 773 | ], 774 | "type": "time" 775 | }, 776 | { 777 | "params": [ 778 | "null" 779 | ], 780 | "type": "fill" 781 | } 782 | ], 783 | "measurement": "ruuvitag", 784 | "orderByTime": "ASC", 785 | "policy": "default", 786 | "refId": "A", 787 | "resultFormat": "time_series", 788 | "select": [ 789 | [ 790 | { 791 | "params": [ 792 | "value" 793 | ], 794 | "type": "field" 795 | }, 796 | { 797 | "params": [], 798 | "type": "mean" 799 | } 800 | ] 801 | ], 802 | "tags": [ 803 | { 804 | "key": "topic", 805 | "operator": "=", 806 | "value": "bt-mqtt-gateway/ruuvitag/tag4/temperature" 807 | } 808 | ] 809 | } 810 | ], 811 | "timeFrom": null, 812 | "timeShift": null, 813 | "title": "Temperature 4", 814 | "type": "gauge" 815 | }, 816 | { 817 | "cacheTimeout": null, 818 | "datasource": null, 819 | "gridPos": { 820 | "h": 5, 821 | "w": 3, 822 | "x": 12, 823 | "y": 8 824 | }, 825 | "id": 12, 826 | "links": [], 827 | "options": { 828 | "fieldOptions": { 829 | "calcs": [ 830 | "lastNotNull" 831 | ], 832 | "defaults": { 833 | "decimals": 1, 834 | "mappings": [ 835 | { 836 | "id": 0, 837 | "op": "=", 838 | "text": "N/A", 839 | "type": 1, 840 | "value": "null" 841 | } 842 | ], 843 | "max": 100, 844 | "min": 0, 845 | "nullValueMode": "connected", 846 | "thresholds": [ 847 | { 848 | "color": "green", 849 | "value": null 850 | }, 851 | { 852 | "color": "red", 853 | "value": 80 854 | } 855 | ], 856 | "unit": "humidity" 857 | }, 858 | "override": {}, 859 | "values": false 860 | }, 861 | "orientation": "horizontal", 862 | "showThresholdLabels": false, 863 | "showThresholdMarkers": true 864 | }, 865 | "pluginVersion": "6.5.0-pre", 866 | "targets": [ 867 | { 868 | "groupBy": [ 869 | { 870 | "params": [ 871 | "$__interval" 872 | ], 873 | "type": "time" 874 | }, 875 | { 876 | "params": [ 877 | "null" 878 | ], 879 | "type": "fill" 880 | } 881 | ], 882 | "measurement": "ruuvitag", 883 | "orderByTime": "ASC", 884 | "policy": "default", 885 | "refId": "A", 886 | "resultFormat": "time_series", 887 | "select": [ 888 | [ 889 | { 890 | "params": [ 891 | "value" 892 | ], 893 | "type": "field" 894 | }, 895 | { 896 | "params": [], 897 | "type": "mean" 898 | } 899 | ] 900 | ], 901 | "tags": [ 902 | { 903 | "key": "topic", 904 | "operator": "=", 905 | "value": "bt-mqtt-gateway/ruuvitag/tag1/humidity" 906 | } 907 | ] 908 | } 909 | ], 910 | "timeFrom": null, 911 | "timeShift": null, 912 | "title": "Humidity 1", 913 | "type": "gauge" 914 | }, 915 | { 916 | "cacheTimeout": null, 917 | "datasource": null, 918 | "gridPos": { 919 | "h": 5, 920 | "w": 3, 921 | "x": 15, 922 | "y": 8 923 | }, 924 | "id": 13, 925 | "links": [], 926 | "options": { 927 | "fieldOptions": { 928 | "calcs": [ 929 | "lastNotNull" 930 | ], 931 | "defaults": { 932 | "decimals": 1, 933 | "mappings": [ 934 | { 935 | "id": 0, 936 | "op": "=", 937 | "text": "N/A", 938 | "type": 1, 939 | "value": "null" 940 | } 941 | ], 942 | "max": 100, 943 | "min": 0, 944 | "nullValueMode": "connected", 945 | "thresholds": [ 946 | { 947 | "color": "green", 948 | "value": null 949 | }, 950 | { 951 | "color": "red", 952 | "value": 80 953 | } 954 | ], 955 | "unit": "humidity" 956 | }, 957 | "override": {}, 958 | "values": false 959 | }, 960 | "orientation": "horizontal", 961 | "showThresholdLabels": false, 962 | "showThresholdMarkers": true 963 | }, 964 | "pluginVersion": "6.5.0-pre", 965 | "targets": [ 966 | { 967 | "groupBy": [ 968 | { 969 | "params": [ 970 | "$__interval" 971 | ], 972 | "type": "time" 973 | }, 974 | { 975 | "params": [ 976 | "null" 977 | ], 978 | "type": "fill" 979 | } 980 | ], 981 | "measurement": "ruuvitag", 982 | "orderByTime": "ASC", 983 | "policy": "default", 984 | "refId": "A", 985 | "resultFormat": "time_series", 986 | "select": [ 987 | [ 988 | { 989 | "params": [ 990 | "value" 991 | ], 992 | "type": "field" 993 | }, 994 | { 995 | "params": [], 996 | "type": "mean" 997 | } 998 | ] 999 | ], 1000 | "tags": [ 1001 | { 1002 | "key": "topic", 1003 | "operator": "=", 1004 | "value": "bt-mqtt-gateway/ruuvitag/tag2/humidity" 1005 | } 1006 | ] 1007 | } 1008 | ], 1009 | "timeFrom": null, 1010 | "timeShift": null, 1011 | "title": "Humidity 2", 1012 | "type": "gauge" 1013 | }, 1014 | { 1015 | "cacheTimeout": null, 1016 | "datasource": null, 1017 | "gridPos": { 1018 | "h": 5, 1019 | "w": 3, 1020 | "x": 18, 1021 | "y": 8 1022 | }, 1023 | "id": 14, 1024 | "links": [], 1025 | "options": { 1026 | "fieldOptions": { 1027 | "calcs": [ 1028 | "lastNotNull" 1029 | ], 1030 | "defaults": { 1031 | "decimals": 1, 1032 | "mappings": [ 1033 | { 1034 | "id": 0, 1035 | "op": "=", 1036 | "text": "N/A", 1037 | "type": 1, 1038 | "value": "null" 1039 | } 1040 | ], 1041 | "max": 100, 1042 | "min": 0, 1043 | "nullValueMode": "connected", 1044 | "thresholds": [ 1045 | { 1046 | "color": "green", 1047 | "value": null 1048 | }, 1049 | { 1050 | "color": "red", 1051 | "value": 80 1052 | } 1053 | ], 1054 | "unit": "humidity" 1055 | }, 1056 | "override": {}, 1057 | "values": false 1058 | }, 1059 | "orientation": "horizontal", 1060 | "showThresholdLabels": false, 1061 | "showThresholdMarkers": true 1062 | }, 1063 | "pluginVersion": "6.5.0-pre", 1064 | "targets": [ 1065 | { 1066 | "groupBy": [ 1067 | { 1068 | "params": [ 1069 | "$__interval" 1070 | ], 1071 | "type": "time" 1072 | }, 1073 | { 1074 | "params": [ 1075 | "null" 1076 | ], 1077 | "type": "fill" 1078 | } 1079 | ], 1080 | "measurement": "ruuvitag", 1081 | "orderByTime": "ASC", 1082 | "policy": "default", 1083 | "refId": "A", 1084 | "resultFormat": "time_series", 1085 | "select": [ 1086 | [ 1087 | { 1088 | "params": [ 1089 | "value" 1090 | ], 1091 | "type": "field" 1092 | }, 1093 | { 1094 | "params": [], 1095 | "type": "mean" 1096 | } 1097 | ] 1098 | ], 1099 | "tags": [ 1100 | { 1101 | "key": "topic", 1102 | "operator": "=", 1103 | "value": "bt-mqtt-gateway/ruuvitag/tag3/humidity" 1104 | } 1105 | ] 1106 | } 1107 | ], 1108 | "timeFrom": null, 1109 | "timeShift": null, 1110 | "title": "Humidity 3", 1111 | "type": "gauge" 1112 | }, 1113 | { 1114 | "cacheTimeout": null, 1115 | "datasource": null, 1116 | "gridPos": { 1117 | "h": 5, 1118 | "w": 3, 1119 | "x": 21, 1120 | "y": 8 1121 | }, 1122 | "id": 15, 1123 | "links": [], 1124 | "options": { 1125 | "fieldOptions": { 1126 | "calcs": [ 1127 | "lastNotNull" 1128 | ], 1129 | "defaults": { 1130 | "decimals": 1, 1131 | "mappings": [ 1132 | { 1133 | "id": 0, 1134 | "op": "=", 1135 | "text": "N/A", 1136 | "type": 1, 1137 | "value": "null" 1138 | } 1139 | ], 1140 | "max": 100, 1141 | "min": 0, 1142 | "nullValueMode": "connected", 1143 | "thresholds": [ 1144 | { 1145 | "color": "green", 1146 | "value": null 1147 | }, 1148 | { 1149 | "color": "red", 1150 | "value": 80 1151 | } 1152 | ], 1153 | "unit": "humidity" 1154 | }, 1155 | "override": {}, 1156 | "values": false 1157 | }, 1158 | "orientation": "horizontal", 1159 | "showThresholdLabels": false, 1160 | "showThresholdMarkers": true 1161 | }, 1162 | "pluginVersion": "6.5.0-pre", 1163 | "targets": [ 1164 | { 1165 | "groupBy": [ 1166 | { 1167 | "params": [ 1168 | "$__interval" 1169 | ], 1170 | "type": "time" 1171 | }, 1172 | { 1173 | "params": [ 1174 | "null" 1175 | ], 1176 | "type": "fill" 1177 | } 1178 | ], 1179 | "measurement": "ruuvitag", 1180 | "orderByTime": "ASC", 1181 | "policy": "default", 1182 | "refId": "A", 1183 | "resultFormat": "time_series", 1184 | "select": [ 1185 | [ 1186 | { 1187 | "params": [ 1188 | "value" 1189 | ], 1190 | "type": "field" 1191 | }, 1192 | { 1193 | "params": [], 1194 | "type": "mean" 1195 | } 1196 | ] 1197 | ], 1198 | "tags": [ 1199 | { 1200 | "key": "topic", 1201 | "operator": "=", 1202 | "value": "bt-mqtt-gateway/ruuvitag/tag4/humidity" 1203 | } 1204 | ] 1205 | } 1206 | ], 1207 | "timeFrom": null, 1208 | "timeShift": null, 1209 | "title": "Humidity 4", 1210 | "type": "gauge" 1211 | }, 1212 | { 1213 | "cacheTimeout": null, 1214 | "datasource": null, 1215 | "gridPos": { 1216 | "h": 8, 1217 | "w": 12, 1218 | "x": 0, 1219 | "y": 13 1220 | }, 1221 | "id": 17, 1222 | "links": [], 1223 | "options": { 1224 | "displayMode": "lcd", 1225 | "fieldOptions": { 1226 | "calcs": [ 1227 | "lastNotNull" 1228 | ], 1229 | "defaults": { 1230 | "mappings": [], 1231 | "max": 3000, 1232 | "min": 0, 1233 | "thresholds": [ 1234 | { 1235 | "color": "red", 1236 | "value": null 1237 | }, 1238 | { 1239 | "color": "#EAB839", 1240 | "value": 2000 1241 | }, 1242 | { 1243 | "color": "green", 1244 | "value": 2500 1245 | } 1246 | ], 1247 | "unit": "mvolt" 1248 | }, 1249 | "override": {}, 1250 | "values": false 1251 | }, 1252 | "orientation": "horizontal" 1253 | }, 1254 | "pluginVersion": "6.5.0-pre", 1255 | "targets": [ 1256 | { 1257 | "alias": "RuuviTag 1", 1258 | "groupBy": [ 1259 | { 1260 | "params": [ 1261 | "$__interval" 1262 | ], 1263 | "type": "time" 1264 | }, 1265 | { 1266 | "params": [ 1267 | "null" 1268 | ], 1269 | "type": "fill" 1270 | } 1271 | ], 1272 | "measurement": "ruuvitag", 1273 | "orderByTime": "ASC", 1274 | "policy": "default", 1275 | "refId": "A", 1276 | "resultFormat": "time_series", 1277 | "select": [ 1278 | [ 1279 | { 1280 | "params": [ 1281 | "value" 1282 | ], 1283 | "type": "field" 1284 | }, 1285 | { 1286 | "params": [], 1287 | "type": "mean" 1288 | } 1289 | ] 1290 | ], 1291 | "tags": [ 1292 | { 1293 | "key": "topic", 1294 | "operator": "=", 1295 | "value": "bt-mqtt-gateway/ruuvitag/tag1/battery" 1296 | } 1297 | ] 1298 | }, 1299 | { 1300 | "alias": "RuuviTag 2", 1301 | "groupBy": [ 1302 | { 1303 | "params": [ 1304 | "$__interval" 1305 | ], 1306 | "type": "time" 1307 | }, 1308 | { 1309 | "params": [ 1310 | "null" 1311 | ], 1312 | "type": "fill" 1313 | } 1314 | ], 1315 | "measurement": "ruuvitag", 1316 | "orderByTime": "ASC", 1317 | "policy": "default", 1318 | "refId": "B", 1319 | "resultFormat": "time_series", 1320 | "select": [ 1321 | [ 1322 | { 1323 | "params": [ 1324 | "value" 1325 | ], 1326 | "type": "field" 1327 | }, 1328 | { 1329 | "params": [], 1330 | "type": "mean" 1331 | } 1332 | ] 1333 | ], 1334 | "tags": [ 1335 | { 1336 | "key": "topic", 1337 | "operator": "=", 1338 | "value": "bt-mqtt-gateway/ruuvitag/tag2/battery" 1339 | } 1340 | ] 1341 | }, 1342 | { 1343 | "alias": "RuuviTag 3", 1344 | "groupBy": [ 1345 | { 1346 | "params": [ 1347 | "$__interval" 1348 | ], 1349 | "type": "time" 1350 | }, 1351 | { 1352 | "params": [ 1353 | "null" 1354 | ], 1355 | "type": "fill" 1356 | } 1357 | ], 1358 | "measurement": "ruuvitag", 1359 | "orderByTime": "ASC", 1360 | "policy": "default", 1361 | "refId": "C", 1362 | "resultFormat": "time_series", 1363 | "select": [ 1364 | [ 1365 | { 1366 | "params": [ 1367 | "value" 1368 | ], 1369 | "type": "field" 1370 | }, 1371 | { 1372 | "params": [], 1373 | "type": "mean" 1374 | } 1375 | ] 1376 | ], 1377 | "tags": [ 1378 | { 1379 | "key": "topic", 1380 | "operator": "=", 1381 | "value": "bt-mqtt-gateway/ruuvitag/tag3/battery" 1382 | } 1383 | ] 1384 | }, 1385 | { 1386 | "alias": "RuuviTag 4", 1387 | "groupBy": [ 1388 | { 1389 | "params": [ 1390 | "$__interval" 1391 | ], 1392 | "type": "time" 1393 | }, 1394 | { 1395 | "params": [ 1396 | "null" 1397 | ], 1398 | "type": "fill" 1399 | } 1400 | ], 1401 | "measurement": "ruuvitag", 1402 | "orderByTime": "ASC", 1403 | "policy": "default", 1404 | "refId": "D", 1405 | "resultFormat": "time_series", 1406 | "select": [ 1407 | [ 1408 | { 1409 | "params": [ 1410 | "value" 1411 | ], 1412 | "type": "field" 1413 | }, 1414 | { 1415 | "params": [], 1416 | "type": "mean" 1417 | } 1418 | ] 1419 | ], 1420 | "tags": [ 1421 | { 1422 | "key": "topic", 1423 | "operator": "=", 1424 | "value": "bt-mqtt-gateway/ruuvitag/tag4/battery" 1425 | } 1426 | ] 1427 | } 1428 | ], 1429 | "timeFrom": null, 1430 | "timeShift": null, 1431 | "title": "Battery", 1432 | "type": "bargauge" 1433 | } 1434 | ], 1435 | "refresh": false, 1436 | "schemaVersion": 20, 1437 | "style": "dark", 1438 | "tags": [], 1439 | "templating": { 1440 | "list": [] 1441 | }, 1442 | "time": { 1443 | "from": "now-6h", 1444 | "to": "now" 1445 | }, 1446 | "timepicker": { 1447 | "refresh_intervals": [ 1448 | "5s", 1449 | "10s", 1450 | "30s", 1451 | "1m", 1452 | "5m", 1453 | "15m", 1454 | "30m", 1455 | "1h", 1456 | "2h", 1457 | "1d" 1458 | ] 1459 | }, 1460 | "timezone": "", 1461 | "title": "RuuviTag Dashboard", 1462 | "uid": "jcHKTQggz", 1463 | "version": 1 1464 | } 1465 | --------------------------------------------------------------------------------