Find a failed Bluetooth proxy before sensors vanish
When several Bluetooth sensors disappear together, replacing all their batteries is the wrong first move. Monitor the proxy itself, its 2.4 GHz Wi-Fi or Ethernet path and which nearby devices remain visible so the fault points to the shared link.
The fault this project detects
This project detects an offline ESPHome Bluetooth proxy, a weak network path and a group of BLE entities becoming unavailable together. It separates one flat sensor battery from a shared proxy or coverage failure.
The example uses an ESP32 proxy in a hallway, native ESPHome API connectivity, a Wi-Fi signal entity and two nearby Bluetooth sensors. Ethernet-capable hardware can replace Wi-Fi where the installation and board support it.
The evidence model
A useful alarm does not promote one noisy reading straight to an emergency. It combines measurements that fail in different ways:
- Value: proxy network signal and device availability expose the current path.
- Direction: a Statistics helper can reveal a deteriorating RSSI baseline before disconnections.
- Agreement: two nearby BLE entities failing together supports a shared coverage or proxy fault.
- Context and time: native API status and persistence prevent normal advertisement gaps from paging you.
BLE advertisements are intermittent by design. The monitor therefore requires duration or agreement and treats the proxy's own API connection as independent evidence.
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 note the longest gap between meaningful state changes you actually see across a normal week in your own History - that measured interval is the freshness threshold to record for your setup. 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 points from your own History, divide the change in the measured value between them by the elapsed seconds, and that result is 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
Capture several days of proxy uptime, Wi-Fi signal and update gaps for each important BLE device. Include busy 2.4 GHz periods and closed-door conditions.
| Measurement | Why it matters | Required value |
|---|---|---|
binary_sensor.hall_ble_proxy_status | Native API connectivity | Note how this entity behaves across a real restart and any outage you can observe on your own network, so you know its normal pattern. |
sensor.hall_ble_proxy_wifi_signal | Backhaul quality | Watch this over a few days and note your own normal and weak dBm ranges for that particular install position. |
sensor.freezer_ble_temperature | First nearby advertisement stream | Check History and note the longest gap between updates you see during genuinely normal operation. |
sensor.laundry_ble_humidity | Second nearby advertisement stream | Check History and note the longest gap between updates you see during genuinely normal operation. |
Bluetooth integration diagnostics | Which proxy hears each device | Review the diagnostics page over normal operation and note which proxy healthily routes and covers each device in your own home. |
The experiment that makes this article defensible
- Record normal updates for every important BLE sensor through a full day and night.
- During a supervised maintenance window, reboot the proxy and observe entity recovery without cycling the sensors.
- Move the proxy only through practical installation positions and compare coverage; do not infer range from one reading.
- If using Wi-Fi, repeat during busy 2.4 GHz conditions; a 5 GHz-only network will not serve common ESP32 proxy hardware.
Before you rely on any of this, gather your own timestamped readings, note the Home Assistant version you're running, record your exact entity IDs, and save one or two History or dashboard screenshots as your evidence baseline. 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 entity | Purpose | Setting |
|---|---|---|
sensor.hall_ble_proxy_wifi_signal_mean | Statistics helper for recent RSSI mean | Choose a sampling size and maximum age by checking how much your own raw RSSI reading bounces around between samples. |
input_number.bluetooth_proxy_hold_minutes | Persistence before Warning | Set this long enough to ride out your own network's normal brief dropouts without a false Warning. |
input_number.bluetooth_proxy_weak_wifi_dbm | Site-specific weak-link threshold | Set this to the weak dBm figure you measured for your own install position in the data-collection step above. |
input_boolean.bluetooth_proxy_monitoring_enabled | Master notification gate | Leave this off while you test the automation against your own readings, then switch it on once you trust the thresholds. |
input_number.bluetooth_proxy_critical_repeat_minutes | Critical repeat spacing | Choose a repeat spacing greater than zero based on how often you want a critical alert repeated for your own household. |
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.
Expose proxy connectivity and network signal in ESPHome
Merge these components into the existing ESPHome node. Keep the normal board, network, API, OTA and security settings from the device's validated configuration.
esp32_ble_tracker:
bluetooth_proxy:
active: true
binary_sensor:
- platform: status
name: "Hall BLE proxy status"
sensor:
- platform: wifi_signal
name: "Hall BLE proxy WiFi signal"Corroborate proxy and sensor failures in Home Assistant
Unavailable BLE entities are used here because not every Bluetooth integration exposes a changing source timestamp. For a critical process, use a dedicated heartbeat or second sensing path as well.
template:
- binary_sensor:
- name: "Bluetooth proxy warning"
unique_id: bluetooth_proxy_warning
default_entity_id: binary_sensor.bluetooth_proxy_warning
device_class: problem
delay_on:
minutes: >
{{ states('input_number.bluetooth_proxy_hold_minutes') | int(0) }}
state: >
{% set proxy_off = is_state('binary_sensor.hall_ble_proxy_status', 'off') %}
{% set weak = has_value('sensor.hall_ble_proxy_wifi_signal_mean')
and (states('sensor.hall_ble_proxy_wifi_signal_mean') | float)
<= (states('input_number.bluetooth_proxy_weak_wifi_dbm') | float) %}
{% set a_missing = states('sensor.freezer_ble_temperature') in ['unknown', 'unavailable'] %}
{% set b_missing = states('sensor.laundry_ble_humidity') in ['unknown', 'unavailable'] %}
{{ proxy_off or weak or (a_missing and b_missing) }}
attributes:
reason: >-
Proxy is {{ states('binary_sensor.hall_ble_proxy_status') }},
mean Wi-Fi signal is {{ states('sensor.hall_ble_proxy_wifi_signal_mean') }} dBm,
freezer sensor is {{ states('sensor.freezer_ble_temperature') }} and
laundry sensor is {{ states('sensor.laundry_ble_humidity') }}.
- name: "Bluetooth proxy critical"
unique_id: bluetooth_proxy_critical
default_entity_id: binary_sensor.bluetooth_proxy_critical
device_class: problem
state: >
{{ is_state('binary_sensor.bluetooth_proxy_warning', 'on')
and is_state('binary_sensor.hall_ble_proxy_status', 'off')
and states('sensor.freezer_ble_temperature') in ['unknown', 'unavailable'] }}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: bluetooth_proxy_warning_push
alias: "Bluetooth proxy - Warning push"
mode: single
triggers:
- trigger: state
entity_id: binary_sensor.bluetooth_proxy_warning
to: "on"
conditions:
- condition: state
entity_id: input_boolean.bluetooth_proxy_monitoring_enabled
state: "on"
actions:
- action: notify.mobile_app_your_phone
data:
title: "Bluetooth proxy warning"
message: >-
{{ state_attr('binary_sensor.bluetooth_proxy_warning', 'reason')
or 'The warning condition is active. Check Home Assistant for evidence.' }}
- id: bluetooth_proxy_critical_repeat
alias: "Bluetooth proxy - Critical repeat"
mode: restart
triggers:
- trigger: state
entity_id: binary_sensor.bluetooth_proxy_critical
to: "on"
conditions:
- condition: state
entity_id: input_boolean.bluetooth_proxy_monitoring_enabled
state: "on"
actions:
- action: notify.mobile_app_your_phone
data:
title: "Bluetooth proxy 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.bluetooth_proxy_critical
state: "on"
- condition: template
value_template: >-
{{ states('input_number.bluetooth_proxy_critical_repeat_minutes') | int(0) > 0 }}
sequence:
- delay:
minutes: >-
{{ states('input_number.bluetooth_proxy_critical_repeat_minutes') | int(0) }}
- condition: state
entity_id: binary_sensor.bluetooth_proxy_critical
state: "on"
- action: notify.mobile_app_your_phone
data:
title: "Bluetooth proxy 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
Proxy API state observes the bridge, RSSI observes its backhaul, and multiple BLE entities observe radio coverage. Their different failure modes make agreement far more useful than repeatedly alerting on every individual sensor.
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
| Tier | Meaning | Action |
|---|---|---|
| Advisory | Proxy signal or device update behaviour has moved outside its recent baseline. | Dashboard only |
| Warning | The proxy is offline, its link remains weak, or multiple nearby BLE entities fail together. | One push notification |
| Critical | A required BLE measurement is unavailable while its serving proxy is also confirmed offline. | 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
- The status entity is missing
- Add the ESPHome status binary sensor, compile and adopt the updated node, then confirm the generated entity ID.
- BLE devices jump between proxies
- That can be healthy. Inspect Bluetooth diagnostics and design around coverage overlap rather than pinning a device without evidence.
- Wi-Fi RSSI is good but BLE is poor
- The Wi-Fi and Bluetooth radios have different paths and interference; reposition based on BLE observations too.
- Active connections are unreliable
- Active proxy connections consume resources and differ from passive advertisements. Check the current ESPHome limits and device behaviour.
- A single sensor vanishes
- Check its battery, advertisement interval and local obstruction before blaming the shared proxy.
Safety and limits
A Bluetooth proxy is not a certified alarm receiver. Do not make freezers, medical equipment, security or life safety depend on one ESP32 and one network path. Use approved local alarms and redundant sensing where consequences are serious.
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
- Fix unreliable entities before trusting alarms
- Separate smart-home devices without breaking discovery
- Reduce false alarms without hiding real faults
FAQ
Does an unavailable Bluetooth sensor prove the proxy failed?
No. The sensor battery, advertising interval, obstruction or integration can fail independently. Proxy status and a second nearby sensor provide corroboration.
Does an ESP32 Bluetooth proxy use 5 GHz Wi-Fi?
Common ESP32 proxy boards use 2.4 GHz Wi-Fi. Confirm the exact board specification and provide suitable 2.4 GHz coverage or supported Ethernet.
Why monitor proxy Wi-Fi signal?
The BLE radio can be healthy while the proxy's Home Assistant backhaul is weak. Both paths must work for observations to arrive.
Should I enable active Bluetooth connections?
Only if the devices need them. Active connections use proxy resources, so check the current ESPHome documentation and measured workload.
How many proxies do I need?
There is no fixed count. Place them from measured coverage, building materials, device density and the consequence of losing one path.