Case study · Applied systems · Team project

My Heat Pump Explained

Smart heat pumps do things that look wrong — heating at 2 a.m., easing off when the grid asks — and people who cannot see why switch the automation off. This tool explains the behaviour. The design decision that matters: the diagnosis is deterministic, and the language model only writes the sentence.

Context
Diverse AI Hackathon 2025, "Flex for All" track, November 2025. Team entry from QMUL; no placement is recorded
My part
The code in the repository: rule engine, benefit calculations, LLM explanation layer with fallback, Streamlit dashboard
Stack
Python, Streamlit, Anthropic API (Claude Sonnet 4), Plotly, pandas
Data
Property profiles extracted from the Electrification of Heat (EoH) dataset; scenarios are synthetic, seeded from those profiles
Code
github.com/M-Rodani1/My-Heat-pump-explained

Eight ordered rules decide what the heat pump is doing. Claude gets the detected condition, the measurements and the computed benefit, and returns at most fifty words. If the API fails, a template says the same thing.

Problem

A heat pump on a smart tariff or a grid-flexibility contract makes decisions on the householder's behalf: pre-heating before a cold snap, shifting demand into cheap-rate hours, curtailing output when the grid operator signals. Seen from the living room, each of these looks like a malfunction. The hackathon brief was to make automated behaviour understandable enough that people keep it switched on. The requirement I set myself was that an explanation must never be invented: whatever the app says is happening has to come from a check on the numbers, not from a model's guess.

Architecture

  1. Measurementsindoor and target temperature, outdoor temperature, COP, power draw, tariff price, grid (VPP) signal, weather forecast, timestamp
  2. Rule engineordered threshold checks return one pattern with severity, confidence, trigger text and category
  3. Benefit calculationfor tariff-shift and curtailment patterns: money saved today and CO₂ avoided, from the scenario's price and consumption
  4. ExplanationClaude Sonnet 4 writes ≤50 plain-language words from the pattern, measurements and benefit; templated fallback on any error
  5. Guidancethree tiers — normal / check / urgent — with an action, chosen from the pattern, not the text
  6. DashboardStreamlit: scenario explorer, "what we detected", explanation, action, and a transparency panel showing the raw data and the rule that fired

The rule engine

detect_pattern() checks conditions in a fixed order and returns on the first match, so anomalies take priority over benign explanations. Thresholds are as written in the code.

Detection rules in evaluation order
#ConditionPatternSeverityCategory
1COP < 1.8 with outdoor temperature above 0 °CEfficiency anomalyHighPotential fault
2Indoor more than 2 °C below target while status is "heating"Temperature deficitMediumPerformance issue
3Power draw above 1.5 × a 3 kW residential referenceHigh power consumptionMediumEfficiency concern
4Hour 00–05 and electricity price below £0.12 / kWhTariff optimisationNoneSmart behaviour
5Grid (VPP) signal active and indoor below targetGrid curtailmentNoneSmart behaviour
6Forecast temperature more than 5 °C below current outdoorPredictive pre-heatingNoneSmart behaviour
7Outdoor below 0 °C with COP between 2.0 and 2.8Cold-weather operationNoneNormal operation
8Nothing above matchedNormal operationNoneNormal operation

Each pattern also carries a human-readable trigger (for example "COP of 1.6 is unusually low for 4 °C") that the dashboard shows verbatim in its transparency panel. That string, not the generated prose, is the audit trail.

The explanation layer

The model call is deliberately narrow. The prompt contains the detected pattern, the measurements and the computed benefits, and asks for an explanation in under fifty words with no jargon. The call uses claude-sonnet-4-20250514 with max_tokens=120; the response is word-counted and truncated if it exceeds fifty words. The whole call sits in a try block: any exception — no API key, network failure, rate limit — falls through to _fallback_explanation(), a per-pattern template filled with the same numbers, so the dashboard degrades to slightly stiffer sentences rather than to an error.

Keeping diagnosis out of the model is what makes the system defensible. Two users with the same measurements get the same detected condition and the same recommended action; only the wording varies. It also makes the hard part testable: the rule engine is plain Python with no external dependency.

Data

property_profiles.py reads the EoH summary table, keeps installations included in the seasonal-performance analysis, and extracts a handful of representative properties across SPF bands (above 3.0, 2.5–3.0, below 2.5) with their heat-pump type, size and building age. The scenarios the dashboard replays are synthetic: generated from those profiles with plausible values (for example a night-time COP of the property's SPF minus 0.2), then saved to scenarios_from_real_data.json. Real property characteristics, invented moments in time. The app does not ingest live telemetry.

Dashboard

Dashboard screenshot. Left sidebar: scenario explorer with property EOH0021, detached, 1991–2000, Mitsubishi 8.5 kW, annual SPF 3.37. Main panel: 'Early Morning Heating on Cheap Tariff' at 02:30; detected pattern Tariff Optimization, status Normal, trigger 'Operating during cheap-rate hours'; explanation 'Your smart tariff shifted heating to cheap-rate hours, saving you £1.53 today. The system ran efficiently despite −3.0 °C outside.'; action 'No action needed'. Right panel: system-efficiency gauge reading 3.17, indoor 19.2 °C, outdoor −3.0 °C, target 20.0 °C, COP 3.2, power 2.5 kW.
Tariff-optimisation scenario: rule 4 fired, the benefit calculation produced £1.53, and the model wrote 19 words. Click to enlarge.

A screen recording of the dashboard in use is also in the repository assets: demo_heat_pump_video.mp4 (MP4, about 5 MB).

Limitations

What this is and is not

The thresholds are hand-set from domain reading, not learned from labelled faults, and have not been validated against real fault records. "COP below 1.8 above freezing" is a reasonable heuristic, not a calibrated detector.

The scenarios are synthetic. The EoH dataset grounds the property profiles; it does not supply the minute-by-minute measurements the rules evaluate.

The generated explanations were checked for length and tone by hand during the hackathon. There is no systematic faithfulness evaluation — no test that the prose never contradicts the rule that fired.

The repository README quotes adoption, household-savings and CO₂ figures from the pitch. They are not sourced in the repository, so they are not repeated here. The evidence for this project is the implementation.

No user testing was done.

What I would do next

  • Fit the anomaly thresholds from EoH telemetry (COP conditional on outdoor temperature and flow temperature) and report false-positive rates per property type.
  • Log the rule trace next to every generated explanation and add an automatic contradiction check before the text is shown.
  • Replace the fixed 3 kW power reference with a per-property expectation derived from unit size and outdoor temperature.
  • Run a small user study on whether the explanations change the decision to override.