Files
homelab/kubernetes/apps/monitoring/dashboards/heatpump-roi.json
claude-botandClaude Opus 4.8 0cda291aba fix(heatpump-roi): repair solar ratio in cost chart, honest annual savings, since-install blended price
- Historical Cost Comparison: the chained joins renamed _value columns to
  _value_j2/_value (not _value_gi/_value_sp as the map expected), so the
  solar ratio was always null->0 and the whole cost line was priced at full
  grid price (EUR 4.67K instead of ~3.4K). Streams now pre-rename columns
  to h/d/gi/ge/sp so the join is unambiguous.
- Blended price now uses counter deltas since heat-pump install (Oct 2024)
  instead of device-lifetime counters that include 1.4 years of PV-only
  history from May 2023 (ratio 0.369 vs 0.322 -> EUR 0.236/kWh vs 0.248).
  Applied consistently to all cost/savings/ROI stats and the cost chart
  (chart subtracts install-date counter baselines).
- Annual savings / payback: replace the calendar-YTD extrapolation
  (12 / (month + day/30) - which overcounts elapsed time by a month and is
  seasonally biased) with the actual trailing-365-day consumption spread.
- Fix stale legend 'Last' values (10734/1230/EUR 9.47K): the counters'
  friendly_name tag changed 2026-03-04, splitting each series into two
  tables; group()+sort() merges them before aggregateWindow.
- New oil_baseline dashboard variable (6650) replaces six hardcoded copies.
- Running Cost: neutral color (was green/yellow/red on a forever-growing
  cumulative), retitled '(Since Install)'; added descriptions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:20:50 +02:00

460 lines
53 KiB
JSON

{
"annotations": {"list": []},
"description": "Heat Pump ROI Analysis - Viessmann Vitocal 250-A. All panels read from InfluxDB (bucket suedstr28), which is authoritative and live. After the 2026-07 ViCare->open3e/MQTT migration the heat-pump/PV entities were renamed; panels query the current entity_ids and, where an entity was renamed, UNION the pre-migration e3_* series with the post-migration series so history stays unbroken. The blended electricity price weights solar vs grid by the self-consumption ratio accumulated since the heat-pump install (Oct 2024); annual savings and payback use the actual trailing-12-month consumption.",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"id": null,
"links": [],
"panels": [
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "blue", "value": null}]}, "unit": "currencyEUR"},
"overrides": []
},
"gridPos": {"h": 4, "w": 4, "x": 0, "y": 0},
"id": 1,
"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\"\narray.from(rows: [{_time: now(), _value: float(v: \"${investment_cost}\")}])", "refId": "A"}],
"title": "Investment Cost",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "orange", "value": null}]}, "unit": "currencyEUR"},
"overrides": []
},
"gridPos": {"h": 4, "w": 4, "x": 4, "y": 0},
"id": 2,
"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\"\narray.from(rows: [{_time: now(), _value: float(v: \"${oil_baseline}\")}])", "refId": "A"}],
"title": "Oil Baseline (Annual)",
"description": "Based on 2022-2023 oil orders: ~5,000L at ~1.33 EUR/L average",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}]}, "unit": "currencyEUR"},
"overrides": []
},
"gridPos": {"h": 4, "w": 4, "x": 8, "y": 0},
"id": 3,
"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\"\ngi = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nge = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nsp = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nselfc = sp[0] - ge[0]\ntotal = gi[0] + selfc\nblended = (selfc / total) * ${solar_price} + (1.0 - selfc / total) * ${grid_price}\narray.from(rows: [{_time: now(), _value: blended}])", "refId": "A"}],
"title": "Blended Price",
"description": "Weighted average: solar self-consumption @ EUR ${solar_price}, grid @ EUR ${grid_price}. Self-consumption ratio from Vitocharge PV/grid counter deltas since heat-pump install (Oct 2024), unioning pre-migration ViCare with post-migration open3e counters.",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "purple", "value": null}]}, "unit": "kwh"},
"overrides": []
},
"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: -30d) |> filter(fn: (r) => r._field == \"value\" and (r.entity_id == \"e3_vitocal_heating_energy_consumption_total\" or r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\")) |> group(columns: [\"entity_id\"]) |> max() |> group() |> sum()", "refId": "A"}],
"title": "Total Consumption",
"description": "Cumulative heat-pump electricity since install: sum of the heating + DHW utility_meter counters.",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "yellow", "value": null}]}, "unit": "currencyEUR"},
"overrides": []
},
"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": "import \"array\"\ngi = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nge = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nsp = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nselfc = sp[0] - ge[0]\ntotal = gi[0] + selfc\nblended = (selfc / total) * ${solar_price} + (1.0 - selfc / total) * ${grid_price}\ncons = from(bucket: \"suedstr28\") |> range(start: -30d) |> filter(fn: (r) => r._field == \"value\" and (r.entity_id == \"e3_vitocal_heating_energy_consumption_total\" or r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\")) |> group(columns: [\"entity_id\"]) |> max() |> group() |> sum() |> findColumn(fn: (key) => true, column: \"_value\")\narray.from(rows: [{_time: now(), _value: cons[0] * blended}])", "refId": "A"}],
"title": "Running Cost (Since Install)",
"description": "Total consumption times blended price. Cumulative since Oct 2024, not an annual figure.",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}]}, "unit": "currencyEUR"},
"overrides": []
},
"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\"\ngi = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nge = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nsp = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nselfc = sp[0] - ge[0]\ntotal = gi[0] + selfc\nblended = (selfc / total) * ${solar_price} + (1.0 - selfc / total) * ${grid_price}\nyr = from(bucket: \"suedstr28\") |> range(start: -30d) |> filter(fn: (r) => r._field == \"value\" and (r.entity_id == \"vitocal_250_a_vitocal_heating_electric_energy_year\" or r.entity_id == \"vitocal_250_a_vitocal_dhw_electric_energy_year\")) |> group(columns: [\"entity_id\"]) |> last() |> group() |> sum() |> findColumn(fn: (key) => true, column: \"_value\")\narray.from(rows: [{_time: now(), _value: yr[0] * blended}])", "refId": "A"}],
"title": "Cost This Year",
"description": "Device year counters (heating + DHW electric energy) times blended price.",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "decimals": 1, "mappings": [], "max": 5, "min": 1, "thresholds": {"mode": "absolute", "steps": [{"color": "red", "value": null}, {"color": "yellow", "value": 2.5}, {"color": "green", "value": 3}]}},
"overrides": []
},
"gridPos": {"h": 5, "w": 3, "x": 0, "y": 4},
"id": 7,
"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: -30d) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"vitocal_250_a_vitocal_scop_total\") |> last() |> keep(columns: [\"_value\"])", "refId": "A"}],
"title": "Overall SPF",
"type": "gauge"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "decimals": 1, "mappings": [], "max": 5, "min": 1, "thresholds": {"mode": "absolute", "steps": [{"color": "red", "value": null}, {"color": "yellow", "value": 2.5}, {"color": "green", "value": 3}]}},
"overrides": []
},
"gridPos": {"h": 5, "w": 3, "x": 3, "y": 4},
"id": 8,
"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: -30d) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"vitocal_250_a_vitocal_scop_heating\") |> last() |> keep(columns: [\"_value\"])", "refId": "A"}],
"title": "Heating SPF",
"type": "gauge"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "decimals": 1, "mappings": [], "max": 5, "min": 1, "thresholds": {"mode": "absolute", "steps": [{"color": "red", "value": null}, {"color": "yellow", "value": 2.5}, {"color": "green", "value": 3}]}},
"overrides": []
},
"gridPos": {"h": 5, "w": 3, "x": 6, "y": 4},
"id": 9,
"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: -30d) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"vitocal_250_a_vitocal_scop_dhw\") |> last() |> keep(columns: [\"_value\"])", "refId": "A"}],
"title": "DHW SPF",
"type": "gauge"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}]}, "unit": "currencyEUR"},
"overrides": []
},
"gridPos": {"h": 5, "w": 4, "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\"\ngi = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nge = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nsp = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nselfc = sp[0] - ge[0]\ntotal = gi[0] + selfc\nblended = (selfc / total) * ${solar_price} + (1.0 - selfc / total) * ${grid_price}\nhp = from(bucket: \"suedstr28\") |> range(start: -365d) |> filter(fn: (r) => r._field == \"value\" and (r.entity_id == \"e3_vitocal_heating_energy_consumption_total\" or r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\")) |> group(columns: [\"entity_id\"]) |> spread() |> group() |> sum() |> findColumn(fn: (key) => true, column: \"_value\")\narray.from(rows: [{_time: now(), _value: float(v: \"${oil_baseline}\") - hp[0] * blended}])", "refId": "A"}],
"title": "Annual Savings (Trailing 12M)",
"description": "Oil baseline minus actual heat-pump electricity cost of the last 365 days (counter deltas x blended price). A true rolling year, immune to seasonal extrapolation bias.",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "decimals": 1, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}, {"color": "yellow", "value": 15}, {"color": "red", "value": 20}]}, "unit": "short"},
"overrides": []
},
"gridPos": {"h": 5, "w": 4, "x": 13, "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\"\ngi = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nge = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nsp = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nselfc = sp[0] - ge[0]\ntotal = gi[0] + selfc\nblended = (selfc / total) * ${solar_price} + (1.0 - selfc / total) * ${grid_price}\nhp = from(bucket: \"suedstr28\") |> range(start: -365d) |> filter(fn: (r) => r._field == \"value\" and (r.entity_id == \"e3_vitocal_heating_energy_consumption_total\" or r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\")) |> group(columns: [\"entity_id\"]) |> spread() |> group() |> sum() |> findColumn(fn: (key) => true, column: \"_value\")\narray.from(rows: [{_time: now(), _value: float(v: \"${investment_cost}\") / (float(v: \"${oil_baseline}\") - hp[0] * blended)}])", "refId": "A"}],
"title": "Payback Period (Years)",
"description": "Investment / annual savings (trailing 12 months)",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}]}, "unit": "currencyEUR"},
"overrides": []
},
"gridPos": {"h": 5, "w": 4, "x": 17, "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\"\ngi = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nge = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nsp = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nselfc = sp[0] - ge[0]\ntotal = gi[0] + selfc\nblended = (selfc / total) * ${solar_price} + (1.0 - selfc / total) * ${grid_price}\ncons = from(bucket: \"suedstr28\") |> range(start: -30d) |> filter(fn: (r) => r._field == \"value\" and (r.entity_id == \"e3_vitocal_heating_energy_consumption_total\" or r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\")) |> group(columns: [\"entity_id\"]) |> max() |> group() |> sum() |> findColumn(fn: (key) => true, column: \"_value\")\narray.from(rows: [{_time: now(), _value: float(v: \"${oil_baseline}\") * (float(v: uint(v: now()) - uint(v: 2024-10-01T00:00:00Z)) / 1000000000.0) / 31536000.0 - cons[0] * blended}])", "refId": "A"}],
"title": "Savings Since Install",
"description": "Oil baseline prorated since Oct 2024 minus actual costs",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "decimals": 1, "mappings": [], "max": 100, "min": 0, "thresholds": {"mode": "absolute", "steps": [{"color": "red", "value": null}, {"color": "yellow", "value": 25}, {"color": "green", "value": 50}]}, "unit": "percent"},
"overrides": []
},
"gridPos": {"h": 5, "w": 3, "x": 21, "y": 4},
"id": 40,
"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\"\ngi = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nge = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nsp = union(tables: [from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))]) |> group() |> spread() |> findColumn(fn: (key) => true, column: \"_value\")\nselfc = sp[0] - ge[0]\ntotal = gi[0] + selfc\nblended = (selfc / total) * ${solar_price} + (1.0 - selfc / total) * ${grid_price}\ncons = from(bucket: \"suedstr28\") |> range(start: -30d) |> filter(fn: (r) => r._field == \"value\" and (r.entity_id == \"e3_vitocal_heating_energy_consumption_total\" or r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\")) |> group(columns: [\"entity_id\"]) |> max() |> group() |> sum() |> findColumn(fn: (key) => true, column: \"_value\")\narray.from(rows: [{_time: now(), _value: (float(v: \"${oil_baseline}\") * (float(v: uint(v: now()) - uint(v: 2024-10-01T00:00:00Z)) / 1000000000.0) / 31536000.0 - cons[0] * blended) / float(v: \"${investment_cost}\") * 100.0}])", "refId": "A"}],
"title": "ROI Progress",
"description": "Percentage of investment recovered through savings",
"type": "gauge"
},
{
"collapsed": false,
"gridPos": {"h": 1, "w": 24, "x": 0, "y": 9},
"id": 30,
"panels": [],
"title": "Historical Data (InfluxDB - full history)",
"type": "row"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {
"color": {"mode": "palette-classic"},
"custom": {"axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 20, "gradientMode": "none", "hideFrom": {"legend": false, "tooltip": false, "viz": false}, "lineInterpolation": "smooth", "lineWidth": 2, "pointSize": 5, "scaleDistribution": {"type": "linear"}, "showPoints": "never", "spanNulls": true, "stacking": {"group": "A", "mode": "none"}, "thresholdsStyle": {"mode": "off"}},
"mappings": [],
"thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}]},
"unit": "kwh"
},
"overrides": [
{"matcher": {"id": "byName", "options": "Heating Total"}, "properties": [{"id": "color", "value": {"fixedColor": "red", "mode": "fixed"}}]},
{"matcher": {"id": "byName", "options": "DHW Total"}, "properties": [{"id": "color", "value": {"fixedColor": "blue", "mode": "fixed"}}]}
]
},
"gridPos": {"h": 10, "w": 12, "x": 0, "y": 10},
"id": 21,
"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\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"kWh\")\n |> filter(fn: (r) => r.entity_id == \"e3_vitocal_heating_energy_consumption_total\")\n |> filter(fn: (r) => r._field == \"value\")\n |> group()\n |> sort(columns: [\"_time\"])\n |> aggregateWindow(every: 1d, fn: last, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\"])\n |> rename(columns: {_value: \"Heating Total\"})",
"refId": "A"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"query": "from(bucket: \"suedstr28\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"kWh\")\n |> filter(fn: (r) => r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\")\n |> filter(fn: (r) => r._field == \"value\")\n |> group()\n |> sort(columns: [\"_time\"])\n |> aggregateWindow(every: 1d, fn: last, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\"])\n |> rename(columns: {_value: \"DHW Total\"})",
"refId": "B"
}
],
"title": "Historical Energy Consumption",
"type": "timeseries"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {
"color": {"mode": "palette-classic"},
"custom": {"axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 20, "gradientMode": "none", "hideFrom": {"legend": false, "tooltip": false, "viz": false}, "lineInterpolation": "smooth", "lineWidth": 2, "pointSize": 5, "scaleDistribution": {"type": "linear"}, "showPoints": "never", "spanNulls": true, "stacking": {"group": "A", "mode": "none"}, "thresholdsStyle": {"mode": "off"}},
"mappings": [],
"thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}]},
"unit": "currencyEUR"
},
"overrides": [
{"matcher": {"id": "byName", "options": "Electricity Cost"}, "properties": [{"id": "color", "value": {"fixedColor": "green", "mode": "fixed"}}]},
{"matcher": {"id": "byName", "options": "Oil Baseline"}, "properties": [{"id": "color", "value": {"fixedColor": "orange", "mode": "fixed"}}]}
]
},
"gridPos": {"h": 10, "w": 12, "x": 12, "y": 10},
"id": 22,
"options": {"legend": {"calcs": ["min", "max", "lastNotNull"], "displayMode": "table", "placement": "right", "showLegend": true}, "tooltip": {"mode": "multi", "sort": "desc"}},
"targets": [
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"query": "// Heat pump consumption (survivor utility_meters, continuous). group()+sort() merges the\n// friendly_name-era table split (tag changed 2026-03) so daily lasts stay time-ordered.\nheatpump_heating = from(bucket: \"suedstr28\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"kWh\" and r.entity_id == \"e3_vitocal_heating_energy_consumption_total\" and r._field == \"value\")\n |> group()\n |> sort(columns: [\"_time\"])\n |> aggregateWindow(every: 1d, fn: last, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\"])\n |> rename(columns: {_value: \"h\"})\n\nheatpump_dhw = from(bucket: \"suedstr28\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"kWh\" and r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\" and r._field == \"value\")\n |> group()\n |> sort(columns: [\"_time\"])\n |> aggregateWindow(every: 1d, fn: last, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\"])\n |> rename(columns: {_value: \"d\"})\n\n// Grid import: old ViCare (Wh) UNION new open3e (kWh) -> normalize to kWh\ngrid_import = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\" and r._field == \"value\") |> 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.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> group()\n |> sort(columns: [\"_time\"])\n |> aggregateWindow(every: 1d, fn: last, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\"])\n |> rename(columns: {_value: \"gi\"})\n\n// Grid export: old ViCare (Wh) UNION new open3e (kWh) -> normalize to kWh\ngrid_export = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\" and r._field == \"value\") |> 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.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> group()\n |> sort(columns: [\"_time\"])\n |> aggregateWindow(every: 1d, fn: last, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\"])\n |> rename(columns: {_value: \"ge\"})\n\n// Solar production: old ViCare (kWh) UNION new open3e (Wh) -> normalize to kWh\nsolar_prod = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))])\n |> group()\n |> sort(columns: [\"_time\"])\n |> aggregateWindow(every: 1d, fn: last, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\"])\n |> rename(columns: {_value: \"sp\"})\n\n// Counter values at heat-pump install (Oct 2024): subtracted so the self-consumption ratio\n// only counts flows since install, not the PV history from May 2023.\ngiB = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_import_from_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})),\n from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_supplied_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> group() |> min() |> findColumn(fn: (key) => true, column: \"_value\")\ngeB = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_energy_export_to_grid\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0})),\n from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_grid_feed_in_energy\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))])\n |> group() |> min() |> findColumn(fn: (key) => true, column: \"_value\")\nspB = union(tables: [\n from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"e3_vitocharge_03_solar_energy_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value})),\n from(bucket: \"suedstr28\") |> range(start: 2024-10-01T00:00:00Z) |> filter(fn: (r) => r.entity_id == \"vitocharge_vx3_vx3_pv_production_total\" and r._field == \"value\") |> map(fn: (r) => ({_time: r._time, _value: r._value / 1000.0}))])\n |> group() |> min() |> findColumn(fn: (key) => true, column: \"_value\")\n\n// Chain joins (Flux only supports 2-way joins). Columns are pre-renamed to h/d/gi/ge/sp because\n// join() suffixes colliding _value columns unpredictably across chained joins.\njoin1 = join(tables: {h: heatpump_heating, d: heatpump_dhw}, on: [\"_time\"])\njoin2 = join(tables: {j1: join1, gi: grid_import}, on: [\"_time\"])\njoin3 = join(tables: {j2: join2, ge: grid_export}, on: [\"_time\"])\njoin(tables: {j3: join3, sp: solar_prod}, on: [\"_time\"])\n |> map(fn: (r) => {\n heatpump = r.h + r.d\n self_consumption = (r.sp - spB[0]) - (r.ge - geB[0])\n total_consumption = (r.gi - giB[0]) + self_consumption\n solar_ratio = if total_consumption > 0.0 then self_consumption / total_consumption else 0.0\n return {_time: r._time, _value: heatpump * ((1.0 - solar_ratio) * ${grid_price} + solar_ratio * ${solar_price})}\n })\n |> rename(columns: {_value: \"Electricity Cost\"})",
"refId": "A"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"query": "from(bucket: \"suedstr28\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"kWh\")\n |> filter(fn: (r) => r.entity_id == \"e3_vitocal_heating_energy_consumption_total\")\n |> filter(fn: (r) => r._field == \"value\")\n |> group()\n |> sort(columns: [\"_time\"])\n |> aggregateWindow(every: 1d, fn: last, createEmpty: false)\n |> keep(columns: [\"_time\", \"_value\"])\n |> map(fn: (r) => ({_time: r._time, _value: float(v: \"${oil_baseline}\") * float(v: uint(v: r._time) - uint(v: 2024-10-01T00:00:00Z)) / 31536000000000000.0}))\n |> rename(columns: {_value: \"Oil Baseline\"})",
"refId": "B"
}
],
"title": "Historical Cost Comparison",
"description": "Actual electricity cost vs what oil would have cost. Solar/grid series union pre-migration ViCare (e3_*) with post-migration open3e (vitocharge_vx3_*), unit-normalized to kWh.",
"type": "timeseries"
},
{
"collapsed": false,
"gridPos": {"h": 1, "w": 24, "x": 0, "y": 20},
"id": 31,
"panels": [],
"title": "Recent Data (InfluxDB)",
"type": "row"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {
"color": {"mode": "palette-classic"},
"custom": {"axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 20, "gradientMode": "none", "hideFrom": {"legend": false, "tooltip": false, "viz": false}, "lineInterpolation": "smooth", "lineWidth": 2, "pointSize": 5, "scaleDistribution": {"type": "linear"}, "showPoints": "never", "spanNulls": false, "stacking": {"group": "A", "mode": "none"}, "thresholdsStyle": {"mode": "off"}},
"mappings": [],
"thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}]},
"unit": "kwh"
},
"overrides": [
{"matcher": {"id": "byName", "options": "Heating Total"}, "properties": [{"id": "color", "value": {"fixedColor": "red", "mode": "fixed"}}]},
{"matcher": {"id": "byName", "options": "DHW Total"}, "properties": [{"id": "color", "value": {"fixedColor": "blue", "mode": "fixed"}}]}
]
},
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 21},
"id": 13,
"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._field == \"value\" and r.entity_id == \"e3_vitocal_heating_energy_consumption_total\") |> aggregateWindow(every: 1h, fn: last, createEmpty: false) |> keep(columns: [\"_time\", \"_value\"]) |> rename(columns: {_value: \"Heating Total\"})", "refId": "A"},
{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\") |> aggregateWindow(every: 1h, fn: last, createEmpty: false) |> keep(columns: [\"_time\", \"_value\"]) |> rename(columns: {_value: \"DHW Total\"})", "refId": "B"}
],
"timeFrom": "7d",
"title": "Cumulative Energy Consumption (Recent)",
"type": "timeseries"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {
"color": {"mode": "palette-classic"},
"custom": {"axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "bars", "fillOpacity": 60, "gradientMode": "none", "hideFrom": {"legend": false, "tooltip": false, "viz": false}, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": {"type": "linear"}, "showPoints": "never", "spanNulls": false, "stacking": {"group": "A", "mode": "none"}, "thresholdsStyle": {"mode": "off"}},
"mappings": [],
"thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}]},
"unit": "kwh"
},
"overrides": []
},
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 21},
"id": 15,
"options": {"legend": {"calcs": ["mean", "sum"], "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._field == \"value\" and r.entity_id == \"e3_vitocal_heating_energy_consumption_total\") |> aggregateWindow(every: 1d, fn: last, createEmpty: false) |> difference(nonNegative: true) |> keep(columns: [\"_time\", \"_value\"]) |> rename(columns: {_value: \"Heating (daily)\"})", "refId": "A"},
{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\") |> aggregateWindow(every: 1d, fn: last, createEmpty: false) |> difference(nonNegative: true) |> keep(columns: [\"_time\", \"_value\"]) |> rename(columns: {_value: \"DHW (daily)\"})", "refId": "B"}
],
"timeFrom": "14d",
"title": "Daily Consumption",
"description": "Daily kWh consumed (delta of cumulative counters). Replaces the retired *_last_seven_days sensors.",
"type": "timeseries"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {
"color": {"mode": "palette-classic"},
"custom": {"axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": {"legend": false, "tooltip": false, "viz": false}, "lineInterpolation": "smooth", "lineWidth": 2, "pointSize": 5, "scaleDistribution": {"type": "linear"}, "showPoints": "never", "spanNulls": true, "stacking": {"group": "A", "mode": "none"}, "thresholdsStyle": {"mode": "off"}},
"mappings": [],
"thresholds": {"mode": "absolute", "steps": [{"color": "green", "value": null}]},
"unit": "short"
},
"overrides": []
},
"gridPos": {"h": 8, "w": 24, "x": 0, "y": 29},
"id": 16,
"options": {"legend": {"calcs": ["mean", "min", "max"], "displayMode": "table", "placement": "right", "showLegend": true}, "tooltip": {"mode": "multi", "sort": "desc"}},
"targets": [
{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"e3_vitocal_seasonal_performance_factor\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"vitocal_250_a_vitocal_scop_total\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> aggregateWindow(every: 6h, fn: last, createEmpty: false) |> keep(columns: [\"_time\", \"_value\"]) |> rename(columns: {_value: \"Overall SPF\"})", "refId": "A"},
{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"e3_vitocal_seasonal_performance_factor_heating\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"vitocal_250_a_vitocal_scop_heating\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> aggregateWindow(every: 6h, fn: last, createEmpty: false) |> keep(columns: [\"_time\", \"_value\"]) |> rename(columns: {_value: \"Heating SPF\"})", "refId": "B"},
{"datasource": {"type": "influxdb", "uid": "influxdb"}, "query": "union(tables: [from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"e3_vitocal_seasonal_performance_factor_domestic_hot_water\") |> map(fn: (r) => ({_time: r._time, _value: r._value})), from(bucket: \"suedstr28\") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"vitocal_250_a_vitocal_scop_dhw\") |> map(fn: (r) => ({_time: r._time, _value: r._value}))]) |> aggregateWindow(every: 6h, fn: last, createEmpty: false) |> keep(columns: [\"_time\", \"_value\"]) |> rename(columns: {_value: \"DHW SPF\"})", "refId": "C"}
],
"timeFrom": "90d",
"title": "Seasonal Performance Factor Over Time",
"type": "timeseries"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "blue", "value": null}]}, "unit": "kwh"},
"overrides": []
},
"gridPos": {"h": 4, "w": 6, "x": 0, "y": 37},
"id": 17,
"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: -30d) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"vitocal_250_a_vitocal_heating_electric_energy_year\") |> last() |> keep(columns: [\"_value\"])", "refId": "A"}],
"title": "Heating This Year",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "blue", "value": null}]}, "unit": "kwh"},
"overrides": []
},
"gridPos": {"h": 4, "w": 6, "x": 6, "y": 37},
"id": 18,
"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: -30d) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"vitocal_250_a_vitocal_dhw_electric_energy_year\") |> last() |> keep(columns: [\"_value\"])", "refId": "A"}],
"title": "DHW This Year",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "purple", "value": null}]}, "unit": "kwh"},
"overrides": []
},
"gridPos": {"h": 4, "w": 6, "x": 12, "y": 37},
"id": 19,
"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: -7d) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"e3_vitocal_heating_energy_consumption_total\") |> spread() |> keep(columns: [\"_value\"])", "refId": "A"}],
"title": "Heating Last 7 Days",
"type": "stat"
},
{
"datasource": {"type": "influxdb", "uid": "influxdb"},
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"}, "mappings": [], "thresholds": {"mode": "absolute", "steps": [{"color": "purple", "value": null}]}, "unit": "kwh"},
"overrides": []
},
"gridPos": {"h": 4, "w": 6, "x": 18, "y": 37},
"id": 20,
"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: -7d) |> filter(fn: (r) => r._field == \"value\" and r.entity_id == \"e3_vitocal_dhw_energy_consumption_total\") |> spread() |> keep(columns: [\"_value\"])", "refId": "A"}],
"title": "DHW Last 7 Days",
"type": "stat"
}
],
"refresh": "5m",
"schemaVersion": 38,
"style": "dark",
"tags": ["homelab", "heatpump", "energy", "roi"],
"templating": {"list": [
{
"current": {"selected": true, "text": "72968", "value": "72968"},
"description": "Heat pump investment cost (excluding water softener EUR 9,225)",
"hide": 0,
"label": "Investment",
"name": "investment_cost",
"options": [
{"selected": true, "text": "72968", "value": "72968"}
],
"query": "72968",
"type": "custom"
},
{
"current": {"selected": true, "text": "6650", "value": "6650"},
"description": "Annual oil heating cost baseline: ~5,000L at ~1.33 EUR/L (2022-2023 oil orders)",
"hide": 0,
"label": "Oil Baseline",
"name": "oil_baseline",
"options": [
{"selected": true, "text": "6650", "value": "6650"}
],
"query": "6650",
"type": "custom"
},
{
"current": {"selected": true, "text": "0.08", "value": "0.08"},
"description": "Electricity price when battery full/solar excess",
"hide": 0,
"label": "Solar Price",
"name": "solar_price",
"options": [
{"selected": true, "text": "0.08", "value": "0.08"}
],
"query": "0.08",
"type": "custom"
},
{
"current": {"selected": true, "text": "0.3271", "value": "0.3271"},
"description": "Grid electricity price EUR/kWh (replaces the retired live input_number.strompreis read)",
"hide": 0,
"label": "Grid Price",
"name": "grid_price",
"options": [
{"selected": true, "text": "0.3271", "value": "0.3271"}
],
"query": "0.3271",
"type": "custom"
}
]},
"time": {"from": "2024-10-01T00:00:00.000Z", "to": "now"},
"timepicker": {},
"timezone": "",
"title": "Heat Pump ROI",
"uid": "heatpump-roi",
"version": 1,
"weekStart": ""
}