Skip to content

Add .env support, hot water correction, README, and bug fixes#2

Open
Appesteijn wants to merge 184 commits into
Rickvdt:mainfrom
Appesteijn:main
Open

Add .env support, hot water correction, README, and bug fixes#2
Appesteijn wants to merge 184 commits into
Rickvdt:mainfrom
Appesteijn:main

Conversation

@Appesteijn
Copy link
Copy Markdown

Summary

  • .env support — Moved HA_URL and TOKEN to .env file using python-dotenv, added .env.example
  • Hot water correction — Gas-only users get automatic DHW baseline subtraction based on warm-day gas usage (>HOT_WATER_OUTSIDE_TEMP_THRESHOLD), with DHW summary in output
  • Heat loss analysis — Combined heat loss analysis for both heat pump and gas data, with balance point temperature calculation and gas vs heat pump comparison
  • README — Added Dutch README with usage guide for heat pump, gas-only, and transitioning users, including JupyterLab HA add-on instructions
  • Bug fixes — Fixed locals() vs globals() for inter-cell variables, deprecated pandas resample('H'), timezone mismatch in joins, stale data from failed fetches
  • Housekeeping — Added .gitignore, moved older notebook versions to archive/

Test plan

  • Run Cell 1 (install deps) — verify python-dotenv is included
  • Run Cell 2 (heat pump data) — verify it reads HA_URL/TOKEN from .env
  • Run Cell 2B (gas data) — verify hot water correction output and DHW summary
  • Run Cell 2B with invalid entity — verify downstream cells don't show stale gas data
  • Run all analysis cells — verify no locals() or timezone errors
  • Run heat loss analysis cell — verify heat loss coefficient and balance point for both data sources

🤖 Generated with Claude Code

Appesteijn and others added 30 commits February 13, 2026 20:35
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add README.md with usage guide for heat pump, gas, and transitioning users
- Move HA_URL and TOKEN to .env file (python-dotenv)
- Add hot water correction for gas-only users based on warm-day baseline
- Fix locals() vs globals() checks for inter-cell variable access
- Fix deprecated pandas resample('H') to resample('h')
- Fix timezone mismatch in gas data join
- Initialize df_gas_daily as empty to prevent stale data in downstream cells

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert Jupyter notebook analysis into a full HA integration with config flow,
sensors, services, and an apexcharts-card dashboard. Move original notebook
and files to archive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
attr_fn was set to None instead of a lambda returning None,
causing a TypeError when HA called extra_state_attributes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename component from "Quatt Stooklijn Analyse" to "Quatt Warmteanalyse"
  across all user-visible locations (manifest, HACS, sensors, config flow,
  dashboard, README). Internal identifiers (domain, module paths) unchanged.
- Fix pandas FutureWarning in quatt.py by dropping all-NA columns before
  DataFrame concatenation.
- Add 94 unit/integration tests covering heat loss regression, stooklijn
  curve fitting, gas processing, sensor value/attr functions, config flow
  validation, and coordinator logic.
- Add .gitignore entries for __pycache__ and .pytest_cache.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The gas analysis period typically predates the heat pump installation,
so outdoor temperature from Quatt hourly data was always empty (no
overlapping dates). Now fetches temperature directly from the HA
recorder for the gas date range using the configured temp_entities.
Falls back to heat pump data if recorder fetch finds nothing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change the last_analysis sensor to display the date in yyyy-mm-dd format
instead of using the timestamp device class which shows a localized
human-readable format.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Extend heat_at_temps to include interpolated COP values per temperature
- Add new columns to dashboard table: COP and electricity consumption
- Calculate accurate electricity usage based on temperature-specific COP
- Update tests for new heat_at_temps structure

This provides more accurate electricity consumption estimates by using
interpolated COP values for each temperature instead of a single average,
giving users better insight into heat pump efficiency across different
temperatures.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Major performance and accuracy improvements:

CACHING SYSTEM:
- Add persistent cache for Quatt insights data to minimize API calls
- Implement 30-day initial fetch limit to prevent API abuse for new users
- Cache grows organically (1 day per run) to full history over time
- 99.6% reduction in API calls after initial run (251 calls → 1 call)
- Cache stored in .storage/quatt_stooklijn_insights_cache

KNEE DETECTION IMPROVEMENTS:
- Use all available Quatt hourly data (grows from 30 to 250+ days)
- Smart filtering removes defrost cycles and partial operation hours
- Fallback to recorder data if Quatt unavailable
- 3x to 25x more data than previous 10-day recorder method
- Progressive accuracy as cache grows

CHANGES:
- Add custom_components/quatt_stooklijn/cache.py (new cache helper)
- Update quatt.py with caching and 30-day limit
- Update stooklijn.py with improved knee detection
- Extensive README documentation on caching and performance
- Add comprehensive documentation files

BENEFITS:
- New users: Safe 30 API calls first run, grows automatically
- Existing users: Instant analyses with 1 API call per run
- Better accuracy: More historical data for knee detection
- No configuration needed: Works transparently

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
After a restart, dashboards showed empty graphs because analysis
data is in-memory only. Now automatically triggers analysis after
EVENT_HOMEASSISTANT_STARTED, using cached data for fast completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of fetching all historical data from the Quatt API (which was
limited to 30 days on first run anyway), now uses HA recorder long-term
statistics for the full configured period. The Quatt API is only used
for the last 30 days of hourly detail data (for knee detection).

This gives months of daily data immediately without any API calls,
and the API data overwrites recorder data for recent days where it's
more accurate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
With recorder statistics now providing months of data including summer,
the COP average and stooklijn regression were skewed by days with no
heating demand. Now filters on totalHeatPerHour >= 200W and COP > 0
to only include meaningful heating days.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The recorder's heatpump_total_quatt_cop sensor averages over 24h
including off-periods (COP=0), giving artificially low values on
warm days when the pump runs fewer hours. Now calculates COP as
totalHpHeat/totalHpElectric which matches the Quatt API behavior
and gives the correct operational COP.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, cached hourly data older than 30 days was ignored.
Now checks cache for the full configured period before fetching
from the API, giving more data points for knee detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The recorder DataFrame used int64 (0) for totalBoilerGas while
the API data contains floats, causing a pandas dtype warning
on df_daily.update().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scatter data for both stooklijn and heat loss charts included
summer days with 0W heating, causing the x-axis to extend to
~29°C. Now uses the same heating_data filter as the regression.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rewrites the "How it works" and "Performance & Caching" sections
to document the recorder + API + cache hybrid approach, adds
auto-startup info, and updates troubleshooting with COP tips.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All trend/stooklijn lines were drawn from -10°C to 25°C regardless
of actual data. Now they stop at max(scatter_data temps) + 1°C,
so lines don't extend far beyond the data points.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lines with shallow slopes (like Quatt estimated) extended far
beyond the relevant range because y > 0 was the only limit.
Now each line stops at min(-intercept/slope + 1, 20)°C, which
is where heat demand reaches zero.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
graph_span reduced from 35h to 30h so x-axis stops at 20°C,
matching the data range. Beyond 20°C there is no heating demand.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Quatt API returns hourly averages, which makes the 2500W power
filter unreliable (partial operation hours pass the filter). This
resulted in an artificially shallow slope (-133 W/°C vs expected -318).

Now uses HA recorder state_changes (minute-level granularity) for the
stooklijn slope estimation, matching the original notebook approach.
Quatt hourly data is still used for knee detection where it works well.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Appesteijn and others added 30 commits April 1, 2026 22:59
…peratuur

- Aanvoertemp in forecast gebruikt nu max(t_return, t_indoor) zodat
  stilstaand koud water bij uitgeschakelde HP geen onrealistische 20°C geeft
- Forecast-temperaturen worden nu op tijdstip gematcht (hours-from-now)
  i.p.v. blind de eerste 6 entries te pakken; voorkomt dat nachttemperaturen
  overdag getoond worden wanneer de weather entity pas later begint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add sound level compensation switch entity
- Support separate max sound levels for day and night
- Add RestoreEntity support for state persistence
- Update extra state attributes with max/effective levels
- Add translations for all 5 languages (nl, en, de, fr)
- Bump version to 0.0.7
- Add sound level compensation switch entity
- Support separate max sound levels for day and night
- Add RestoreEntity support for state persistence
- Update extra state attributes with max/effective levels
- Add translations for all 5 languages (nl, en, de, fr)
- Bump version to 0.0.7
- switch.py: compensatie gebruikt nu live CIC-sensoren voor nachtvenster
  (sensor.cic_sound_night_time_start/end_hour/min) i.p.v. hardcoded 23:00-07:00
- Fallback naar 23:00-07:00 als Quatt-sensoren niet beschikbaar zijn
- _current_level_idx wordt geclampt bij dag/nacht-overgang (voorkómt drift)
- Reset naar effectief max voor huidige periode i.p.v. altijd 'normal'
- Step-up begrensd op effective_max (geen nutteloze stappen voorbij nacht-max)
- Nieuw attribuut: period (dag/nacht), night_window, effective_max
- dashboard: nieuwe view 'Geluid' met 5 grafieken (48u) voor analyse stookgedrag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…view dashboard

- Compensatie leest nachtvenster live uit Quatt-sensoren (sensor.cic_sound_night_time_*)
  i.p.v. hardcoded 23:00-07:00 (bij Mark: 19:00-07:00)
- Interne level geclampt bij dag/nacht-overgang — geen drift meer
- Reset naar effectief max voor huidige periode i.p.v. altijd 'normal'
- Step-up begrensd op dag/nacht-max (geen nutteloze stappen voorbij nacht-plafond)
- Nieuw dashboard-tabblad 'Geluid' met 5 grafieken (48u) voor analyse stookgedrag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oorbereid

- MPC_SUPPLY_TEMP_COOL_MIN = 15.0°C toegevoegd als ondergrens voor koeling
- Early return 'geen warmtevraag → None' verwijderd uit QuattMpcSensor.native_value
- Sensor blijft nu numeriek (≥15°C) ook als er geen verwarmingsbehoefte is,
  waardoor de geluidsniveau-watchdog niet meer onterecht reset

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix: dag/nacht-select schrijft nu alleen naar de actieve periode;
  inactieve select wordt teruggezet naar geconfigureerd maximum zodat
  overgang schoon verloopt (dag-select stond 's ochtends op building87)
- Fix: geluidsniveausensor spiegelt nu current_level van de switch
  (periode-bewust) i.p.v. altijd de dag-select
- Fix: dashboardmelding toont nu het werkelijke effective_max i.p.v.
  hardcoded 'normal'
- Nieuw: reset vindt pas plaats na 10 minuten HP-inactiviteit (was
  direct bij eerste inactieve cyclus)
- Tests: 13 nieuwe tests voor switch-compensatielogica toegevoegd

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Voeg grid.padding.right toe aan grafieken zonder rechter y-as zodat de
x-assen van alle vier de 48-uurs grafieken op één lijn staan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- OTGW-sectie vervangen door geluidsniveaucompensatie documentatie
- Online RC thermisch model (1R1C + RLS) als eigen sectie toegevoegd
- Features lijst, sensortabel, dashboard-tabs en how-it-works diagram bijgewerkt
- Configuratie Stap 3 gecorrigeerd (handmatige stooklijn verwijderd)
- solaredge_ac_power referenties verwijderd

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eluid

- Knikpunt kan nu strikt alleen omlaag: freeze-clamp verplaatst naar
  calculate_stooklijn() zodat ook de warm-side regressie-split de frozen
  waarde gebruikt (was: alleen gerapporteerde waarde werd gecorrigeerd)
- KneeDataStore retentie verhoogd van 3 naar 100 jaar (data nooit weggooien)
- Nachtvenster geluidsbegrenzing nu instelbaar via config flow
  (standaard: 23:00–07:00, HA lokale tijd)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- switch.py: geluidsniveau-slider reset bij dag/nacht-overgang altijd
  beide selects (actief én inactief); periode-overgang detectie via
  _last_is_night zodat inactieve slider direct op max_dag/nacht-max
  wordt gezet zonder te wachten op MPC-actie
- stooklijn.py: warm-kant regressie binned per 1°C voor slope_api;
  voorheen overspoelden duizenden voorjaar-minuutpunten de tientallen
  KneeDataStore-winterpunten (1000:50 ratio), waardoor de helling
  instortte en balance_temp_api op >20°C uitkwam
- sensor.py: nominaal_vermogen_huidig telt niet mee als advies-afwijking
  en wordt als onbetrouwbaar gemarkeerd wanneer balance_temp_api >20°C;
  nieuw attribuut nominaal_vermogen_betrouwbaar
- dashboard: huidig vermogen doorgestreept weergegeven als onbetrouwbaar,
  duidelijke melding over ontbrekende koude meetdata

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tweede criterium toegevoegd aan _stooklijn_reliable(): als |slope_api|
< 80% van |slope_optimal| (huis-warmteverlies) wordt de regressie als
onbetrouwbaar gemarkeerd, ongeacht het evenwichtspunt. Dit pakt de case
waarbij binning het evenwichtspunt net onder 20°C brengt maar de helling
nog steeds te vlak is door overheersende lente-data (~216 vs 284 W/°C).
De foutmelding beschrijft nu ook de reden (evenwichtspunt té hoog vs
helling te vlak).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tt stooklijn

De stookgrens en nominaal vermogen in de advieskaart gebruikten de minuut-regressie
(slope_api), die systematisch een te vlakke helling geeft doordat Quatt bij milde
temperaturen meer levert dan de huisvraag (door de stooklijn-instelling). Validatie
op echte data toonde aan dat geen enkele cutoff-variant dit corrigeert.

Switched naar slope_api_daily (daggemiddelde Quatt output), die over volledige dagen
middelt inclusief OFF-uren en daardoor convergeert naar de werkelijk geconfigureerde
stookgrens (~16°C i.p.v. ~44°C).

- _stooklijn_reliable: gebruikt balance_temp_api_daily + slope_api_daily
- _calc_vermogen: extrapolatie via slope_api_daily/intercept_api_daily
- _count_changes: vergelijkt balance_temp_api_daily vs optimaal
- Dashboard: label "Quatt stooklijn (gemeten)" → "Quatt stooklijn (daggemiddeld)"
- Tests bijgewerkt naar daily-velden

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ibiliteit

- Nieuwe feature: periodieke bijsturing van chMaxWaterTemperatuur op basis van
  de huis-eigen stooklijn of het MPC-model. Instelbaar interval (default 30 min),
  hysteresis (default 1°C) en bronkeuze (stooklijn/mpc). Opt-in via opties.
- Compatibiliteit met Quatt integratie ≥2.0: get_insights → get_cic_insights
  met automatische fallback voor oudere installaties (≤1.0.2).
- Diagnostische sensor voor laatste geschreven max aanvoertemp + tijdstip.
- Test-stub uitgebreid met homeassistant.const (EntityCategory).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DEFAULT_CH_MAX_WATER_ENTITY bijgewerkt naar number.cic_max_water_temperature
  (Quatt 2.0 heeft de heatpump_ prefix verwijderd)
- Auto-detectie fallback: als geconfigureerde entity niet beschikbaar is,
  wordt automatisch de legacy naam (number.heatpump_cic_max_water_temperature)
  geprobeerd met een waarschuwing om de instelling aan te passen
- _clamp() en _write() ontvangen nu de resolved entity_id vanuit _async_tick

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Nieuw grafiek in Geluid-tabblad: thermostaat setpoint vs chMaxWater
  limiet vs werkelijke aanvoer vs stooklijn advies (48u)
- Nieuwe statuskaart: huidige waarden + netto temperatuurverlaging

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
stroke_dasharray is geen geldige series-property in apexcharts-card 2.x;
vervangen door apex_config.stroke.dashArray array op chart-niveau.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Verwijder stroke.dashArray en curve:stepline uit apex_config (veroorzaakten
  configuration error in apexcharts-card 2.2.3)
- Fix markdown tabel: lege regels tussen elke rij zodat > scalar newlines
  bewaart (zonder lege regels vouwt YAML alles samen naar één regel)
- Voeg uitleg toe voor nieuwe gebruikers wat de bijsturing doet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…board

- sensor.quatt_warmteanalyse_max_aanvoertemperatuur_instelling krijgt
  attribuut interval_minutes (het geconfigureerde schrijfinterval)
- Dashboard toont nu het werkelijke interval i.p.v. hardcoded "30 min"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- chMaxWater: automatische fallback van MPC naar stooklijn als MPC-sensor
  niet beschikbaar is (unknown/unavailable)
- active_source bijgehouden en beschikbaar als sensor-attribuut
- Dashboard toont nu beide adviezen (MPC + stooklijn), actieve bron,
  en "(fallback)" melding als stooklijn ingesprongen is voor MPC

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Revert: automatische MPC→stooklijn fallback verwijderd; de gebruiker
  kiest bewust een bron in de config flow en die wordt gerespecteerd
- Dashboard: toont MPC- en stooklijn-advies naast elkaar; de geconfigureerde
  bron is vetgedrukt zodat duidelijk is waarop gestuurd wordt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Voeg leesbare labels toe aan strings.json en nl.json voor alle vijf
ch_max_water config-velden (ontbraken volledig, HA toonde ruwe sleutels):
- ch_max_water_enabled  → Aanvoertemperatuur bijsturing inschakelen
- ch_max_water_entity   → Quatt max. aanvoertemperatuur entiteit
- ch_max_water_source   → Stuuradvies bron (stooklijn of MPC)
- ch_max_water_hysteresis → Minimale wijziging voor schrijfactie (°C)
- ch_max_water_interval → Schrijfinterval (minuten)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Twee tabellen in één > scalar vereisen een dubbele lege regel als
paragraafbreuk; opgelost door alles in één tabel te zetten.
Actieve bron gemarkeerd met ◀ in plaats van een aparte header.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Geluid-tab: dubbelgrafiek samengevoegd; MPC advies toegevoegd aan begrenzing-grafiek
- Verwijder overbodige 'Aanvoertemp. advies vs werkelijk'-grafiek (stond ook al in MPC-tab)
- chMaxWater hernoemd naar 'Aanvoertemperatuur begrenzing' / 'Ingestelde limiet'
- Dead band labels verwijderd uit afwijkingsgrafiek
- Vertaalbestanden (en/de/fr) aangevuld met ontbrekende sound_level en ch_max_water sleutels

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Nieuwe kolom 'Aanvoer (geen zon)' in forecast-tabel: toont benodigde
  aanvoertemperatuur zonder zonnewinst, naast de kolom mét zonnewinst
- Zowel online RC-model (simulate_6h) als batch-fallback berekenen nu
  supply_temp_no_solar
- Verwijderd: '☀️ Zonnewinst verlaagt de warmtevraag.' melding (onjuist
  's avonds door residuele Open-Meteo straling)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant