Skip to content

Commit

Permalink
Advanced configuration where ESPHome devices gets MAC addresses from …
Browse files Browse the repository at this point in the history
…Passive BLE Monitor configuration
  • Loading branch information
myhomeiot committed Dec 5, 2021
1 parent 0b53052 commit c43b5ea
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 20 deletions.
55 changes: 50 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ BLE Gateway component will allow you to forward BLE Advertising data packets for

If the heart of your Home Automation system is Home Assistant or another similar system and you use [ESPHome](https://esphome.io) devices to extend BLE coverage and process data from BLE sensors, you can dramatically decrease system complexity by remove all BLE data processing from ESPHome devices and forward raw BLE Advertising data to external components like [Passive BLE Monitor](https://github.com/custom-components/ble_monitor).

**Important note:** Currently in order to run BLE Gateway you need to make some changes in ESPHome `esp32_ble_tracker` component and Passive BLE Monitor integration, but I make PR's to these components and hopefully they will be accepted:
- [ESPHome PR](https://github.com/esphome/esphome/pull/2854)
- [Passive BLE Monitor PR](https://github.com/custom-components/ble_monitor/pull/572)
**Important note:** Currently in order to run BLE Gateway you need to make [some changes](https://github.com/esphome/esphome/pull/2854) in ESPHome `esp32_ble_tracker` component, I make PR and hopefully it will be accepted.
[Passive BLE Monitor](https://github.com/custom-components/ble_monitor) integration already has required support, thanks to [@Ernst79](https://github.com/Ernst79), please update it to version 6.2 or latest.

#### ESPHome configuration example (with [event](https://esphome.io/components/api.html#homeassistant-event-action), you can use direct ble_monitor.parse_data [service call](https://esphome.io/components/api.html#homeassistant-service-action))
#### ESPHome configuration example
Note: This example use [event](https://esphome.io/components/api.html#homeassistant-event-action), you can use direct `ble_monitor.parse_data` [service call](https://esphome.io/components/api.html#homeassistant-service-action)
```yaml
ble_gateway:
devices:
Expand All @@ -50,7 +50,8 @@ ble_gateway:
packet: !lambda return packet;
```

#### Home Assistant Passive BLE Monitor configuration example (remove automation if you use direct ble_monitor.parse_data service call)
#### Home Assistant Passive BLE Monitor configuration example
Note: Remove automation if you use direct `ble_monitor.parse_data` service call
```yaml
ble_monitor:
discovery: false
Expand All @@ -74,3 +75,47 @@ automation:
data:
packet: "{{ trigger.event.data.packet }}"
```

#### Advanced configuration where ESPHome devices gets MAC addresses from Passive BLE Monitor configuration
None: Be sure that you turn **on** the `input_boolean.settings_ble_gateway` if you want to receive BLE packets from BLE Gateway's.
**Important note:** New device address will be populated to ESPHome devices only after Passive BLE Monitor receives first BLE packet and creates entities for it. If in your configuration you don't have BLE stick and you have only ESPHome devices, you will need to add this device MAC address manually into the `input_text.settings_ble_gateway_add_device`. After Passive BLE Monitor creates entities for new device, you can remove address.
```yaml
# ESPHome
ble_gateway:
id: blegateway
on_ble_advertise:
then:
- homeassistant.event:
event: esphome.on_ble_advertise
data:
packet: !lambda return packet;
text_sensor:
- platform: homeassistant
id: ble_gateway_devices
entity_id: binary_sensor.ble_gateway
attribute: devices
on_value:
then:
- lambda: id(blegateway).set_devices(x);
# Home Assistant
input_boolean:
settings_ble_gateway:
name: BLE Gateway
icon: mdi:bluetooth
input_text:
settings_ble_gateway_add_device:
name: BLE Gateway Add Device
icon: mdi:bluetooth-connect
initial: ''
template:
- binary_sensor:
- name: BLE Gateway
icon: mdi:bluetooth
state: "{{ is_state('input_boolean.settings_ble_gateway', 'on') }}"
attributes:
devices: "{{ states.sensor | selectattr('entity_id', 'search', '^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') }}"
```
8 changes: 3 additions & 5 deletions components/ble_gateway/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
BLEGateway = ble_gateway_ns.class_(
"BLEGateway", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
)
BLEGatewayDevice = ble_gateway_ns.class_("BLEGatewayDevice")

# Triggers
BLEGatewayBLEAdvertiseTrigger = ble_gateway_ns.class_(
Expand All @@ -28,10 +27,9 @@
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(BLEGateway),
cv.Required(CONF_DEVICES): cv.All(
cv.Optional(CONF_DEVICES): cv.All(
cv.ensure_list(
{
cv.GenerateID(): cv.declare_id(BLEGatewayDevice),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
}
),
Expand All @@ -51,8 +49,8 @@ async def to_code(config):
await cg.register_component(var, config)
await esp32_ble_tracker.register_ble_device(var, config)

for conf in config[CONF_DEVICES]:
cg.add(var.register_device(cg.new_Pvariable(conf[CONF_ID], conf[CONF_MAC_ADDRESS].as_hex).address))
if config.get(CONF_DEVICES):
cg.add(var.set_devices("".join(f"{str(conf[CONF_MAC_ADDRESS]).replace(':', '')}" for conf in config[CONF_DEVICES])))

for conf in config.get(CONF_ON_BLE_ADVERTISE):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
Expand Down
22 changes: 20 additions & 2 deletions components/ble_gateway/ble_gateway.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace ble_gateway {

static const char *const TAG = "ble_gateway";

// https://stackoverflow.com/questions/25713995/how-to-decode-a-bluetooth-le-package-frame-beacon-of-a-freetec-px-1737-919-b
std::string scan_rst_to_hci_packet_hex(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {
const char *hex = "0123456789ABCDEF";
char buffer[(HCI_HEADER_LEN + ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX + 1) * 2 + 1];
Expand Down Expand Up @@ -49,7 +50,7 @@ std::string mac_address_to_string(uint64_t address) {
}

void BLEGateway::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Gateway Devices:");
ESP_LOGCONFIG(TAG, "BLE Gateway (%d devices):", this->devices_.size());
for (auto device : this->devices_)
ESP_LOGCONFIG(TAG, " MAC address: %s", mac_address_to_string(device).c_str());
}
Expand All @@ -63,13 +64,30 @@ bool BLEGateway::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
return false;
}

void BLEGateway::register_device(uint64_t device) {
void BLEGateway::add_device(uint64_t device) {
if (std::find(this->devices_.begin(), this->devices_.end(), device) == this->devices_.end())
this->devices_.push_back(device);
else
ESP_LOGW(TAG, "Device with MAC address (%s) already exists", mac_address_to_string(device).c_str());
}

void BLEGateway::set_devices(std::string devices) {
const char *s = devices.c_str();
int len = strlen(s);
ESP_LOGD(TAG, "set_devices: (%s)", s);

if (len % 12 == 0) {
this->devices_.clear();
for (int i = 0; i < len / 12; i++) {
uint64_t mac_address;
sscanf(&s[i * 12], "%12llx", &mac_address);
add_device(mac_address);
}
}
else
ESP_LOGE(TAG, "set_devices: Devices lengths (%d) must be a multiple of 12", len);
}

} // namespace ble_gateway
} // namespace esphome

Expand Down
11 changes: 3 additions & 8 deletions components/ble_gateway/ble_gateway.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,17 @@ namespace ble_gateway {

std::string scan_rst_to_hci_packet_hex(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param);

class BLEGatewayDevice {
public:
BLEGatewayDevice(uint64_t address) { this->address = address; }
uint64_t address;
};

class BLEGateway : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
public:
float get_setup_priority() const override { return setup_priority::DATA; }
void add_callback(std::function<void(const esp32_ble_tracker::ESPBTDevice &, std::string)> &&callback) { this->callback_.add(std::move(callback)); }

void dump_config() override;
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
void register_device(uint64_t device);
void add_device(uint64_t device);
void set_devices(std::string devices);
protected:
std::vector<uint64_t> devices_;
std::vector<uint64_t> devices_{};
CallbackManager<void(const esp32_ble_tracker::ESPBTDevice &, std::string)> callback_;
};

Expand Down

0 comments on commit c43b5ea

Please sign in to comment.