diff --git a/README.md b/README.md index 1036f8e..b108e9a 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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 @@ -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') }}" +``` diff --git a/components/ble_gateway/__init__.py b/components/ble_gateway/__init__.py index b27ab14..f7f1a84 100644 --- a/components/ble_gateway/__init__.py +++ b/components/ble_gateway/__init__.py @@ -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_( @@ -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, } ), @@ -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) diff --git a/components/ble_gateway/ble_gateway.cpp b/components/ble_gateway/ble_gateway.cpp index 6b2b7ff..1f5209a 100644 --- a/components/ble_gateway/ble_gateway.cpp +++ b/components/ble_gateway/ble_gateway.cpp @@ -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 ¶m) { 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]; @@ -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()); } @@ -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 diff --git a/components/ble_gateway/ble_gateway.h b/components/ble_gateway/ble_gateway.h index f8fdfdf..e1d5df8 100644 --- a/components/ble_gateway/ble_gateway.h +++ b/components/ble_gateway/ble_gateway.h @@ -12,12 +12,6 @@ namespace ble_gateway { std::string scan_rst_to_hci_packet_hex(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); -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; } @@ -25,9 +19,10 @@ class BLEGateway : public Component, public esp32_ble_tracker::ESPBTDeviceListen 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 devices_; + std::vector devices_{}; CallbackManager callback_; };