From b52ad9caee0af2fc45761efa015daf59f8a113a4 Mon Sep 17 00:00:00 2001 From: Michal Maly Date: Sat, 14 Mar 2026 00:00:20 +0100 Subject: [PATCH] Add Grafana summary and detail dashboards --- README.md | 27 + .../tasmota-device-detail-dashboard.json | 1700 +++++++++++++++++ .../tasmota-device-summary-dashboard.json | 630 ++++++ docs/grafana/tasmota-simple-dashboard.json | 1519 +++++++++++++++ 4 files changed, 3876 insertions(+) create mode 100644 docs/grafana/tasmota-device-detail-dashboard.json create mode 100644 docs/grafana/tasmota-device-summary-dashboard.json create mode 100644 docs/grafana/tasmota-simple-dashboard.json diff --git a/README.md b/README.md index 36643ff..79e730c 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/docs/grafana/tasmota-device-detail-dashboard.json b/docs/grafana/tasmota-device-detail-dashboard.json new file mode 100644 index 0000000..24b8e80 --- /dev/null +++ b/docs/grafana/tasmota-device-detail-dashboard.json @@ -0,0 +1,1700 @@ +{ + "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": "Dedicated per-device drilldown for a single Tasmota device.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "targetBlank": false, + "title": "Back to device summary", + "type": "link", + "url": "/d/tasmota-device-summary/tasmota-device-summary?refresh=10s" + } + ], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 102, + "panels": [], + "title": "Selected Device Detail", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dateTimeAsIso" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 7, + "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 to_unixtime(max(time)) * 1000 AS last_seen FROM (SELECT time FROM tasmota_state WHERE device = '${device}' UNION ALL SELECT time FROM tasmota_sensor WHERE device = '${device}')", + "rawQuery": true, + "rawSql": "SELECT to_unixtime(max(time)) * 1000 AS last_seen FROM (SELECT time FROM tasmota_state WHERE device = '${device}' UNION ALL SELECT time FROM tasmota_sensor WHERE device = '${device}')", + "refId": "A" + } + ], + "title": "Selected Last Seen", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 300 + }, + { + "color": "red", + "value": 1800 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 1 + }, + "id": 8, + "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 to_unixtime(now()) - to_unixtime(max(time)) AS last_seen_age_s FROM (SELECT time FROM tasmota_state WHERE device = '${device}' UNION ALL SELECT time FROM tasmota_sensor WHERE device = '${device}')", + "rawQuery": true, + "rawSql": "SELECT to_unixtime(now()) - to_unixtime(max(time)) AS last_seen_age_s FROM (SELECT time FROM tasmota_state WHERE device = '${device}' UNION ALL SELECT time FROM tasmota_sensor WHERE device = '${device}')", + "refId": "A" + } + ], + "title": "Seconds Since Last Message", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "watt" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 1 + }, + "id": 9, + "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 energy_power AS current_draw_w FROM tasmota_sensor WHERE device = '${device}' AND energy_power IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT energy_power AS current_draw_w FROM tasmota_sensor WHERE device = '${device}' AND energy_power IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "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": 6, + "x": 18, + "y": 1 + }, + "id": 10, + "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 energy_total AS total_draw_kwh FROM tasmota_sensor WHERE device = '${device}' AND energy_total IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT energy_total AS total_draw_kwh FROM tasmota_sensor WHERE device = '${device}' AND energy_total IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Total Draw", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 4, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "watt" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(energy_power) AS power_w FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND energy_power IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(energy_power) AS power_w FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND energy_power IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Selected Device Power Draw", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "volt" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(energy_voltage) AS voltage_v FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND energy_voltage IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(energy_voltage) AS voltage_v FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND energy_voltage IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Selected Device Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 13 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(analog_temperature) AS temperature_c FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND analog_temperature IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(analog_temperature) AS temperature_c FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND analog_temperature IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Analog Temperature", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dBm" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 13 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(wifi_signal) AS wifi_signal_dbm FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND wifi_signal IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(wifi_signal) AS wifi_signal_dbm FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND wifi_signal IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "WiFi Signal", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "hour" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 13 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, max(uptime_sec) / 3600.0 AS uptime_hours FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND uptime_sec IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, max(uptime_sec) / 3600.0 AS uptime_hours FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND uptime_sec IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Uptime", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "kwatth" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 21 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, max(energy_total) AS energy_total_kwh FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND energy_total IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, max(energy_total) AS energy_total_kwh FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND energy_total IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Energy Total", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 21 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(analog_a0) AS analog_a0_value FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND analog_a0 IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(analog_a0) AS analog_a0_value FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' AND analog_a0 IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Analog A0", + "type": "timeseries" + }, + { + "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": "time" + }, + "properties": [ + { + "id": "unit", + "value": "dateTimeAsIso" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "wifi_signal" + }, + "properties": [ + { + "id": "unit", + "value": "dBm" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "uptime_sec" + }, + "properties": [ + { + "id": "unit", + "value": "s" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 29 + }, + "id": 18, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "time" + } + ] + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT time, device, power, power1, power2, power3, power4, wifi_signal, uptime_sec FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' ORDER BY time DESC LIMIT 50", + "rawQuery": true, + "rawSql": "SELECT time, device, power, power1, power2, power3, power4, wifi_signal, uptime_sec FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' ORDER BY time DESC LIMIT 50", + "refId": "A" + } + ], + "title": "Recent State Snapshots", + "type": "table" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 37 + }, + "id": 103, + "panels": [], + "title": "Selected Device Relay Status", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 38 + }, + "id": 19, + "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 CASE WHEN upper(power) = 'ON' THEN 1 WHEN upper(power) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power) = 'ON' THEN 1 WHEN upper(power) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay Power", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 38 + }, + "id": 20, + "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 CASE WHEN upper(power1) = 'ON' THEN 1 WHEN upper(power1) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power1 IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power1) = 'ON' THEN 1 WHEN upper(power1) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power1 IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay 1", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 38 + }, + "id": 21, + "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 CASE WHEN upper(power2) = 'ON' THEN 1 WHEN upper(power2) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power2 IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power2) = 'ON' THEN 1 WHEN upper(power2) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power2 IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay 2", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 38 + }, + "id": 22, + "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 CASE WHEN upper(power3) = 'ON' THEN 1 WHEN upper(power3) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power3 IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power3) = 'ON' THEN 1 WHEN upper(power3) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power3 IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay 3", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 38 + }, + "id": 23, + "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 CASE WHEN upper(power4) = 'ON' THEN 1 WHEN upper(power4) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power4 IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power4) = 'ON' THEN 1 WHEN upper(power4) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE device = '${device}' AND power4 IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay 4", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 42 + }, + "id": 104, + "panels": [], + "title": "Relay History", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 12, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineWidth": 2, + "pointSize": 3, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 43 + }, + "id": 25, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT time, CASE WHEN upper(power) = 'ON' THEN 1 WHEN upper(power) = 'OFF' THEN 0 ELSE NULL END AS relay_power_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power IS NOT NULL ORDER BY time", + "rawQuery": true, + "rawSql": "SELECT time, CASE WHEN upper(power) = 'ON' THEN 1 WHEN upper(power) = 'OFF' THEN 0 ELSE NULL END AS relay_power_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power IS NOT NULL ORDER BY time", + "refId": "A" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT time, CASE WHEN upper(power1) = 'ON' THEN 1 WHEN upper(power1) = 'OFF' THEN 0 ELSE NULL END AS relay_1_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power1 IS NOT NULL ORDER BY time", + "rawQuery": true, + "rawSql": "SELECT time, CASE WHEN upper(power1) = 'ON' THEN 1 WHEN upper(power1) = 'OFF' THEN 0 ELSE NULL END AS relay_1_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power1 IS NOT NULL ORDER BY time", + "refId": "B" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT time, CASE WHEN upper(power2) = 'ON' THEN 1 WHEN upper(power2) = 'OFF' THEN 0 ELSE NULL END AS relay_2_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power2 IS NOT NULL ORDER BY time", + "rawQuery": true, + "rawSql": "SELECT time, CASE WHEN upper(power2) = 'ON' THEN 1 WHEN upper(power2) = 'OFF' THEN 0 ELSE NULL END AS relay_2_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power2 IS NOT NULL ORDER BY time", + "refId": "C" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT time, CASE WHEN upper(power3) = 'ON' THEN 1 WHEN upper(power3) = 'OFF' THEN 0 ELSE NULL END AS relay_3_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power3 IS NOT NULL ORDER BY time", + "rawQuery": true, + "rawSql": "SELECT time, CASE WHEN upper(power3) = 'ON' THEN 1 WHEN upper(power3) = 'OFF' THEN 0 ELSE NULL END AS relay_3_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power3 IS NOT NULL ORDER BY time", + "refId": "D" + }, + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT time, CASE WHEN upper(power4) = 'ON' THEN 1 WHEN upper(power4) = 'OFF' THEN 0 ELSE NULL END AS relay_4_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power4 IS NOT NULL ORDER BY time", + "rawQuery": true, + "rawSql": "SELECT time, CASE WHEN upper(power4) = 'ON' THEN 1 WHEN upper(power4) = 'OFF' THEN 0 ELSE NULL END AS relay_4_state FROM tasmota_state WHERE $__timeFilter(time) AND device = '${device}' AND power4 IS NOT NULL ORDER BY time", + "refId": "E" + } + ], + "title": "Selected Device Relay History", + "type": "timeseries" + }, + { + "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": "time" + }, + "properties": [ + { + "id": "unit", + "value": "dateTimeAsIso" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "energy_power" + }, + "properties": [ + { + "id": "unit", + "value": "watt" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "energy_voltage" + }, + "properties": [ + { + "id": "unit", + "value": "volt" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "energy_today" + }, + "properties": [ + { + "id": "unit", + "value": "kwatth" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "energy_total" + }, + "properties": [ + { + "id": "unit", + "value": "kwatth" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "analog_temperature" + }, + "properties": [ + { + "id": "unit", + "value": "celsius" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 51 + }, + "id": 24, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "time" + } + ] + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "dfftuvrrhv6kgb" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT time, device, energy_power, energy_voltage, energy_today, energy_total, analog_temperature, analog_a0, temp_unit FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' ORDER BY time DESC LIMIT 50", + "rawQuery": true, + "rawSql": "SELECT time, device, energy_power, energy_voltage, energy_today, energy_total, analog_temperature, analog_a0, temp_unit FROM tasmota_sensor WHERE $__timeFilter(time) AND device = '${device}' ORDER BY time DESC LIMIT 50", + "refId": "A" + } + ], + "title": "Recent Sensor Snapshots", + "type": "table" + } + ], + "refresh": "30s", + "schemaVersion": 39, + "style": "dark", + "tags": [ + "mqtt", + "tasmota", + "influxdb" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "", + "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": false, + "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 Detail", + "uid": "tasmota-device-detail", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/docs/grafana/tasmota-device-summary-dashboard.json b/docs/grafana/tasmota-device-summary-dashboard.json new file mode 100644 index 0000000..0e3d61f --- /dev/null +++ b/docs/grafana/tasmota-device-summary-dashboard.json @@ -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": "" +} \ No newline at end of file diff --git a/docs/grafana/tasmota-simple-dashboard.json b/docs/grafana/tasmota-simple-dashboard.json new file mode 100644 index 0000000..a314e5d --- /dev/null +++ b/docs/grafana/tasmota-simple-dashboard.json @@ -0,0 +1,1519 @@ +{ + "__inputs": [ + { + "name": "DS_INFLUXDB", + "label": "InfluxDB", + "type": "datasource", + "pluginId": "influxdb", + "pluginName": "InfluxDB" + } + ], + "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": "Simple overview of Tasmota STATE and SENSOR data written by mqqt-scrubber into InfluxDB v3.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 17, + "panels": [], + "title": "Fleet Summary", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 1 + }, + { + "color": "red", + "value": 5 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT count(DISTINCT device) AS device_count FROM tasmota_state WHERE $__timeFilter(time)", + "rawQuery": true, + "rawSql": "SELECT count(DISTINCT device) AS device_count FROM tasmota_state WHERE $__timeFilter(time)", + "refId": "A" + } + ], + "title": "Devices Reporting State", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 1 + }, + { + "color": "red", + "value": 3 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT count(DISTINCT device) AS sensor_devices FROM tasmota_sensor WHERE $__timeFilter(time)", + "rawQuery": true, + "rawSql": "SELECT count(DISTINCT device) AS sensor_devices FROM tasmota_sensor WHERE $__timeFilter(time)", + "refId": "A" + } + ], + "title": "Devices Reporting Sensor Data", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT count(*) AS state_messages FROM tasmota_state WHERE $__timeFilter(time)", + "rawQuery": true, + "rawSql": "SELECT count(*) AS state_messages FROM tasmota_state WHERE $__timeFilter(time)", + "refId": "A" + } + ], + "title": "State Messages", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 18, + "panels": [], + "title": "Selected Device", + "type": "row" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 6 + }, + "id": 12, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT CASE WHEN upper(power) = 'ON' THEN 1 WHEN upper(power) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power) = 'ON' THEN 1 WHEN upper(power) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay Power", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 6 + }, + "id": 13, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT CASE WHEN upper(power1) = 'ON' THEN 1 WHEN upper(power1) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power1 IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power1) = 'ON' THEN 1 WHEN upper(power1) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power1 IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay 1", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 6 + }, + "id": 14, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT CASE WHEN upper(power2) = 'ON' THEN 1 WHEN upper(power2) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power2 IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power2) = 'ON' THEN 1 WHEN upper(power2) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power2 IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay 2", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 6 + }, + "id": 15, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT CASE WHEN upper(power3) = 'ON' THEN 1 WHEN upper(power3) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power3 IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power3) = 'ON' THEN 1 WHEN upper(power3) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power3 IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay 3", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "text": "OFF" + }, + "1": { + "color": "green", + "text": "ON" + }, + "OFF": { + "color": "red", + "text": "OFF" + }, + "ON": { + "color": "green", + "text": "ON" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 6 + }, + "id": 16, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT CASE WHEN upper(power4) = 'ON' THEN 1 WHEN upper(power4) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power4 IS NOT NULL ORDER BY time DESC LIMIT 1", + "rawQuery": true, + "rawSql": "SELECT CASE WHEN upper(power4) = 'ON' THEN 1 WHEN upper(power4) = 'OFF' THEN 0 ELSE NULL END AS relay_state FROM tasmota_state WHERE (('${relay_device}' != '*' AND device = '${relay_device}') OR ('${relay_device}' = '*' AND '${device}' != '*' AND device = '${device}') OR ('${relay_device}' = '*' AND '${device}' = '*')) AND power4 IS NOT NULL ORDER BY time DESC LIMIT 1", + "refId": "A" + } + ], + "title": "Relay 4", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dateTimeAsIso" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 8, + "x": 0, + "y": 10 + }, + "id": 19, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT max(time) AS last_seen FROM (SELECT time FROM tasmota_state WHERE ('${device}' = '*' OR device = '${device}') UNION ALL SELECT time FROM tasmota_sensor WHERE ('${device}' = '*' OR device = '${device}'))", + "rawQuery": true, + "rawSql": "SELECT max(time) AS last_seen FROM (SELECT time FROM tasmota_state WHERE ('${device}' = '*' OR device = '${device}') UNION ALL SELECT time FROM tasmota_sensor WHERE ('${device}' = '*' OR device = '${device}'))", + "refId": "A" + } + ], + "title": "Last Seen", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 300 + }, + { + "color": "red", + "value": 1800 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 8, + "x": 8, + "y": 10 + }, + "id": 20, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT to_unixtime(now()) - to_unixtime(max(time)) AS last_seen_age_s FROM (SELECT time FROM tasmota_state WHERE ('${device}' = '*' OR device = '${device}') UNION ALL SELECT time FROM tasmota_sensor WHERE ('${device}' = '*' OR device = '${device}'))", + "rawQuery": true, + "rawSql": "SELECT to_unixtime(now()) - to_unixtime(max(time)) AS last_seen_age_s FROM (SELECT time FROM tasmota_state WHERE ('${device}' = '*' OR device = '${device}') UNION ALL SELECT time FROM tasmota_sensor WHERE ('${device}' = '*' OR device = '${device}'))", + "refId": "A" + } + ], + "title": "Seconds Since Last Message", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 8, + "x": 16, + "y": 10 + }, + "id": 21, + "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": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT count(*) AS messages_in_range FROM (SELECT time FROM tasmota_state WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') UNION ALL SELECT time FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}'))", + "rawQuery": true, + "rawSql": "SELECT count(*) AS messages_in_range FROM (SELECT time FROM tasmota_state WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') UNION ALL SELECT time FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}'))", + "refId": "A" + } + ], + "title": "Messages In Range", + "type": "stat" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 4, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "watt" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(energy_power) AS power_w FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND energy_power IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(energy_power) AS power_w FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND energy_power IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Selected Device Power Draw", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "volt" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(energy_voltage) AS voltage_v FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND energy_voltage IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(energy_voltage) AS voltage_v FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND energy_voltage IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Selected Device Voltage", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 22 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(analog_temperature) AS temperature_c FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND analog_temperature IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(analog_temperature) AS temperature_c FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND analog_temperature IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Analog Temperature", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "dBm" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 22 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(wifi_signal) AS wifi_signal_dbm FROM tasmota_state WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND wifi_signal IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(wifi_signal) AS wifi_signal_dbm FROM tasmota_state WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND wifi_signal IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "WiFi Signal", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "hour" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 22 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, max(uptime_sec) / 3600.0 AS uptime_hours FROM tasmota_state WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND uptime_sec IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, max(uptime_sec) / 3600.0 AS uptime_hours FROM tasmota_state WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND uptime_sec IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Uptime", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "kwatth" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 30 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, max(energy_total) AS energy_total_kwh FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND energy_total IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, max(energy_total) AS energy_total_kwh FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND energy_total IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Energy Total", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 30 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "time_series", + "query": "SELECT $__dateBin(time) AS time, device, avg(analog_a0) AS analog_a0_value FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND analog_a0 IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "rawQuery": true, + "rawSql": "SELECT $__dateBin(time) AS time, device, avg(analog_a0) AS analog_a0_value FROM tasmota_sensor WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') AND analog_a0 IS NOT NULL GROUP BY 1, device ORDER BY 1, device", + "refId": "A" + } + ], + "title": "Analog A0", + "type": "timeseries" + }, + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 38 + }, + "id": 11, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "time" + } + ] + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "editorMode": "code", + "format": "table", + "query": "SELECT time, device, power, power1, power2, power3, power4, wifi_signal, uptime_sec FROM tasmota_state WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') ORDER BY time DESC LIMIT 50", + "rawQuery": true, + "rawSql": "SELECT time, device, power, power1, power2, power3, power4, wifi_signal, uptime_sec FROM tasmota_state WHERE $__timeFilter(time) AND ('${device}' = '*' OR device = '${device}') ORDER BY time DESC LIMIT 50", + "refId": "A" + } + ], + "title": "Recent State Snapshots", + "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": "${DS_INFLUXDB}" + }, + "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" + }, + { + "allValue": "*", + "current": { + "selected": true, + "text": "All", + "value": "*" + }, + "datasource": { + "type": "influxdb", + "uid": "${DS_INFLUXDB}" + }, + "definition": "SELECT DISTINCT device FROM tasmota_state WHERE power IS NOT NULL OR power1 IS NOT NULL OR power2 IS NOT NULL OR power3 IS NOT NULL OR power4 IS NOT NULL ORDER BY device", + "hide": 0, + "includeAll": true, + "label": "Relay Device", + "multi": false, + "name": "relay_device", + "options": [], + "query": { + "query": "SELECT DISTINCT device FROM tasmota_state WHERE power IS NOT NULL OR power1 IS NOT NULL OR power2 IS NOT NULL OR power3 IS NOT NULL OR power4 IS NOT NULL ORDER BY device", + "refId": "InfluxDBVariableQueryEditor-RelayDevice" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Tasmota Overview", + "uid": "tasmota-overview", + "version": 2, + "weekStart": "" +} \ No newline at end of file