diff --git a/esphome/components/ble_presence/binary_sensor.py b/esphome/components/ble_presence/binary_sensor.py index 43ec9455d4..1cf8009384 100644 --- a/esphome/components/ble_presence/binary_sensor.py +++ b/esphome/components/ble_presence/binary_sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, CONF_ID +from esphome.const import CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_ID DEPENDENCIES = ['esp32_ble_tracker'] @@ -9,10 +9,12 @@ ble_presence_ns = cg.esphome_ns.namespace('ble_presence') BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor, cg.Component, esp32_ble_tracker.ESPBTDeviceListener) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ +CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(BLEPresenceDevice), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) + cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( + cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) def to_code(config): @@ -21,4 +23,14 @@ def to_code(config): yield esp32_ble_tracker.register_ble_device(var, config) yield binary_sensor.register_binary_sensor(var, config) - cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + if CONF_MAC_ADDRESS in config: + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + + if CONF_SERVICE_UUID in config: + if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): + cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) + elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format): + cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) + elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): + uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) + cg.add(var.set_service_uuid128(uuid128)) diff --git a/esphome/components/ble_presence/ble_presence_device.h b/esphome/components/ble_presence/ble_presence_device.h index 262cc3eedf..9d7ec83bc7 100644 --- a/esphome/components/ble_presence/ble_presence_device.h +++ b/esphome/components/ble_presence/ble_presence_device.h @@ -13,17 +13,67 @@ class BLEPresenceDevice : public binary_sensor::BinarySensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { public: - void set_address(uint64_t address) { address_ = address; } + void set_address(uint64_t address) { + this->by_address_ = true; + this->address_ = address; + } + void set_service_uuid16(uint16_t uuid) { + this->by_address_ = false; + this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid); + } + void set_service_uuid32(uint32_t uuid) { + this->by_address_ = false; + this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint32(uuid); + } + void set_service_uuid128(uint8_t *uuid) { + this->by_address_ = false; + this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid); + } void on_scan_end() override { if (!this->found_) this->publish_state(false); this->found_ = false; } bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { - if (device.address_uint64() == this->address_) { - this->publish_state(true); - this->found_ = true; - return true; + if (this->by_address_) { + if (device.address_uint64() == this->address_) { + this->publish_state(true); + this->found_ = true; + return true; + } + } 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; + } + } } return false; } @@ -32,7 +82,9 @@ class BLEPresenceDevice : public binary_sensor::BinarySensor, protected: bool found_{false}; + bool by_address_{false}; uint64_t address_; + esp32_ble_tracker::ESPBTUUID uuid_; }; } // namespace ble_presence diff --git a/esphome/components/ble_rssi/ble_rssi_sensor.h b/esphome/components/ble_rssi/ble_rssi_sensor.h index 2c296b3831..17dd0d4a7d 100644 --- a/esphome/components/ble_rssi/ble_rssi_sensor.h +++ b/esphome/components/ble_rssi/ble_rssi_sensor.h @@ -11,17 +11,67 @@ namespace ble_rssi { class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { public: - void set_address(uint64_t address) { address_ = address; } + void set_address(uint64_t address) { + this->by_address_ = true; + this->address_ = address; + } + void set_service_uuid16(uint16_t uuid) { + this->by_address_ = false; + this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid); + } + void set_service_uuid32(uint32_t uuid) { + this->by_address_ = false; + this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint32(uuid); + } + void set_service_uuid128(uint8_t *uuid) { + this->by_address_ = false; + this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid); + } void on_scan_end() override { if (!this->found_) this->publish_state(NAN); this->found_ = false; } bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { - if (device.address_uint64() == this->address_) { - this->publish_state(device.get_rssi()); - this->found_ = true; - return true; + if (this->by_address_) { + if (device.address_uint64() == this->address_) { + this->publish_state(device.get_rssi()); + this->found_ = true; + return true; + } + } 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; + } + } } return false; } @@ -30,7 +80,9 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi protected: bool found_{false}; + bool by_address_{false}; uint64_t address_; + esp32_ble_tracker::ESPBTUUID uuid_; }; } // namespace ble_rssi diff --git a/esphome/components/ble_rssi/sensor.py b/esphome/components/ble_rssi/sensor.py index ee8f71632f..76a27e6f2b 100644 --- a/esphome/components/ble_rssi/sensor.py +++ b/esphome/components/ble_rssi/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL +from esphome.const import CONF_SERVICE_UUID, CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL DEPENDENCIES = ['esp32_ble_tracker'] @@ -9,10 +9,12 @@ ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi') BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component, esp32_ble_tracker.ESPBTDeviceListener) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({ +CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({ cv.GenerateID(): cv.declare_id(BLERSSISensor), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) + cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( + cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) def to_code(config): @@ -21,4 +23,14 @@ def to_code(config): yield esp32_ble_tracker.register_ble_device(var, config) yield sensor.register_sensor(var, config) - cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + if CONF_MAC_ADDRESS in config: + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + + if CONF_SERVICE_UUID in config: + if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): + cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) + elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format): + cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) + elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): + uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) + cg.add(var.set_service_uuid128(uuid128)) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 7e998e77b1..4aa5b1610a 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -1,3 +1,4 @@ +import re import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, ESP_PLATFORM_ESP32, CONF_INTERVAL, \ @@ -32,6 +33,50 @@ def validate_scan_parameters(config): return config +bt_uuid16_format = 'XXXX' +bt_uuid32_format = 'XXXXXXXX' +bt_uuid128_format = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' + + +def bt_uuid(value): + in_value = cv.string_strict(value) + value = in_value.upper() + + if len(value) == len(bt_uuid16_format): + pattern = re.compile("^[A-F|0-9]{4,}$") + if not pattern.match(value): + raise cv.Invalid( + u"Invalid hexadecimal value for 16 bit UUID format: '{}'".format(in_value)) + return value + if len(value) == len(bt_uuid32_format): + pattern = re.compile("^[A-F|0-9]{8,}$") + if not pattern.match(value): + raise cv.Invalid( + u"Invalid hexadecimal value for 32 bit UUID format: '{}'".format(in_value)) + return value + if len(value) == len(bt_uuid128_format): + pattern = re.compile( + "^[A-F|0-9]{8,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{12,}$") + if not pattern.match(value): + raise cv.Invalid( + u"Invalid hexadecimal value for 128 UUID format: '{}'".format(in_value)) + return value + raise cv.Invalid( + u"Service UUID must be in 16 bit '{}', 32 bit '{}', or 128 bit '{}' format".format( + bt_uuid16_format, bt_uuid32_format, bt_uuid128_format)) + + +def as_hex(value): + return cg.RawExpression('0x{}ULL'.format(value)) + + +def as_hex_array(value): + value = value.replace("-", "") + cpp_array = ['0x{}'.format(part) for part in [value[i:i+2] for i in range(0, len(value), 2)]] + return cg.RawExpression( + '(uint8_t*)(const uint8_t[16]){{{}}}'.format(','.join(reversed(cpp_array)))) + + CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(ESP32BLETracker), cv.Optional(CONF_SCAN_PARAMETERS, default={}): cv.All(cv.Schema({ diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 7a5bd733a2..4b67277f16 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -245,6 +245,7 @@ bool ESPBTUUID::contains(uint8_t data1, uint8_t data2) const { } return false; } +esp_bt_uuid_t ESPBTUUID::get_uuid() { return this->uuid_; } std::string ESPBTUUID::to_string() { char sbuf[64]; switch (this->uuid_.len) { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 82e8e553fc..280c3fc45f 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -25,6 +25,8 @@ class ESPBTUUID { bool contains(uint8_t data1, uint8_t data2) const; + esp_bt_uuid_t get_uuid(); + std::string to_string(); protected: diff --git a/esphome/const.py b/esphome/const.py index ffef106945..dc607d62ff 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -382,6 +382,7 @@ CONF_SENSORS = 'sensors' CONF_SEQUENCE = 'sequence' CONF_SERVERS = 'servers' CONF_SERVICE = 'service' +CONF_SERVICE_UUID = 'service_uuid' CONF_SERVICES = 'services' CONF_SETUP_MODE = 'setup_mode' CONF_SETUP_PRIORITY = 'setup_priority' diff --git a/tests/test2.yaml b/tests/test2.yaml index 097c29285a..78e1ab149a 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -61,6 +61,15 @@ sensor: - platform: ble_rssi mac_address: AC:37:43:77:5F:4C name: "BLE Google Home Mini RSSI value" + - platform: ble_rssi + service_uuid: '11aa' + name: "BLE Test Service 16" + - platform: ble_rssi + service_uuid: '11223344' + name: "BLE Test Service 32" + - platform: ble_rssi + service_uuid: '11223344-5566-7788-99aa-bbccddeeff00' + name: "BLE Test Service 128" - platform: xiaomi_hhccjcy01 mac_address: 94:2B:FF:5C:91:61 temperature: @@ -170,6 +179,15 @@ binary_sensor: - platform: ble_presence mac_address: AC:37:43:77:5F:4C name: "ESP32 BLE Tracker Google Home Mini" + - platform: ble_presence + service_uuid: '11aa' + name: "BLE Test Service 16 Presence" + - platform: ble_presence + service_uuid: '11223344' + name: "BLE Test Service 32 Presence" + - platform: ble_presence + service_uuid: '11223344-5566-7788-99aa-bbccddeeff00' + name: "BLE Test Service 128 Presence" - platform: esp32_touch name: "ESP32 Touch Pad GPIO27" pin: GPIO27