HA template-sensor cookbook
HeatSync ships ~50 entities to Home Assistant via either the
HACS custom integration
or MQTT auto-discovery. The recipes below combine those into derived
sensors, automations and Energy dashboard wiring. Drop them into
configuration.yaml and reload.
All examples assume the default friendly-name slug heatsync_*. If
you renamed the device, adjust the entity_ids accordingly.
Efficiency
Section titled “Efficiency”Lifetime SCOP
Section titled “Lifetime SCOP”Average heat-pump efficiency over the unit’s lifetime. Pair with the Energy dashboard.
template: - sensor: - name: "HeatSync lifetime SCOP" unit_of_measurement: "" icon: mdi:gauge state: >- {% set gen = states('sensor.heatsync_energy_generated') | float(0) %} {% set con = states('sensor.heatsync_energy') | float(0) %} {{ (gen / con) | round(2) if con > 0 else 'n/a' }}Typical UK ASHP: 2.8–3.5. Below 2.5 → check curve, flow temp, fault codes.
Instantaneous COP
Section titled “Instantaneous COP”Real-time efficiency from flow rate × ΔT × specific heat / power. Available only when the compressor is running.
template: - sensor: - name: "HeatSync instant COP" unit_of_measurement: "" availability: "{{ states('binary_sensor.heatsync_compressor') == 'on' }}" state: >- {% set p = states('sensor.heatsync_power') | float(0) %} {% set flow = states('sensor.heatsync_flow_rate') | float(0) %} {% set tin = states('sensor.heatsync_water_inlet') | float(0) %} {% set tout = states('sensor.heatsync_flow_actual') | float(0) %} {# heat output kW ≈ L/min × ΔT × 4.18 / 60 #} {% set heat_kw = (flow * (tout - tin) * 4.18 / 60) %} {{ (heat_kw * 1000 / p) | round(2) if p > 100 else 'n/a' }}Caveats. This is a 60-second moving picture, not calorimeter-grade. Anti-condensation defrosts will dip it briefly. Useful as a “is it running efficiently right now” gauge, not for billing.
Cost & tariff
Section titled “Cost & tariff”HeatSync’s /api/cost/status already exposes pence-cost calculations,
but if you’d rather compute it inside HA (e.g. to use a dynamic Agile
rate), here are the sensors. Replace 0.30 and 0.08 with your own
peak / off-peak £/kWh.
Real-time pence per hour
Section titled “Real-time pence per hour”template: - sensor: - name: "Heat pump running cost" unit_of_measurement: "p/h" device_class: monetary state: >- {% set p = states('sensor.heatsync_power') | float(0) %} {% set hour = now().hour %} {% set off_peak = hour >= 23 or hour < 7 %} {# match your DHW schedule window #} {% set rate = 8.0 if off_peak else 30.0 %} {# p/kWh #} {{ (p / 1000 * rate) | round(2) }}Running daily cost via Riemann integral
Section titled “Running daily cost via Riemann integral”For longer windows, use HA’s built-in Riemann sum integral helper
on sensor.heatsync_power:
- Settings → Devices & services → Helpers → Create helper
- Integration → input
sensor.heatsync_power(W), unit prefixk, methodleft, time unithours - Name it
Heat pump kWh today(it auto-resets when its source hasstate_class: total_increasing). - Apply a separate template sensor to multiply by tariff:
template: - sensor: - name: "Heat pump cost today" unit_of_measurement: "£" device_class: monetary state: >- {% set kwh = states('sensor.heat_pump_kwh_today') | float(0) %} {# average rate is good enough at day scale; refine if Agile-style #} {{ (kwh * 0.20) | round(2) }}Schedule visibility
Section titled “Schedule visibility””Is the schedule active right now?”
Section titled “”Is the schedule active right now?””template: - binary_sensor: - name: "HeatSync off-peak window" device_class: power state: >- {% set h = now().hour %} {{ h >= 23 or h < 7 }}Adjust the hours to match dhwSchedOffPeak* in HeatSync.
Dashboard card: heat-curve at a glance
Section titled “Dashboard card: heat-curve at a glance”Use a Glance card with these entities side by side:
type: glancetitle: Heat pumpentities: - entity: sensor.heatsync_outdoor_temp name: Outdoor - entity: sensor.heatsync_flow_actual name: Flow - entity: sensor.heatsync_water_law_target name: Curve target - entity: sensor.heatsync_water_law_offset name: Offset - entity: binary_sensor.heatsync_compressor name: CompAlerts
Section titled “Alerts”Fault notification
Section titled “Fault notification”automation: - alias: "Heat-pump fault notification" description: "Pushes to the phone when HeatSync raises a fault binary_sensor." trigger: - platform: state entity_id: binary_sensor.heatsync_fault to: "on" action: - service: notify.mobile_app_your_phone data: title: "⚠️ Heat-pump fault" message: "Check the HeatSync dashboard for the error code."Compressor short-cycling alert
Section titled “Compressor short-cycling alert”If compressor toggles on more than 3× in an hour, ping. Symptom of
either a too-aggressive water-law curve or load-comp Kp.
template: - sensor: - name: "Heat pump cycles last hour" unit_of_measurement: "/h" state: >- {{ state_attr('binary_sensor.heatsync_compressor', 'cycles_last_hour') | int(0) }}
automation: - alias: "Heat-pump short-cycling" trigger: - platform: numeric_state entity_id: sensor.heat_pump_cycles_last_hour above: 3 for: "00:30:00" action: - service: notify.persistent_notification data: title: "Heat pump short-cycling" message: "Compressor cycled {{ states('sensor.heat_pump_cycles_last_hour') }}× in the last hour. Review curve / load-comp Kp."(HeatSync’s /api/load-comp/status exposes cyclesLastHour too — same
data, different consumer.)
Energy dashboard
Section titled “Energy dashboard”Both sensor.heatsync_energy (consumed) and sensor.heatsync_energy_generated
(thermal delivered) ship with state_class: total_increasing and the
right device_class, so they slot straight into the
Settings → Dashboards → Energy flow:
- Individual devices → add HeatSync’s Energy sensor as consumption.
- (Optional) Gas/Heat → add Energy generated if you want delivered-heat tracked separately.
The Energy dashboard automatically computes daily/weekly/monthly breakdowns, no template required.
Logbook + history filters
Section titled “Logbook + history filters”HeatSync events (faults, schedule writes, OTAs) land on the device’s
/api/events endpoint, not in HA. If you want them in HA’s Logbook
too, expose them via MQTT — easiest is to publish a retained message
on homeassistant/sensor/heatsync_event/state whenever a notable event
fires. (Open feature; not shipped yet.)
Per-mode cost breakdown
Section titled “Per-mode cost breakdown”The controller buckets today’s draw into heating / DHW / defrost /
standby (/api/cost/status returns each as heatingKwh, dhwKwh,
etc.). If you publish those via MQTT (or scrape the API) you can
build a per-mode cost sensor:
template: - sensor: - name: "HeatSync heating cost today" unit_of_measurement: "p" state: > {% set kwh = states('sensor.heatsync_heating_kwh') | float(0) %} {% set rate = states('sensor.octopus_current_rate') | float(30) %} {{ (kwh * rate) | round(0) }}This pairs nicely with the Octopus Energy or any time-of-use integration
that exposes the live unit rate. Repeat for dhw_kwh to track hot-water
spend separately.
Holiday-mode automation
Section titled “Holiday-mode automation”Hook HeatSync’s Away toggle into your “I’m leaving for holiday” flow. Calls the controller’s REST endpoint directly — no MQTT round-trip.
automation: - alias: "HeatSync · Away when calendar says holiday" trigger: - platform: calendar event: start entity_id: calendar.family_holidays action: - service: rest_command.heatsync_away_on - alias: "HeatSync · Home when holiday ends" trigger: - platform: calendar event: end entity_id: calendar.family_holidays action: - service: rest_command.heatsync_away_off
rest_command: heatsync_away_on: url: "http://heatsync.local/api/control/away?on=1" method: POST headers: Cookie: "hs=YOUR_COOKIE_HERE" heatsync_away_off: url: "http://heatsync.local/api/control/away?on=0" method: POST headers: Cookie: "hs=YOUR_COOKIE_HERE"Get the cookie value by logging into the device in a browser and
copying the hs cookie from devtools → Application → Cookies.
Short-cycle alert
Section titled “Short-cycle alert”Compressor cycles are tracked daily on the energy ring (cycleCount
on each /api/energy/daily entry). If you push that into HA as a
sensor, alert when yesterday’s count exceeds a sensible ceiling:
automation: - alias: "HeatSync · short-cycling alert" trigger: - platform: numeric_state entity_id: sensor.heatsync_cycles_yesterday above: 40 action: - service: notify.mobile_app_phone data: title: "Heat pump short-cycling" message: > {{ trigger.to_state.state }} compressor starts yesterday. Consider lowering the heating curve or load-comp Kp.40 starts/day ≈ slightly less than 2/hour — well above the healthy 6–12 range for a modulating ASHP in shoulder weather.
Weekly efficiency rollup
Section titled “Weekly efficiency rollup”Pair the daily energy entries (kwhElec and kwhThermal) into a
weekly SCOP via a template sensor:
template: - sensor: - name: "HeatSync weekly SCOP" state: > {% set days = state_attr('sensor.heatsync_energy_daily', 'days') or [] %} {% set ns = namespace(e=0, t=0) %} {% for d in days[-7:] %} {% set ns.e = ns.e + (d.kwhElec | float(0)) %} {% set ns.t = ns.t + (d.kwhThermal | float(0)) %} {% endfor %} {{ (ns.t / ns.e) | round(2) if ns.e > 0 else 'n/a' }}Compare against your install’s design SCOP from MCS. Persistent under-performance suggests the curve is too steep (poor low-temp efficiency) or the system is short-cycling.
Weather-aware Boost
Section titled “Weather-aware Boost”When tomorrow’s forecast is unusually cold, pre-warm the house overnight using the heating Boost endpoint. Pair with any HA weather entity:
automation: - alias: "HeatSync · pre-warm before cold snap" trigger: - platform: time at: "22:00:00" condition: - condition: numeric_state entity_id: weather.met_office_home attribute: forecast.0.templow below: -3 action: - service: rest_command.heatsync_heating_boostThe Boost is +1 °C for 1 hour, then auto-restores. For more aggressive
pre-warming, write the heating target directly via
/api/control/heating-target?value=22.
Persistent fault surfacing
Section titled “Persistent fault surfacing”The device keeps a NVS-backed ring of WARN/ERROR events at
/api/events/persisted. Surface fresh entries as HA persistent
notifications:
automation: - alias: "HeatSync · surface persistent faults" trigger: - platform: state entity_id: sensor.heatsync_persistent_fault_count condition: - condition: numeric_state entity_id: sensor.heatsync_persistent_fault_count above: 0 action: - service: persistent_notification.create data: title: "HeatSync recorded a fault" message: "{{ states('sensor.heatsync_latest_fault_msg') }}" notification_id: heatsync_faultWire heatsync_persistent_fault_count to the events.length attribute
from a REST sensor polling /api/events/persisted every few minutes.