Add Grafana summary and detail dashboards

This commit is contained in:
2026-03-14 00:00:20 +01:00
parent 3de376a2e1
commit b52ad9caee
4 changed files with 3876 additions and 0 deletions
+27
View File
@@ -108,6 +108,33 @@ docker-compose up --build -d
It expects a local `config.json` next to the compose file and exposes port `8080` for health and metrics.
## Grafana
A simple starter dashboard for the current Tasmota schema is included at `docs/grafana/tasmota-simple-dashboard.json`.
Assumptions:
- Grafana uses the built-in `InfluxDB` datasource plugin
- the datasource is configured for InfluxDB v3 with query language set to `SQL`
- the datasource points at the same database name configured in `MQTT_SCRUBBER_INFLUX_DATABASE`
Import the dashboard JSON and bind the `DS_INFLUXDB` input to your InfluxDB datasource.
The dashboard includes a `Device` variable for the selected-device section and a `Relay Device` variable that only lists devices publishing relay fields.
Use `Device` for the health row, time series, and recent-state table. Use `Relay Device` for the relay status row so non-relay devices do not clutter that picker.
The dashboard currently visualizes fields that are known to be emitted by the parser today and that exist with the default topic subscription set:
- `tasmota_sensor`: `energy_power`, `energy_voltage`, `energy_total`, `analog_temperature`, `analog_a0`
- `tasmota_state`: `wifi_signal`, `uptime_sec`
If you also want LWT availability panels, add `tele/+/LWT` to the configured MQTT topics so the `tasmota_lwt` measurement is created.
Relay panels use the latest `power`, `power1`, `power2`, `power3`, and `power4` values from `tasmota_state`. Devices that do not publish a given relay field will show no value for that panel.
The dashboard is split into a fleet summary section and a selected-device section. The selected-device section also includes `Last Seen`, `Seconds Since Last Message`, and `Messages In Range` panels derived from both `tasmota_state` and `tasmota_sensor` timestamps.
## Notes
- The repo name is kept as `mqqt-scrubber` to match the existing folder.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,630 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"description": "Fleet overview of known Tasmota devices with drilldown links into a dedicated per-device dashboard.",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"panels": [
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 100,
"panels": [],
"title": "Fleet Summary",
"type": "row"
},
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 5,
"x": 0,
"y": 1
},
"id": 1,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "center",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "11.1.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"editorMode": "code",
"format": "table",
"query": "SELECT count(DISTINCT device) AS total_devices FROM (SELECT device FROM tasmota_state WHERE $__timeFilter(time) UNION ALL SELECT device FROM tasmota_sensor WHERE $__timeFilter(time))",
"rawQuery": true,
"rawSql": "SELECT count(DISTINCT device) AS total_devices FROM (SELECT device FROM tasmota_state WHERE $__timeFilter(time) UNION ALL SELECT device FROM tasmota_sensor WHERE $__timeFilter(time))",
"refId": "A"
}
],
"title": "Total Devices",
"type": "stat"
},
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 5,
"x": 5,
"y": 1
},
"id": 2,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "center",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "11.1.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"editorMode": "code",
"format": "table",
"query": "SELECT count(DISTINCT device) AS power_devices FROM tasmota_sensor WHERE $__timeFilter(time) AND (energy_power IS NOT NULL OR energy_today IS NOT NULL OR energy_total IS NOT NULL)",
"rawQuery": true,
"rawSql": "SELECT count(DISTINCT device) AS power_devices FROM tasmota_sensor WHERE $__timeFilter(time) AND (energy_power IS NOT NULL OR energy_today IS NOT NULL OR energy_total IS NOT NULL)",
"refId": "A"
}
],
"title": "Devices With Energy Metrics",
"type": "stat"
},
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "orange",
"value": 1000
},
{
"color": "red",
"value": 3000
}
]
},
"unit": "watt"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 5,
"x": 10,
"y": 1
},
"id": 3,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "center",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "11.1.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"editorMode": "code",
"format": "table",
"query": "SELECT coalesce(sum(sensor.energy_power), 0) AS fleet_current_draw_w FROM (SELECT s.device, s.energy_power FROM tasmota_sensor s INNER JOIN (SELECT device, max(time) AS time FROM tasmota_sensor WHERE $__timeFilter(time) GROUP BY device) latest ON s.device = latest.device AND s.time = latest.time) sensor",
"rawQuery": true,
"rawSql": "SELECT coalesce(sum(sensor.energy_power), 0) AS fleet_current_draw_w FROM (SELECT s.device, s.energy_power FROM tasmota_sensor s INNER JOIN (SELECT device, max(time) AS time FROM tasmota_sensor WHERE $__timeFilter(time) GROUP BY device) latest ON s.device = latest.device AND s.time = latest.time) sensor",
"refId": "A"
}
],
"title": "Fleet Current Draw",
"type": "stat"
},
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "kwatth"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 4,
"x": 15,
"y": 1
},
"id": 4,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "center",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "11.1.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"editorMode": "code",
"format": "table",
"query": "SELECT coalesce(sum(sensor.energy_today), 0) AS fleet_daily_draw_kwh FROM (SELECT s.device, s.energy_today FROM tasmota_sensor s INNER JOIN (SELECT device, max(time) AS time FROM tasmota_sensor WHERE $__timeFilter(time) GROUP BY device) latest ON s.device = latest.device AND s.time = latest.time) sensor",
"rawQuery": true,
"rawSql": "SELECT coalesce(sum(sensor.energy_today), 0) AS fleet_daily_draw_kwh FROM (SELECT s.device, s.energy_today FROM tasmota_sensor s INNER JOIN (SELECT device, max(time) AS time FROM tasmota_sensor WHERE $__timeFilter(time) GROUP BY device) latest ON s.device = latest.device AND s.time = latest.time) sensor",
"refId": "A"
}
],
"title": "Fleet Daily Draw",
"type": "stat"
},
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "kwatth"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 5,
"x": 19,
"y": 1
},
"id": 5,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "center",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "11.1.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"editorMode": "code",
"format": "table",
"query": "SELECT coalesce(sum(sensor.energy_total), 0) AS fleet_total_draw_kwh FROM (SELECT s.device, s.energy_total FROM tasmota_sensor s INNER JOIN (SELECT device, max(time) AS time FROM tasmota_sensor WHERE $__timeFilter(time) GROUP BY device) latest ON s.device = latest.device AND s.time = latest.time) sensor",
"rawQuery": true,
"rawSql": "SELECT coalesce(sum(sensor.energy_total), 0) AS fleet_total_draw_kwh FROM (SELECT s.device, s.energy_total FROM tasmota_sensor s INNER JOIN (SELECT device, max(time) AS time FROM tasmota_sensor WHERE $__timeFilter(time) GROUP BY device) latest ON s.device = latest.device AND s.time = latest.time) sensor",
"refId": "A"
}
],
"title": "Fleet Total Draw",
"type": "stat"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 5
},
"id": 101,
"panels": [],
"title": "Device Grid",
"type": "row"
},
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "device"
},
"properties": [
{
"id": "custom.width",
"value": 180
},
{
"id": "links",
"value": [
{
"targetBlank": false,
"title": "Open device details",
"url": "/d/tasmota-device-detail/tasmota-device-detail?var-device=${__value.raw}&${__url_time_range}&timezone=browser&refresh=10s"
}
]
}
]
},
{
"matcher": {
"id": "byName",
"options": "last_seen"
},
"properties": [
{
"id": "unit",
"value": "dateTimeAsIso"
},
{
"id": "custom.width",
"value": 190
}
]
},
{
"matcher": {
"id": "byName",
"options": "age_s"
},
"properties": [
{
"id": "unit",
"value": "s"
},
{
"id": "custom.width",
"value": 120
}
]
},
{
"matcher": {
"id": "byName",
"options": "current_draw_w"
},
"properties": [
{
"id": "unit",
"value": "watt"
},
{
"id": "custom.width",
"value": 130
}
]
},
{
"matcher": {
"id": "byName",
"options": "daily_draw_kwh"
},
"properties": [
{
"id": "unit",
"value": "kwatth"
},
{
"id": "custom.width",
"value": 130
}
]
},
{
"matcher": {
"id": "byName",
"options": "total_draw_kwh"
},
"properties": [
{
"id": "unit",
"value": "kwatth"
},
{
"id": "custom.width",
"value": 130
}
]
},
{
"matcher": {
"id": "byName",
"options": "wifi_signal_dbm"
},
"properties": [
{
"id": "unit",
"value": "dBm"
},
{
"id": "custom.width",
"value": 130
}
]
},
{
"matcher": {
"id": "byName",
"options": "uptime_hours"
},
"properties": [
{
"id": "unit",
"value": "hour"
},
{
"id": "custom.width",
"value": 120
}
]
}
]
},
"gridPos": {
"h": 14,
"w": 24,
"x": 0,
"y": 6
},
"id": 6,
"options": {
"cellHeight": "md",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": [
{
"desc": false,
"displayName": "device"
}
]
},
"pluginVersion": "11.1.0",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"editorMode": "code",
"format": "table",
"query": "SELECT devices.device, devices.last_seen, to_unixtime(now()) - to_unixtime(devices.last_seen) AS age_s, sensor.energy_power AS current_draw_w, sensor.energy_today AS daily_draw_kwh, sensor.energy_total AS total_draw_kwh, state.wifi_signal AS wifi_signal_dbm, state.uptime_sec / 3600.0 AS uptime_hours FROM (SELECT device, max(time) AS last_seen FROM (SELECT device, time FROM tasmota_state WHERE $__timeFilter(time) UNION ALL SELECT device, time FROM tasmota_sensor WHERE $__timeFilter(time)) all_messages GROUP BY device) devices LEFT JOIN (SELECT s.device, s.time, s.energy_power, s.energy_today, s.energy_total FROM tasmota_sensor s INNER JOIN (SELECT device, max(time) AS time FROM tasmota_sensor WHERE $__timeFilter(time) GROUP BY device) latest_sensor ON s.device = latest_sensor.device AND s.time = latest_sensor.time) sensor ON devices.device = sensor.device LEFT JOIN (SELECT st.device, st.time, st.wifi_signal, st.uptime_sec FROM tasmota_state st INNER JOIN (SELECT device, max(time) AS time FROM tasmota_state WHERE $__timeFilter(time) GROUP BY device) latest_state ON st.device = latest_state.device AND st.time = latest_state.time) state ON devices.device = state.device ORDER BY devices.device",
"rawQuery": true,
"rawSql": "SELECT devices.device, devices.last_seen, to_unixtime(now()) - to_unixtime(devices.last_seen) AS age_s, sensor.energy_power AS current_draw_w, sensor.energy_today AS daily_draw_kwh, sensor.energy_total AS total_draw_kwh, state.wifi_signal AS wifi_signal_dbm, state.uptime_sec / 3600.0 AS uptime_hours FROM (SELECT device, max(time) AS last_seen FROM (SELECT device, time FROM tasmota_state WHERE $__timeFilter(time) UNION ALL SELECT device, time FROM tasmota_sensor WHERE $__timeFilter(time)) all_messages GROUP BY device) devices LEFT JOIN (SELECT s.device, s.time, s.energy_power, s.energy_today, s.energy_total FROM tasmota_sensor s INNER JOIN (SELECT device, max(time) AS time FROM tasmota_sensor WHERE $__timeFilter(time) GROUP BY device) latest_sensor ON s.device = latest_sensor.device AND s.time = latest_sensor.time) sensor ON devices.device = sensor.device LEFT JOIN (SELECT st.device, st.time, st.wifi_signal, st.uptime_sec FROM tasmota_state st INNER JOIN (SELECT device, max(time) AS time FROM tasmota_state WHERE $__timeFilter(time) GROUP BY device) latest_state ON st.device = latest_state.device AND st.time = latest_state.time) state ON devices.device = state.device ORDER BY devices.device",
"refId": "A"
}
],
"title": "Latest Device Summary",
"type": "table"
}
],
"refresh": "30s",
"schemaVersion": 39,
"style": "dark",
"tags": [
"mqtt",
"tasmota",
"influxdb"
],
"templating": {
"list": [
{
"allValue": "*",
"current": {
"selected": true,
"text": "All",
"value": "*"
},
"datasource": {
"type": "influxdb",
"uid": "dfftuvrrhv6kgb"
},
"definition": "SELECT device FROM (SELECT DISTINCT device FROM tasmota_state UNION SELECT DISTINCT device FROM tasmota_sensor) ORDER BY device",
"hide": 0,
"includeAll": true,
"label": "Device",
"multi": false,
"name": "device",
"options": [],
"query": {
"query": "SELECT device FROM (SELECT DISTINCT device FROM tasmota_state UNION SELECT DISTINCT device FROM tasmota_sensor) ORDER BY device",
"refId": "InfluxDBVariableQueryEditor-VariableQuery"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
}
]
},
"time": {
"from": "now-24h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "Tasmota Device Summary",
"uid": "tasmota-device-summary",
"version": 6,
"weekStart": ""
}
File diff suppressed because it is too large Load Diff