Smart Home Fix

Catch an electricity cost fault before the bill

A daily kWh total can be technically correct while a stuck appliance or wrong tariff selector quietly adds cost. Compare current base load with its normal context, calculate cost from your own bill rates, and alarm separately when the accounting path stops making sense.

The fault this project detects

This project detects persistent out-of-hours base load, stale or decreasing source energy and a tariff selector that disagrees with the configured schedule. It builds an indicative daily cost from your own rates without pretending to reproduce every retailer adjustment.

The example assumes a whole-site power sensor in watts, a cumulative import-energy sensor in kWh, a two-tariff plan and a Schedule helper for the actual peak period. Demand charges, controlled load, solar export, shoulder rates and billing adjustments need their own reviewed model.

Electricity cost fault evidence flow Multiple measurements are combined into advisory, warning and critical fault states. Mean grid powerDaily tariff energyTariff selectorExpected load window Evidence model direction + context agreement + duration Advisory dashboard only Warning one push Critical repeat + audible
Measured load, tariff allocation and schedule agreement identify both physical consumption faults and accounting faults.

The evidence model

A useful alarm does not promote one noisy reading straight to an emergency. It combines measurements that fail in different ways:

  1. Value: daily kWh and calculated AUD show the accumulating consequence.
  2. Direction: a rising out-of-hours power baseline reveals leakage before the billing total is large.
  3. Agreement: power, cumulative energy and tariff buckets should move consistently.
  4. Context and time: the tariff and occupancy or operating schedule define what consumption is expected, while persistence rejects kettle and compressor starts.

The monitor keeps physical consumption and tariff bookkeeping as separate diagnoses. A wrong tariff selector should not be reported as a failed appliance, and a genuine base-load rise should not wait for the next bill.

Freshness is separate evidence

An entity can keep a plausible old value without becoming unavailable. Inspect last_changed on a source that should genuinely change, and work out your own longest healthy interval between meaningful state changes by scrolling back through that entity's History over a normal week and timing the longest gap between real updates. If the measured process can legitimately stay flat, add a changing source heartbeat or timestamp; do not treat an unchanged temperature, level or power value as proof that the transport is alive.

Rate maths: never copy a gradient

The Trend integration expresses min_gradient in units per second. The arithmetic example from the project brief is 4 °C/hour ÷ 3600 = 0.00111 °C/s. That is an example conversion, not a threshold for this equipment. Calculate the rate from your own timestamped readings: pick two points from that entity's History, subtract the earlier value from the later one for your measured change, divide by the number of seconds between their timestamps for the elapsed seconds, and that division gives you the site-specific gradient. A Derivative helper can instead display a friendlier per-hour unit when configured that way.

Collect this data before choosing a threshold

Use the meter and rates printed on your own current bill. Record several normal weekdays, weekends and hot-weather days before declaring an abnormal base load.

MeasurementWhy it mattersRequired value
sensor.grid_powerInstantaneous importWatch this sensor's History over a normal week and note the typical wattage when the house is occupied and active, versus when it is quiet.
sensor.grid_import_energyCumulative sourceOpen this entity's settings and confirm its unit, its state class, and that its History only ever rises.
sensor.grid_power_recent_meanTransient-resistant base loadCheck this helper's History during known quiet periods, such as overnight or when nobody is home, and note the normal wattage range.
select.grid_energy_dailyActive tariff allocationCompare this selector's History against your actual peak and off-peak times and confirm it switches when your tariff really changes.
Current electricity billRates and fixed chargesRead the peak and off-peak rates and the daily supply charge straight off your own current bill, noting whether GST is already included.

The experiment that makes this article defensible

  1. Reconcile the cumulative Home Assistant energy change with the physical or retailer meter over a documented period.
  2. Record quiet-period mean power across several normal days, including fridge, freezer and hot-water cycles.
  3. Observe every tariff transition for at least one complete plan cycle and confirm the correct Utility Meter bucket increments.
  4. Switch one safe plug-in test load on and off to confirm direction and timing; do not alter switchboard wiring or deliberately create a high load.

Keep your own record as you go: timestamped readings, your Home Assistant version, the exact entity IDs you used, and one or two History or dashboard screenshots. Do not stage an unsafe electrical, refrigeration, pressure, battery or water fault merely to make a graph.

Create the helpers

Use Settings → Devices & services → Helpers. Create the named Number or Toggle helpers before loading the templates. Where this guide uses a Derivative, Statistics, History Stats, Integral, Schedule or Utility Meter helper, the current Home Assistant interface can create it; the source links below describe the current options.

Helper or derived entityPurposeSetting
sensor.grid_power_recent_meanStatistics helper using the recent meanTry a maximum age and sampling size against your own History and pick the combination that stays flat during known-normal quiet periods without smoothing out a real change.
schedule.electricity_peak_periodActual two-tariff peak scheduleEnter the peak start and end times, plus any weekend or public-holiday exceptions, exactly as written in your own electricity contract.
input_number.electricity_peak_rate_aud_per_kwhCurrent peak import rateEnter the peak rate in AUD per kWh straight off your own current bill.
input_number.electricity_off_peak_rate_aud_per_kwhCurrent off-peak import rateEnter the off-peak rate in AUD per kWh straight off your own current bill.
input_number.electricity_daily_supply_charge_audCurrent fixed daily chargeEnter the fixed daily supply charge in AUD straight off your own current bill.
input_number.electricity_quiet_load_warning_wMeasured abnormal quiet-period meanSet this above the normal quiet-period range you measured earlier, high enough that only a genuinely stuck load would trip it.
input_number.electricity_cost_hold_minutesPersistence before WarningSet a hold time long enough to ride out normal appliance cycling, based on what you saw in your own History.
input_number.electricity_cost_critical_audSite-specific daily-cost escalationSet a daily-cost figure that would genuinely concern you, based on what a normal day costs on your own bill.
input_boolean.electricity_cost_monitoring_enabledMaster notification gateLeave this switched off until you have tested the automation end to end, then turn it on.
input_number.electricity_cost_critical_repeat_minutesCritical repeat spacingSet a repeat interval greater than zero that matches how often you actually want to be reminded of a critical fault.

Current Home Assistant YAML

Merge top-level keys with your existing configuration instead of duplicating them. Replace the example entity IDs consistently. If you keep automations in automations.yaml, paste only the automation list items there, without a top-level automation: wrapper.

Split cumulative import into daily tariff buckets

This is for a genuine two-tariff plan only. Confirm that sensor.grid_import_energy is a suitable cumulative kWh source. Merge the top-level key if Utility Meter configuration already exists.

utility_meter:
  grid_energy_daily:
    source: sensor.grid_import_energy
    cycle: daily
    tariffs:
      - peak
      - off_peak

automation:
  - id: select_daily_electricity_tariff
    alias: "Electricity - Select daily tariff"
    mode: restart
    triggers:
      - trigger: state
        entity_id: schedule.electricity_peak_period
    actions:
      - action: select.select_option
        target:
          entity_id: select.grid_energy_daily
        data:
          option: >-
            {{ 'peak' if is_state('schedule.electricity_peak_period', 'on')
               else 'off_peak' }}

Calculate indicative cost and flag a persistent quiet load

The cost excludes anything you have not modelled. Compare it with bills before relying on it, and keep rates in helpers so a contract change does not require editing templates.

template:
  - sensor:
      - name: "Grid electricity daily indicative cost"
        unique_id: grid_electricity_daily_indicative_cost
        default_entity_id: sensor.grid_electricity_daily_indicative_cost
        device_class: monetary
        state_class: total
        unit_of_measurement: "AUD"
        availability: >
          {{ has_value('sensor.grid_energy_daily_peak')
             and has_value('sensor.grid_energy_daily_off_peak') }}
        state: >
          {% set peak = (states('sensor.grid_energy_daily_peak') | float)
             * (states('input_number.electricity_peak_rate_aud_per_kwh') | float) %}
          {% set off_peak = (states('sensor.grid_energy_daily_off_peak') | float)
             * (states('input_number.electricity_off_peak_rate_aud_per_kwh') | float) %}
          {{ (peak + off_peak
              + (states('input_number.electricity_daily_supply_charge_aud') | float)) | round(2) }}

  - binary_sensor:
      - name: "Electricity cost warning"
        unique_id: electricity_cost_warning
        default_entity_id: binary_sensor.electricity_cost_warning
        device_class: problem
        delay_on:
          minutes: >
            {{ states('input_number.electricity_cost_hold_minutes') | int(0) }}
        availability: >
          {{ has_value('sensor.grid_power_recent_mean') }}
        state: >
          {{ is_state('binary_sensor.house_quiet_period', 'on')
             and (states('sensor.grid_power_recent_mean') | float)
                 >= (states('input_number.electricity_quiet_load_warning_w') | float) }}
        attributes:
          reason: >-
            Quiet-period mean import is {{ states('sensor.grid_power_recent_mean') }} W;
            today's indicative cost is
            {{ states('sensor.grid_electricity_daily_indicative_cost') }} AUD and tariff is
            {{ states('select.grid_energy_daily') }}.

      - name: "Electricity cost critical"
        unique_id: electricity_cost_critical
        default_entity_id: binary_sensor.electricity_cost_critical
        device_class: problem
        state: >
          {{ is_state('binary_sensor.electricity_cost_warning', 'on')
             and (states('sensor.grid_electricity_daily_indicative_cost') | float(-999999))
                 >= (states('input_number.electricity_cost_critical_aud') | float(999999)) }}

Warning and Critical notification actions

Replace the mobile notification action and media-player entity with your own. Put critical-monitoring-alert.mp3 in /media, test it manually, and leave the monitoring Toggle off until calibration is complete.

automation:
  - id: electricity_cost_warning_push
    alias: "Electricity cost - Warning push"
    mode: single
    triggers:
      - trigger: state
        entity_id: binary_sensor.electricity_cost_warning
        to: "on"
    conditions:
      - condition: state
        entity_id: input_boolean.electricity_cost_monitoring_enabled
        state: "on"
    actions:
      - action: notify.mobile_app_your_phone
        data:
          title: "Electricity cost warning"
          message: >-
            {{ state_attr('binary_sensor.electricity_cost_warning', 'reason')
                or 'The warning condition is active. Check Home Assistant for evidence.' }}

  - id: electricity_cost_critical_repeat
    alias: "Electricity cost - Critical repeat"
    mode: restart
    triggers:
      - trigger: state
        entity_id: binary_sensor.electricity_cost_critical
        to: "on"
    conditions:
      - condition: state
        entity_id: input_boolean.electricity_cost_monitoring_enabled
        state: "on"
    actions:
      - action: notify.mobile_app_your_phone
        data:
          title: "Electricity cost critical"
          message: "The critical condition is active. Check the equipment and evidence now."
      - action: media_player.play_media
        target:
          entity_id: media_player.alert_speaker
        data:
          media_content_id: "media-source://media_source/local/critical-monitoring-alert.mp3"
          media_content_type: "audio/mpeg"
      - repeat:
          while:
            - condition: state
              entity_id: binary_sensor.electricity_cost_critical
              state: "on"
            - condition: template
              value_template: >-
                {{ states('input_number.electricity_cost_critical_repeat_minutes') | int(0) > 0 }}
          sequence:
            - delay:
                minutes: >-
                  {{ states('input_number.electricity_cost_critical_repeat_minutes') | int(0) }}
            - condition: state
              entity_id: binary_sensor.electricity_cost_critical
              state: "on"
            - action: notify.mobile_app_your_phone
              data:
                title: "Electricity cost still critical"
                message: "The critical condition remains active."
            - action: media_player.play_media
              target:
                entity_id: media_player.alert_speaker
              data:
                media_content_id: "media-source://media_source/local/critical-monitoring-alert.mp3"
                media_content_type: "audio/mpeg"

Why the logic works

A Statistics mean filters brief loads, the quiet-period context identifies when the baseline matters, and Utility Meter buckets preserve tariff allocation. The cost consequence raises priority without claiming retailer-grade billing accuracy.

The design is intentionally diagnostic rather than controlling. It reports an impossible or abnormal relationship; it does not bypass equipment protection or decide that a single suspected cause is proven.

Severity tiers without alert fatigue

TierMeaningAction
AdvisoryQuiet-period power or tariff allocation differs from its recent normal pattern.Dashboard only
WarningMean import remains above the calibrated quiet-period baseline after ordinary transients are filtered.One push notification
CriticalThe unexplained load persists and the indicative daily cost crosses the site-specific escalation amount.Push, repeat and audible alert

A weak clue remains visible without interrupting you. A Warning requires persistence or corroboration. Critical is reserved for direct danger or several independent signals. That separation is what stops a useful monitoring system becoming notification wallpaper.

Troubleshooting

Utility Meter totals do not match the bill
Check source units, resets, import versus net energy, tariff timing, GST, fixed charges and retailer adjustments.
Peak and off-peak are reversed
Inspect the Schedule and <code>select.grid_energy_daily</code> around every transition; do not assume off means off-peak on a three-period plan.
Hot water causes expected warnings
Exclude its genuine schedule or build separate baselines that still catch a stuck load.
Solar export creates negative power
Use a true grid-import sensor or separate import and export explicitly; do not feed ambiguous net power into this logic.
Rates changed
Update the helper values from the current contract and preserve the effective date for historical interpretation.

Safety and limits

Do not install current clamps or work inside a switchboard unless appropriately licensed. Use data from compliant meters and integrations. Smart Home Fix is not an electricity retailer, receives zero commissions and does not promise that this indicative cost equals your bill.

If a webhook is added later, treat its webhook_id like a password, keep local_only: true unless a reviewed design requires otherwise, and never use an unauthenticated webhook for locks, garage doors, power isolation or anything destructive.

Official sources

These are the primary documentation pages used for the design. Check them against the Home Assistant or ESPHome version you are actually running.

Related Smart Home Fix guides

FAQ

Will this match my electricity bill exactly?

Not automatically. Retailer rounding, GST, controlled load, demand charges, solar, plan changes and adjustments can differ. Reconcile it against actual bills.

Where should tariff rates come from?

Use your current retailer contract or bill and record whether values include GST. Smart Home Fix is not an electricity retailer and takes zero commissions.

Why monitor mean power instead of instantaneous power?

A recent mean filters kettles, compressors and other brief starts so the alarm focuses on persistent base-load change.

Can I use this with three tariff periods?

Yes, but not by copying the two-tariff selector. Add every actual tariff and schedule explicitly, including overlaps and plan exceptions.

Can I fit a current clamp myself?

Not inside a switchboard unless you are appropriately licensed. Use a compliant meter or arrange licensed electrical work.