Esphome, esp32_ble_tracker and HA's mqtt_room platform sensor

Hey.

No idea if this has been covered before but my plan is to put a bunch of esphome ESP-32 based sensor nodes around the house (standard stuff) but I was also thinking that with the ESP-32’s having BLE built-in it would be good if those nodes could also act as BLE room presence sensors to avoid having to install dedicated nodes with, say, ESP32-mqtt-room on them.

I couldn’t find a sensible way of doing this “out of the box” with esphome but I have managed to create something that appears to work quite well with my solitary test iBeacon (nrf51822) and test sensor node.

It’s available here: https://gist.github.com/kmdm/c01c0693b1380d826af200a3e9b2a656

Basically, what I’m wondering is:

  • Is there a better approach?
  • Have I missed something really obvious?

It just seems odd to have to write a C++ lambda in esphome to get this working. (Yes, the C++ could probably be improved!)

If not, I hope this helps somebody!

Thanks,.
Kenny

3 Likes

Dear Kenny,

I think that your approach is very interesting. I’m using some esp32 directly with mqtt_room integration and it’s working well, but access by ESPHome is more easy to configure and change parameters and upload to the esp32 devices. But integrating directly with mqtt_room (without ESPHome) I use Atom to upload the configuration to each ESP32, but in this integration you can fix the distance that you consider when a beacon for example is in one room or not. I cant see in your approach how is possible to fix this distance for each room esp32 that I have.

For example, in the configuration file that I use there are the following variables that you can fix to change some functionalities:

// Define bluetooth scan parameters
#define scanInterval 5 // Define the interval in seconds between scans
#define singleScanTime 5 // Define the duration of a single scan in seconds
#define activeScan true // Active scan uses more power, but get results faster
#define bleScanInterval 0x80 // Used to determine antenna sharing between Bluetooth and WiFi. Do not modify unless you are confident you know what you're doing
#define bleScanWindow 0x10 // Used to determine antenna sharing between Bluetooth and WiFi. Do not modify unless you are confident you know what you're doing

// Maximum distance (in meters) to report. Devices that are calculated to be further than this distance in meters will not be reported
#define maxDistance 6

Waht do you think about this?,

mmmm, its is possible to define the distance modifiying your code, for example:

if ( dist < 2 ) {
                  ESP_LOGD("ble_adv", "<2Sending MQTT room update for '%s' (%s): %.03fm (%d rssi)",
                           x.get_name().c_str(), uuid.c_str(), dist, x.get_rssi());
                  id(mqtt_client).publish_json(id(room_topic), [=](JsonObject &root) {
                      root["id"] = uuid;
                      root["name"] = x.get_name();
                      root["distance"] = dist;
                   });
                }

And then, in the sensor.yaml add timeout and away_timeout like:

- platform: mqtt_room
  name: Test beacon 1
  device_id: ec14d4d5-551c-46a8-9e91-8bcaa54606f0-0001-0000
  state_topic: room_presence
  timeout: 10
  away_timeout: 5

With this modification your code is working well for me. I hope with this suggestion you can improve a lot your code.

Many thanks to share.

Jose

1 Like

Hey @kmdm

Similar situation here, and I like the look of how you’re doing this.
I used your code, however I’m not seeing my phone show up anywhere (using the Beacon suppor tin the Android app)

My full yaml is below. If I don’t use your lamda function and disable the mqtt functionality, then it shows up as a binary sensor in HA, so I know the BLE part works there from the ESP32. Any ideas?

esphome:
  name: office-plant-monitor
  platform: ESP32
  board: esp-wrover-kit
  
mqtt:
  broker: !secret mqtt_broker
  username: !secret mqtt_username
  password: !secret mqtt_password
  discovery: false # Only if you use the HA API usually
  id: mqtt_client
  
substitutions:
  room_name: office
  
globals:
  - id: room_topic
    type: std::string
    initial_value: '"room_presence/${room_name}"'

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: 192.168.1.242
    gateway: 192.168.1.1
    subnet: 255.255.255.0
  ap:
    ssid: "Office-Esp32 Fallback Hotspot"
    password: "h3KtOafB1jpR"

esp32_ble_tracker:
  on_ble_advertise:
    - then:
        - lambda: |-
            if (x.get_ibeacon().has_value()) {
                // Convert the uuid from 11:22:33:44:55... to 12345678-1234-1234-1234....
                std::string orig_uuid = x.get_ibeacon().value().get_uuid().to_string();
                std::string uuid;
                std::transform(orig_uuid.begin(), orig_uuid.end(), orig_uuid.begin(),
                               [](unsigned char c){ return std::tolower(c); });
                for (int i = 0; i < 46; i += 3) {
                    uuid.append(orig_uuid.substr(i, 2));
                    if (i == 3 * 3 || i == 5 * 3 || i == 7 * 3 || i == 9 * 3) {
                        uuid.append("-");
                    }
                }
                // Add major and minor to the uuid
                char sbuf[11] = {0};
                uint16_t major = x.get_ibeacon().value().get_major();
                uint16_t minor = x.get_ibeacon().value().get_minor();
                sprintf(sbuf, "-%02x%02x-%02x%02x",
                        major >> 8, major & 0xFF, minor >> 8, minor & 0xFF);
                uuid.append(sbuf);
                // Calculate the distance
                int8_t tx_power = -69;
                float dist = pow(10, (float)(tx_power - x.get_rssi()) / (10 * 2));
                ESP_LOGD("ble_adv", "Sending MQTT room update for '%s' (%s): %.03fm (%d rssi)",
                         x.get_name().c_str(), uuid.c_str(), dist, x.get_rssi());
                id(mqtt_client).publish_json(id(room_topic), [=](JsonObject &root) {
                    root["id"] = uuid;
                    root["name"] = x.get_name();
                    root["distance"] = dist;
                });
            }

binary_sensor:
  - platform: ble_presence
    ibeacon_uuid: '9aef3cee-c905-408d-b90e-53dc2b2e0782'
    name: "Greg Phone Beacon"

sensor:
  - platform: xiaomi_hhccjcy01
    mac_address: 'C4:7C:8D:6C:D5:7E'
    temperature:
      name: "Rubber Plant Temperature"
    moisture:
      name: "Rubber Plant Moisture"
    illuminance:
      name: "Rubber Plant Illuminance"
    conductivity:
      name: "Rubber Plant Soil Conductivity"
    battery_level:
      name: "Rubber Plant Battery Level"
  - platform: xiaomi_hhccjcy01
    mac_address: 'C4:7C:8D:6C:08:EA'
    temperature:
      name: "Avocado Plant Temperature"
    moisture:
      name: "Avocado Plant Moisture"
    illuminance:
      name: "Avocado Plant Illuminance"
    conductivity:
      name: "Avocado Plant Soil Conductivity"
    battery_level:
      name: "Avocado Plant Battery Level"
  - platform: wifi_signal
    name: "Office Plant Monitor Signal Sensor"
    update_interval: 60s
  - platform: uptime
    name: "Office Plant Monitor Uptime Sensor"

# Enable logging
logger:
  level: VERBOSE

# Enable Home Assistant API
api:

ota:

Hi!

I’ve just updated the gist because the UUID format was changed a while ago (I didn’t realise anyone else was using it):

Please note that the new gist fixes a bug in esphome where the UUID is seemingly incorrectly reversed:

Hope this helps,
Kenny

Hey,

Ah that makes sense! I’m just trying it now, but still seemingly have the same problem.
I can see all the requests for my Xiaomi plant sensors showing up and sending, but it doesn’t seem to detect the phone from the advertisement. Only as a binary sensor :thinking:

Ah OK, now I’ve got it working, however the name is blank from the HA app and Beacon Simulator
[17:20:24][D][ble_adv:063]: Sending MQTT room update for '' (9aef3ceec90540-8db9-0e53-dc2b-2e0782-100-1): 3.981m (-81 rssi)

It seems this isn’t broadcast from Android, however I’ve noticed that in the app, my uuid is 9aef3cee-c905-408d-b90e-53dc2b2e0782, however the your function seems to record it as 9aef3ceec90540-8db9-0e53-dc2b-2e0782-100-1

I’ve put together something similar, adding a filtering scheme similar to ESPresense. It also only reports devices that are manually whitelisted, and reports unknown when the beacon is not detected for a configurable amount of time. It uses a template sensor and the HA API instead of MQTT, and the logic is in a header file so your lambdas are a single line instead of cluttering up the config file. It’s available on github at GitHub - rpatel3001/BleDistance: Distance tracking for BLE iBeacons in esphome

Kenny, I think your approach might be simplified by using the ble_rssi sensor and a template sensor that calculates the distance in a lambda instead of doing all the processing that’s already in ble_rssi.