diff --git a/kubernetes/apps/monitoring/dashboards/power-flow.json b/kubernetes/apps/monitoring/dashboards/power-flow.json index 4f596274..f2235b4b 100644 --- a/kubernetes/apps/monitoring/dashboards/power-flow.json +++ b/kubernetes/apps/monitoring/dashboards/power-flow.json @@ -16,11 +16,11 @@ "getOption": "const data = context.panel.data;\nconst getVal = (refId) => {\n const s = data.series.find(s => s.refId === refId);\n return s?.fields[1]?.values[0] || 0;\n};\n\n// Get all values\nconst solar = getVal('solar');\nconst grid = getVal('grid');\nconst batteryIn = getVal('batteryIn');\nconst batteryOut = getVal('batteryOut');\nconst batterySoc = getVal('soc');\nconst keller = getVal('keller');\nconst eg = getVal('eg');\nconst og = getVal('og');\nconst dg = getVal('dg');\nconst server = getVal('server');\nconst heatpump = getVal('heatpump');\nconst garden = getVal('garden');\n\n// Calculate derived values - using separate in/out sensors for battery\nconst solarPower = Math.max(0, solar);\nconst gridImport = Math.max(0, grid);\nconst gridExport = Math.max(0, -grid);\nconst batteryDischarge = Math.max(0, batteryOut); // battery_power_out_absolute = discharging\nconst batteryCharge = Math.max(0, batteryIn); // battery_power_in_absolute = charging\nconst homePower = keller + eg + og + dg;\n\nconst fmt = (w) => Math.abs(w) >= 1000 ? (w/1000).toFixed(1) + ' kW' : Math.round(w) + ' W';\n\n// Chart dimensions\nconst w = context.panel.chart.getWidth();\nconst h = context.panel.chart.getHeight();\n\n// Battery display: show discharge or charge with direction indicator\nconst batteryDisplay = batteryDischarge > 0 ? fmt(batteryDischarge) + ' ↑' : (batteryCharge > 0 ? fmt(batteryCharge) + ' ↓' : '0 W');\nconst gridDisplay = gridImport > 0 ? fmt(gridImport) + ' →' : (gridExport > 0 ? fmt(gridExport) + ' ←' : '0 W');\n\n// Node definitions with percentage positions\nconst nodes = [\n // Sources (left side)\n { name: 'Solar', value: solarPower, x: w * 0.1, y: h * 0.15, color: '#F59E0B', icon: '☀️' },\n { name: 'Grid', value: gridImport > 0 ? gridImport : -gridExport, x: w * 0.1, y: h * 0.5, color: '#3B82F6', icon: '⚡', displayText: gridDisplay },\n { name: 'Battery', value: batteryDischarge > 0 ? batteryDischarge : -batteryCharge, x: w * 0.1, y: h * 0.85, color: '#10B981', icon: '🔋', extra: Math.round(batterySoc) + '%', displayText: batteryDisplay },\n \n // Home (center)\n { name: 'Home', value: homePower, x: w * 0.4, y: h * 0.5, color: '#F97316', icon: '🏠', size: 70 },\n \n // Floors (right side - top)\n { name: 'Keller', value: keller, x: w * 0.7, y: h * 0.1, color: '#1F60C4', icon: 'KG' },\n { name: 'EG', value: eg, x: w * 0.7, y: h * 0.3, color: '#37872D', icon: 'EG' },\n { name: 'OG', value: og, x: w * 0.7, y: h * 0.5, color: '#E0B400', icon: 'OG' },\n { name: 'DG', value: dg, x: w * 0.7, y: h * 0.7, color: '#C4162A', icon: 'DG' },\n \n // Large consumers (right side - bottom)\n { name: 'Server', value: server, x: w * 0.9, y: h * 0.2, color: '#8F3BB8', icon: '🖥️' },\n { name: 'Heat Pump', value: heatpump, x: w * 0.9, y: h * 0.45, color: '#5794F2', icon: '🌡️' },\n { name: 'Garden', value: garden, x: w * 0.9, y: h * 0.7, color: '#96D98D', icon: '🌳' }\n];\n\n// Build graphic elements\nconst elements = [];\n\n// Helper to draw flow line\nconst drawLine = (x1, y1, x2, y2, value, color) => {\n if (value <= 0) return;\n const width = Math.max(2, Math.min(value / 300, 15));\n elements.push({\n type: 'line',\n shape: { x1, y1, x2, y2 },\n style: { stroke: color, lineWidth: width, opacity: 0.6 }\n });\n};\n\n// Draw flow lines from sources to home\nif (solarPower > 0) drawLine(w*0.1+30, h*0.15, w*0.4-35, h*0.5, solarPower, '#F59E0B');\nif (gridImport > 0) drawLine(w*0.1+30, h*0.5, w*0.4-35, h*0.5, gridImport, '#3B82F6');\nif (gridExport > 0) drawLine(w*0.4-35, h*0.5, w*0.1+30, h*0.5, gridExport, '#22C55E');\nif (batteryDischarge > 0) drawLine(w*0.1+30, h*0.85, w*0.4-35, h*0.5, batteryDischarge, '#10B981');\nif (batteryCharge > 0) drawLine(w*0.4-35, h*0.5, w*0.1+30, h*0.85, batteryCharge, '#60A5FA');\n\n// Draw flow lines from home to floors\nif (keller > 0) drawLine(w*0.4+35, h*0.5, w*0.7-25, h*0.1, keller, '#1F60C4');\nif (eg > 0) drawLine(w*0.4+35, h*0.5, w*0.7-25, h*0.3, eg, '#37872D');\nif (og > 0) drawLine(w*0.4+35, h*0.5, w*0.7-25, h*0.5, og, '#E0B400');\nif (dg > 0) drawLine(w*0.4+35, h*0.5, w*0.7-25, h*0.7, dg, '#C4162A');\n\n// Draw flow lines to large consumers (from floors)\nif (server > 0) drawLine(w*0.7+25, h*0.1, w*0.9-25, h*0.2, server, '#8F3BB8');\nif (heatpump > 0) drawLine(w*0.7+25, h*0.3, w*0.9-25, h*0.45, heatpump, '#5794F2');\nif (garden > 0) drawLine(w*0.7+25, h*0.5, w*0.9-25, h*0.7, garden, '#96D98D');\n\n// Draw nodes\nnodes.forEach(node => {\n const size = node.size || 50;\n // Circle\n elements.push({\n type: 'circle',\n shape: { cx: node.x, cy: node.y, r: size/2 },\n style: { fill: '#1a1a2e', stroke: node.color, lineWidth: 4 }\n });\n // Icon\n elements.push({\n type: 'text',\n style: { text: node.icon, x: node.x, y: node.y - 8, fontSize: 18, textAlign: 'center', textVerticalAlign: 'middle' }\n });\n // Value\n let valueText = node.displayText || fmt(Math.abs(node.value));\n if (node.extra) valueText += '\\n' + node.extra;\n elements.push({\n type: 'text',\n style: { text: valueText, x: node.x, y: node.y + 14, fontSize: 11, fill: '#fff', textAlign: 'center', textVerticalAlign: 'middle' }\n });\n // Label\n elements.push({\n type: 'text',\n style: { text: node.name, x: node.x, y: node.y + size/2 + 12, fontSize: 11, fill: '#9CA3AF', textAlign: 'center' }\n });\n});\n\nreturn { backgroundColor: 'transparent', graphic: { elements } };" }, "targets": [ - {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.e3_vitocharge_03_solar_power\"}", "instant": true, "refId": "solar"}, + {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.vitocharge_vx3_vx3_pv_power\"}", "instant": true, "refId": "solar"}, {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{friendly_name=\"Smart Meter Sum active instantaneous power\"}", "instant": true, "refId": "grid"}, - {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.e3_vitocharge_03_battery_power_in_absolute\"}", "instant": true, "refId": "batteryIn"}, - {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.e3_vitocharge_03_battery_power_out_absolute\"}", "instant": true, "refId": "batteryOut"}, - {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_battery_percent{entity=\"sensor.e3_vitocharge_03_battery\"}", "instant": true, "refId": "soc"}, + {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.vitocharge_vx3_vx3_battery_power\"} * -1", "instant": true, "refId": "batteryIn"}, + {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.vitocharge_vx3_vx3_battery_power\"}", "instant": true, "refId": "batteryOut"}, + {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_battery_percent{entity=\"sensor.vitocharge_vx3_vx3_battery_soc\"}", "instant": true, "refId": "soc"}, {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.keller_power\"}", "instant": true, "refId": "keller"}, {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.erdgeschoss_power\"}", "instant": true, "refId": "eg"}, {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.obergeschoss_power\"}", "instant": true, "refId": "og"}, @@ -61,8 +61,8 @@ "id": 40, "options": {"legend": {"calcs": ["mean", "max", "lastNotNull"], "displayMode": "table", "placement": "right", "showLegend": true}, "tooltip": {"mode": "multi", "sort": "desc"}}, "targets": [ - {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.e3_vitocharge_03_solar_power\"}", "legendFormat": "Solar", "refId": "A"}, - {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.e3_vitocharge_03_battery_power_out_absolute\"} - homeassistant_sensor_power_w{entity=\"sensor.e3_vitocharge_03_battery_power_in_absolute\"}", "legendFormat": "Battery", "refId": "B"}, + {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.vitocharge_vx3_vx3_pv_power\"}", "legendFormat": "Solar", "refId": "A"}, + {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{entity=\"sensor.vitocharge_vx3_vx3_battery_power\"}", "legendFormat": "Battery", "refId": "B"}, {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "homeassistant_sensor_power_w{friendly_name=\"Smart Meter Sum active instantaneous power\"}", "legendFormat": "Grid", "refId": "C"}, {"datasource": {"type": "prometheus", "uid": "prometheus"}, "expr": "sum(homeassistant_sensor_power_w{entity=~\"sensor.keller_power|sensor.erdgeschoss_power|sensor.obergeschoss_power|sensor.dachgeschoss_power\"})", "legendFormat": "Home", "refId": "D"} ], diff --git a/kubernetes/apps/monitoring/dashboards/pv-system-roi.json b/kubernetes/apps/monitoring/dashboards/pv-system-roi.json index 85fafdbe..2638eb71 100644 --- a/kubernetes/apps/monitoring/dashboards/pv-system-roi.json +++ b/kubernetes/apps/monitoring/dashboards/pv-system-roi.json @@ -1,6 +1,6 @@ { "annotations": {"list": []}, - "description": "PV System ROI Analysis - Viessmann Vitocharge with Battery Storage", + "description": "PV System ROI Analysis - Viessmann Vitocharge with Battery Storage. Reads InfluxDB (bucket suedstr28). After the 2026-07 ViCare->open3e/MQTT migration the Vitocharge entities were renamed (e3_vitocharge_03_* -> vitocharge_vx3_vx3_*) and some units changed (PV/battery energy now Wh not kWh; grid energy now kWh not Wh; battery power is a single signed sensor, negative=charge / positive=discharge; grid power negative=export). Timeseries panels UNION the pre-migration e3_* series with the post-migration series (unit-normalized) so history stays unbroken.", "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 1, @@ -30,7 +30,7 @@ "gridPos": {"h": 4, "w": 4, "x": 4, "y": 0}, "id": 2, "options": {"colorMode": "value", "graphMode": "area", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_power\") |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_power\") |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)", "refId": "A"}], "title": "Solar Power", "type": "stat" }, @@ -43,7 +43,7 @@ "gridPos": {"h": 4, "w": 4, "x": 8, "y": 0}, "id": 3, "options": {"minVizHeight": 75, "minVizWidth": 75, "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "showThresholdLabels": false, "showThresholdMarkers": true}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"%\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_battery\") |> last()", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"%\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_battery_soc\") |> last()", "refId": "A"}], "title": "Battery Level", "type": "gauge" }, @@ -56,7 +56,7 @@ "gridPos": {"h": 4, "w": 4, "x": 12, "y": 0}, "id": 4, "options": {"colorMode": "value", "graphMode": "area", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_today\") |> last()", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_today\") |> last() |> map(fn: (r) => ({r with _value: r._value / 1000.0}))", "refId": "A"}], "title": "Production Today", "type": "stat" }, @@ -69,7 +69,7 @@ "gridPos": {"h": 4, "w": 4, "x": 16, "y": 0}, "id": 5, "options": {"colorMode": "value", "graphMode": "area", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> last()", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> last() |> map(fn: (r) => ({r with _value: r._value / 1000.0}))", "refId": "A"}], "title": "Total Production", "type": "stat" }, @@ -82,7 +82,7 @@ "gridPos": {"h": 4, "w": 4, "x": 20, "y": 0}, "id": 6, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\narray.from(rows: [{_time: now(), _value: (solar - export_kwh) / solar * 100.0}])", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\narray.from(rows: [{_time: now(), _value: (solar - export_kwh) / solar * 100.0}])", "refId": "A"}], "title": "Self-Consumption", "description": "Percentage of solar energy used directly (not exported)", "type": "stat" @@ -96,7 +96,7 @@ "gridPos": {"h": 4, "w": 3, "x": 0, "y": 4}, "id": 7, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\") |> last() |> map(fn: (r) => ({r with _value: r._value / 1000.0}))", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\") |> last()", "refId": "A"}], "title": "Grid Import", "type": "stat" }, @@ -109,7 +109,7 @@ "gridPos": {"h": 4, "w": 3, "x": 3, "y": 4}, "id": 8, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> last() |> map(fn: (r) => ({r with _value: r._value / 1000.0}))", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> last()", "refId": "A"}], "title": "Grid Export", "type": "stat" }, @@ -122,7 +122,7 @@ "gridPos": {"h": 4, "w": 3, "x": 6, "y": 4}, "id": 9, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\narray.from(rows: [{_time: now(), _value: solar - export_kwh}])", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\narray.from(rows: [{_time: now(), _value: solar - export_kwh}])", "refId": "A"}], "title": "Self-Consumed", "description": "Total energy used directly without grid export", "type": "stat" @@ -136,7 +136,7 @@ "gridPos": {"h": 4, "w": 3, "x": 9, "y": 4}, "id": 10, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nprice = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\narray.from(rows: [{_time: now(), _value: (solar - export_kwh) * price}])", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nprice = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\narray.from(rows: [{_time: now(), _value: (solar - export_kwh) * price}])", "refId": "A"}], "title": "Self-Use Savings", "description": "Value of self-consumed energy (grid price avoided)", "type": "stat" @@ -150,7 +150,7 @@ "gridPos": {"h": 4, "w": 3, "x": 12, "y": 4}, "id": 11, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\narray.from(rows: [{_time: now(), _value: export_kwh * ${feed_in_tariff}}])", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\narray.from(rows: [{_time: now(), _value: export_kwh * ${feed_in_tariff}}])", "refId": "A"}], "title": "Feed-in Revenue", "description": "Revenue from exported energy at feed-in tariff", "type": "stat" @@ -164,7 +164,7 @@ "gridPos": {"h": 4, "w": 3, "x": 15, "y": 4}, "id": 12, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nprice = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\narray.from(rows: [{_time: now(), _value: (solar - export_kwh) * price + export_kwh * ${feed_in_tariff}}])", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nprice = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\narray.from(rows: [{_time: now(), _value: (solar - export_kwh) * price + export_kwh * ${feed_in_tariff}}])", "refId": "A"}], "title": "Total Value", "description": "Self-use savings + feed-in revenue", "type": "stat" @@ -178,7 +178,7 @@ "gridPos": {"h": 4, "w": 3, "x": 18, "y": 4}, "id": 13, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nprice = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\ntotal_value = (solar - export_kwh) * price + export_kwh * ${feed_in_tariff}\nelapsed_years = (float(v: int(v: now())) / 1000000000.0 - float(v: ${pv_install_date})) / 31536000.0\nannual_value = total_value / elapsed_years\narray.from(rows: [{_time: now(), _value: ${pv_investment} / annual_value}])", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nprice = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\ntotal_value = (solar - export_kwh) * price + export_kwh * ${feed_in_tariff}\nelapsed_years = (float(v: int(v: now())) / 1000000000.0 - float(v: ${pv_install_date})) / 31536000.0\nannual_value = total_value / elapsed_years\narray.from(rows: [{_time: now(), _value: ${pv_investment} / annual_value}])", "refId": "A"}], "title": "Payback (Years)", "description": "Investment / Annual Value", "type": "stat" @@ -192,7 +192,7 @@ "gridPos": {"h": 4, "w": 3, "x": 21, "y": 4}, "id": 14, "options": {"minVizHeight": 75, "minVizWidth": 75, "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "showThresholdLabels": false, "showThresholdMarkers": true}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nprice = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\ntotal_value = (solar - export_kwh) * price + export_kwh * ${feed_in_tariff}\narray.from(rows: [{_time: now(), _value: total_value / ${pv_investment} * 100.0}])", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\nsolar = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value / 1000.0\nexport_kwh = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nprice = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\ntotal_value = (solar - export_kwh) * price + export_kwh * ${feed_in_tariff}\narray.from(rows: [{_time: now(), _value: total_value / ${pv_investment} * 100.0}])", "refId": "A"}], "title": "ROI Progress", "description": "Percentage of investment recovered", "type": "gauge" @@ -202,7 +202,7 @@ "gridPos": {"h": 1, "w": 24, "x": 0, "y": 8}, "id": 30, "panels": [], - "title": "Real-time Power Flow", + "title": "Power Flow Over Time", "type": "row" }, { @@ -225,9 +225,9 @@ "id": 20, "options": {"legend": {"calcs": ["mean", "max", "lastNotNull"], "displayMode": "table", "placement": "right", "showLegend": true}, "tooltip": {"mode": "multi", "sort": "desc"}}, "targets": [ - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_power\") |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Solar Power\")", "refId": "A"}, - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_battery_power_in_absolute\") |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Battery Charge\")", "refId": "B"}, - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_battery_power_out_absolute\") |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Battery Discharge\")", "refId": "C"} + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_power\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_power\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Solar Power\")", "refId": "A"}, + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_battery_power_in_absolute\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_battery_power\") |> map(fn: (r) => ({_time: r._time, _value: if r._value < 0.0 then -r._value else 0.0}))])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Battery Charge\")", "refId": "B"}, + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_battery_power_out_absolute\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_battery_power\") |> map(fn: (r) => ({_time: r._time, _value: if r._value > 0.0 then r._value else 0.0}))])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Battery Discharge\")", "refId": "C"} ], "title": "Solar & Battery Power", "type": "timeseries" @@ -250,7 +250,7 @@ "id": 21, "options": {"legend": {"calcs": ["mean", "min", "max"], "displayMode": "table", "placement": "right", "showLegend": true}, "tooltip": {"mode": "multi", "sort": "desc"}}, "targets": [ - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_power_exchange_with_grid\") |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Grid Exchange\")", "refId": "A"} + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_power_exchange_with_grid\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"W\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_power\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Grid Exchange\")", "refId": "A"} ], "title": "Grid Exchange (+ Import / - Export)", "type": "timeseries" @@ -271,7 +271,7 @@ "id": 22, "options": {"legend": {"calcs": ["mean", "min", "max"], "displayMode": "table", "placement": "right", "showLegend": true}, "tooltip": {"mode": "multi", "sort": "desc"}}, "targets": [ - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"%\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_battery\") |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Battery Level\")", "refId": "A"} + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"%\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_battery\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"%\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_battery_soc\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> set(key: \"_field\", value: \"Battery Level\")", "refId": "A"} ], "title": "Battery State of Charge", "type": "timeseries" @@ -304,9 +304,9 @@ "id": 23, "options": {"legend": {"calcs": ["min", "max", "lastNotNull"], "displayMode": "table", "placement": "right", "showLegend": true}, "tooltip": {"mode": "multi", "sort": "desc"}}, "targets": [ - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"_field\", value: \"Solar Production\")", "refId": "A"}, - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> map(fn: (r) => ({r with _value: r._value / 1000.0})) |> set(key: \"_field\", value: \"Grid Export\")", "refId": "B"}, - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> map(fn: (r) => ({r with _value: r._value / 1000.0})) |> set(key: \"_field\", value: \"Grid Import\")", "refId": "C"} + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))])\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"_field\", value: \"Solar Production\")", "refId": "A"}, + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"_field\", value: \"Grid Export\")", "refId": "B"}, + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"_field\", value: \"Grid Import\")", "refId": "C"} ], "title": "Cumulative Energy", "type": "timeseries" @@ -331,9 +331,9 @@ "id": 24, "options": {"legend": {"calcs": ["min", "max", "lastNotNull"], "displayMode": "table", "placement": "right", "showLegend": true}, "tooltip": {"mode": "multi", "sort": "desc"}}, "targets": [ - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "solar = from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"src\", value: \"solar\")\nexport_kwh = from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> map(fn: (r) => ({r with _value: r._value / 1000.0})) |> set(key: \"src\", value: \"export\")\nprice = from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"src\", value: \"price\")\nunion(tables: [solar, export_kwh, price]) |> pivot(rowKey: [\"_time\"], columnKey: [\"src\"], valueColumn: \"_value\") |> filter(fn: (r) => exists r.solar and exists r.export and exists r.price) |> map(fn: (r) => ({_time: r._time, _value: (r.solar - r.export) * r.price, _field: \"Self-Use Savings\"}))", "refId": "A"}, - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> map(fn: (r) => ({r with _value: r._value / 1000.0 * ${feed_in_tariff}})) |> set(key: \"_field\", value: \"Feed-in Revenue\")", "refId": "B"}, - {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "solar = from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"src\", value: \"solar\")\nexport_kwh = from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> map(fn: (r) => ({r with _value: r._value / 1000.0})) |> set(key: \"src\", value: \"export\")\nprice = from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"src\", value: \"price\")\nunion(tables: [solar, export_kwh, price]) |> pivot(rowKey: [\"_time\"], columnKey: [\"src\"], valueColumn: \"_value\") |> filter(fn: (r) => exists r.solar and exists r.export and exists r.price) |> map(fn: (r) => ({_time: r._time, _value: (r.solar - r.export) * r.price + r.export * ${feed_in_tariff}, _field: \"Total Value\"}))", "refId": "C"} + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "solar = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))])\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"src\", value: \"solar\")\nexport_kwh = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"src\", value: \"export\")\nprice = from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> keep(columns: [\"_time\", \"_value\"]) |> set(key: \"src\", value: \"price\")\nunion(tables: [solar, export_kwh, price]) |> pivot(rowKey: [\"_time\"], columnKey: [\"src\"], valueColumn: \"_value\") |> filter(fn: (r) => exists r.solar and exists r.export and exists r.price) |> map(fn: (r) => ({_time: r._time, _value: (r.solar - r.export) * r.price, _field: \"Self-Use Savings\"}))", "refId": "A"}, + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> map(fn: (r) => ({_time: r._time, _value: r._value * ${feed_in_tariff}, _field: \"Feed-in Revenue\"}))", "refId": "B"}, + {"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "solar = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))])\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"src\", value: \"solar\")\nexport_kwh = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> set(key: \"src\", value: \"export\")\nprice = from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"EUR/kWh\" and r._field == \"value\" and r.entity_id == \"strompreis\") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> keep(columns: [\"_time\", \"_value\"]) |> set(key: \"src\", value: \"price\")\nunion(tables: [solar, export_kwh, price]) |> pivot(rowKey: [\"_time\"], columnKey: [\"src\"], valueColumn: \"_value\") |> filter(fn: (r) => exists r.solar and exists r.export and exists r.price) |> map(fn: (r) => ({_time: r._time, _value: (r.solar - r.export) * r.price + r.export * ${feed_in_tariff}, _field: \"Total Value\"}))", "refId": "C"} ], "title": "Cumulative Value", "type": "timeseries" @@ -381,7 +381,7 @@ "gridPos": {"h": 4, "w": 6, "x": 12, "y": 35}, "id": 27, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_battery_current_energy\") |> last()", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"Wh\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_battery_energy\") |> last() |> map(fn: (r) => ({r with _value: r._value / 1000.0}))", "refId": "A"}], "title": "Battery Energy", "type": "stat" }, @@ -394,8 +394,9 @@ "gridPos": {"h": 4, "w": 6, "x": 18, "y": 35}, "id": 28, "options": {"colorMode": "value", "graphMode": "none", "justifyMode": "auto", "orientation": "auto", "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, "textMode": "auto"}, - "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.entity_id == \"e3_vitocharge_03_energy_reserved\") |> last()", "refId": "A"}], + "targets": [{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "import \"array\"\ncap = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"kWh\" and r._field == \"value\" and r.domain == \"input_number\" and r.entity_id == \"e3_vitocharge_03_battery_installed_capacity\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\nlimit = (from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._measurement == \"%\" and r._field == \"value\" and r.entity_id == \"vitocharge_vx3_vx3_backup_discharge_limit\") |> last() |> findRecord(fn: (key) => true, idx: 0))._value\narray.from(rows: [{_time: now(), _value: cap * limit / 100.0}])", "refId": "A"}], "title": "Reserved Energy", + "description": "Battery capacity reserved for backup (installed capacity x backup discharge limit)", "type": "stat" } ],