HEART Embedded Systems logoHEART

Guide 5: A HEART-Embedded smart thermostat#

Reading tracks2 Intermediate3 Advanced
Intermediate: every change is physical and waits for a person. Its AI suggestions preview Advanced (A3). Which track am I?

A case study. A maker of smart thermostats wants its products to be HEART-embedded systems: each thermostat should know when it is unwell, say so in one standard language, tell its owner, never leak anything it should not, and take changes (a new setpoint, a new mode, an AI's suggestion to save energy) only in a way that keeps a person in charge. This guide goes step by step from day 1 to a system that can claim HEART Basic, Autonomy Level A1, shows the A3 gate ("suggests and waits for a yes") working, and shows how an owner registers it and watches it on HEART Hub.

A working demo comes with it: thermostatSimulator/, one Python file that simulates a house, a thermostat, a gas boiler and its HEART, and reports to https://heartcortex.ai.

Status. A design case study with a simulator. No real thermostat, boiler or home was involved. Everything marked simulated below is a stand-in; everything marked a real product needs is work the simulator does not do for you. Nothing here is advice on gas safety or a route to any appliance approval.


Before you start: what HEART will and will not do#

A thermostat's job is to keep a room at a temperature by switching a boiler or heat pump on and off. That loop runs every second and sits next to the appliance's safety controls. HEART stays out of both.

Stays in the thermostat's firmware and the boiler's controlsGoes to HEART
The control loop: reading the room, deciding when to call for heatKnowing whether the heating is actually working
Frost protection (5 °C, in every mode, even "off")Watching the sensor, batteries, Wi-Fi and the boiler's cycling
The 28 °C maximum, and anti-short-cycle timersRaising alerts with standard fault codes, and explaining them
The boiler's flame supervision, overheat stat and lockoutTelling the owner, even with no internet
Guards: which changes the firmware will accept, and whenTaking a person's change request, and passing it to the firmware only after proof of presence
Anything a gas-appliance or controls standard coversAnswering "how is the thermostat?" in one standard format

This is the general rule of the standard (Part 6, Section 9): HEART stays out of the safety chain and out of real-time control loops, and never bypasses an interlock. The standard names the thermostat loop as its own example. HEART changes only the targets the loop works to, and the firmware has the last word: it clamps every value to its own limits, whatever was asked.


Step 1: Decide where HEART runs#

A typical smart thermostat is two or three boxes:

wall unit ──(radio)──► hub with Wi-Fi ──(wires or OpenTherm)──► boiler or heat pump
 sensor, display,         HEART runs here                         its own controls
 button, AA cells                                    └──(internet)──► owner's app, HEART Hub
PartWhat it doesHEART's place
Wall unitMeasures the room, shows the target, has the button; runs on two AA cellsNone. It is read, and its button is the proof that a person is in the house
Hub (a receiver at the boiler, with Wi-Fi)Runs the control loop and switches the boilerRuns the HEART beside the control firmware (an ESP32 or a small Linux module)
Boiler or heat pumpHeats; has its own flame and safety controlsNone. Read only, through OpenTherm or its status contacts

Simulated: all three are classes in one file (Thermostat, Boiler, and the House around them). A real product needs: the HEART in its own task or process on the hub, so a HEART crash never stops the heating (Part 6, Section 9, rule 6).


Step 2: Give the system an identity#

The hub's HEART serves a Card at /.well-known/heart-card.json:

{ "system": { "id": "com.example.heating.thermostat", "name": "Example smart thermostat", "version": "1.0.0" },
  "level": "basic", "bindings": ["http-server"], "pulse": { "intervalSec": 30 },
  "modules": ["properties", "control"],
  "shares": { "pulse": "public", "arrhythmia": "public", "property": "public", "control": "public",
              "action": "private", "rhythm": "private" },
  "autonomy": { "level": 1 }, "template": "heart/embeddedstandard/esp32-full/v1.2611.2" }

The simulator uses org.heartaispec.demo.thermostat and the template heart/embeddedstandard/thermostat-simulator/v1.2611.2. The system ID names the product, never the household. The device ID used by HEART Hub (Step 8) is random, and nothing links it to an address or a person except the owner's own registration.


Step 3: List what the system knows, and how private each item is#

A thermostat looks harmless, but its data shows when people are home, when they sleep and when the house is empty. That is valuable to a burglar. Decide every value's privacy level before writing code.

ValueSourcePrivacyWhy
Healthy or not; open fault codesDerived by HEARTpublicThe only things HEART Hub ever receives (Step 8)
Sensor battery status: ok or lowWall unitpublicNeeded to act; reveals nothing about the household
Room temperature, sensor qualityWall unitinternalFor the owner and their installer
Setpoint, mode, current targetFirmwareinternalA setting, not a schedule
Call for heat, boiler firing, lockout, flow temperature, burner startsFirmware, boilerinternalDiagnosis. Over time, heating use hints at presence, so not public
Outdoor temperatureHub (weather)internalHints at location
Battery voltage, Wi-Fi stateWall unit, hubinternal
Frost protection, maximum setpointFirmwareinternalFixed limits, shown, never writable
Night temperature and night hours (the schedule)OwnerprivateShows when people sleep
Occupancy: living room occupied, anyone homeMotion sensorprivateShows when the house is empty
Windows openedInferredprivateShows activity in the home
Rhythm (history): arrivals, departures, windows, actionsHEARTprivateThe household's daily life
Address, names, account detailsThe maker's cloudnot on the hubThe HEART does not need them, so it never has them

Two consequences. First, a waiting change offered on the monitor site (Step 7) carries only a title and a change in plain words, "Night temperature: 21 to 18 °C at night", never the hours or the reason "the living room is empty then". Second, the mock AI's explanation, which does mention when the room is empty, is in the Action's private view, for the owner only.


Step 4: Say what "doing its main job" means#

The main job of a heating thermostat is: when the room needs heat, the heating delivers it. The HEART checks this from the firmware's own values:

def check_main_function():
    """CHANGE: return (True, "") if heating works, else (False, "what is wrong, in plain words")."""
    f = firmware.status()
    if f.call_for_heat and f.seconds_since_call_or_flame >= 120:
        return False, "Called for heat for over 2 minutes, but the burner has not fired. The house will get cold."
    return True, ""

A condition must hold for a few seconds before it is raised, because one odd reading is not a fault. In the simulator, the burner may rest for 90 s between starts (the firmware's anti-short-cycle timer), so "not firing" counts only after 120 s.


Step 5: The faults it recognises#

Every alert carries a fault code from Part 3, Section 5, so the hub, the owner's app and any monitor site read it the same way. Every code below is in Part 3's list.

What happensFault codeSeverityStatus of the system
Called for heat for 2 minutes, burner not firing (no ignition, lockout)system.main-function-lostcriticalfailing
4 or more burner starts in 10 minutes (low flow cuts the flame)actuators.short-cyclingwarningdegraded
Room reading has not changed at all for 90 ssensors.stuckerrordegraded
Room reading outside -10 to 50 °Csensors.out-of-rangeerrordegraded
Room more than 1.5 °C above its target, while the target was not just loweredperformance.overshootwarningdegraded
Wall unit's batteries under a month leftpower.battery-lowwarningdegraded
Hub lost its Wi-Ficonnectivity.link-lostwarningdegraded
Hub silent for three Pulse intervalssystem.flatlineraised by HEART Hub

Each alert tells the owner what it means and what to do, with a likely cause and a confidence (the A2 part). For each of these faults the honest answer to "can HEART fix it?" is no: it cannot light a boiler, bleed a radiator, cool a room or change batteries, and it must not reset a boiler lockout. So its explanations say "No fix HEART can make" and who can (the owner, the installer). That is allowed at A3: a system offers a fix for the faults the maker lists as fixable, and otherwise says it has none (Part 2, Section 4).

The thresholds are illustrations. A real product needs its own, from its firmware and the boiler maker's data (for example, many makers count short-cycling per hour).


Step 6: Changes, approvals and Stop#

This is where a thermostat differs from the pacemaker of Guide 4. Its owner should be able to change things through HEART: the setpoint, the night temperature, the mode (heat, eco, off). But heating acts on the physical world. A wrong change can freeze pipes, overheat a baby's room or waste a week's gas, and a key in an app can be held by anyone, including an AI agent. So the standard's gate applies (Part 6, Section 2.3; API Standard 6.3):

Who asksThe changeWhat the HEART does
The owner, with the owner keySetpoint, night temperature, mode: physicalChecks range and guards, then answers 202 with an Action that waits for proof of presence: a press of the thermostat's button. The display shows a 6-character code from the Action's digest, so the person can see which change they approve
The owner, approving over the network with only the keyAny403 presence-required. A key is not a person
The mock AIAny of the aboveAn ai Action. Physical and started by an AI, so treated as destructive: it always waits for a person, whatever any setting says
AnyoneFrost protection, the maximum, the boiler, the anti-short-cycle timers409 conflict: they are read-only. HEART never overrides the firmware's limits
AnyoneOutside 10 to 28 °C422 out-of-range
AnyoneThe same setting again within 5 minutes, or while the room sensor's reading is not good409 guard-failed. The guards are the firmware's own, checked when asked and again just before the change is applied
Nobody answersThe Action expires (the owner's approvalTimeoutSec) and nothing changes
Anyonedeny or cancelDone at once, with no proof: saying no is always safe

Only one change waits at a time, so one press can never approve the wrong thing.

The mock AI. The simulator contains a MockAI class, labelled as a mock: a few plain rules over the private occupancy log, no model and no network. It suggests "Night temperature: 21 to 18 °C at night. The living room has been empty 23:00 to 06:00 on recent nights." It says what changes (only the night setpoint) and what does not (the day setpoint, the mode, frost protection, the boiler's limits). A real product needs: a real model behind a Reasoning Gateway, or its own rules, with private data kept from the model unless the owner consented (Part 6, Section 5). Its output still enters as an ai Action, through the same gate.

Approving on HEART Hub (optional). A person may not be at home. With approvals turned on both at the device and on the owner's device page, a waiting change can also be approved on the monitor site: the owner presses Approve… and types a code the site emails them; the site signs the decision with its Ed25519 key, and the device checks the signature with the key it pinned at registration, the digest, and that the decision is at most 300 seconds old (HEART Hub, Section 7.1; presence method remote-site). That proves the owner's inbox, not a person in the house, so for physical changes it needs a second opt-in at the device (remoteSitePhysical; in the simulator --monitor-approvals-physical). Both are off by default. The live heartcortex.ai needs a redeploy with its SITE_SIGNING_KEY set before approvals work there.

Stop. POST /heart/v1/tending/stop, or holding the thermostat's button, takes effect at once, needs no approval and no internet, and cancels every waiting change (Part 6, Section 6). After Stop the HEART still notices and explains, the mock AI suggests nothing, and the owner's own changes still pass the gate as before. Resuming suggestions raises autonomy, so it is destructive: it waits for the button.

Every action is recorded in the private Rhythm: what was asked, by whom, the outcome, and how it was approved (device-button or remote-site).

Simulated: the button is a file, button.press, as in the ESP32 Full starter's Linux port. device-button is not a presence method the API accepts (its proof format is not defined yet, API Standard 6.3), so it is used only as a press on the device itself, never sent over the network. A real product needs: a real button on the wall unit, with the code on its display, and an Idempotency-Key required on every change (the simulator accepts one but does not insist).


Step 7: Tell a person#

Autonomy Level A1 needs an escalation route. For a thermostat there are two:

  1. The owner's app, as the product already has: a notification for each alert, in plain words.
  2. Locally, with no internet: a symbol and a short message on the wall unit's display. The person who notices is often not the one with the app.

Change escalate() to use them. HEART Hub (Step 8) adds a third: an email when the hub goes silent.


Step 8: Register and watch it on HEART Hub#

HEART Hub lets an owner see a device's health from anywhere, on a private page.

  1. First start. The hub makes a random device ID, claim code and key, keeps them, and shows the claim code only to the person holding it: on a label in the box, or on the wall unit's display.
  2. Sign in. The owner opens https://heartcortex.ai/login and enters an email address. A 6-digit code arrives; there are no passwords.
  3. Register. At https://heartcortex.ai/register?id=<device ID> (a QR code on the label can open it), the owner enters the claim code. Holding the device is the proof of ownership.
  4. Watch. The device's page, https://heartcortex.ai/device/<device ID>, shows its status and open alerts, private to the owner until they share it.
  5. Silence. If no report arrives for three Pulse intervals, the page shows a Flatline and the owner gets one email.
  6. Approvals, if wanted. The owner can let the device ask for approvals on its page (Step 6).

Only the public Pulse and public alerts are sent, and the site keeps only the status, timing and fault codes. Never a temperature, the boiler's state, the schedule, or whether anyone is home.

Privacy note for a real product. Even status over time can say something ("its Wi-Fi drops every weekday at 8:30"). The page is private by default for that reason. A maker with many customers should consider running its own monitor site (the code is open, in heart-monitor/).


Step 9: Try it with the simulator#

python3 thermostatSimulator/thermostatSimulator.py --scenario all

It prints a device ID and claim code; register them as in Step 8 and watch the page while it stages each fault in turn: sun overheating the room, a Wi-Fi loss, flat batteries, a frozen sensor, an impossible reading, a boiler that will not light, and short-cycling from air in the radiators. Each is "fixed by the installer" a few minutes later. Then try the gate:

KEY=...    # the owner key it printed
curl -X PUT -H "Authorization: Bearer $KEY" -d '{"value": 21}' \
  http://127.0.0.1:43278/heart/v1/properties/org.heartaispec.demo.thermostat.setpoint   # 202: it waits
touch button.press                                                                       # it runs

After about five minutes the mock AI suggests a lower night temperature, and waits for the same press. Details and every option are in thermostatSimulator/README.md.


Step 10: Test it, rate it and claim only what passed#

  1. Run the HEART-Rating client (heart-rating/client/heart_rating.py) against the hub, and the hands-on tests of Part 9: cause each fault in the table of Step 5 on a test rig, cut the internet, cut the Wi-Fi, pull the batteries.
  2. Run the gate tests: an approval with only a key is refused; an AI's suggestion never runs without a person; an unanswered change expires; Stop cancels what waits. The simulator's test_e2e.py does these against the simulator.
  3. Publish a Declaration with every result, and claim only what passed.
LevelFits a thermostat?
A1 (notices and tells a person)Yes: what the simulator's Card claims, at HEART Basic
A2 (says why, with a confidence)Yes. Needs HEART Connected, because the evidence points to Rhythm events (Part 2, Section 8). The simulator already gives a cause and confidence on each alert
A3 (suggests a fix, does it once a person approves)Yes, with a person's yes for every change, area actuators. Needs HEART Full: approvals, the mode, Stop (Part 2, Section 8). The simulator demonstrates the A3 gate, but does not claim it: a real product claims A3 after running on a Full implementation (heartd from heart-core-rs, or the ESP32 Full starter grown to Full) and passing Part 2's A3-1 to A3-5
A4 (fixes known problems by itself)Only for non-physical fixes, such as reconnecting Wi-Fi or restarting the hub's radio: an adopted Playbook plus an allowlist entry for rule. A Playbook may run a physical change only if a person adopted it as physical (Part 6, Section 4.2), and the firmware's limits still apply
A5 (fixes new problems)Needs the ai and evolving modules. Even then, a physical change started by an AI always waits for a person (Part 2, Rule 3). For a thermostat, A5 adds little that A3 does not

HEART conformance says nothing about appliance or controls approval. A real thermostat still needs its own (for example the EN 60730 family for automatic electrical controls, and the boiler maker's interface rules).


What the case study shows#

  • A HEART can take changes and still keep a person in charge. Every change to heating is physical, so every one waits for a press on the device. A key alone, and an AI that holds the key, can never approve.
  • The firmware has the last word. Range checks in the API, guards in the firmware, limits the firmware clamps to, and safety controls in the boiler: four layers, and HEART owns none of the last three.
  • An AI's suggestion is just another Action. The mock AI is plain rules, but a real model would enter by the same door, be treated as destructive because its change is physical, and wait the same way.
  • Approving from afar is a deliberate, double opt-in. HEART Hub can carry a yes, signed and checked, but for a physical change only if the owner allowed it at the device itself.
  • "Private" is decided by what data reveals, not what it is. A temperature is harmless; a schedule and an occupancy log tell a stranger when the house is empty. Only status and fault codes leave the home.