feat(monitoring): add prometheus metrics, alerts, and dashboard for taupi fan
This commit is contained in:
parent
1ad356a7b4
commit
6627372ec0
|
|
@ -0,0 +1,266 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: grafana-dashboard-taupi-fan
|
||||
namespace: monitoring
|
||||
labels:
|
||||
grafana_dashboard: '1'
|
||||
annotations:
|
||||
grafana_dashboard_folder: 🏠 Home
|
||||
data:
|
||||
taupi-fan.json: |
|
||||
{
|
||||
"uid": "taupi-fan",
|
||||
"title": "💨 Cellar Fan (TAUPI)",
|
||||
"tags": ["climate", "fan", "cellar", "prometheus"],
|
||||
"timezone": "browser",
|
||||
"schemaVersion": 39,
|
||||
"version": 1,
|
||||
"refresh": "30s",
|
||||
"editable": false,
|
||||
"graphTooltip": 1,
|
||||
"time": {
|
||||
"from": "now-24h",
|
||||
"to": "now"
|
||||
},
|
||||
"panels": [
|
||||
{
|
||||
"title": "⚡ Status & Controls",
|
||||
"type": "row",
|
||||
"gridPos": {"h": 1, "w": 24, "x": 0, "y": 0},
|
||||
"collapsed": false
|
||||
},
|
||||
{
|
||||
"title": "Fan Power Relay",
|
||||
"type": "stat",
|
||||
"datasource": {"type": "prometheus", "uid": "prometheus"},
|
||||
"gridPos": {"h": 4, "w": 6, "x": 0, "y": 1},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{"type": "value", "options": {"0": {"text": "OFF", "color": "blue"}, "1": {"text": "ON", "color": "orange"}}}
|
||||
],
|
||||
"color": {"mode": "thresholds"}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"graphMode": "none",
|
||||
"colorMode": "background",
|
||||
"textMode": "auto",
|
||||
"reduceOptions": {"values": false, "calcs": ["lastNotNull"]}
|
||||
},
|
||||
"targets": [
|
||||
{"expr": "taupi_relay_status", "legendFormat": "Fan Status"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Mold Danger Status",
|
||||
"type": "stat",
|
||||
"datasource": {"type": "prometheus", "uid": "prometheus"},
|
||||
"gridPos": {"h": 4, "w": 6, "x": 6, "y": 1},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{"type": "value", "options": {"0": {"text": "SAFE", "color": "green"}, "1": {"text": "CRITICAL", "color": "red"}}}
|
||||
]
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"graphMode": "none",
|
||||
"colorMode": "background",
|
||||
"textMode": "auto",
|
||||
"reduceOptions": {"values": false, "calcs": ["lastNotNull"]}
|
||||
},
|
||||
"targets": [
|
||||
{"expr": "taupi_is_critical", "legendFormat": "Mold Danger"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Activation Criteria Met",
|
||||
"type": "stat",
|
||||
"datasource": {"type": "prometheus", "uid": "prometheus"},
|
||||
"gridPos": {"h": 4, "w": 6, "x": 12, "y": 1},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{"type": "value", "options": {"0": {"text": "NO", "color": "gray"}, "1": {"text": "YES", "color": "green"}}}
|
||||
]
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"graphMode": "none",
|
||||
"colorMode": "background",
|
||||
"textMode": "auto",
|
||||
"reduceOptions": {"values": false, "calcs": ["lastNotNull"]}
|
||||
},
|
||||
"targets": [
|
||||
{"expr": "taupi_would_fan_activate", "legendFormat": "Dewpoint Condition"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Shelly Plug Reachability",
|
||||
"type": "stat",
|
||||
"datasource": {"type": "prometheus", "uid": "prometheus"},
|
||||
"gridPos": {"h": 4, "w": 6, "x": 18, "y": 1},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{"type": "value", "options": {"0": {"text": "OFFLINE", "color": "red"}, "1": {"text": "ONLINE", "color": "green"}}}
|
||||
]
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"graphMode": "none",
|
||||
"colorMode": "background",
|
||||
"textMode": "auto",
|
||||
"reduceOptions": {"values": false, "calcs": ["lastNotNull"]}
|
||||
},
|
||||
"targets": [
|
||||
{"expr": "up{job=\"taupi-fan\"}", "legendFormat": "Connectivity"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "🌡️ Climate Details",
|
||||
"type": "row",
|
||||
"gridPos": {"h": 1, "w": 24, "x": 0, "y": 5},
|
||||
"collapsed": false
|
||||
},
|
||||
{
|
||||
"title": "Temperatures",
|
||||
"type": "timeseries",
|
||||
"datasource": {"type": "prometheus", "uid": "prometheus"},
|
||||
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 6},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "celsius",
|
||||
"custom": {
|
||||
"lineWidth": 2,
|
||||
"fillOpacity": 5,
|
||||
"spanNulls": true,
|
||||
"showPoints": "never"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"legend": {"displayMode": "table", "placement": "bottom", "calcs": ["min", "max", "mean"]},
|
||||
"tooltip": {"mode": "multi", "sort": "desc"}
|
||||
},
|
||||
"targets": [
|
||||
{"expr": "taupi_temperature_celsius_innen", "legendFormat": "Indoor Temperature"},
|
||||
{"expr": "taupi_temperature_celsius_aussen", "legendFormat": "Outdoor Temperature"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Dew Points",
|
||||
"type": "timeseries",
|
||||
"datasource": {"type": "prometheus", "uid": "prometheus"},
|
||||
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 6},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "celsius",
|
||||
"custom": {
|
||||
"lineWidth": 2,
|
||||
"fillOpacity": 5,
|
||||
"spanNulls": true,
|
||||
"showPoints": "never"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"legend": {"displayMode": "table", "placement": "bottom", "calcs": ["min", "max", "mean"]},
|
||||
"tooltip": {"mode": "multi", "sort": "desc"}
|
||||
},
|
||||
"targets": [
|
||||
{"expr": "taupi_dewpoint_celsius_innen", "legendFormat": "Indoor Dewpoint"},
|
||||
{"expr": "taupi_dewpoint_celsius_aussen", "legendFormat": "Outdoor Dewpoint"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Relative Humidity vs. Mold Threshold",
|
||||
"type": "timeseries",
|
||||
"datasource": {"type": "prometheus", "uid": "prometheus"},
|
||||
"gridPos": {"h": 8, "w": 24, "x": 0, "y": 14},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "percent",
|
||||
"custom": {
|
||||
"lineWidth": 2,
|
||||
"fillOpacity": 5,
|
||||
"spanNulls": true,
|
||||
"showPoints": "never"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"legend": {"displayMode": "table", "placement": "bottom", "calcs": ["min", "max", "mean"]},
|
||||
"tooltip": {"mode": "multi", "sort": "desc"}
|
||||
},
|
||||
"targets": [
|
||||
{"expr": "taupi_humidity_percent_innen", "legendFormat": "Indoor Humidity"},
|
||||
{"expr": "taupi_humidity_percent_aussen", "legendFormat": "Outdoor Humidity"},
|
||||
{"expr": "taupi_critical_humidity_threshold_percent", "legendFormat": "Mold Danger Threshold"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "🔋 BLE Sensors Diagnostic",
|
||||
"type": "row",
|
||||
"gridPos": {"h": 1, "w": 24, "x": 0, "y": 22},
|
||||
"collapsed": false
|
||||
},
|
||||
{
|
||||
"title": "Sensor Batteries",
|
||||
"type": "gauge",
|
||||
"datasource": {"type": "prometheus", "uid": "prometheus"},
|
||||
"gridPos": {"h": 6, "w": 12, "x": 0, "y": 23},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "percent",
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{"color": "red", "value": null},
|
||||
{"color": "orange", "value": 20},
|
||||
{"color": "green", "value": 50}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"reduceOptions": {"values": false, "calcs": ["lastNotNull"]},
|
||||
"showThresholdLabels": false,
|
||||
"showThresholdMarkers": true
|
||||
},
|
||||
"targets": [
|
||||
{"expr": "taupi_battery_percent_innen", "legendFormat": "Indoor Sensor Battery"},
|
||||
{"expr": "taupi_battery_percent_aussen", "legendFormat": "Outdoor Sensor Battery"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Sensor Connection Age (Staleness)",
|
||||
"type": "timeseries",
|
||||
"datasource": {"type": "prometheus", "uid": "prometheus"},
|
||||
"gridPos": {"h": 6, "w": 12, "x": 12, "y": 23},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "s",
|
||||
"custom": {
|
||||
"lineWidth": 2,
|
||||
"fillOpacity": 5,
|
||||
"spanNulls": true,
|
||||
"showPoints": "never"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"legend": {"displayMode": "table", "placement": "bottom", "calcs": ["lastNotNull"]},
|
||||
"tooltip": {"mode": "multi", "sort": "desc"}
|
||||
},
|
||||
"targets": [
|
||||
{"expr": "taupi_lost_connection_seconds_innen", "legendFormat": "Indoor Connection Stale Time"},
|
||||
{"expr": "taupi_lost_connection_seconds_aussen", "legendFormat": "Outdoor Connection Stale Time"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: taupi-fan
|
||||
namespace: monitoring
|
||||
labels:
|
||||
app: taupi-fan
|
||||
spec:
|
||||
ports:
|
||||
- name: http-metrics
|
||||
port: 80
|
||||
targetPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: taupi-fan
|
||||
namespace: monitoring
|
||||
labels:
|
||||
app: taupi-fan
|
||||
subsets:
|
||||
- addresses:
|
||||
- ip: 192.168.10.168
|
||||
ports:
|
||||
- name: http-metrics
|
||||
port: 80
|
||||
---
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: taupi-fan
|
||||
namespace: monitoring
|
||||
labels:
|
||||
app: taupi-fan
|
||||
release: prometheus-operator
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: taupi-fan
|
||||
namespaceSelector:
|
||||
matchNames:
|
||||
- monitoring
|
||||
endpoints:
|
||||
- port: http-metrics
|
||||
path: /script/1/prometheus
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
---
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: taupi-fan-alerts
|
||||
namespace: monitoring
|
||||
labels:
|
||||
release: prometheus-operator
|
||||
spec:
|
||||
groups:
|
||||
- name: taupi-fan.rules
|
||||
rules:
|
||||
- alert: TaupiFanOffline
|
||||
expr: up{job="taupi-fan"} == 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Id: {{ $labels.instance }} - TAUPI Fan Plug is unreachable"
|
||||
description: "The Shelly plug at 192.168.10.168 is unreachable by Prometheus. Check Wi-Fi connection or power status."
|
||||
- alert: TaupiFanSensorsStale
|
||||
expr: taupi_lost_connection_seconds_innen > 600 or taupi_lost_connection_seconds_aussen > 600
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "TAUPI Fan BLE sensors stale"
|
||||
description: "The BLE sensors for temperature and humidity inside or outside have not sent new data for over 10 minutes."
|
||||
- alert: TaupiFanMoldDanger
|
||||
expr: taupi_is_critical == 1
|
||||
for: 30m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Cellar mold risk detected"
|
||||
description: "The cellar relative humidity has exceeded the critical mold threshold (inside humidity is high enough that mold can grow on cold wall surfaces)."
|
||||
|
|
@ -170,46 +170,20 @@ const uint8 = 0;
|
|||
const int8 = 1;
|
||||
const uint16 = 2;
|
||||
const int16 = 3;
|
||||
const uint24 = 4;
|
||||
const int24 = 5;
|
||||
const dimmert = 6; // special data type for dimmer event
|
||||
const uint32 = 7;
|
||||
const int32 = 8;
|
||||
|
||||
// The BTH object defines the structure of the BTHome data
|
||||
// The BTH object defines the structure of the BTHome data (trimmed to temperature/humidity/battery)
|
||||
const BTH = {
|
||||
0x00: { n: "pid", t: uint8 },
|
||||
0x01: { n: "battery", t: uint8, u: "%" },
|
||||
0x02: { n: "temperature", t: int16, f: 0.01, u: "tC" },
|
||||
0x04: { n: "atm. pressure", t: int24, f: 0.01, u: "hPa" },
|
||||
0x03: { n: "humidity", t: uint16, f: 0.01, u: "%" },
|
||||
0x05: { n: "illuminance", t: uint24, f: 0.01 },
|
||||
0x08: { n: "dew point", t: uint16, f: 0.01, u: "tC" },
|
||||
0x0C: { n: "capacitor voltage", t: uint16, f: 0.001, u: "V" },
|
||||
0x1e: { n: "light", t: uint8 },
|
||||
0x20: { n: "raining", t: uint8},
|
||||
0x21: { n: "motion", t: uint8 },
|
||||
0x2d: { n: "window", t: uint8 },
|
||||
0x2e: { n: "humidity", t: uint8, u: "%" },
|
||||
0x3a: { n: "button", t: uint8 },
|
||||
0x3c: { n: "dimmer", t: dimmert },
|
||||
0x3f: { n: "rotation", t: int16, f: 0.1 },
|
||||
0x44: { n: "wind speed", t: int16, f: 0.01, u: "m/s" },
|
||||
0x45: { n: "temperature", t: int16, f: 0.1, u: "tC" },
|
||||
0x46: { n: "UV index", t: int8, f: 0.1 },
|
||||
0x5E: { n: "wind direction", t: int16, f: 0.01 },
|
||||
0x5F: { n: "precipitation", t: int16, f: 0.1, u: "mm" },
|
||||
0x60: { n: "channel", t: uint8 },
|
||||
0xf0: { n: "dev_type", t: uint16 },
|
||||
0xf1: { n: "fw_version", t: uint32 },
|
||||
0xf2: { n: "fw_version_short", t: uint24 },
|
||||
0x01: { n: "battery", t: uint8 },
|
||||
0x02: { n: "temperature", t: int16, f: 0.01 },
|
||||
0x03: { n: "humidity", t: uint16, f: 0.01 },
|
||||
0x2e: { n: "humidity", t: uint8 },
|
||||
0x45: { n: "temperature", t: int16, f: 0.1 }
|
||||
};
|
||||
|
||||
function getByteSize(type) {
|
||||
if (type === uint8 || type === int8) return 1;
|
||||
if (type === uint16 || type === int16 || type === dimmert) return 2;
|
||||
if (type === uint24 || type === int24) return 3;
|
||||
if (type === uint32 || type === int32) return 4;
|
||||
if (type === uint16 || type === int16) return 2;
|
||||
return 255;
|
||||
}
|
||||
|
||||
|
|
@ -222,9 +196,6 @@ const BTHomeDecoder = {
|
|||
getUInt8: function (buffer) {
|
||||
return buffer.at(0);
|
||||
},
|
||||
getDimmer: function (buffer) {
|
||||
return {"dimmer": buffer.at(0), "dimmersteps": buffer.at(1)};
|
||||
},
|
||||
getInt8: function (buffer) {
|
||||
return this.utoi(this.getUInt8(buffer), 8);
|
||||
},
|
||||
|
|
@ -234,33 +205,13 @@ const BTHomeDecoder = {
|
|||
getInt16LE: function (buffer) {
|
||||
return this.utoi(this.getUInt16LE(buffer), 16);
|
||||
},
|
||||
getUInt24LE: function (buffer) {
|
||||
return (
|
||||
0x00ffffff & ((buffer.at(2) << 16) | (buffer.at(1) << 8) | buffer.at(0))
|
||||
);
|
||||
},
|
||||
getInt24LE: function (buffer) {
|
||||
return this.utoi(this.getUInt24LE(buffer), 24);
|
||||
},
|
||||
getUInt32LE: function (buffer) {
|
||||
return 0xffffffff & ((buffer.at(3) << 24) | (buffer.at(2) << 16) | (buffer.at(1) << 8) | buffer.at(0));
|
||||
},
|
||||
getInt32LE: function (buffer) {
|
||||
return this.utoi(this.getUInt32LE(buffer), 32);
|
||||
},
|
||||
getBufValue: function (type, buffer) {
|
||||
if (buffer.length < getByteSize(type)) return null;
|
||||
let res = null;
|
||||
if (type === uint8) res = this.getUInt8(buffer);
|
||||
if (type === int8) res = this.getInt8(buffer);
|
||||
if (type === uint16) res = this.getUInt16LE(buffer);
|
||||
if (type === int16) res = this.getInt16LE(buffer);
|
||||
if (type === uint24) res = this.getUInt24LE(buffer);
|
||||
if (type === int24) res = this.getInt24LE(buffer);
|
||||
if (type === uint32) res = this.getUInt32LE(buffer);
|
||||
if (type === int32) res = this.getInt32LE(buffer);
|
||||
if (type === dimmert) res = this.getDimmer(buffer);
|
||||
return res;
|
||||
if (type === uint8) return this.getUInt8(buffer);
|
||||
if (type === int8) return this.getInt8(buffer);
|
||||
if (type === uint16) return this.getUInt16LE(buffer);
|
||||
if (type === int16) return this.getInt16LE(buffer);
|
||||
return null;
|
||||
},
|
||||
|
||||
unpack: function (buffer) {
|
||||
|
|
@ -278,18 +229,11 @@ const BTHomeDecoder = {
|
|||
while (buffer.length > 0) {
|
||||
_bth = BTH[buffer.at(0)];
|
||||
if (typeof _bth === "undefined") {
|
||||
print("BTH: Unknown type: ", buffer.at(0));
|
||||
break;
|
||||
}
|
||||
buffer = buffer.slice(1);
|
||||
_value = this.getBufValue(_bth.t, buffer);
|
||||
|
||||
// handle dimmer special case
|
||||
if (typeof _value === "object" && _bth.t === dimmert) {
|
||||
result["dimmersteps"] = _value["dimmersteps"];
|
||||
_value = _value["dimmer"];
|
||||
}
|
||||
|
||||
if (_value === null) break;
|
||||
if (typeof _bth.f !== "undefined") _value = _value * _bth.f;
|
||||
|
||||
|
|
@ -378,3 +322,62 @@ function initBLE() {
|
|||
}
|
||||
|
||||
initBLE();
|
||||
|
||||
// Expose metrics for Prometheus
|
||||
if (typeof HTTPServer !== "undefined" && typeof HTTPServer.registerEndpoint === "function") {
|
||||
HTTPServer.registerEndpoint("prometheus", function(req, res) {
|
||||
var body = "";
|
||||
|
||||
function addGauge(name, val, help) {
|
||||
if ((typeof val === "number" && !isNaN(val)) || typeof val === "boolean") {
|
||||
var numVal = (typeof val === "boolean") ? (val ? 1 : 0) : val;
|
||||
if (help) {
|
||||
body += "# HELP " + name + " " + help + "\n";
|
||||
}
|
||||
body += "# TYPE " + name + " gauge\n";
|
||||
body += name + " " + numVal + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate helper values
|
||||
var rh_crit = (typeof temperatur_innen !== "undefined" && !isNaN(temperatur_innen)) ? (critical_humi_base - critical_humi_temp_coeff * (temperatur_innen - critical_humi_ref_temp)) : undefined;
|
||||
var rh_activation = (typeof rh_crit !== "undefined" && !isNaN(rh_crit)) ? (rh_crit - critical_humi_buffer) : undefined;
|
||||
var is_critical_val = (typeof humidity_innen !== "undefined" && !isNaN(humidity_innen) && typeof rh_activation !== "undefined" && !isNaN(rh_activation)) ? (humidity_innen >= rh_activation) : undefined;
|
||||
|
||||
var would_fan_activate = false;
|
||||
if (typeof is_critical_val !== "undefined" && is_critical_val !== null &&
|
||||
typeof temperatur_innen !== "undefined" && !isNaN(temperatur_innen) &&
|
||||
typeof humidity_innen !== "undefined" && !isNaN(humidity_innen) &&
|
||||
typeof taupunkt_innen !== "undefined" && !isNaN(taupunkt_innen) &&
|
||||
typeof taupunkt_aussen !== "undefined" && !isNaN(taupunkt_aussen)) {
|
||||
would_fan_activate = is_critical_val &&
|
||||
(temperatur_innen > mindesttemperatur) &&
|
||||
(humidity_innen > mindesthumi) &&
|
||||
(taupunkt_innen > taupunkt_aussen + taupunktschwelle);
|
||||
}
|
||||
|
||||
addGauge("taupi_temperature_celsius_innen", temperatur_innen, "Indoor temperature in Celsius");
|
||||
addGauge("taupi_temperature_celsius_aussen", temperatur_aussen, "Outdoor temperature in Celsius");
|
||||
addGauge("taupi_humidity_percent_innen", humidity_innen, "Indoor relative humidity in percent");
|
||||
addGauge("taupi_humidity_percent_aussen", humidity_aussen, "Outdoor relative humidity in percent");
|
||||
addGauge("taupi_dewpoint_celsius_innen", taupunkt_innen, "Indoor dew point in Celsius");
|
||||
addGauge("taupi_dewpoint_celsius_aussen", taupunkt_aussen, "Outdoor dew point in Celsius");
|
||||
addGauge("taupi_battery_percent_innen", battery_innen, "Indoor sensor battery percentage");
|
||||
addGauge("taupi_battery_percent_aussen", battery_aussen, "Outdoor sensor battery percentage");
|
||||
addGauge("taupi_lost_connection_seconds_innen", lost_connection_innen, "Seconds since last contact with indoor sensor");
|
||||
addGauge("taupi_lost_connection_seconds_aussen", lost_connection_aussen, "Seconds since last contact with outdoor sensor");
|
||||
addGauge("taupi_critical_humidity_threshold_percent", rh_activation, "Calculated relative humidity threshold above which it is critical");
|
||||
addGauge("taupi_is_critical", is_critical_val, "Flag indicating mold danger inside (humidity exceeds threshold)");
|
||||
addGauge("taupi_would_fan_activate", would_fan_activate, "Flag indicating if all fan activation criteria are met");
|
||||
|
||||
var relay_status = Shelly.getComponentStatus("switch", 0);
|
||||
if (relay_status && typeof relay_status.output !== "undefined") {
|
||||
addGauge("taupi_relay_status", relay_status.output, "Physical switch status (1 = on, 0 = off)");
|
||||
}
|
||||
|
||||
res.body = body;
|
||||
res.headers = [["Content-Type", "text/plain; version=0.0.4"]];
|
||||
res.code = 200;
|
||||
res.send();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue