mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[ble_device_base] Migrate BLE sensor platforms to the neutral layer (batch 6: thermopro_ble, exposure_notifications, xiaomi_ble) (#18168)
This commit is contained in:
@@ -1,33 +1,59 @@
|
||||
from typing import Any
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble_tracker
|
||||
from esphome.components import ble_device_base
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_TRIGGER_ID
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
AUTO_LOAD = ["ble_device_base"]
|
||||
|
||||
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,
|
||||
ble_device_base.ESPBTDeviceListener,
|
||||
automation.Trigger.template(ExposureNotification),
|
||||
)
|
||||
|
||||
CONF_ON_EXPOSURE_NOTIFICATION = "on_exposure_notification"
|
||||
|
||||
_RENAME_HUB_ID = ble_device_base.rename_legacy_hub_id("exposure_notifications")
|
||||
|
||||
_VALIDATE_AUTOMATION = automation.validate_automation(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ExposureNotificationTrigger),
|
||||
}
|
||||
# The trigger is the BLE listener, so the hub id lives on it.
|
||||
).extend(ble_device_base.BLE_DEVICE_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
# validate_automation() needs a dict-based schema, so the rename cannot go
|
||||
# inside it and has to run on the option value first. That value may also be a
|
||||
# list of automations or malformed, and rename_legacy_hub_id() is dict-only, so
|
||||
# map over lists and let validate_automation() report anything else.
|
||||
# schema_extractor keeps the key typed as a trigger in the generated editor
|
||||
# schema; build_language_schema.py recurses into cv.All but not into a plain
|
||||
# function.
|
||||
@schema_extractor("automation")
|
||||
def _validate_on_exposure_notification(value: Any) -> list[ConfigType]:
|
||||
if value is SCHEMA_EXTRACT:
|
||||
return _VALIDATE_AUTOMATION(value)
|
||||
if isinstance(value, dict):
|
||||
value = _RENAME_HUB_ID(value)
|
||||
elif isinstance(value, list):
|
||||
value = [_RENAME_HUB_ID(v) if isinstance(v, dict) else v for v in value]
|
||||
return _VALIDATE_AUTOMATION(value)
|
||||
|
||||
|
||||
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)
|
||||
),
|
||||
cv.Required(CONF_ON_EXPOSURE_NOTIFICATION): _validate_on_exposure_notification,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -36,4 +62,4 @@ async def to_code(config):
|
||||
for conf in config.get(CONF_ON_EXPOSURE_NOTIFICATION, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||
await automation.build_automation(trigger, [(ExposureNotification, "x")], conf)
|
||||
await esp32_ble_tracker.register_ble_device(trigger, conf)
|
||||
await ble_device_base.register_ble_device(trigger, conf)
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome::exposure_notifications {
|
||||
|
||||
using namespace esp32_ble_tracker;
|
||||
using namespace ble_device_base;
|
||||
|
||||
static const char *const TAG = "exposure_notifications";
|
||||
|
||||
@@ -43,5 +41,3 @@ bool ExposureNotificationTrigger::parse_device(const ESPBTDevice &device) {
|
||||
}
|
||||
|
||||
} // namespace esphome::exposure_notifications
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/ble_device_base/ble_device.h"
|
||||
#include <array>
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome::exposure_notifications {
|
||||
|
||||
struct ExposureNotification {
|
||||
@@ -17,11 +15,9 @@ struct ExposureNotification {
|
||||
};
|
||||
|
||||
class ExposureNotificationTrigger final : public Trigger<ExposureNotification>,
|
||||
public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
public ble_device_base::ESPBTDeviceListener {
|
||||
public:
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
bool parse_device(const ble_device_base::ESPBTDevice &device) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::exposure_notifications
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble_tracker, sensor
|
||||
from esphome.components import ble_device_base, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BATTERY_LEVEL,
|
||||
@@ -22,14 +22,15 @@ from esphome.const import (
|
||||
|
||||
CODEOWNERS = ["@sittner"]
|
||||
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
AUTO_LOAD = ["ble_device_base"]
|
||||
|
||||
thermopro_ble_ns = cg.esphome_ns.namespace("thermopro_ble")
|
||||
ThermoProBLE = thermopro_ble_ns.class_(
|
||||
"ThermoProBLE", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
|
||||
"ThermoProBLE", ble_device_base.ESPBTDeviceListener, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
ble_device_base.rename_legacy_hub_id("thermopro_ble"),
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ThermoProBLE),
|
||||
@@ -68,15 +69,15 @@ CONFIG_SCHEMA = (
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(ble_device_base.BLE_DEVICE_SCHEMA),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
await ble_device_base.register_ble_device(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#include <cmath>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome::thermopro_ble {
|
||||
|
||||
// this size must be large enough to hold the largest data frame
|
||||
@@ -34,7 +32,7 @@ void ThermoProBLE::dump_config() {
|
||||
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||
}
|
||||
|
||||
bool ThermoProBLE::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
bool ThermoProBLE::parse_device(const ble_device_base::ESPBTDevice &device) {
|
||||
// check for matching mac address
|
||||
if (device.address_uint64() != this->address_) {
|
||||
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
|
||||
@@ -66,8 +64,8 @@ bool ThermoProBLE::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
}
|
||||
|
||||
// reconstruct whole record from 2 byte uuid and data
|
||||
esp_bt_uuid_t uuid = service_data.uuid.get_uuid();
|
||||
uint8_t data[MAX_DATA_SIZE] = {static_cast<uint8_t>(uuid.uuid.uuid16), static_cast<uint8_t>(uuid.uuid.uuid16 >> 8)};
|
||||
uint16_t svc_uuid16 = service_data.uuid.uuid16();
|
||||
uint8_t data[MAX_DATA_SIZE] = {static_cast<uint8_t>(svc_uuid16), static_cast<uint8_t>(svc_uuid16 >> 8)};
|
||||
std::copy(service_data.data.begin(), service_data.data.end(), std::begin(data) + 2);
|
||||
|
||||
// dispatch data to parser
|
||||
@@ -202,5 +200,3 @@ static optional<ParseResult> parse_tp3(const uint8_t *data, std::size_t data_siz
|
||||
}
|
||||
|
||||
} // namespace esphome::thermopro_ble
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include "esphome/components/ble_device_base/ble_device.h"
|
||||
|
||||
namespace esphome::thermopro_ble {
|
||||
|
||||
@@ -17,11 +15,11 @@ struct ParseResult {
|
||||
|
||||
using DeviceParser = optional<ParseResult> (*)(const uint8_t *data, std::size_t data_size);
|
||||
|
||||
class ThermoProBLE final : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
class ThermoProBLE final : public Component, public ble_device_base::ESPBTDeviceListener {
|
||||
public:
|
||||
void set_address(uint64_t address) { this->address_ = address; };
|
||||
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
bool parse_device(const ble_device_base::ESPBTDevice &device) override;
|
||||
void dump_config() override;
|
||||
void set_signal_strength(sensor::Sensor *signal_strength) { this->signal_strength_ = signal_strength; }
|
||||
void set_temperature(sensor::Sensor *temperature) { this->temperature_ = temperature; }
|
||||
@@ -45,5 +43,3 @@ class ThermoProBLE final : public Component, public esp32_ble_tracker::ESPBTDevi
|
||||
};
|
||||
|
||||
} // namespace esphome::thermopro_ble
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import esp32_ble_tracker
|
||||
from esphome.components import ble_device_base
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
AUTO_LOAD = ["ble_device_base"]
|
||||
|
||||
xiaomi_ble_ns = cg.esphome_ns.namespace("xiaomi_ble")
|
||||
XiaomiListener = xiaomi_ble_ns.class_(
|
||||
"XiaomiListener", esp32_ble_tracker.ESPBTDeviceListener
|
||||
"XiaomiListener", ble_device_base.ESPBTDeviceListener
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(XiaomiListener),
|
||||
}
|
||||
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
ble_device_base.rename_legacy_hub_id("xiaomi_ble"),
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(XiaomiListener),
|
||||
}
|
||||
).extend(ble_device_base.BLE_DEVICE_SCHEMA),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
await ble_device_base.register_ble_device(var, config)
|
||||
|
||||
@@ -2,14 +2,21 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <vector>
|
||||
|
||||
// AES-CCM backend for encrypted-payload (bindkey) decryption:
|
||||
// - ESP32 + ESP-IDF >= 6.0 -> PSA crypto (psa_aead_decrypt), hardware-backed.
|
||||
// - every other platform -> the portable software AES-CCM in ble_device_base, so
|
||||
// decryption never depends on the SDK exposing mbedtls/PSA to application code.
|
||||
#ifdef USE_ESP32
|
||||
#include <esp_idf_version.h>
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
|
||||
#include <psa/crypto.h>
|
||||
#else
|
||||
#include "mbedtls/ccm.h"
|
||||
#define XIAOMI_CRYPTO_PSA
|
||||
#endif
|
||||
#endif
|
||||
#ifndef XIAOMI_CRYPTO_PSA
|
||||
#include "esphome/components/ble_device_base/ble_aes_ccm.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::xiaomi_ble {
|
||||
@@ -166,7 +173,7 @@ bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult
|
||||
return success;
|
||||
}
|
||||
|
||||
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data) {
|
||||
optional<XiaomiParseResult> parse_xiaomi_header(const ble_device_base::ServiceData &service_data) {
|
||||
XiaomiParseResult result;
|
||||
if (!service_data.uuid.contains(0x95, 0xFE)) {
|
||||
ESP_LOGVV(TAG, "parse_xiaomi_header(): no service data UUID magic bytes.");
|
||||
@@ -318,7 +325,7 @@ bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, c
|
||||
memcpy(vector.iv + 6, v + 2, 3); // sensor type (2) + packet id (1)
|
||||
memcpy(vector.iv + 9, v + raw.size() - 7, 3); // payload counter
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
|
||||
#ifdef XIAOMI_CRYPTO_PSA
|
||||
// PSA AEAD expects ciphertext + tag concatenated
|
||||
uint8_t ct_with_tag[sizeof(vector.ciphertext) + sizeof(vector.tag)];
|
||||
memcpy(ct_with_tag, vector.ciphertext, vector.datasize);
|
||||
@@ -344,20 +351,10 @@ bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, c
|
||||
psa_destroy_key(key_id);
|
||||
bool decrypt_ok = (status == PSA_SUCCESS && plaintext_length == vector.datasize);
|
||||
#else
|
||||
mbedtls_ccm_context ctx;
|
||||
mbedtls_ccm_init(&ctx);
|
||||
|
||||
int ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, vector.key, vector.keysize * 8);
|
||||
if (ret) {
|
||||
ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): mbedtls_ccm_setkey() failed.");
|
||||
mbedtls_ccm_free(&ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = mbedtls_ccm_auth_decrypt(&ctx, vector.datasize, vector.iv, vector.ivsize, vector.authdata, vector.authsize,
|
||||
vector.ciphertext, vector.plaintext, vector.tag, vector.tagsize);
|
||||
mbedtls_ccm_free(&ctx);
|
||||
bool decrypt_ok = (ret == 0);
|
||||
// Portable software AES-CCM (ble_device_base) — no SDK mbedtls/PSA dependency.
|
||||
bool decrypt_ok = ble_device_base::aes_ccm_auth_decrypt(vector.key, vector.iv, vector.ivsize, vector.authdata,
|
||||
vector.authsize, vector.ciphertext, vector.datasize,
|
||||
vector.plaintext, vector.tag, vector.tagsize);
|
||||
#endif
|
||||
|
||||
if (!decrypt_ok) {
|
||||
@@ -448,7 +445,7 @@ bool report_xiaomi_results(const optional<XiaomiParseResult> &result, const char
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XiaomiListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
bool XiaomiListener::parse_device(const ble_device_base::ESPBTDevice &device) {
|
||||
// Previously the message was parsed twice per packet, once by XiaomiListener::parse_device()
|
||||
// and then again by the respective device class's parse_device() function. Parsing the header
|
||||
// here and then for each device seems to be unnecessary and complicates the duplicate packet filtering.
|
||||
@@ -460,5 +457,3 @@ bool XiaomiListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
||||
}
|
||||
|
||||
} // namespace esphome::xiaomi_ble
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/ble_device_base/ble_device.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome::xiaomi_ble {
|
||||
|
||||
struct XiaomiParseResult {
|
||||
@@ -68,15 +66,13 @@ struct XiaomiAESVector {
|
||||
|
||||
bool parse_xiaomi_value(uint16_t value_type, const uint8_t *data, uint8_t value_length, XiaomiParseResult &result);
|
||||
bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult &result);
|
||||
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data);
|
||||
optional<XiaomiParseResult> parse_xiaomi_header(const ble_device_base::ServiceData &service_data);
|
||||
bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, const uint64_t &address);
|
||||
bool report_xiaomi_results(const optional<XiaomiParseResult> &result, const char *address);
|
||||
|
||||
class XiaomiListener final : public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
class XiaomiListener final : public ble_device_base::ESPBTDeviceListener {
|
||||
public:
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
bool parse_device(const ble_device_base::ESPBTDevice &device) override;
|
||||
};
|
||||
|
||||
} // namespace esphome::xiaomi_ble
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,33 @@ const uint8_t TAG[4] = {0x48, 0x4d, 0xaa, 0x56};
|
||||
const uint8_t PLAINTEXT[7] = {0x02, 0x01, 0x64, 0x03, 0x10, 0x8a, 0x01};
|
||||
} // namespace
|
||||
|
||||
// Xiaomi's parameters differ from BTHome's: a 12-byte nonce and a 1-byte AAD
|
||||
// (0x11). Both the AAD block and the l = 3 length encoding are only reachable
|
||||
// through this shape, so they need their own vector. Generated the same way.
|
||||
namespace {
|
||||
const uint8_t NONCE_XIAOMI[12] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b};
|
||||
const uint8_t AAD_XIAOMI[1] = {0x11};
|
||||
const uint8_t CIPHERTEXT_XIAOMI[5] = {0xc3, 0x7e, 0x0a, 0x1d, 0x23};
|
||||
const uint8_t TAG_XIAOMI[4] = {0x98, 0x79, 0x87, 0xc6};
|
||||
const uint8_t PLAINTEXT_XIAOMI[5] = {0x04, 0x10, 0x02, 0xd4, 0x00};
|
||||
} // namespace
|
||||
|
||||
TEST(BleAesCcm, DecryptsXiaomiShapedVector) {
|
||||
uint8_t out[sizeof(PLAINTEXT_XIAOMI)] = {};
|
||||
EXPECT_TRUE(aes_ccm_auth_decrypt(KEY, NONCE_XIAOMI, sizeof(NONCE_XIAOMI), AAD_XIAOMI, sizeof(AAD_XIAOMI),
|
||||
CIPHERTEXT_XIAOMI, sizeof(CIPHERTEXT_XIAOMI), out, TAG_XIAOMI, sizeof(TAG_XIAOMI)));
|
||||
EXPECT_EQ(0, memcmp(out, PLAINTEXT_XIAOMI, sizeof(PLAINTEXT_XIAOMI)));
|
||||
}
|
||||
|
||||
TEST(BleAesCcm, RejectsWrongAssociatedData) {
|
||||
uint8_t bad_aad[sizeof(AAD_XIAOMI)];
|
||||
memcpy(bad_aad, AAD_XIAOMI, sizeof(AAD_XIAOMI));
|
||||
bad_aad[0] ^= 0x01;
|
||||
uint8_t out[sizeof(PLAINTEXT_XIAOMI)] = {};
|
||||
EXPECT_FALSE(aes_ccm_auth_decrypt(KEY, NONCE_XIAOMI, sizeof(NONCE_XIAOMI), bad_aad, sizeof(bad_aad),
|
||||
CIPHERTEXT_XIAOMI, sizeof(CIPHERTEXT_XIAOMI), out, TAG_XIAOMI, sizeof(TAG_XIAOMI)));
|
||||
}
|
||||
|
||||
TEST(BleAesCcm, DecryptsAndAuthenticatesKnownVector) {
|
||||
uint8_t out[sizeof(PLAINTEXT)] = {};
|
||||
EXPECT_TRUE(aes_ccm_auth_decrypt(KEY, NONCE, sizeof(NONCE), nullptr, 0, CIPHERTEXT, sizeof(CIPHERTEXT), out, TAG,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
exposure_notifications:
|
||||
on_exposure_notification:
|
||||
then:
|
||||
- lambda: |
|
||||
ESP_LOGD("main", "RSSI: %d", x.rssi);
|
||||
@@ -1,7 +1,10 @@
|
||||
esp32_ble_tracker:
|
||||
id: ble_tracker_hub
|
||||
|
||||
exposure_notifications:
|
||||
on_exposure_notification:
|
||||
# Explicit ble_hub_id: pins the neutral binding as a declared key.
|
||||
ble_hub_id: ble_tracker_hub
|
||||
then:
|
||||
- lambda: |
|
||||
ESP_LOGD("main", "Got notification:");
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
ln882h_ble_tracker: !include ../ln882h_ble_tracker/common.yaml
|
||||
exposure_notifications: !include common-ln.yaml
|
||||
@@ -0,0 +1,15 @@
|
||||
# Config-only: the CI base board (generic-bk7252, BLE 4.2) cannot compile the
|
||||
# BLE 5.x tracker, so this fixture proves validation (schema + neutral binding)
|
||||
# on a non-esp32 platform; codegen and compilation are not exercised here.
|
||||
bk72xx_ble_tracker:
|
||||
id: ble_tracker_hub
|
||||
|
||||
exposure_notifications:
|
||||
on_exposure_notification:
|
||||
# Explicit ble_hub_id: pins the neutral binding as a declared key.
|
||||
ble_hub_id: ble_tracker_hub
|
||||
then:
|
||||
- lambda: |
|
||||
ESP_LOGD("main", "Got notification:");
|
||||
ESP_LOGD("main", " RPI: %s", format_hex(x.rolling_proximity_identifier).c_str());
|
||||
ESP_LOGD("main", " RSSI: %d", x.rssi);
|
||||
@@ -0,0 +1,7 @@
|
||||
sensor:
|
||||
- platform: thermopro_ble
|
||||
mac_address: FE:74:B8:6A:97:B7
|
||||
temperature:
|
||||
name: ThermoPro Temperature
|
||||
humidity:
|
||||
name: ThermoPro Humidity
|
||||
@@ -1,7 +1,10 @@
|
||||
esp32_ble_tracker:
|
||||
id: ble_tracker_hub
|
||||
|
||||
sensor:
|
||||
# Explicit ble_hub_id: pins the neutral binding as a declared key.
|
||||
- platform: thermopro_ble
|
||||
ble_hub_id: ble_tracker_hub
|
||||
mac_address: FE:74:B8:6A:97:B7
|
||||
temperature:
|
||||
name: "ThermoPro Temperature"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
ln882h_ble_tracker: !include ../ln882h_ble_tracker/common.yaml
|
||||
thermopro_ble: !include common-ln.yaml
|
||||
@@ -0,0 +1,24 @@
|
||||
# Config-only: the CI base board (generic-bk7252, BLE 4.2) cannot compile the
|
||||
# BLE 5.x tracker, so this fixture proves validation (schema + neutral binding)
|
||||
# on a non-esp32 platform; codegen and compilation are not exercised here.
|
||||
bk72xx_ble_tracker:
|
||||
id: ble_tracker_hub
|
||||
|
||||
sensor:
|
||||
# Explicit ble_hub_id: pins the neutral binding as a declared key.
|
||||
- platform: thermopro_ble
|
||||
ble_hub_id: ble_tracker_hub
|
||||
mac_address: FE:74:B8:6A:97:B7
|
||||
temperature:
|
||||
name: "ThermoPro Temperature"
|
||||
humidity:
|
||||
name: "ThermoPro Humidity"
|
||||
battery_level:
|
||||
name: "ThermoPro Battery Level"
|
||||
signal_strength:
|
||||
name: "ThermoPro Signal Strength"
|
||||
# No ble_hub_id: exercises the generated binding real configs use.
|
||||
- platform: thermopro_ble
|
||||
mac_address: FE:74:B8:6A:97:B8
|
||||
temperature:
|
||||
name: BK ThermoPro Implicit Temperature
|
||||
@@ -0,0 +1 @@
|
||||
xiaomi_ble:
|
||||
@@ -1,3 +1,6 @@
|
||||
esp32_ble_tracker:
|
||||
id: ble_tracker_hub
|
||||
|
||||
# Explicit ble_hub_id: pins the neutral binding as a declared key.
|
||||
xiaomi_ble:
|
||||
ble_hub_id: ble_tracker_hub
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
ln882h_ble_tracker: !include ../ln882h_ble_tracker/common.yaml
|
||||
xiaomi_ble: !include common-ln.yaml
|
||||
@@ -0,0 +1,9 @@
|
||||
# Config-only: the CI base board (generic-bk7252, BLE 4.2) cannot compile the
|
||||
# BLE 5.x tracker, so this fixture proves validation (schema + neutral binding)
|
||||
# on a non-esp32 platform; codegen and compilation are not exercised here.
|
||||
bk72xx_ble_tracker:
|
||||
id: ble_tracker_hub
|
||||
|
||||
# Explicit ble_hub_id: pins the neutral binding as a declared key.
|
||||
xiaomi_ble:
|
||||
ble_hub_id: ble_tracker_hub
|
||||
Reference in New Issue
Block a user