mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Bluetooth advertising automation (#995)
* esp32_ble_tracker: introduce UUID comparison function * ble_presence, ble_rssi: use new UUID comparison function * esp32_ble_tracker: introduce automation on BLE advertising * test2.yaml: remove deep_sleep due to firmware size restrictions
This commit is contained in:
parent
31ae337931
commit
ba1222eae4
8 changed files with 221 additions and 66 deletions
|
@ -43,35 +43,10 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
|||
}
|
||||
} else {
|
||||
for (auto uuid : device.get_service_uuids()) {
|
||||
switch (this->uuid_.get_uuid().len) {
|
||||
case ESP_UUID_LEN_16:
|
||||
if (uuid.get_uuid().len == ESP_UUID_LEN_16 &&
|
||||
uuid.get_uuid().uuid.uuid16 == this->uuid_.get_uuid().uuid.uuid16) {
|
||||
this->publish_state(true);
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ESP_UUID_LEN_32:
|
||||
if (uuid.get_uuid().len == ESP_UUID_LEN_32 &&
|
||||
uuid.get_uuid().uuid.uuid32 == this->uuid_.get_uuid().uuid.uuid32) {
|
||||
this->publish_state(true);
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ESP_UUID_LEN_128:
|
||||
if (uuid.get_uuid().len == ESP_UUID_LEN_128) {
|
||||
for (int i = 0; i < ESP_UUID_LEN_128; i++) {
|
||||
if (this->uuid_.get_uuid().uuid.uuid128[i] != uuid.get_uuid().uuid.uuid128[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this->publish_state(true);
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
if (this->uuid_ == uuid) {
|
||||
this->publish_state(device.get_rssi());
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,35 +41,10 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
|
|||
}
|
||||
} else {
|
||||
for (auto uuid : device.get_service_uuids()) {
|
||||
switch (this->uuid_.get_uuid().len) {
|
||||
case ESP_UUID_LEN_16:
|
||||
if (uuid.get_uuid().len == ESP_UUID_LEN_16 &&
|
||||
uuid.get_uuid().uuid.uuid16 == this->uuid_.get_uuid().uuid.uuid16) {
|
||||
this->publish_state(device.get_rssi());
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ESP_UUID_LEN_32:
|
||||
if (uuid.get_uuid().len == ESP_UUID_LEN_32 &&
|
||||
uuid.get_uuid().uuid.uuid32 == this->uuid_.get_uuid().uuid.uuid32) {
|
||||
this->publish_state(device.get_rssi());
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ESP_UUID_LEN_128:
|
||||
if (uuid.get_uuid().len == ESP_UUID_LEN_128) {
|
||||
for (int i = 0; i < ESP_UUID_LEN_128; i++) {
|
||||
if (uuid.get_uuid().uuid.uuid128[i] != this->uuid_.get_uuid().uuid.uuid128[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this->publish_state(device.get_rssi());
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
if (this->uuid_ == uuid) {
|
||||
this->publish_state(device.get_rssi());
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import re
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.const import CONF_ID, ESP_PLATFORM_ESP32, CONF_INTERVAL, \
|
||||
CONF_DURATION
|
||||
CONF_DURATION, CONF_TRIGGER_ID, CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_MANUFACTURER_ID, \
|
||||
CONF_ON_BLE_ADVERTISE, CONF_ON_BLE_SERVICE_DATA_ADVERTISE, \
|
||||
CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE
|
||||
from esphome.core import coroutine
|
||||
|
||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||
|
@ -15,6 +18,17 @@ CONF_ACTIVE = 'active'
|
|||
esp32_ble_tracker_ns = cg.esphome_ns.namespace('esp32_ble_tracker')
|
||||
ESP32BLETracker = esp32_ble_tracker_ns.class_('ESP32BLETracker', cg.Component)
|
||||
ESPBTDeviceListener = esp32_ble_tracker_ns.class_('ESPBTDeviceListener')
|
||||
ESPBTDevice = esp32_ble_tracker_ns.class_('ESPBTDevice')
|
||||
ESPBTDeviceConstRef = ESPBTDevice.operator('ref').operator('const')
|
||||
adv_data_t = cg.std_vector.template(cg.uint8)
|
||||
adv_data_t_const_ref = adv_data_t.operator('ref').operator('const')
|
||||
# Triggers
|
||||
ESPBTAdvertiseTrigger = esp32_ble_tracker_ns.class_(
|
||||
'ESPBTAdvertiseTrigger', automation.Trigger.template(ESPBTDeviceConstRef))
|
||||
BLEServiceDataAdvertiseTrigger = esp32_ble_tracker_ns.class_(
|
||||
'BLEServiceDataAdvertiseTrigger', automation.Trigger.template(adv_data_t_const_ref))
|
||||
BLEManufacturerDataAdvertiseTrigger = esp32_ble_tracker_ns.class_(
|
||||
'BLEManufacturerDataAdvertiseTrigger', automation.Trigger.template(adv_data_t_const_ref))
|
||||
|
||||
|
||||
def validate_scan_parameters(config):
|
||||
|
@ -85,6 +99,20 @@ CONFIG_SCHEMA = cv.Schema({
|
|||
cv.Optional(CONF_WINDOW, default='30ms'): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_ACTIVE, default=True): cv.boolean,
|
||||
}), validate_scan_parameters),
|
||||
cv.Optional(CONF_ON_BLE_ADVERTISE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPBTAdvertiseTrigger),
|
||||
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
}),
|
||||
cv.Optional(CONF_ON_BLE_SERVICE_DATA_ADVERTISE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BLEServiceDataAdvertiseTrigger),
|
||||
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Required(CONF_SERVICE_UUID): bt_uuid,
|
||||
}),
|
||||
cv.Optional(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE): automation.validate_automation({
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BLEManufacturerDataAdvertiseTrigger),
|
||||
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Required(CONF_MANUFACTURER_ID): bt_uuid,
|
||||
}),
|
||||
|
||||
cv.Optional('scan_interval'): cv.invalid("This option has been removed in 1.14 (Reason: "
|
||||
"it never had an effect)"),
|
||||
|
@ -103,6 +131,35 @@ def to_code(config):
|
|||
cg.add(var.set_scan_interval(int(params[CONF_INTERVAL].total_milliseconds / 0.625)))
|
||||
cg.add(var.set_scan_window(int(params[CONF_WINDOW].total_milliseconds / 0.625)))
|
||||
cg.add(var.set_scan_active(params[CONF_ACTIVE]))
|
||||
for conf in config.get(CONF_ON_BLE_ADVERTISE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
if CONF_MAC_ADDRESS in conf:
|
||||
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
|
||||
yield automation.build_automation(trigger, [(ESPBTDeviceConstRef, 'x')], conf)
|
||||
for conf in config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
if len(conf[CONF_SERVICE_UUID]) == len(bt_uuid16_format):
|
||||
cg.add(trigger.set_service_uuid16(as_hex(conf[CONF_SERVICE_UUID])))
|
||||
elif len(conf[CONF_SERVICE_UUID]) == len(bt_uuid32_format):
|
||||
cg.add(trigger.set_service_uuid32(as_hex(conf[CONF_SERVICE_UUID])))
|
||||
elif len(conf[CONF_SERVICE_UUID]) == len(bt_uuid128_format):
|
||||
uuid128 = as_hex_array(conf[CONF_SERVICE_UUID])
|
||||
cg.add(trigger.set_service_uuid128(uuid128))
|
||||
if CONF_MAC_ADDRESS in conf:
|
||||
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
|
||||
yield automation.build_automation(trigger, [(adv_data_t_const_ref, 'x')], conf)
|
||||
for conf in config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
if len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid16_format):
|
||||
cg.add(trigger.set_manufacturer_uuid16(as_hex(conf[CONF_MANUFACTURER_ID])))
|
||||
elif len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid32_format):
|
||||
cg.add(trigger.set_manufacturer_uuid32(as_hex(conf[CONF_MANUFACTURER_ID])))
|
||||
elif len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid128_format):
|
||||
uuid128 = as_hex_array(conf[CONF_MANUFACTURER_ID])
|
||||
cg.add(trigger.set_manufacturer_uuid128(uuid128))
|
||||
if CONF_MAC_ADDRESS in conf:
|
||||
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
|
||||
yield automation.build_automation(trigger, [(adv_data_t_const_ref, 'x')], conf)
|
||||
|
||||
|
||||
@coroutine
|
||||
|
|
82
esphome/components/esp32_ble_tracker/automation.h
Normal file
82
esphome/components/esp32_ble_tracker/automation.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace esp32_ble_tracker {
|
||||
class ESPBTAdvertiseTrigger : public Trigger<const ESPBTDevice &>, public ESPBTDeviceListener {
|
||||
public:
|
||||
explicit ESPBTAdvertiseTrigger(ESP32BLETracker *parent) { parent->register_listener(this); }
|
||||
void set_address(uint64_t address) { this->address_ = address; }
|
||||
|
||||
bool parse_device(const ESPBTDevice &device) override {
|
||||
if (this->address_ && device.address_uint64() != this->address_) {
|
||||
return false;
|
||||
}
|
||||
this->trigger(device);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint64_t address_ = 0;
|
||||
};
|
||||
|
||||
class BLEServiceDataAdvertiseTrigger : public Trigger<const adv_data_t &>, public ESPBTDeviceListener {
|
||||
public:
|
||||
explicit BLEServiceDataAdvertiseTrigger(ESP32BLETracker *parent) { parent->register_listener(this); }
|
||||
void set_address(uint64_t address) { this->address_ = address; }
|
||||
void set_service_uuid16(uint16_t uuid) { this->uuid_ = ESPBTUUID::from_uint16(uuid); }
|
||||
void set_service_uuid32(uint32_t uuid) { this->uuid_ = ESPBTUUID::from_uint32(uuid); }
|
||||
void set_service_uuid128(uint8_t *uuid) { this->uuid_ = ESPBTUUID::from_raw(uuid); }
|
||||
|
||||
bool parse_device(const ESPBTDevice &device) override {
|
||||
if (this->address_ && device.address_uint64() != this->address_) {
|
||||
return false;
|
||||
}
|
||||
for (auto &service_data : device.get_service_datas()) {
|
||||
if (service_data.uuid == this->uuid_) {
|
||||
this->trigger(service_data.data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint64_t address_ = 0;
|
||||
ESPBTUUID uuid_;
|
||||
};
|
||||
|
||||
class BLEManufacturerDataAdvertiseTrigger : public Trigger<const adv_data_t &>, public ESPBTDeviceListener {
|
||||
public:
|
||||
explicit BLEManufacturerDataAdvertiseTrigger(ESP32BLETracker *parent) { parent->register_listener(this); }
|
||||
void set_address(uint64_t address) { this->address_ = address; }
|
||||
void set_manufacturer_uuid16(uint16_t uuid) { this->uuid_ = ESPBTUUID::from_uint16(uuid); }
|
||||
void set_manufacturer_uuid32(uint32_t uuid) { this->uuid_ = ESPBTUUID::from_uint32(uuid); }
|
||||
void set_manufacturer_uuid128(uint8_t *uuid) { this->uuid_ = ESPBTUUID::from_raw(uuid); }
|
||||
|
||||
bool parse_device(const ESPBTDevice &device) override {
|
||||
if (this->address_ && device.address_uint64() != this->address_) {
|
||||
return false;
|
||||
}
|
||||
for (auto &manufacturer_data : device.get_manufacturer_datas()) {
|
||||
if (manufacturer_data.uuid == this->uuid_) {
|
||||
this->trigger(manufacturer_data.data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint64_t address_ = 0;
|
||||
ESPBTUUID uuid_;
|
||||
};
|
||||
|
||||
} // namespace esp32_ble_tracker
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
|
@ -223,6 +223,22 @@ ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) {
|
|||
ret.uuid_.uuid.uuid128[i] = data[i];
|
||||
return ret;
|
||||
}
|
||||
ESPBTUUID ESPBTUUID::as_128bit() const {
|
||||
if (this->uuid_.len == ESP_UUID_LEN_128) {
|
||||
return *this;
|
||||
}
|
||||
uint8_t data[] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
uint32_t uuid32;
|
||||
if (this->uuid_.len == ESP_UUID_LEN_32) {
|
||||
uuid32 = this->uuid_.uuid.uuid32;
|
||||
} else {
|
||||
uuid32 = this->uuid_.uuid.uuid16;
|
||||
}
|
||||
for (uint8_t i = 0; i < this->uuid_.len; i++) {
|
||||
data[12 + i] = ((uuid32 >> i * 8) & 0xFF);
|
||||
}
|
||||
return ESPBTUUID::from_raw(data);
|
||||
}
|
||||
bool ESPBTUUID::contains(uint8_t data1, uint8_t data2) const {
|
||||
if (this->uuid_.len == ESP_UUID_LEN_16) {
|
||||
return (this->uuid_.uuid.uuid16 >> 8) == data2 || (this->uuid_.uuid.uuid16 & 0xFF) == data1;
|
||||
|
@ -241,16 +257,43 @@ bool ESPBTUUID::contains(uint8_t data1, uint8_t data2) const {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const {
|
||||
if (this->uuid_.len == uuid.uuid_.len) {
|
||||
switch (this->uuid_.len) {
|
||||
case ESP_UUID_LEN_16:
|
||||
if (uuid.uuid_.uuid.uuid16 == this->uuid_.uuid.uuid16) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ESP_UUID_LEN_32:
|
||||
if (uuid.uuid_.uuid.uuid32 == this->uuid_.uuid.uuid32) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ESP_UUID_LEN_128:
|
||||
for (int i = 0; i < ESP_UUID_LEN_128; i++) {
|
||||
if (uuid.uuid_.uuid.uuid128[i] != this->uuid_.uuid.uuid128[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return this->as_128bit() == uuid.as_128bit();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
esp_bt_uuid_t ESPBTUUID::get_uuid() { return this->uuid_; }
|
||||
std::string ESPBTUUID::to_string() {
|
||||
char sbuf[64];
|
||||
switch (this->uuid_.len) {
|
||||
case ESP_UUID_LEN_16:
|
||||
sprintf(sbuf, "%02X:%02X", this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16);
|
||||
sprintf(sbuf, "%02X:%02X", this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff);
|
||||
break;
|
||||
case ESP_UUID_LEN_32:
|
||||
sprintf(sbuf, "%02X:%02X:%02X:%02X", this->uuid_.uuid.uuid32 >> 24, this->uuid_.uuid.uuid32 >> 16,
|
||||
this->uuid_.uuid.uuid32 >> 8, this->uuid_.uuid.uuid32);
|
||||
sprintf(sbuf, "%02X:%02X:%02X:%02X", this->uuid_.uuid.uuid32 >> 24, (this->uuid_.uuid.uuid32 >> 16 & 0xff),
|
||||
(this->uuid_.uuid.uuid32 >> 8 & 0xff), this->uuid_.uuid.uuid32 & 0xff);
|
||||
break;
|
||||
default:
|
||||
case ESP_UUID_LEN_128:
|
||||
|
|
|
@ -23,8 +23,12 @@ class ESPBTUUID {
|
|||
|
||||
static ESPBTUUID from_raw(const uint8_t *data);
|
||||
|
||||
ESPBTUUID as_128bit() const;
|
||||
|
||||
bool contains(uint8_t data1, uint8_t data2) const;
|
||||
|
||||
bool operator==(const ESPBTUUID &uuid) const;
|
||||
|
||||
esp_bt_uuid_t get_uuid();
|
||||
|
||||
std::string to_string();
|
||||
|
|
|
@ -235,6 +235,7 @@ CONF_MAC_ADDRESS = 'mac_address'
|
|||
CONF_MAINS_FILTER = 'mains_filter'
|
||||
CONF_MAKE_ID = 'make_id'
|
||||
CONF_MANUAL_IP = 'manual_ip'
|
||||
CONF_MANUFACTURER_ID = 'manufacturer_id'
|
||||
CONF_MASK_DISTURBER = 'mask_disturber'
|
||||
CONF_MAX_CURRENT = 'max_current'
|
||||
CONF_MAX_DURATION = 'max_duration'
|
||||
|
@ -280,6 +281,9 @@ CONF_NUM_LEDS = 'num_leds'
|
|||
CONF_NUMBER = 'number'
|
||||
CONF_OFFSET = 'offset'
|
||||
CONF_ON = 'on'
|
||||
CONF_ON_BLE_ADVERTISE = 'on_ble_advertise'
|
||||
CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE = 'on_ble_manufacturer_data_advertise'
|
||||
CONF_ON_BLE_SERVICE_DATA_ADVERTISE = 'on_ble_service_data_advertise'
|
||||
CONF_ON_BOOT = 'on_boot'
|
||||
CONF_ON_CLICK = 'on_click'
|
||||
CONF_ON_DOUBLE_CLICK = 'on_double_click'
|
||||
|
|
|
@ -49,10 +49,6 @@ web_server:
|
|||
username: admin
|
||||
password: admin
|
||||
|
||||
deep_sleep:
|
||||
run_duration: 20s
|
||||
sleep_duration: 50s
|
||||
|
||||
as3935_i2c:
|
||||
irq_pin: GPIO12
|
||||
|
||||
|
@ -233,6 +229,25 @@ binary_sensor:
|
|||
name: "Storm Alert"
|
||||
|
||||
esp32_ble_tracker:
|
||||
on_ble_advertise:
|
||||
- mac_address: AC:37:43:77:5F:4C
|
||||
then:
|
||||
- lambda: !lambda |-
|
||||
ESP_LOGD("main", "The device address is %s", x.address_str().c_str());
|
||||
- then:
|
||||
- lambda: !lambda |-
|
||||
ESP_LOGD("main", "The device address is %s", x.address_str().c_str());
|
||||
on_ble_service_data_advertise:
|
||||
- service_uuid: ABCD
|
||||
then:
|
||||
- lambda: !lambda |-
|
||||
ESP_LOGD("main", "Length of service data is %i", x.size());
|
||||
on_ble_manufacturer_data_advertise:
|
||||
- manufacturer_id: ABCD
|
||||
then:
|
||||
- lambda: !lambda |-
|
||||
ESP_LOGD("main", "Length of manufacturer data is %i", x.size());
|
||||
|
||||
|
||||
#esp32_ble_beacon:
|
||||
# type: iBeacon
|
||||
|
|
Loading…
Reference in a new issue