Commit Graph
4354 Commits
Author SHA1 Message Date
k.renner 7b84daa74f Merge pull request 'fix(node-metrics): repair dead dashboard — nonexistent 'host' label broke every panel' (#281) from claude-bot/dash-node-metrics into master
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: gitea_admin/homelab#281
2026-07-17 11:21:40 +00:00
k.renner 33cbf94cd8 Merge pull request 'fix(power-flow): dead sensors, balance-based Home total, dedup history' (#278) from claude-bot/dash-power-flow into master
Reviewed-on: gitea_admin/homelab#278
2026-07-17 11:21:05 +00:00
k.renner 8a13629803 Merge pull request 'fix(home-climate): real room sensors only, repair empty heating panels, readable legends' (#280) from claude-bot/dash-home-climate into master
Reviewed-on: gitea_admin/homelab#280
2026-07-17 11:20:48 +00:00
k.renner a1a68e0230 Merge pull request 'fix(heatpump-heating-loading): Duplikat-Serien kollabieren, ehrliches No-Data' (#277) from claude-bot/dash-heatpump-heating-loading into master
Reviewed-on: gitea_admin/homelab#277
2026-07-17 11:20:14 +00:00
k.renner 5cdec44ed6 Merge pull request 'fix(heatpump-dhw-loading): dedupe scrape-residue series (broken scatter, 8x stat)' (#276) from claude-bot/dash-heatpump-dhw-loading into master
Reviewed-on: gitea_admin/homelab#276
2026-07-17 11:19:49 +00:00
k.renner 808e832ad3 Merge pull request 'feat(claude-workstation): off-site headless Claude Code host on nas-2' (#247) from feat/claude-workstation-nas2 into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: gitea_admin/homelab#247
2026-07-17 11:00:27 +00:00
k.renner 5febd33298 Merge pull request 'fix(gitea-overview): repair fully dead dashboard (unreachable prometheus-legacy datasource)' (#292) from claude-bot/dash-gitea-overview into master
Reviewed-on: gitea_admin/homelab#292
2026-07-17 10:59:51 +00:00
k.renner 874c3c8e55 Merge pull request 'fix(alloy-overview): repoint retired prometheus-legacy datasource, repair host-label legends' (#293) from claude-bot/dash-alloy-overview into master
Reviewed-on: gitea_admin/homelab#293
2026-07-17 10:59:37 +00:00
k.renner cbf642430f Merge pull request 'fix(monitoring): stop 3x-ingesting pve cluster metrics' (#294) from claude-bot/pve-scrape-dedup into master
Reviewed-on: gitea_admin/homelab#294
2026-07-17 10:59:21 +00:00
k.renner 8e2c569bb0 Merge pull request 'fix(homeassistant-overview): repair dead panels, curate sensors, honest stats' (#279) from claude-bot/dash-homeassistant-overview into master
Reviewed-on: gitea_admin/homelab#279
2026-07-17 10:59:08 +00:00
claude-botandClaude Fable 5 16d4996dff fix(monitoring): stop 3x-ingesting pve cluster metrics
The single pve scrape job hit all three node IPs with cluster=1, and
pve-exporter returns the full cluster-wide resource set regardless of
which node answers - every node/guest series landed in the TSDB three
times (same id, differing instance). Dashboards papered over it with
max by (id); the cardinality cost stayed.

Split per upstream prometheus-pve-exporter guidance: cluster collectors
scrape one node only, node-local collectors keep scraping all three
under a new pve-node job. No dashboard queries filter on job=, and all
already aggregate by id, so this is query-compatible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 08:46:30 +02:00
claude-botandClaude Opus 4.8 8519ad875d fix(alloy-overview): repoint retired prometheus-legacy datasource, repair host label legends
Every panel was dead: all 11 datasource refs pointed at the
prometheus-legacy datasource (retired 2026-05-09). Repoint to the
active prometheus datasource.

The alloy job also carries no 'host' label, so all legends and the
Agent Versions table rendered empty labels. Join via kube_pod_info
(pod -> node) so DaemonSet panels are labelled k8s-1/2/3, collapse
the eval-time histogram per pod before quantile, and tidy the
versions table to node/pod/version columns.

All queries verified against live Prometheus (3 agents, one clean
series per node).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:30:10 +02:00
claude-bot 035f46cd04 fix(gitea-overview): point panels at live Prometheus, dedupe migration residue
All 10 panels were dead: the dashboard queried datasource uid
prometheus-legacy (prometheus.srv.renner.uno:9090), which no longer
resolves (Grafana proxy: 502 dial tcp lookup i/o timeout). Gitea now
runs in-cluster and is scraped by the in-cluster Prometheus
(job="gitea", instance gitea.apps.svc.cluster.local:3000), so switch
every panel to uid prometheus.

Also wrap all targets in max(): the pre-migration instance
gitea.srv.renner.uno:3000 stopped 2026-07-12, and windows spanning
that date would otherwise render two identically-named series per
query. Stat panels additionally get instant:true so a dead exporter
shows No data instead of a stale last value.
2026-07-17 01:28:53 +02:00
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
claude-botandClaude Opus 4.8 81281ca20e fix(authentik-overview): repair dead Status panel, dedupe stats, fix outpost panel labels
- App Servers Up: job="authentik" matches nothing (real job label is
  authentik-server-metrics) -> panel showed 'No data'. Also 'or vector(0)'
  + red threshold so a dead scrape shows a red 0 instead of No data.
- Task/Admin Workers, Flows/Policies Cached: raw gauges return 4 series
  (2 pods x 2 pids) -> stats rendered 8 repeated numbers. Wrapped in max().
- Outposts Connected: raw per-outpost/per-pid series rendered an unreadable
  grid of ~30 0/1 values (0s were per-process views, not real disconnects).
  Now counts outposts with >=1 active WebSocket connection (10/10).
- Outpost Connections: legendFormat used non-existent labels {{name}}/{{uid}}
  (real: outpost_name/uuid) -> every legend row was '-'. Replaced with
  server-side authentik_outposts_connected as a state-timeline, one row per
  outpost, red mapping for 'getrennt'; covers all 10 outposts instead of the
  4 with scraped sidecar metrics.
- All stat queries switched to instant so dead sensors render honestly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:20:50 +02:00
claude-botandClaude Opus 4.8 ac623c1e9c fix(node-overview): repair 4 dead panels, exclude rbd/lxc devices, nodename legends
- Nodes Online/Down used job="node" but the scrape job is "node-exporter":
  both stats showed No data. Nodes Down additionally gets OR on() vector(0)
  so it renders 0 instead of No data when all nodes are up, and green-at-0/
  red-at-1 thresholds (was permanently red).
- Avg Disk Usage + Disk Usage by Node filtered mountpoint="/" which does not
  exist on Talos (only /var is a real xfs filesystem): both panels were empty.
- Disk I/O summed all block devices including the ~12 Rook-Ceph rbd* mappings
  per node (~438 kB/s vs 8-129 kB/s physical): filter to nvme.*|vd.*.
- Network I/O filter was written for a Docker host (veth/docker/br-) and let
  ~40 Cilium lxc* per-pod interfaces plus kernel pseudo-devices through,
  double-counting pod traffic: filter to eth.* (bond0 is down, 0 slaves).
- Legends showed instance IP:port; join node_uname_info via
  on(instance) group_left(nodename) so panels label k8s-1/2/3. Both join
  sides are wrapped in max by (...) so historical duplicate series from
  pod restarts / the pre-2026-07-16 duplicate-scrape residue cannot break
  the join or rainbow-duplicate long-window views.
- Stat panels reduce lastNotNull instead of mean: show current value, not
  a time-average over the selected window.

All queries verified against live Prometheus before applying.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:08:35 +02:00
claude-botandClaude Opus 4.8 30d6eb3af8 fix(blackbox-overview): repoint retired datasource, drop dead TCP/DNS panels
The dashboard was 100% dead: every panel still referenced the
prometheus-legacy datasource retired on 2026-05-09, so all panels
rendered 'No data' with datasource errors.

- Repoint all panels prometheus-legacy -> prometheus
- Remove TCP Endpoint Status panel: the blackbox-tcp Probe was
  deliberately deleted 2026-05-20 (see probes-blackbox.yaml), the job
  no longer exists
- Remove DNS Status panel: no blackbox-dns job has ever existed in
  this Prometheus (the dns-servers job is Alloy on the DNS VMs, not a
  probe)
- Replace the freed row with HTTP Status Codes (probe_http_status_code,
  instant, 2xx green / 3xx orange / 4xx+ and probe-fail red)
- Convert SSL Certificate Expiry from a seconds-countdown timeseries to
  a days-remaining bar gauge (instant, red <7d, orange <30d, green)
- Make status stat queries instant so dead probes render No data
  instead of a stale last-known value
- Pin Uptime y-axis to 0-100% and drop the fill (10 overlapping series
  at 100% rendered as a muddy block)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:08:21 +02:00
claude-botandClaude Opus 4.8 7e5ebb1e3c fix(proxmox-cluster): dedupe 3x-scraped series, repair broken panels
The pve-exporter is scraped via all 3 PVE nodes and each scrape returns the
full cluster view, so every series exists in triplicate. All stat counts were
3x too high (Online Nodes showed 39 instead of 3) and every bargauge/table
row appeared three times. Wrap all queries in max by (id) / count by (id).

- Online Nodes: count unique node/* ids with pve_up==1 (was 39, now 3);
  threshold green at 3 so a lost node turns red
- Total VMs: count unique qemu guests excluding templates (was 12, now 3)
- Total LXC: count unique ids (was 9, now 3)
- Running Guests: pve_guest_info has no status label in this exporter
  version (panel showed No data); derive from pve_up==1 per guest instead
- Node CPU/Memory bargauges + timeseries: filter to id=~"node/.*" — they
  showed every guest (mostly lxc rows), not nodes
- Guest Status table: dedupe rows, add Status column from pve_up (color
  mapped 1=Running/0=Stopped), drop useless Info column, migrate
  custom.displayMode -> custom.cellOptions (schemaVersion 38 skips the
  Grafana 10 migration, so gradient gauges/color background were silently
  ignored)
- Node Disk I/O -> Guest Disk I/O: pve_disk_read/write metrics only exist
  for qemu/lxc guests, never node/* (panel was permanently No data); use
  the *_total counters with rate()

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:02:32 +02:00
claude-botandClaude Opus 4.8 c7e9fc8bb6 fix(postgresql-overview): scrape CNPG metrics and rewrite dead pg_* queries
The dashboard was entirely dead: every panel queried postgres_exporter
metrics (pg_up, pg_stat_activity_count, ...) that have never existed in
this Prometheus. Postgres runs as four CloudNativePG clusters (ak-pg,
im-pg, gf-pg, pprl-pg) whose built-in exporter on :9187 was not scraped
at all (enablePodMonitor is false and no PodMonitor existed).

- add PodMonitors for the four CNPG clusters in monitoring-rules,
  labelled release=kube-prometheus-stack so the Prometheus
  podMonitorSelector actually picks them up (CNPG's own
  enablePodMonitor would generate unlabelled PodMonitors that this
  Prometheus ignores)
- rewrite all dashboard queries to the cnpg_* metrics the exporter
  actually exposes (verified against the pods' /metrics endpoints)
- dedupe multi-instance clusters (max by namespace/datname for sizes,
  primary-only filter via cnpg_pg_replication_in_recovery == 0 for
  per-database counters)
- replace the Active Locks stat (no lock metric in the CNPG default
  queries) with Max Replication Lag
- instant queries on all stat panels so dead instances show No data
  instead of a stale last value

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:02:28 +02:00
claude-bot ccde43b367 fix(infrastructure-overview): repair all 5 panels (wrong job, missing labels, Talos mountpoint)
- Healthy Hosts: job 'integrations/unix' does not exist here -> node-exporter;
  thresholds now red/yellow/green(3) so a single down node shows yellow
- Unhealthy Services: 'count(up == 0)' returns empty vector when healthy ->
  'or vector(0)' renders an honest 0; both stats now instant queries
- CPU: 'avg by(host)' collapsed all 3 nodes into one series (no host label) ->
  avg by(instance) joined with node_uname_info for k8s-1/2/3 legend names
- Memory: legend showed raw label dumps ({{host}} does not exist) -> nodename join
- Disk: Talos has no '/' mountpoint in node_filesystem -> use /var (the
  persistent data disk); retitle panel accordingly
2026-07-17 00:48:31 +02:00
claude-botandClaude Opus 4.8 019f1bc47c fix(pv-system-roi): repair Cumulative Value panel, honest current-power stat, unit + gap fixes
- Cumulative Value: Self-Use Savings and Total Value series rendered nothing.
  The union'd solar/export/price tables had mismatched group keys (pivot ran
  per-table) AND the sparse strompreis series almost never landed in the same
  aggregation window. Fix: group() before pivot + fill(usePrevious) per column.
  Verified via /api/ds/query: 471 points, final value EUR 4363 == stat panel.
- Solar Power stat: was aggregateWindow(mean) over the dashboard range (~17 h
  windows at 3 y), showing 3.04 kW at midnight. Now last() -> honest 0 W at night.
- Solar Installed: HA input_number stores 8140 (Wp, mislabeled kWp); with unit
  kwatt the panel would read 8.14 MW. Divide by 1000 -> 8.14 kW, documented.
- Timeseries gap honesty: e3_* series have a real data hole (Nov 2025 - Feb 2026,
  verified 0 points/month). createEmpty:false let Grafana draw fake decay ramps
  across it. Now createEmpty:true so gaps render as gaps (panels 20/21/22).
- Units: 'kwh' is not a valid Grafana unit id (rendered lowercase 'kwh');
  replaced with 'kwatth' everywhere -> proper kWh/MWh scaling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:47:04 +02:00
claude-botandClaude Opus 4.8 91620e5f14 fix(home-energy): repair dead panels, wrong queries, misleading semantics
- Current by Phase: regex /smart_meter.*current/ matched nothing (smart
  meter exposes no current entities) -> use verified
  e380_energy_meter_e380_current_l[123]; spanNulls for sparse updates
- Energy Consumption: measurement "kWh" does not exist in the bucket
  -> plot verified Wh daily counters (grid import/export, PV production,
  battery charge/discharge) with readable display names
- Active Lights: light-domain data is never recorded under measurement
  "%" -> replace dead table with Lights/Standby/Switches power
  timeseries from verified powercalc aggregates (all_*_power)
- Top 20 Devices: add group() so sort/limit work globally (was
  alphabetical, unsorted, per-table); exclude grid meters, PV/battery,
  forecasts, powercalc room/floor aggregates and _device_power twins;
  fresh 30-min window instead of stale last() over the full range;
  rowsToFields for clean labels (was "_value xyz_power")
- Grid semantics: retitle Total Power -> Grid Power (+Import/-Export),
  blue threshold for export; unstack mixed-sign phase graph; short
  L1/L2/L3 legend names; taller voltage/current panels so the legend
  table is not cut off

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:43:57 +02:00
claude-botandClaude Opus 4.8 509163fbc4 fix(home-security): repair alarm stats, curate sensor lists, dedup batteries
- Alarm stats: '== 1' dropped the series when false, so Armed Home/Away and
  Triggered could only ever show 'No data' and never their 0-state; Disarmed
  rendered 5 overlapping series. Use min()/max() aggregation over the one-hot
  alarm_control_panel_state series so every tile always shows a real 0/1.
- Windows/Doors tables: replace friendly-name regexes (matched battery-low
  sensors, HmIP 'Window/door is CLOSED' entities with inverted semantics,
  intrusion/tamper diagnostics, _2 twins, pond-pump/fridge sensors) with a
  curated entity list: one canonical is-open entity per physical sensor.
  Collapse scrape labels via max by so the tables show Sensor/Name/Status.
- Motion panels: old regex required 'Bewegungs.*Occupancy' and missed the
  Eingang sensor (typo 'Bewegunsmelder'), all motion-class sensors and the
  mmWave Praesenzmelder. Curated list of physical detectors.
- Covers: name-based exclusion (.*Nord.*|.*Ost.* etc.) dropped 8 physical
  covers incl. the garage door while letting the eg_schlafzimmer/eg_kuche
  group entities through. Exclude the 5 group covers by entity instead;
  label bars by entity because friendly names repeat ('Rollo' x3).
- Batteries: filter battery_notes '+' twin entities, drop PV storage SoC,
  remove '> 0' (which would hide a 0% dead battery), sort ascending so the
  most critical batteries are on top, instant query so stale values drop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:41:55 +02:00
claude-bot bd8a632334 fix(node-metrics): repair dead dashboard — nonexistent 'host' label broke every panel
The template variable queried label_values(node_uname_info, host), but no
node-exporter metric in this Prometheus carries a 'host' label (that label
only exists on blackbox-probe metrics). The variable resolved to empty and
all five panels rendered blank.

- Replace $host with a visible $node variable (nodename: k8s-1/2/3) plus a
  hidden chained $instance variable (nodename -> instance via node_uname_info)
- Filter Cilium pod interfaces (lxc*, cilium_*) and always-zero kernel
  pseudo-devices (ip6tnl0, sit0, teql0, tunl0) out of Network I/O
- Filter Ceph RBD-mapped volumes (rbd0..11) out of Disk I/O so the panel
  shows physical disks (nvme0n1, vda) only
- Exclude the rootfs bind mount (/etc/extensions.yaml) from Filesystem
  Usage, leaving the meaningful /var xfs mount

All queries verified against live Prometheus before commit.
2026-07-17 00:40:54 +02:00
claude-bot f248e0cc77 fix(home-climate): real room sensors only, repair empty heating panels, readable legends
- All temperature/humidity panels: replace vendor regexes (shelly.*temp etc.)
  that dragged in device-internal temperatures (pond pump 34C, power meter 60C,
  relay 76C) with explicit allow-lists of verified room sensors
- Add missing sensors: shelly_blu_h_t_7203 (KG Schlafzimmer),
  treppenhaus_eg_bewegungsmelder (EG Treppenhaus), vento humidity sensors,
  Shelly Wall Display (DG Buero)
- OG & DG panel: regex had no og_ alternative and showed only device internals;
  now honest DG-only series + description (OG deliberately has no sensor)
- Heating System / Hot Water panels were empty (domain climate/water_heater
  does not exist in the C measurement) -> use vitocal_250_a_* sensors
- Current-value panels: fixed -6h window so dead sensors (EG Shelly H&T trio,
  DG Meter Pro) disappear instead of showing stale values as live
- Legends: map entity_ids to room names in Flux + displayName, removes the
  '_value xyz' prefix
- Align CO2 thresholds between gauge and history (800/1000/1500)
2026-07-17 00:35:15 +02:00
claude-botandClaude Opus 4.8 1c333826ff fix(power-flow): dead sensors, balance-based Home total, dedup history
- Server node: sensor.keller_server_power is dead (UPS retired) -> Wave PM
  Mini server outlet (kg_vorratskeller_wave_pm_mini_server_steckdose)
- Garden node: sensor.eg_garten_teichpumpe_power_2 is dead ->
  sensor.shelly_teichpumpe_power_0 (live, peaks ~77 W)
- Home node/series: floor sums only cover submetered circuits (702 W shown
  vs ~1.1 kW real); Home is now the meter balance PV + Grid + Battery, with
  a new grey 'Rest' node for the non-submetered remainder
- Flow lines: Heat Pump is metered inside Keller (was drawn from EG);
  Garden is on floor Aussenbereich, in no floor sum (was drawn from OG)
- Drop redundant batteryIn target (derived from the signed battery sensor);
  update stale comments from the retired *_absolute sensors
- History: max by (entity) guards against duplicate-scrape residue that
  doubled the summed Home series; sign hints in Battery/Grid legends;
  grid query pinned to entity id instead of friendly_name

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:33:05 +02:00
claude-botandClaude Opus 4.8 4c16f6ac89 fix(homeassistant-overview): repair dead panels, curate sensors, honest stats
- Status panel: job label is home-assistant (hyphen), was homeassistant -> No data
- Doors & Windows / Motion: exporter has no device_class label; select curated
  entity lists instead (one canonical is-open entity per physical contact,
  physical motion/presence sensors without zone/magic_areas duplicates)
- Temperature: was all 61 temp series incl. device-internal electronics temps
  (fridge 5C, PoE switch 75C, Shelly 58-75C); curated to room/ambient sensors
  + outdoor temp, incl. currently-dead EG H&T sensors so they reappear
- Power Consumption: exclude non-consumption series (Solcast forecasts,
  battery max charge/discharge capability, derived night-load stats)
- Energy: cumulative lifetime counters (up to 80 MWh) told nothing; now
  bargauge of top 12 increase() over 24h, forecast counters excluded,
  max by () dedup against scrape residue
- Switches: 248 unreadable micro-tiles -> single 'Switches On' count stat
- Battery: bottomk(12) instant, battery_notes *_battery_plus twins excluded
- Entity counts: max by (entity) dedup, instant queries on all stats
- Unavailable Entities: neutral blue; ~1139 is the structural baseline
  (verified against HA API), old yellow>=1/red>=5 thresholds were meaningless

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:32:47 +02:00
claude-botandClaude Opus 4.8 1165afce02 fix(heatpump-heating-loading): collapse pod-churn duplicate series, honest no-data text
All queries selected raw series by entity label only; over a 90d window HA pod
restarts plus the (since fixed) duplicate proxy scrape produce 30 distinct
series per entity, rendering the Außentemperatur & Puffer panel as an
unreadable 30-entry legend jumble. Wrap every query in max() so exactly one
series per entity survives — this also keeps the xychart joinByField transform
correct once heating data arrives, since the join needs unique field names.

The stat panel now says 'keine Heizdaten im Zeitraum' instead of a giant
'No data': the logging sensor is intentionally unavailable outside active
heating loading, so an empty summer dashboard is by design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:32:18 +02:00
claude-bot f9cbeb0fc0 fix(heatpump-dhw-loading): dedupe scrape-residue series with max by (entity)
All four panels queried raw HA metrics that still carry ~12 duplicate
series per entity in the 90d window (two scrape jobs x six pod
lifetimes; the duplicate-scrape bug was fixed 2026-07-16 but the
residue stays in range queries until it ages out). This broke the
scatter join (no points rendered), made the stat panel show 8
conflicting averages, and drew rainbow duplicates in both time series.

Wrapping every target in max by (entity) (...) collapses the residue
to one series per entity; values between the duplicate jobs were
near-identical, so max is lossless.
2026-07-17 00:20:41 +02:00
k.renner d24af8d12f Merge pull request 'fix(home-overview): repair broken tiles, wrong sensors, double-counted power' (#275) from claude-bot/dash-home-overview into master
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: gitea_admin/homelab#275
2026-07-16 22:12:01 +00:00
claude-botandClaude Opus 4.8 05fd813e9f fix(home-overview): repair broken tiles, wrong sensors, double-counted power
- Windows/Doors/Batteries tiles: literal ${__value.raw} never interpolates in
  value mappings; replaced range mappings with suffix units. Counts now use
  curated entity lists (one canonical 'is open' entity per physical sensor)
  instead of friendly-name regexes that matched inverted *_is_closed sensors,
  'Battery low' sensors and duplicate _2 entities.
- Alarm tile: old min(...==1) query could never show TRIGGERED; now
  max(...{state="triggered"}) with 0=OK/1=TRIGGERED mapping.
- Power Usage: old regex summed phases A+B+C AND total_active_power AND
  device_power -> ~2x real value; now reads total_active_power only.
- Floor temperature panels: select verified room sensors by entity id instead
  of vendor regexes (which pulled 65-75C device-internal temps and missed the
  I-9PSL sensors that live in the EG). Instant queries so dead sensors do not
  render stale values. OG: no room sensor exists (owner-confirmed) -> honest
  noValue; DG SwitchBot offline -> noValue 'Sensor offline'.
- Tables: dedup via max by (entity, friendly_name), drop k8s scrape label
  columns, colour only the value column; battery list excludes battery_notes
  *_battery_plus twin entities and no longer hides 0% batteries.
- Power Consumption: L1/L2/L3 legends, Total from total_active_power.
- Indoor Temperatures: room sensors only (y-axis no longer squashed to 80C).
- Heating row: Hot Water/Heating pointed at post-open3e-migration vitocal
  sensors (old queries returned nothing resp. only the fridge); humidity/CO2
  deduplicated; row renamed to Heating & Air Quality.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:06:58 +02:00
k.renner 5996a72dcf Merge pull request 'fix(pv-system-roi): clean timeseries legend labels' (#274) from claude-bot/pv-roi-legend-fix into master
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: gitea_admin/homelab#274
2026-07-16 20:36:42 +00:00
claude-botandClaude Opus 4.8 55596a2bac fix(pv-system-roi): clean timeseries legend labels
The union-based timeseries series were named via set(_field, ...), which Grafana's InfluxDB frontend rendered as "_value {_start=...}" instead of the intended labels (and broke the byName color overrides). Data was correct; only the legend/colors were affected.

Switch to the keep(columns:[_time,_value]) |> rename(_value -> "Name") pattern (as used in heatpump-roi), which names each series cleanly. Verified in Grafana Explore. No data/logic change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiLCinB7NLp7YdCjGYzvfh
2026-07-16 22:35:21 +02:00
k.renner 592fef04fc Merge pull request 'fix(dashboards): repoint power-flow + pv-system-roi to open3e entities' (#273) from claude-bot/pv-powerflow-open3e into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: gitea_admin/homelab#273
2026-07-16 20:24:22 +00:00
k.renner d3841b6eb6 Merge pull request 'fix(decommission): rustfs shims v2 with explicit timeouts (stage 2 retry)' (#272) from chore/rustfs-destroy-stage2b into master
ci/woodpecker/push/deploy-infrastructure Pipeline was successful
Reviewed-on: gitea_admin/homelab#272
2026-07-16 20:24:07 +00:00
claude-botandClaude Opus 4.8 2a2a5d9a0b fix(dashboards): repoint power-flow + pv-system-roi to open3e entities
The 2026-07 ViCare->open3e/MQTT migration renamed the Vitocharge PV/battery entities (e3_vitocharge_03_* -> vitocharge_vx3_vx3_*) and changed some units. Both dashboards still queried the retired entities and showed No data.

power-flow (Prometheus, real-time): solar_power->pv_power, battery SoC->battery_soc; the two absolute battery-power sensors are replaced by a single signed battery_power (open3e: positive=discharge, negative=charge), so charge/discharge derive from its sign.

pv-system-roi (InfluxDB): repoint every query; fix units (PV/battery energy now Wh not kWh -> /1000; grid energy now kWh not Wh); grid power sign unchanged (negative=export). Timeseries panels UNION pre-migration e3_* with post-migration series (unit-normalized) so history back to 2023 stays unbroken. Reserved Energy recomputed as capacity x backup_discharge_limit. strompreis + the two input_number config entities survived and are kept.

Validated live via Grafana Explore: signed-battery charge/discharge union, unit normalization, pivot/value patterns (reused from merged heatpump-roi fix #271).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiLCinB7NLp7YdCjGYzvfh
2026-07-16 22:22:26 +02:00
claude-botandClaude Fable 5 1b44ca5cfc fix(decommission): rustfs shims v2 with explicit timeouts (stage 2 retry)
Both destroy applies (runs 1180/1181) died instantly with 'context
deadline exceeded' on the pre-destroy status GET while the PVE API
answers in <1s: the bpg provider reads timeout_delete from STATE at
destroy time, and the stage-1 import stored 0 for the timeout-less
shims — a zero-second context. Read paths carry their own timeout,
which is why import and plan were clean.

v2 declares all timeouts explicitly (one in-place, no-API update
writes them into state) and mirrors the real container attributes
(unprivileged=true is ForceNew — mirroring prevents a replace).
No ignore_changes this time: the timeout update must actually apply.
Flow: merge -> plan (must be in-place only) -> apply -> follow-up PR
deletes the file -> destroy apply.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV7bZzZLeoFUEdAeCSLEei
2026-07-16 21:58:04 +02:00
k.renner 9f996f9907 Merge pull request 'fix(heatpump-roi): rebuild dashboard on InfluxDB after ViCare->open3e migration' (#271) from claude-bot/heatpump-roi-influxdb into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: gitea_admin/homelab#271
2026-07-16 19:56:59 +00:00
claude-botandClaude Opus 4.8 0aad90bea9 fix(heatpump-roi): rebuild dashboard on InfluxDB after ViCare->open3e migration
The ViCare cloud integration was replaced by open3e/MQTT (2026-07), renaming all heat-pump/PV entities (e3_vitocal_*/e3_vitocharge_03_* -> vitocal_250_a_vitocal_*/vitocharge_vx3_vx3_*). The dashboard still queried the retired IDs, and a duplicate Prometheus scrape broke the surviving-entity panels with many-to-many errors.

Rebuild every panel on InfluxDB (bucket suedstr28, authoritative + live) using current entity IDs. Where an entity was renamed, UNION the pre-migration e3_* series with the post-migration series (unit-normalized: PV Wh->kWh) so history stays unbroken. Drop all Prometheus targets. Add a grid_price variable (0.3271) replacing the retired live strompreis read.

Also rename the home-assistant-bgp Service port http->ha so the home-assistant ServiceMonitor no longer scrapes HA a second time via the LB (:8123), which duplicated homeassistant_sensor_* series cluster-wide.

Queries validated live via Grafana Explore: blended price 0.248 EUR/kWh, total consumption 14284 kWh, projected annual savings 4698 EUR, oil proration 11912 EUR, old-union-new grid-import continuous.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QiLCinB7NLp7YdCjGYzvfh
2026-07-16 21:54:56 +02:00
k.renner fc4e30f469 Merge pull request 'chore(decommission): drop rustfs import shims — destroy via apply (stage 2/2)' (#270) from chore/rustfs-destroy-stage2 into master
ci/woodpecker/push/deploy-infrastructure Pipeline was successful
ci/woodpecker/manual/deploy-infrastructure Pipeline failed
Reviewed-on: gitea_admin/homelab#270
2026-07-16 19:47:38 +00:00
claude-botandClaude Fable 5 ad6161c226 chore(decommission): drop rustfs import shims — apply destroys LXC 800/801/802 (stage 2/2)
Stage 1 (#268) adopted the never-tofu-managed rustfs containers into
state (import run 1173: all three imported, plan 'No changes').
Removing the shims makes the next apply destroy the containers —
guest lifecycle fully through tofu, per owner rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV7bZzZLeoFUEdAeCSLEei
2026-07-16 21:07:29 +02:00
security-bot b6a5ae0877 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784228462
ci/woodpecker/manual/deploy-infrastructure Pipeline was successful
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-16 19:04:11 +00:00
k.renner 182bf8f5df Merge pull request 'chore(decommission): rustfs import shims — destroy via tofu (stage 1/2)' (#268) from chore/rustfs-import-adopt into master
ci/woodpecker/push/deploy-infrastructure Pipeline was successful
Reviewed-on: gitea_admin/homelab#268
2026-07-16 19:01:06 +00:00
k.renner e5a5c48431 Merge pull request 'feat(alert-monitor): blip lookback — catch alerts that resolve between ticks' (#269) from feat/alert-monitor-blip-lookback into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: gitea_admin/homelab#269
2026-07-16 19:00:56 +00:00
claude-botandClaude Fable 5 c10c7af3c8 feat(alert-monitor): blip lookback — catch alerts that resolve between ticks
The live 15-min snapshot structurally misses anything that fires and
resolves between ticks: on 2026-07-16 a 17-minute KubeHpaMaxedOut
(authentik) and last evening's four-alert home-assistant incident were
invisible to the monitor (owner noticed the blips; monitor did not).

The fetch step now also queries Prometheus's ALERTS history over a
35-minute window (~2 ticks + jitter). The dedup step synthesizes
pseudo-alerts for series not currently firing at all — checked against
the PRE-filter snapshot so live-but-null-routed alerts don't reappear
as blips — with a stable sha256-derived fingerprint so one noise
verdict suppresses a flappy alert permanently. Blips then flow through
the same title/noise dedup and triage as live alerts; the prompt
explains their semantics (self-resolved; judge the underlying cause;
null-routed per the repo route config = noise).

Verified against live data: currently-firing KubeCPUOvercommit
excluded, injected KubeHpaMaxedOut blip synthesized correctly;
sha256sum confirmed present in ci-claude; lookback query tested via
the service proxy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV7bZzZLeoFUEdAeCSLEei
2026-07-16 20:50:10 +02:00
claude-botandClaude Fable 5 5307d0db5b chore(decommission): import shims for rustfs LXCs — destroy via tofu, stage 1/2
Owner rule: ALL guest lifecycle goes through tofu; no manual pct/qm
destroy, by anyone. The rustfs trio (800/801/802) was never
tofu-managed, so the #265 sweep could not reach it. Stage 1 adopts the
stopped containers into state (pipeline TOFU_IMPORT hook, commands in
the file header); stage 2 deletes this file and the apply destroys
them.

lifecycle ignore_changes=all + started=false keep the shims inert:
tofu must never reconcile or start these, only own their destruction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV7bZzZLeoFUEdAeCSLEei
2026-07-16 19:27:30 +02:00
security-bot 396f02b35b chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784221484
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-16 17:16:27 +00:00
security-bot b9c4f3dd54 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784220647
ci/woodpecker/manual/deploy-infrastructure Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-16 16:53:40 +00:00
security-bot b594c57ac3 chore(image-automation): bump gitea.renner.uno/k.renner/shelly-manager:build.1784218615
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-07-16 16:19:25 +00:00
k.renner 0a90cef59b Merge pull request 'feat(shelly-manager): wire Home Assistant onboarding (token + BaseUrl)' (#267) from feat/shelly-manager-ha-onboarding into master
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: gitea_admin/homelab#267
2026-07-16 15:58:28 +00:00