Native Home Assistant integration for the BWT AQA Perla water softener via Bluetooth Low Energy (BLE).
No MQTT broker required. Works with ESPHome Bluetooth proxies.
- 🔵 Native BLE — uses Home Assistant's Bluetooth stack
- 📡 Bluetooth proxy support — works with ESPHome proxies (no USB BLE adapter needed)
- 🔍 Auto-discovery — detects the BWT automatically via BLE service UUID
- 📊 20 entities — salt level, water consumption, regenerations, power cuts, salt autonomy, diagnostics
- 🌍 Multilingual — French, English, German, Italian
| Entity | Unit | Description |
|---|---|---|
| Salt level | % | Remaining salt percentage |
| Salt remaining | kg | Remaining salt mass |
| Salt capacity | kg | Total brine tank capacity |
| Water meter | L | Cumulative water consumption, never resets — use it for the Water dashboard |
| Water since last regeneration | L | Water softened since the last regeneration started |
| Consumption today | L | Water softened since midnight |
| Consumption yesterday | L | Water softened the previous day |
| Consumption 7 days | L | Water softened over the last 7 days |
| Regenerations today | regenerations | Regeneration cycles today |
| Power cuts today | cuts | Power supply interruptions of the softener detected today |
| Salt autonomy | days | Estimated days of salt remaining |
| Salt autonomy (weeks) | weeks | Estimated weeks of salt remaining |
| Salt autonomy end date | — | Estimated date when salt runs out |
| Average daily consumption 30 days | L | Average daily water consumption |
| Last sync | — | Last successful BLE sync |
| Firmware | — | Device firmware version |
| Daily rollover time | — | Time at which the softener starts a new day in its daily buffer, learned automatically (diagnostic) |
| Quarter-hour offset | min | Minute within each quarter-hour at which the softener writes its data, learned automatically (diagnostic) |
| BROADCAST frames (debug) | — | Raw BROADCAST frames (disabled by default) |
| Salt alarm | — | "OK" or "Alarm" |
The integration includes a diagnostic entity (disabled by default) that captures raw BROADCAST frames for troubleshooting.
- Go to Settings → Devices & Services → BWT AQA Perla BLE → [your device]
- Enable the entity BROADCAST frames (debug)
- Wait for the next BLE cycle (~1-2 minutes)
The entity displays the last 10 BROADCAST frames received with timestamps:
2026-04-27 15:32:10 [20B]: 7c 36 02 00 ab 07 98 03 bc 07 34 00 12 02 15 54 00 00 00 00
2026-04-27 15:47:23 [20B]: 7c 36 02 00 ac 07 99 03 bc 07 34 00 12 02 15 54 00 00 00 00
...
- Firmware debugging — if you encounter unexpected salt level values (see Issue #4)
- Support requests — provide raw data when reporting issues
- Protocol analysis — understand BWT's BLE communication
Note: This entity is disabled by default to avoid unnecessary resource usage. Enable it only when needed for diagnostics.
- Home Assistant 2024.x or newer
- BWT AQA Perla water softener
- Bluetooth adapter or at least one ESPHome Bluetooth proxy within BLE range of the softener
Tip: The BWT BLE signal is weak (~-80 dBm through walls). Place the ESP32 proxy within 3–5 meters of the softener with line of sight for best results.
The button above opens this repository directly in your HACS. Then click Download and restart Home Assistant.
Add it manually instead
- Open HACS → Integrations
- Click ⋮ → Custom repositories
- Add
https://github.com/Micka41/bwt-aqa-perla-ble— Category: Integration - Install BWT AQA Perla BLE
- Restart Home Assistant
cp -r custom_components/bwt_aqa_perla_ble \
/config/custom_components/bwt_aqa_perla_bleRestart Home Assistant.
Home Assistant will automatically detect the BWT via its BLE service UUID and show a notification in Settings → Integrations to confirm the setup.
- Settings → Integrations → Add integration
- Search for BWT AQA Perla BLE
- Enter the Bluetooth MAC address of your device
To extend BLE range, flash an ESP32 with the Bluetooth proxy firmware. Make sure active: true is set:
bluetooth_proxy:
active: trueThe softener keeps two histories:
- a quarter-hour buffer covering the last 30 days, to the litre;
- a daily buffer covering up to 5 years, in tens of litres.
The integration uses a dual polling cycle:
- Fast cycle (every 14 min): reads the BROADCAST characteristic and the new quarter-hour entries, which update today's consumption, regenerations and power cuts → ~5 s BLE connection
- Full cycle (every hour): re-reads recent quarter-hours and the last 365 days → ~20 s BLE connection. The first one each day, from 00:20, reaches back 7 days of quarter-hours to compute yesterday and the last 7 days.
Today, yesterday and the last 7 days are all computed from quarter-hour data: calendar days from midnight to midnight, to the litre — the same figures as the BWT app.
Water meter is a cumulative counter that never resets. It adds every quarter-hour exactly once, and persists across Home Assistant restarts. It starts at 0 when the integration is installed; quarter-hours missed while Home Assistant was stopped are caught up afterwards, as long as they are less than 30 days old.
It is the sensor to use in Settings → Dashboards → Energy → Water consumption. Consumption today is meant for display: the quarter-hour from 23:45 to midnight is written by the softener at midnight, after the last reading of the day, so it never appears in that sensor.
Water since last regeneration adds up the litres from the first quarter-hour of the last regeneration: every quarter-hour entry carries the litres used and a regeneration flag. On installation the integration reads the whole quarter-hour buffer once (30 days, about a minute over Bluetooth) to find the last regeneration; after that, each quarter-hour is added as it is counted by the water meter. The sensor stays Unknown if no regeneration occurred in the last 30 days.
The water left before the next regeneration, shown on some softeners' display, is not transmitted over Bluetooth. It cannot be estimated reliably either, since many softeners regenerate at a fixed time rather than on volume.
The daily buffer does not start its days at midnight. Each softener opens its next daily slot at a time of its own — around 4:00 on one unit, around 9:30 on another — which is undocumented and unrelated to the regeneration time. The integration learns it by watching when the daily index moves, over the last 7 observations, and uses it to date the daily slots correctly. Until the first rollover has been observed (at most 24 hours after installation), slots are assumed to change at midnight.
The learned time is shown by the Daily rollover time diagnostic sensor on the device page (e.g. 04:02); it stays Unknown until the first rollover has been observed. Its observations attribute tells how many rollovers the value is based on.
The softener writes each quarter-hour according to its own clock, which is usually a few minutes off Home Assistant's. If the integration assumed the quarter-hours change exactly at :00, :15, :30 and :45, the dating would depend on the minute of the reading: a reading just before the softener writes its quarter and one just after would shift every quarter-hour by one, and move up to a quarter-hour of consumption from one day to the next.
The integration therefore learns at which minute the softener writes its quarter-hours. It polls every 14 minutes rather than 15, so the minute of the reading shifts at each cycle, and it narrows down the moment the index moves; this takes about 3½ hours after installation, and is then kept up to date if the softener's clock drifts. Quarter-hours stay labelled on the :00/:15/:30/:45 grid of the softener's own clock. The learned minute is shown by the Quarter-hour offset diagnostic sensor (e.g. 9.8 min); it stays Unknown until learned. Until it is learned, quarter-hours are dated on Home Assistant's clock as before.
Four services are available to retrieve the complete history from the BWT device. The last 29 days come from the quarter-hour buffer (to the litre, midnight to midnight); older days from the daily buffer. Each call reads both buffers in full over BLE, which takes about 1 to 1½ minutes; a failed read is retried automatically.
Returns the total water consumption in liters since the device was put into service (up to 1825 days), up to yesterday included — today's consumption is already provided by the Consumption today sensor.
{
"total_liters": 125430,
"days_count": 365,
"from_date": "2024-04-07",
"to_date": "2025-04-06",
"day_rollover_learned": true
}day_rollover_learned stays false until the integration has observed the softener's daily rollover — at most 24 hours after installation. Until then, the boundary between daily slots and quarter-hour data is assumed to be at midnight, and the total may be off by a few hours of consumption. If an automation accumulates this total, check the flag first:
{% if result.day_rollover_learned %} … {% endif %}Returns daily water consumption (in liters) structured by year/month/day.
{
"2024": {
"04": { "07": 120, "08": 95, "09": 110 },
"05": { "01": 130 }
}
}Returns the number of regeneration cycles structured by year/month/day.
{
"2024": {
"04": { "07": 0, "08": 1, "09": 0 },
"05": { "01": 0 }
}
}Returns the number of power cuts of the softener structured by year/month/day. For the last 29 days each power cut is counted; before that, the daily buffer only records whether a day had one, so a day counts at most 1.
{
"2026": {
"10": { "05": 0, "06": 2, "07": 0 }
}
}These services can be called from Developer Tools → Services in Home Assistant or from an automation.
If connections keep failing while the device is clearly advertising — visible in Settings → Devices & Services → Bluetooth with a healthy RSSI — the host's Bluetooth stack itself may have degraded. This is known to happen on a Raspberry Pi's onboard adapter after long uptimes.
Restarting Home Assistant does not reset BlueZ. The daemon runs on the host and Home Assistant only talks to it over D-Bus, so reloading the config entry or restarting Home Assistant Core changes nothing. Only a host reboot clears it.
A useful early warning, sometimes days before anything breaks:
hci0: Error stopping scanner during active-window flip: [org.bluez.Error.InProgress]
Since 2026.9.1 the integration rides this out instead of failing permanently: connection attempts time out cleanly and Home Assistant retries with backoff, so sensors recover on their own once the adapter is healthy again.
History services return calendar days for the last 29 days, rebuilt from the quarter-hour buffer — they match the BWT app to the litre. Older days come from the daily buffer, which only exists in tens of litres and follows the softener's own day: those values end in 0 and may differ from the app on a given day, while the total stays exact.
Tested on:
- BWT AQA Perla 10
- BWT Calypso 2
- BWT Blue 22l
Should work with other BWT AQA Perla variants. Please open an issue if you have a different model and it doesn't work.
Issues and pull requests are welcome. Please include:
- Home Assistant version
- Integration logs (enable
debuglevel forcustom_components.bwt_aqa_perla_ble) - BWT firmware version (visible in the Firmware sensor)
GNU General Public License v3.0 — see LICENSE

