Smart Home Fix

Separate an NBN outage from a local network fault

An external ping failing does not prove the NBN is down: Home Assistant may have lost Wi-Fi, the modem may be rebooting or one remote host may be unavailable. Use several independent checks and persistence so the alert says what the evidence supports.

The fault this project detects

This project distinguishes likely upstream internet loss from local gateway loss and degraded-but-not-down service. It uses a local gateway target, two unrelated external targets and Ping diagnostic sensors for latency and packet loss.

The design suits an Australian home or small business on NBN with Home Assistant connected by Ethernet where practical. Configure each Ping integration in the interface, keep the router target local, and choose external targets you are permitted to monitor.

Internet fault classification flow Multiple measurements are combined into advisory, warning and critical fault states. Gateway reachabilityExternal target AExternal target BLoss and latency Evidence model direction + context agreement + duration Advisory dashboard only Warning one push Critical repeat + audible
A local gateway and two external targets prevent one host or one Wi-Fi dropout from being labelled an NBN outage.

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: round-trip time and packet-loss diagnostics show service quality.
  2. Direction: rising latency or loss is visible before a complete outage.
  3. Agreement: two external failures make a remote-host fault less likely.
  4. Context and time: local gateway reachability and persistence separate upstream loss from Home Assistant losing the LAN.

The result is a classification, not an accusation. It can say that upstream reachability appears lost while the local gateway remains available; it cannot prove which NBN component or provider system failed.

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 pull up its own History graph over a normal stretch of days to find the longest healthy interval between meaningful state changes — that gap is the figure to record. 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: take two readings from that entity's History, then work out your measured change (in the sensor's own unit) ÷ the elapsed seconds between those readings = your 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

Collect baseline round-trip time, jitter and packet loss at quiet and busy periods. Include scheduled modem reboots, backup jobs and known congestion periods.

MeasurementWhy it mattersRequired value
binary_sensor.nbn_gatewayLocal path healthWatch it over a normal week and note how consistently available it stays and what a normal response trace looks like
binary_sensor.external_target_aFirst upstream checkWatch it through a real outage or scheduled maintenance window if you get the chance, and note how it behaves
binary_sensor.external_target_bIndependent upstream checkDo the same for this target, watching how it behaves during an outage or maintenance window
sensor.external_target_a_round_trip_time_averageLatency baselineCheck its History over a normal week for the everyday ms range, then compare that with a period you know was degraded
sensor.external_target_a_packet_lossLoss baselineDo the same comparison for packet loss: a normal-week baseline versus a known-bad period

The experiment that makes this article defensible

  1. Record a normal day, including periods when the connection is busy.
  2. Disconnect a non-critical test device from the LAN to understand a local client failure without touching the NBN service.
  3. During a planned modem restart, capture gateway, external targets, loss and latency on one timeline.
  4. If a genuine provider outage occurs, preserve the exact sequence and recovery instead of manufacturing repeated WAN interruptions.

Before you rely on this automation, capture your own evidence from that walkthrough: timestamped readings for each step, the Home Assistant version you're running, the exact entity IDs involved, 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
Ping: nbn_gatewayUI-created Ping integration for the local router or modemThe local IP address of your own router or modem, found on its admin page or in your network settings
Ping: external_target_aUI-created Ping integration for one permitted external targetA permitted external host name or IP you're comfortable pinging regularly (check the target's acceptable-use terms first)
Ping: external_target_bSecond independent external targetA second target on a different operator or network path from target A, so the two won't fail together for the same reason
input_number.internet_fault_hold_minutesPersistence before classifying an outageHow many minutes of sustained loss you want before calling it an outage, long enough to ignore a brief blip
input_number.internet_warning_packet_loss_percentDegraded-service loss thresholdThe packet-loss percentage you'd call degraded rather than normal jitter, set from your own baseline readings above
input_number.internet_warning_latency_msDegraded-service latency thresholdThe latency in ms you'd call degraded for your connection, taken from the normal-versus-degraded comparison above
input_boolean.internet_reliability_monitoring_enabledMaster notification gateLeave this off until you've tested the automation end to end, then switch it on
input_number.internet_reliability_critical_repeat_minutesCritical repeat spacingHow many minutes between repeat critical alerts — enough to avoid spamming yourself, short enough not to miss a genuine emergency

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.

Classify local, upstream and degraded states

Enable the Ping diagnostic entities you need after adding the integrations. Entity IDs vary with the names you choose, so replace every example consistently.

template:
  - binary_sensor:
      - name: "Internet reliability warning"
        unique_id: internet_reliability_warning
        default_entity_id: binary_sensor.internet_reliability_warning
        device_class: problem
        delay_on:
          minutes: >
            {{ states('input_number.internet_fault_hold_minutes') | int(0) }}
        availability: >
          {{ has_value('sensor.external_target_a_packet_loss')
             and has_value('sensor.external_target_a_round_trip_time_average') }}
        state: >
          {% set loss = states('sensor.external_target_a_packet_loss') | float %}
          {% set latency = states('sensor.external_target_a_round_trip_time_average') | float %}
          {{ is_state('binary_sensor.nbn_gateway', 'on')
             and (loss >= (states('input_number.internet_warning_packet_loss_percent') | float)
                  or latency >= (states('input_number.internet_warning_latency_ms') | float)) }}
        attributes:
          reason: >-
            Gateway is {{ states('binary_sensor.nbn_gateway') }};
            target A is {{ states('binary_sensor.external_target_a') }} with
            {{ states('sensor.external_target_a_packet_loss') }}% loss and
            {{ states('sensor.external_target_a_round_trip_time_average') }} ms average RTT;
            target B is {{ states('binary_sensor.external_target_b') }}.

      - name: "Internet reliability critical"
        unique_id: internet_reliability_critical
        default_entity_id: binary_sensor.internet_reliability_critical
        device_class: problem
        delay_on:
          minutes: >
            {{ states('input_number.internet_fault_hold_minutes') | int(0) }}
        state: >
          {{ is_state('binary_sensor.nbn_gateway', 'on')
             and is_state('binary_sensor.external_target_a', 'off')
             and is_state('binary_sensor.external_target_b', 'off') }}
        attributes:
          reason: >-
            The local gateway responds, but both external targets do not.
            This supports an upstream reachability fault; it does not identify the cause.

      - name: "Home network path fault"
        unique_id: home_network_path_fault
        default_entity_id: binary_sensor.home_network_path_fault
        device_class: problem
        delay_on:
          minutes: >
            {{ states('input_number.internet_fault_hold_minutes') | int(0) }}
        state: >
          {{ is_state('binary_sensor.nbn_gateway', 'off') }}

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: internet_reliability_warning_push
    alias: "Internet link - Warning push"
    mode: single
    triggers:
      - trigger: state
        entity_id: binary_sensor.internet_reliability_warning
        to: "on"
    conditions:
      - condition: state
        entity_id: input_boolean.internet_reliability_monitoring_enabled
        state: "on"
    actions:
      - action: notify.mobile_app_your_phone
        data:
          title: "Internet link warning"
          message: >-
            {{ state_attr('binary_sensor.internet_reliability_warning', 'reason')
                or 'The warning condition is active. Check Home Assistant for evidence.' }}

  - id: internet_reliability_critical_repeat
    alias: "Internet link - Critical repeat"
    mode: restart
    triggers:
      - trigger: state
        entity_id: binary_sensor.internet_reliability_critical
        to: "on"
    conditions:
      - condition: state
        entity_id: input_boolean.internet_reliability_monitoring_enabled
        state: "on"
    actions:
      - action: notify.mobile_app_your_phone
        data:
          title: "Internet link 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.internet_reliability_critical
              state: "on"
            - condition: template
              value_template: >-
                {{ states('input_number.internet_reliability_critical_repeat_minutes') | int(0) > 0 }}
          sequence:
            - delay:
                minutes: >-
                  {{ states('input_number.internet_reliability_critical_repeat_minutes') | int(0) }}
            - condition: state
              entity_id: binary_sensor.internet_reliability_critical
              state: "on"
            - action: notify.mobile_app_your_phone
              data:
                title: "Internet link 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 reachable gateway proves Home Assistant still has a local path. Agreement between two external failures makes a single remote host less likely, while latency and loss expose degradation before the connection disappears completely.

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
AdvisoryLatency or loss is unusual but not persistent enough to interrupt you.Dashboard only
WarningMeasured loss or latency remains outside the calibrated service envelope while the gateway is reachable.One push notification
CriticalThe gateway remains reachable while both independent external targets fail for the hold time.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

One target is always off
Choose a host that permits and reliably answers ICMP, or replace that signal with another reviewed health check.
Wi-Fi Home Assistant reports false outages
Use Ethernet where practical and monitor Home Assistant's local gateway path separately.
Diagnostic sensors are missing
Open the Ping device and enable the disabled diagnostic entities you actually need.
Busy uploads increase latency
Treat load-dependent latency as a separate baseline or add a traffic-context entity.
The provider status page disagrees
Your monitor measures reachability from your premises, not the provider's whole network or official fault classification.

Safety and limits

Do not automate repeated modem power-cycling from this diagnosis. It can hide evidence, interrupt emergency communications and damage support troubleshooting. Keep a mobile fallback for genuinely important notifications.

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

Does this prove that the NBN is down?

No. It shows that the local gateway responds while multiple external targets do not. That supports an upstream reachability fault but does not identify the failed provider component.

Why use two external targets?

One host can block pings, undergo maintenance or have its own routing problem. Agreement reduces that ambiguity.

Where do the latency and packet-loss sensors come from?

The current Ping integration exposes diagnostic sensors on its device. Some are disabled by default and must be enabled deliberately.

Should Home Assistant reboot the modem automatically?

Not from this evidence alone. Automatic power-cycling can erase useful diagnostics, prolong outages and interrupt services that were still working.

Can alerts work during an internet outage?

Local dashboard and audible actions can. Cloud push delivery may not, so important sites need a separately designed communication path such as a reviewed mobile backup.