mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Add exposure notifications (#1135)
This commit is contained in:
parent
412351fd1e
commit
465cd3d1f9
4 changed files with 109 additions and 0 deletions
|
@ -28,6 +28,7 @@ class ESPBTUUID {
|
||||||
bool contains(uint8_t data1, uint8_t data2) const;
|
bool contains(uint8_t data1, uint8_t data2) const;
|
||||||
|
|
||||||
bool operator==(const ESPBTUUID &uuid) const;
|
bool operator==(const ESPBTUUID &uuid) const;
|
||||||
|
bool operator!=(const ESPBTUUID &uuid) const { return !(*this == uuid); }
|
||||||
|
|
||||||
esp_bt_uuid_t get_uuid();
|
esp_bt_uuid_t get_uuid();
|
||||||
|
|
||||||
|
@ -74,6 +75,8 @@ class ESPBTDevice {
|
||||||
|
|
||||||
uint64_t address_uint64() const;
|
uint64_t address_uint64() const;
|
||||||
|
|
||||||
|
const uint8_t *address() const { return address_; }
|
||||||
|
|
||||||
esp_ble_addr_type_t get_address_type() const { return this->address_type_; }
|
esp_ble_addr_type_t get_address_type() const { return this->address_type_; }
|
||||||
int get_rssi() const { return rssi_; }
|
int get_rssi() const { return rssi_; }
|
||||||
const std::string &get_name() const { return this->name_; }
|
const std::string &get_name() const { return this->name_; }
|
||||||
|
|
28
esphome/components/exposure_notifications/__init__.py
Normal file
28
esphome/components/exposure_notifications/__init__.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome import automation
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import esp32_ble_tracker
|
||||||
|
from esphome.const import CONF_TRIGGER_ID
|
||||||
|
|
||||||
|
DEPENDENCIES = ['esp32_ble_tracker']
|
||||||
|
|
||||||
|
exposure_notifications_ns = cg.esphome_ns.namespace('exposure_notifications')
|
||||||
|
ExposureNotification = exposure_notifications_ns.struct('ExposureNotification')
|
||||||
|
ExposureNotificationTrigger = exposure_notifications_ns.class_(
|
||||||
|
'ExposureNotificationTrigger', esp32_ble_tracker.ESPBTDeviceListener,
|
||||||
|
automation.Trigger.template(ExposureNotification))
|
||||||
|
|
||||||
|
CONF_ON_EXPOSURE_NOTIFICATION = 'on_exposure_notification'
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.Required(CONF_ON_EXPOSURE_NOTIFICATION): automation.validate_automation(cv.Schema({
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ExposureNotificationTrigger),
|
||||||
|
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
for conf in config.get(CONF_ON_EXPOSURE_NOTIFICATION, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||||
|
yield automation.build_automation(trigger, [(ExposureNotification, 'x')], conf)
|
||||||
|
yield esp32_ble_tracker.register_ble_device(trigger, conf)
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include "exposure_notifications.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace exposure_notifications {
|
||||||
|
|
||||||
|
using namespace esp32_ble_tracker;
|
||||||
|
|
||||||
|
static const char *TAG = "exposure_notifications";
|
||||||
|
|
||||||
|
bool ExposureNotificationTrigger::parse_device(const ESPBTDevice &device) {
|
||||||
|
// See also https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
|
||||||
|
if (device.get_service_uuids().size() != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Exposure notifications have Service UUID FD 6F
|
||||||
|
ESPBTUUID uuid = device.get_service_uuids()[0];
|
||||||
|
// constant service identifier
|
||||||
|
const ESPBTUUID expected_uuid = ESPBTUUID::from_uint16(0xFD6F);
|
||||||
|
if (uuid != expected_uuid)
|
||||||
|
return false;
|
||||||
|
if (device.get_service_datas().size() != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The service data should be 20 bytes
|
||||||
|
// First 16 bytes are the rolling proximity identifier (RPI)
|
||||||
|
// Then 4 bytes of encrypted metadata follow which can be used to get the transmit power level.
|
||||||
|
ServiceData service_data = device.get_service_datas()[0];
|
||||||
|
if (service_data.uuid != expected_uuid)
|
||||||
|
return false;
|
||||||
|
auto data = service_data.data;
|
||||||
|
if (data.size() != 20)
|
||||||
|
return false;
|
||||||
|
ExposureNotification notification{};
|
||||||
|
memcpy(¬ification.address[0], device.address(), 6);
|
||||||
|
memcpy(¬ification.rolling_proximity_identifier[0], &data[0], 16);
|
||||||
|
memcpy(¬ification.associated_encrypted_metadata[0], &data[16], 4);
|
||||||
|
notification.rssi = device.get_rssi();
|
||||||
|
this->trigger(notification);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace exposure_notifications
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace exposure_notifications {
|
||||||
|
|
||||||
|
struct ExposureNotification {
|
||||||
|
std::array<uint8_t, 6> address;
|
||||||
|
int rssi;
|
||||||
|
std::array<uint8_t, 16> rolling_proximity_identifier;
|
||||||
|
std::array<uint8_t, 4> associated_encrypted_metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExposureNotificationTrigger : public Trigger<ExposureNotification>,
|
||||||
|
public esp32_ble_tracker::ESPBTDeviceListener {
|
||||||
|
public:
|
||||||
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace exposure_notifications
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue