ESPHome BLE Gateway and other BLE components

Hi, has anyone thought of doing a full tutorial on how to set up a BLE Gateway on the ESP-32 and adding some devices? I have a Xiomi Mi Temperature and Humidity sensor, but there is insufficient signal to one room from the Raspberry, so I’d like to send data from the sensor to the ESP-32 and from it to HomeAssistant via wifi, but I’m not able to do it myself (for which I apologize). Maybe creating a tutorial is unnecessary, but it might help other users solve their problems. Thanks!

If you already using Home Assistant and build in Bluetooth or Bluetooth dongle I suggest you start from ESPHome Bluetooth Proxy it’s has installer site which allow you install and try it without any additional knowledge.

This BLE Gateway and the idea on which it is based was the predecessor of ESPHome Bluetooth Proxy which is now a part of ESPHome. So if you are new for it and don’t use BLE Passive Monitor integration, I suggest you to start from ESPHome Bluetooth Proxy.

Hope this helps.

1 Like

omg this looks amazing, I can’t believe i didn’t come across it when googling… thanks!

I am jumping into home assistant since a few days trying to collect data from my Govee devices (H5075).

I am using esphome runing on a esp32-s3-devkitc-1 board.

I have configured external_components, identified my devices.
I can see these logs

[21:41:15][D][ble_gateway:063]: [A4:C1:38:44:C4:A6] Packet 043E4602010000A6C44438C1A43A0D09475648353037355F43344136030388EC02010509FF88EC00021EB460001AFF4C000215494E54454C4C495F524F434B535F48575075F2FFC2BD
[21:41:30][D][ble_gateway:063]: [A4:C1:38:B5:93:B1] Packet 043E4602010000B193B538C1A43A0D09475648353037355F39334231030388EC02010509FF88EC0003048362001AFF4C000215494E54454C4C495F524F434B535F48575075F2FFC2BF
[21:41:30][D][esp32_ble_tracker:720]: Found device A4:C1:38:B5:93:B1 RSSI=-65
[21:41:30][D][esp32_ble_tracker:741]:   Address Type: PUBLIC
[21:41:30][D][esp32_ble_tracker:743]:   Name: 'GVH5075_93B1'

It seems that govee devices are well detected and that the gateway is working.
But when trying to setup the govee integration, no devices are detected.
What am I missing ?

configurations files :

esphome-configuration.yaml

external_components:
  - source: github://myhomeiot/esphome-components
  - source: github://pr#2854
    components: [esp32_ble_tracker]

# Enable ble tracker
esp32_ble_tracker:

# BLE GW
ble_gateway:
  id: blegw01
  devices:
    - mac_address: A4:C1:38:17:4C:B2
    - mac_address: A4:C1:38:82:1B:1C
    - mac_address: A4:C1:38:01:CC:88
    - mac_address: A4:C1:38:44:C4:A6
    - mac_address: A4:C1:38:B5:93:B1
    - mac_address: A4:C1:38:7A:D0:D5
  on_ble_advertise:
    then:
      homeassistant.event:
        event: esphome.on_ble_advertise
        data:
          packet: !lambda return packet;

homeassistant-configuration.yaml

# Home Assistant                                   
input_boolean:                               
  settings_ble_gateway:                      
    name: BLE Gateway                        
    icon: mdi:bluetooth                  
  settings_ble_gateway_discovery:        
    name: BLE Gateway Discovery      
    icon: mdi:bluetooth-connect      
                                     
template:                            
  - binary_sensor:                   
      - name: BLE Gateway            
        icon: mdi:bluetooth      
        state: "{{ is_state('input_boolean.settings_ble_gateway', 'on') }}"
        attributes:                                                        
          discovery: "{{ is_state('input_boolean.settings_ble_gateway_discovery', 'on') }}"
          # devices: "{{ states | selectattr('entity_id', 'search', '^(device_tracker|sensor).ble_') | selectattr('attributes.mac address', 'defined') | map(attribute='attributes.mac address') | unique | sort | join('') | replace(':', '') ~ (states('input_text.settings_ble_gateway_add_device') | replace(':', '') | trim) if is_state('binary_sensor.ble_gateway', 'on') }}"
          # Important note: In Passive BLE Monitor version 7.8.2 and later 'attributes.mac address' was changed to 'attributes.mac_address', please update your config                                                                                                                                                                                                              
          # devices: "{{ states | selectattr('entity_id', 'search', '^(device_tracker|sensor).ble_') | selectattr('attributes.mac_address', 'defined') | map(attribute='attributes.mac_address') | unique | sort | join('') | replace(':', '') ~ (states('input_text.settings_ble_gateway_add_device') | replace(':', '') | trim) if is_state('binary_sensor.ble_gateway', 'on') }}"
          # Note: In Home Assistant 2022.x, Passive BLE Monitor version 8.x and later you can use device attribute identifiers                                                                                                                                                                                                                                                      
          devices: >-                                                                                                                                                                                                                                                                                                                                                               
            {% set devices = namespace(items = []) %}                                                                                                                                                                                                                                                                                                                               
            {% for s in states | selectattr('entity_id', 'search', '^(device_tracker|sensor).ble_') | map(attribute='entity_id') %}                                                                                                                                                                                                                                                 
              {% set devices.items = devices.items + [device_id(s)] %}                                                                                                                                                                                                                                                                                                              
            {% endfor %}                                                                                                                                                                                                                                                                                                                                                            
            {% set ns = namespace(items = []) %}                                                                                                                                                                                                                                                                                                                                    
            {% for s in devices.items | unique %}                                                                                                                                                                                                                                                                                                                                   
              {% set ns.items = ns.items + [(device_attr(s, 'identifiers') | first)[1]] %}                                                                                                                                                                                                                                                                                          
            {% endfor %}                                                                                                                                                                                                                                                                                                                                                            
            {{ ns.items | unique | sort | join('') | replace(':', '') ~ (states('input_text.settings_ble_gateway_add_device') | replace(':', '') | trim) if is_state('binary_sensor.ble_gateway', 'on') }}                                  

BLE Gateway works with Passive BLE Monitor, so you should have it installed and configured.
Also please check that Passive BLE Monitor support your govee device.

If you want to use build in HA govee integration, you need to use ESPHome Bluetooth Proxy instead.

For BLE Gateway if you use latest version of ESPHome (2022.1+) you should use this:

external_components:
  - source: custom_components

Instead of this:

external_components:
  - source: github://myhomeiot/esphome-components
  - source: github://pr#2854
    components: [esp32_ble_tracker]

Thanks for this fast answer :slight_smile:

I will have a look to the proxy.

I tried without the PR patch and it was not working ( I am using the last HA 2022.12.7 - the official docker package )

I’ll keep you posted.
Thanks again, you rocks !

Well well well :slight_smile:

ble_proxy is working perfectly and govee integration detects my govee devices automatically.

Thanks for your help !

Is it possible (is there component) to advertise in the same passive way like šaomi telink based sensors do?

My set of components works as BLE Client, in order to advertise something it’s should work as BLE server. As starting point you can try to look this BLE Server component.

PS: In ESP chips it’s almost impossible to make BLE client and server works simultaneously, so if you use BLE Server, you can’t use BLE Client components.

Yes that woks, i even submitted PR with example for the light.

Hi! I’m relatively new to Home Assistant and am trying to get this set up.

I’ve configured my ESP32 and set up the documented HA automations. However, I do not see any firings of the event esphome.on_ble_advertise.

Is it possible I’m missing something simple?

The ESP32 is configured as follows

...

# in esphome.yaml
ble_gateway:
  id: blegateway
  devices:
    - mac_address: A4:C1:38:41:XX:XX
  on_ble_advertise:
    then:
      homeassistant.event:
        event: esphome.on_ble_advertise
        data:
          packet: !lambda return packet;

I’ve also set up the following automation:

# in automations.yaml
alias: ESPHome BLE Advertise
trigger:
  - platform: event
    event_type: esphome.on_ble_advertise
action:
  - service: ble_monitor.parse_data
    data:
      packet: "{{ trigger.event.data.packet }}"
mode: queued

In Home Assistant’s configuration.yaml

...

input_boolean:
  settings_ble_gateway:
    name: BLE Gateway
    icon: mdi:bluetooth
  settings_ble_gateway_discovery:
    name: BLE Gateway Discovery
    icon: mdi:bluetooth-connect

input_text:
  settings_ble_gateway_add_device:
    name: BLE Gateway Add Device
    icon: mdi:bluetooth-connect
    initial: ""

templates:
- binary_sensor:
    - name: BLE Gateway
      icon: mdi:bluetooth
      state: "{{ is_state('input_boolean.settings_ble_gateway', 'on') }}"
      attributes:
        discovery: "{{ is_state('input_boolean.settings_ble_gateway_discovery', 'on') }}"
        devices: >-
          {% set devices = namespace(items = []) %}
          {% for s in states | selectattr('entity_id', 'search', '^(device_tracker|sensor).ble_') | map(attribute='entity_id') %}
            {% set devices.items = devices.items + [device_id(s)] %}
          {% endfor %}
          {% set ns = namespace(items = []) %}
          {% for s in devices.items | unique %}
            {% set ns.items = ns.items + [(device_attr(s, 'identifiers') | first)[1]] %}
          {% endfor %}
          {{ ns.items | unique | sort | join('') | replace(':', '') ~ (states('input_text.settings_ble_gateway_add_device') | replace(':', '') | trim) if is_state('binary_sensor.ble_gateway', 'on') }}

In the ESP32 logs, I see messages such as this, which seem to indicate that packegs from my device are being received.

[11:54:56][D][ble_gateway:063]: [A4:C1:38:41:XX:XX] Packet 043E4002010000511C4138C1A434020106030388EC1109476F7665655F48353037345F314335311AFF4C000215494E54454C4C495F524F434B535F48575075F2XXXXX

Hello,

If you have debug log [A4:C1:38:41:XX:XX] Packet 043E.. this means that on_ble_advertise section is called. Do you see esphome.on_ble_advertise events in Home Assistant → Developer Tools → Events (be sure that you specify full event name and press press start listening)?

If you see this events, the problem in Passive BLE Monitor configuration, check if it’s has device with MAC A4:C1:38:41:XX:XX configured or discovery enabled (you can disable discovery after device will be discovered and added to Passive BLE Monitor configuration).

I suggest you to use Advanced configuration instead specifying device MAC address at ESP. For this configuration you miss some parts from ESPHome configuration like text_sensor with id ble_gateway_devices.

The esphome.on_ble_advertise does not show any events when I press “start listening”, even though the BLE Gateway is showing many packets being received.

There is a device with the correct MAC address configured in the BLE Gateway configuration.

Do you have on_ble_advertise section of ble_gateway configuration at ESPHome? In this section you should have call to homeassistant.event service which is send event esphome.on_ble_advertise to Home Assistant. Example:

ble_gateway:
  devices:
    - mac_address: 01:23:45:67:89:AB
    - mac_address: !secret lywsd03mmc_mac
  on_ble_advertise:
    then:
      homeassistant.event:
        event: esphome.on_ble_advertise
        data:
          packet: !lambda return packet;

Yes, I have it configured as follows:

ble_gateway:
  id: blegateway
  on_ble_advertise:
    then:
      homeassistant.event:
        event: esphome.on_ble_advertise
        data:
          packet: !lambda return packet;

Very strange, I have only last idea, did you add you ESPHome to Home Assistant?
Can you see your ESP at ESPHome integration of Home Assistant?

image

Yes, the ESPHome integration is added (this is what I used to configure the ESP32 devices).

You can try enable very verbose log level and you should see how events sends to Home Assistant.

logger:
  level: VERY_VERBOSE

Can anyone help me starting from scratch? I have a M5Stack Atom Lite that I would like to use as a BLE proxy for an Hormann garage door and possibly also for a SwitchBot Bot that I’m going to buy afterwards.

However I’m not able to configure the BLE gateway at all. These are the steps I’ve done so far:

  1. I installed ESPHome Bluetooth Proxy via USB with Chrome from the website and my HA Core instance running with Docker immediately discovered it.
  2. As I cannot run the ESPHome Add-on in HA Core, I started a VM with HA Supervised and installed the add-on there. It discovered my Atom too and offered to adopt it.
  3. It downloaded the toolchain (took about 3900 seconds) and compiled a configuration which forced me to update the API Key on the HA Core instance to see it again. The Add-on in the VM seems to work:

M5Stack on ESPHome Add-on

  1. Finally I clicked the Edit button to change the YAML configuration, by adding the same lines described above:
# ESPHome Bluetooth Proxy Configuration
external_components:
  - source: github://myhomeiot/esphome-components
  - source: github://pr#2854
    components: [esp32_ble_tracker]

# Enable ble tracker
esp32_ble_tracker:

# BLE GW
ble_gateway:
  id: blegw01
  devices:
    # My Hormann Garage
    - mac_address: F7:30:0D:52:F4:32
  on_ble_advertise:
    then:
      homeassistant.event:
        event: esphome.on_ble_advertise
        data:
          packet: !lambda return packet;

If I click Save and then Validate I have no errors, but when clicking Install and choosing Wirelessly a lot of errors appear and compilation fails.

INFO ESPHome 2023.7.1
INFO Reading configuration /config/esphome/atom-bluetooth-proxy-855f10.yaml...
INFO Generating C++ source...
INFO Compiling app...
Processing atom-bluetooth-proxy-855f10 (board: m5stack-atom; framework: espidf; platform: platformio/[email protected])
--------------------------------------------------------------------------------
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
 - framework-espidf @ 3.40404.0 (4.4.4) 
 - tool-cmake @ 3.16.4 
 - tool-ninja @ 1.7.1 
 - toolchain-esp32ulp @ 2.35.0-20220830 
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
Reading CMake configuration...
Dependency Graph
|-- noise-c @ 0.1.4
|-- Improv @ 1.2.3
Compiling /data/atom-bluetooth-proxy-855f10/.pioenvs/atom-bluetooth-proxy-855f10/src/esphome/components/api/api_connection.o
Compiling /data/atom-bluetooth-proxy-855f10/.pioenvs/atom-bluetooth-proxy-855f10/src/esphome/components/api/api_pb2.o
In file included from src/esphome/components/bluetooth_proxy/bluetooth_proxy.h:10,
                 from src/esphome/components/api/api_connection.cpp:17:
src/esphome/components/esp32_ble_client/ble_client_base.h:80:43: error: 'esphome::esp32_ble_client::espbt::ConnectionType' has not been declared
   virtual void set_connection_type(espbt::ConnectionType ct) { this->connection_type_ = ct; }
                                           ^~~~~~~~~~~~~~
src/esphome/components/esp32_ble_client/ble_client_base.h:93:10: error: 'ConnectionType' in namespace 'esphome::esp32_ble_client::espbt' does not name a type
   espbt::ConnectionType connection_type_{espbt::ConnectionType::V1};
          ^~~~~~~~~~~~~~
src/esphome/components/esp32_ble_client/ble_client_base.h:32:8: error: conflicting return type specified for 'virtual bool esphome::esp32_ble_client::BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t*)'
   bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
        ^~~~~~~~~~~~~~~~~~~
In file included from src/esphome/components/esp32_ble_client/ble_client_base.h:5,
                 from src/esphome/components/bluetooth_proxy/bluetooth_proxy.h:10,
                 from src/esphome/components/api/api_connection.cpp:17:
src/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h:156:16: note: overridden function is 'virtual void esphome::esp32_ble_tracker::ESPBTClient::gattc_event_handler(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t*)'
   virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
                ^~~~~~~~~~~~~~~~~~~
In file included from src/esphome/components/bluetooth_proxy/bluetooth_proxy.h:10,
                 from src/esphome/components/api/api_connection.cpp:17:
src/esphome/components/esp32_ble_client/ble_client_base.h:34:8: error: 'void esphome::esp32_ble_client::BLEClientBase::gap_event_handler(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t*)' marked 'override', but does not override
   void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
        ^~~~~~~~~~~~~~~~~
src/esphome/components/esp32_ble_client/ble_client_base.h: In member function 'virtual void esphome::esp32_ble_client::BLEClientBase::set_connection_type(int)':
src/esphome/components/esp32_ble_client/ble_client_base.h:80:70: error: 'class esphome::esp32_ble_client::BLEClientBase' has no member named 'connection_type_'; did you mean 'connection_index_'?
   virtual void set_connection_type(espbt::ConnectionType ct) { this->connection_type_ = ct; }
                                                                      ^~~~~~~~~~~~~~~~
                                                                      connection_index_
In file included from src/esphome/components/bluetooth_proxy/bluetooth_proxy.h:16,
                 from src/esphome/components/api/api_connection.cpp:17:
src/esphome/components/bluetooth_proxy/bluetooth_connection.h: At global scope:
src/esphome/components/bluetooth_proxy/bluetooth_connection.h:17:22: error: 'AdvertisementParserType' in namespace 'esphome::esp32_ble_tracker' does not name a type
   esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
                      ^~~~~~~~~~~~~~~~~~~~~~~
src/esphome/components/bluetooth_proxy/bluetooth_connection.h:16:8: error: 'void esphome::bluetooth_proxy::BluetoothConnection::gap_event_handler(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t*)' marked 'override', but does not override
   void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
        ^~~~~~~~~~~~~~~~~
In file included from src/esphome/components/api/api_connection.cpp:17:
src/esphome/components/bluetooth_proxy/bluetooth_proxy.h:54:22: error: 'AdvertisementParserType' in namespace 'esphome::esp32_ble_tracker' does not name a type
   esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
                      ^~~~~~~~~~~~~~~~~~~~~~~
src/esphome/components/bluetooth_proxy/bluetooth_proxy.h:51:8: error: 'bool esphome::bluetooth_proxy::BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param*, size_t)' marked 'override', but does not override
   bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override;
        ^~~~~~~~~~~~~
*** [/data/atom-bluetooth-proxy-855f10/.pioenvs/atom-bluetooth-proxy-855f10/src/esphome/components/api/api_connection.o] Error 1
========================== [FAILED] Took 5.78 seconds ==========================

If I edit the YAML again and change as suggested here:

I get this error when installing:

INFO ESPHome 2023.7.1
INFO Reading configuration /config/esphome/atom-bluetooth-proxy-855f10.yaml...
Failed config

external_components: [source /config/esphome/atom-bluetooth-proxy-855f10.yaml:19]
  - 
    Source is not a file system path, in expected github://username/name[@branch-or-tag] or github://pr#1234 format!.
    source: custom_components

Can you please suggest what I’m doing wrong here? What’s the correct procedure? And will I be able to use both Passive BLE integration and SwitchBot integration in the HA Core instance at the end?

I am new to this and am not the right guy to be answering, but it seems to me the last item is saying you did not create that folder in your installation. That config is if you want to download the branch and put it in a local folder. It is one of two options given for installation.
Apologies if I misunderstood.