Bermuda - Bluetooth/BLE Room Presence and tracking [custom integration]

Happy New Year and thank you for trying to help me. Your response clarified that i’ve understood the calibration tasks correctly and i might have some other issues.
I explain my steps in detail, so you can see what is happening

  • choose one proxy and positioned iphone 1 m away
  • calibrated to -98 / 3 get constant 1 m readings
  • positioned iphone 5 m away
  • changing Attenuation 1-3 even with .5 .1 steps. Nothing changes in measurement
  • then i thought wtf are my shellies faulty and activated my ESP32 bluetooth proxy
  • waited until it was seen by bermuda
  • made measurement in the reference proxy area with the ESP selected (Calibration 1)
  • got error “unkown error occured” (see below)
  • again: wtf. Booted. Nothing changes
  • unplugged ESP32. bermuda came back with the “old 3 shelly config”
  • still the same error. Doesn’t matter which proxy i choose (Calibration 1)
  • i am still able to get a measurement in Calibration 2, but there every proxy is set to 0 Offset and the measurements are in the range 0.37 - 0.63. In reality the proxies are 10-20 m away

So to summarize the above

  • i’m now unable to get a measurement in Calibration 1
  • the measurements in Calibration 2 are still possible but not helpful
  • i got this “error occured” today the first time after starting an ESP32 bluetooth proxy
  • removing the ESP32 , rebooting HA doesn’t change anything

here is the error i’m talking about from the logs:

Logger: aiohttp.server
Source: /usr/local/lib/python3.13/site-packages/aiohttp/web_protocol.py:451
First occurred: 10:56:40 (2 occurrences)
Last logged: 10:57:01
Error handling request

File “/usr/src/homeassistant/homeassistant/data_entry_flow.py”, line 565, in _async_handle_step
self._raise_if_step_does_not_exist(flow, result[“step_id”])
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/src/homeassistant/homeassistant/data_entry_flow.py”, line 590, in _raise_if_step_does_not_exist
raise UnknownStep(
f"Handler {self.class.name} doesn’t support step {step_id}"
)
homeassistant.data_entry_flow.UnknownStep: Handler OptionsFlowManager doesn’t support step calibration_global

BTW: i already changed the values yesterday back to -55/3 back from my idiotic “300” values. I positioned the iphone 1 m from the kitchen proxy. The phone constantly reported “living room” (10 m away). Then i positioned it this morning in another room with a proxy 20 m away from the living room and kitchen proxy. Phone still in living room.

Lmao, that’s the first time I’ve heard that nomenclature.

2 Likes

his wiki is full of such stuff. I guess he’s eaten a clown for breakfast every day.

1 Like

Hello,

I had a perfectly fine running espresence installation, which took ages to set-up properly, but in the end it worked fine.

Out of curiosity I changed it to BLE trilateraion, because I love the idea of adjusting the settings centrally in home assistant, not in each scanner separately.

However, I am facing the same problem as many others: Trackers turning to status unkown - this causes the area where the device is detected to jump. It does so several times per minute. Unfortunately, for days I can’t figure out any setting to prevent the status turning to unkown. If I can’t fix it, I need to go back to espresence sigh. I need it for room presence detection, and when a stationary device is constantly changing rooms, it is unusable.

have you lowered the Max radius? (Global options). Set it above 20 or even 100.
You may would take a the wiki here: https://github.com/agittins/bermuda/wiki/

another odd situation with my environment (btw: bermuda and private bluetooth device removed completely several times) is, that even though i have a lot proxies, i can only measure two of them (kitchen & living room) so it kinda makes sense, that the device is only founf by these two proxies. Even though the others are listed…

Radius is at 20. But I get the unknown status even when the phone is touching the ESPhome tracker.
Edit: I just changed it to 1000. Doesn’t help, unfortunately. But thanks for the help.

try 100 instead of 1000. And play around with the Calibration db value directly on the device page. Yeah it’s a work in progress. I guess there are a few bugs still flying around

Not a blueprint, but I made a custom sensor template that check from the devices in a person entity and check its area. It also check for my watch in priority :

template:
  - sensor:
      - name: "Sofiane Current Room"
        unique_id: "sofiane_current_room"
        state: >
          {% set person_entity = 'person.soflane' %}
          {% set associated_devices = state_attr(person_entity, 'device_trackers') | default([]) %}
          
          {% set device_friendly_names = namespace(names=[]) %}
          {% for device_id in associated_devices %}
            {% set friendly_name = state_attr(device_id, 'friendly_name') | default(device_id) | replace('Bermuda Tracker', '')%}
            {% set device_friendly_names.names = device_friendly_names.names + [friendly_name] %}
          {% endfor %}
          {# Filter sensors for the person based on a specific attribute (e.g., owner) #}
          {% set sensors = states.sensor 
               | selectattr('attributes.device_class', 'eq', 'bermuda__custom_device_class') 
               | selectattr('attributes.area_name', 'defined') 
               | selectattr('attributes.area_name', 'ne', None) 
               | selectattr('attributes.area_name', 'ne', 'unknown') 
               | list %}
        
            {% set sens = namespace(names=[]) %}
            
            {% for sensor in sensors  %}
            {% for name in device_friendly_names.names %}
              {% if name in sensor.attributes.friendly_name %}
                {% set sens.names = sens.names + [sensor] %}
              {% endif %}
            {% endfor %}
            {% endfor %}
          {# Check if sensors list is empty #}
          {% if sens.names | length == 0 %}
            {{ states(person_entity) }}
          {% else %}
            {# Prioritize sensors with "watch" in their name #}
            {% set prioritized_sensor = sens.names | selectattr('entity_id', 'search', 'watch') | first %}
            
            {# Fallback to the first valid sensor if no "watch" sensor exists #}
            {% set selected_sensor = prioritized_sensor if prioritized_sensor else sens.names | first %}
            
            {# Return the area_name of the selected sensor, fallback to person's state if "unknown" #}
            {{ selected_sensor.attributes.area_name if selected_sensor.attributes.area_name != 'unknown' else states(person_entity) }}
          {% endif %}

I also made a little automation that turns my watch BLE tracker off when charging :

alias: Control Watch BLE Transmitter Based on Charging State
description: Enable or disable BLE transmitter based on charging state.
triggers:
  - entity_id: sensor.galaxy_watch5_pro_xxxx_battery_state
    trigger: state
conditions: []
actions:
  - choose:
      - conditions:
          - condition: state
            entity_id: sensor.galaxy_watch5_pro_xxxx_battery_state
            state:
              - charging
              - full
        sequence:
          - data:
              message: command_ble_transmitter
              data:
                command: turn_off
            action: notify.mobile_app_galaxy_watch5_pro_xxxx
      - conditions:
          - condition: state
            entity_id: sensor.galaxy_watch5_pro_xxxx_battery_state
            state:
              - discharging
              - not_charging
        sequence:
          - data:
              message: command_ble_transmitter
              data:
                command: turn_on
            action: notify.mobile_app_galaxy_watch5_pro_xxxx
mode: single

So from now, I got a sensor for each person based on their watch (if any that is not charging), or on their phone :

Which can be based on to trigger automations :slight_smile:

2 Likes

I took a different approach, and wanted to keep it simple. Here is what I did:

  • I created this template sensor as a helper from the UI, which makes it easier to edit and allowed me to add this sensor as an entity part of my iphone HA Device:
  • It is not automatically getting the devices from the person entity like yours, but it’s always going to be 1 or 2 devices per person anyway.
  • It will give priority to the watch, but automatically switch to the phone if the watch is charging.


Template:

{% if is_state('sensor.iphone_watch_battery_state', 'Charging') or is_state('sensor.apple_watch_area', 'unknown') %}
{{ states('sensor.ble_iphone_area') }}
{% else %}
{{ states('sensor.apple_watch_area') }}
{% endif %}
2 Likes

Nice one too!
In my next template I will try to directly check if the battery is charging.

I have 3 roomates, I wanted the users to change their phone without changing the whole sensors each time :slight_smile:

1 Like

Hi all. First of all thank you to those who have provided this integration.

I’ve got 5 Everything presence Lite BT proxies and recently added 4 M5 stack atom lites. These devices are clearly very different in power.

I’ve read the wiki and performed calibration. The results are ok. I’m hoping for better.

I’m thinking of simply turning off the ESP lite proxies and adding more atom lites so every proxy is identical.

Has anyone tried this or got anything to add before I spend some money?

Thanks in advance

Just deployed some esp32-c3 devices only as a BT proxy for this integration but I’m noticing the c3 reporting distances as much further away.

For example I have my global config as 5m as none of my rooms are larger than 5m.

Sitting in my lounge i am around 3m from the esp but im just outside of the 5m range so im unknown.

If i move within 1m of the esp my device reports distance of around 3-4m.

How can i calibrate the device to be more accurate. My other esp (older dev kits) seem fine so only an issue with the c3 model.

Last week I started experimenting with ESPresnse and I had a blast with a couple of cheap ESP32’s from Amazon. It seemed to work great and I even got ESPresense Companion dialed in. But somehow I found the calibration rather frustrating. And I noticed that ESPresense seemed to lag behind severely compared to reality. Most of the time by 30 seconds, but up to a minute.

So I tried Bermuda and I love the relative simplicity. I don’t really need fancy-spancy triangulation for now. Calibration seemed to b a breeze. And of course, it’s not 100% accurate, but that’s not what I’m after at the moment. I also set up my ESP32’s as a beacon with ESPHome’s esp32_ble_beacon which seems to work fine :person_shrugging: . I was looking into that for some kind of triangulation if I wanted to. Or at least in the future.

The thing that is a real downer for me is that I cannot seem to get my wife’s Apple Watch to work reliably. I set up her device as a Private BLE Device with the IRK from ESPresense. That integrations device tracker shows her at home most of the time, but Bermuda doesn’t.

Her iPhone on the other hand works like 90% of the time, but her watch shows her location as unknown whenever the display is not active. We have also tried setting up Always On Display, but to no avail.

I found Dale’s GitHub repo where he tracks a single Apple Watch and that would suit me, but I don’t know how I could incorporatie that into Bermuda at the moment :neutral_face: .

I would really appreciate any pointers on this. And of course thanks for the great project and :love_letter: you put into it :smile:

Howdy all, hope everyone had an enjoyable time over the new year break!

The calibration screen will only show proxies that have received adverts from that device, so I am guessing that even though you have more proxies, none of them have recently received an advert from that particular device.

Hmm… -98 is a fairly low ref_power for an iPhone. I’d usually expect something around -60. It’s possible that if the signal really is that weak then it won’t be picked up at all at 5m. If you can include some screenshots that might help work out what’s going on (assuming you’re still having trouble with it!). Do you have a calibration db value set on the iphone device’s page? This might mess with the process, it should be set to 0 while you are calibrating the rest of the system.

Ah thanks - that’s a bug, it will be fixed in next release. This only happens when in the calibration1 screen and you choose a proxy that hasn’t got any data for that device. It should just give a message and refresh, but instead bombs out. This won’t be causing any other issues you’re having though.

:rofl: hadn’t heard that one before!

Hi Steffen! A few things to check:

  • Make sure all your bluetooth sources are assigned to an area - this includes your bluetooth adaptor, if your HA server has one. There’s an issue open to make this situation a bit more self-evident in future.
  • Using the max_radius setting can cause this - set it to something like 100m while starting out, to ensure it doesn’t affect things while you are getting set up.
  • If your HA server has a bluetooth adaptor (usb or on-board), you will often find it will report much closer distances than other proxies, because they tend to have better antennas (or just tend to report a higher signal strength). This can cause the bluetooth adaptor to “win” the area selection for devices more regularly, which might be incorrect. The solution for this is to use the “Calibration 2: Per-scanner RSSI Offsets” page in configuration to adjust the bluetooth adaptor to read a sensible distance when compared to your other proxies. For example mine needed an offset of -14 to play nice.

There is certainly an issue with Bermuda being a bit too eager to switch areas on a device, but this is definitely related to switching between areas as opposed to a device going “unknown”.

A diagnostics might really help to work out what’s going on here. First “reload” bermuda, wait for 30 seconds or so, then click “Download Diagnostics” and send me the result (you can raise an issue on github and upload it there, email it to me [email protected] or use any other file hosting service).

It’s getting to the stage where there are enough levers in Bermuda that it’s getting hard to know what’s going on in a given setup without the full overview of settings and state that a diagnostics gives.

max_radius just needs to be higher than the maximum distance you tend to get from any proxy. 70 to 100 is usually fine. Values bigger don’t actually “do” anything, it’s just an upper limit at which it discards the values.

I’d really recommend leaving the per-device calibration db alone until one has the overall calibration sorted out, as it just adds another layer of complexity to being able to understand what’s going on.

Nice! Thanks for sharing this.

Awesome! Thanks for this, too.

I’m interested in knowing more - did you find that the per-scanner offests didn’t quite get there, or is it something else?

I wouldn’t have thought this should be necessary, but I’d like to know more about what your experience with calibrating was like. Also it might be worth considering the physical orientation of the devices - all antennae have a varying sensitivity based on what angle a signal arrives at. The “meandering F” antennae on esp32 boards are a bit more consistent (and sensitive) than the ceramic antennas on the Atom boards, so the orientation of them might have a larger effect as well. I’d start with orienting them with the usb socket pointed downward, and the board “top” facing the area being detected, if that makes sense.

Very similar advise will apply to your situation, too :slight_smile: The c3 boards (that I’ve seen) have less sensitive ceramic antennas on them, so they’ll need a positive offset to calibrate their readings in Configuration, Calibration 2: Per-Scanner RSSI Offsets.

Interesting. So regarding device_tracker sensors (not the Area sensor), the way they work is if HA sees an advertisement they get marked as home, and after we haven’t seen an advertisement for a given time, they are marked as not_home / away. So it’s really just a timeout as to whether they appear as home or not.

Private BLE Device uses a hard-coded timeout (I think it’s 5 minutes, from the bluetooth integration’s default), while Bermuda lets you set a value in the DevTracker timeout setting, to suit your own needs. You can also create a template sensor / helper that will hold a longer timeout if you have a need for a per-device customisation.

For the Area sensors, if I recall correctly I think they have a 30 second timeout, so if the watch stops transmitting, its Area sensor will go ‘unknown’ after 30 seconds. This is deliberately shorter, since if one is using a BLE device to track room presense that device should be advertising regularly. I will be revisiting that a bit in future, but the premise of it won’t change much.

There is an issue open for tracking a “last known area” value for each device, which will probably be in the next release. This might give a decent solution if you can’t get the watch to keep transmitting. There are some other posts in this thread about the Apple Watch, there might be an iBeacon app or similar that might keep the watch transmitting when the screen is off - or it might be just what the current watchOS does, we don’t get a lot of control when playing in Apple’s walled garden.

Hadn’t seen that before! That’s very cool, for 3 years ago when it was last updated. Most of the effort there is to work around the IRK issue, which is solved by Private BLE Device. Other than that, Bermuda should work just as well, since it’s not doing anything other than tracking the same sorts of packets.

The only caveat there is that it’s possible the habluetooth library is filtering some apple packets that might be relevant, but I haven’t confirmed yet if that’s actually the case, or if it actually makes a difference.

I only have access to some older iOS devices and no apple watches, so any extra debug info anyone can provide would be helpful. Particularly, using an app like nrfConnect on a phone to show what advertisements a watch sends while its screen is off etc would be super helpful.

2 Likes

Thanks. I have set a positive number of 15 and it seems more inline to true values. Appreciate the reply.

Sorry for partially quoting this part, but I get the gist of how it works. Thanks for taking the time though :smile: . The 5 minute timeout would explain why the Private BLE Tracker integration doesn’t mark the device tracker as away. At least not that often. However I just cannot grasp why the watch would stop sending beacons while I read everywhere and anywhere that an Apple Watch is pretty chatty. Not that you can do anything about that, but having a wearable would be exactly the use case that I want :slight_smile: . The behavior was the same with ESPresense btw.

Yeah, it’s a pretty clever workaround if you ask me. Maybe I will try to setup something to test with. But testing time with the Apple Watch is currently very limited unfortunately.

I tried this briefly but I don’t know what I’m specifically looking for. Perhaps something for another day.


One thing that I just noticed is that the Apple Watch seems to broadcast every ~45 minutes or so. Which would correspond with a Nearby Info message according to Dale’s repo doc:

You can then store the MAC address until it cycles in ~45 minutes. When the MAC does change you rescan for the Nearby Info message 4c00 10 05 01 98 and service characteristic Model Number String Watch3,3 .

Maybe I’m missing something on the Apple Watch and broadcasting is turned off :confused:

Hey Ashley,

I was wondering if you’d be able to give me a hand when you have some time. I’m using Bermuda v 0.7.2. The issue is that Bermuda doesn’t seem to be able to pick up my ESP32 as a scanner. It’s a D1 mini ESP32 board. Here is the config

esphome:
  name: esphome-web-6bc964
  friendly_name: ESPHome Web 6bc964
  min_version: 2024.11.0
  name_add_mac_suffix: false

esp32:
  board: esp32dev
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:
- platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

bluetooth_proxy:
  active: True

esp32_ble_tracker:
  scan_parameters: 
    active: True
    interval: 1000ms
    window: 900ms

The ESPHome Builder shows the device as online, but Bermuda doesn’t pick it up as a scanner.

I’m running home assistant on a mini PC that has built-in Bluetooth, and I also have a long-range dongle plugged in as well (these are hci0 and hci1).

I tried restarting home assistant, re-flashing the board, and re-installing the config, but it doesn’t seem to want to work. I’m also wondering if there is a way to remove those scanners since I don’t really need them.

I also tried making the interval and window smaller and larger with no effect.

I’d be grateful for any advice you’re willing to give. Thank you!

Ok I’m not sure what I did but it showed up all of a sudden.