mirror of
https://github.com/esphome/esphome.git
synced 2026-08-17 19:13:18 +08:00
[ln882h_ble_tracker] Use the shared ble_device_base automation layer (#18089)
This commit is contained in:
@@ -8,8 +8,8 @@
|
||||
|
||||
#include "bk72xx_ble_tracker.h"
|
||||
|
||||
#include "esphome/components/ble_device_base/automation.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::bk72xx_ble_tracker {
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ an explicit start_scan() call."""
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import ble_device_base, ln882h_ble, ota
|
||||
from esphome.components.const import CONF_SCAN_PARAMETERS, CONF_WINDOW
|
||||
from esphome.components.ble_device_base import automation as ble_automation
|
||||
from esphome.components.const import CONF_ON_SCAN_END, CONF_SCAN_PARAMETERS, CONF_WINDOW
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ACTIVE,
|
||||
@@ -13,19 +14,16 @@ from esphome.const import (
|
||||
CONF_DURATION,
|
||||
CONF_ID,
|
||||
CONF_INTERVAL,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_MANUFACTURER_ID,
|
||||
CONF_ON_BLE_ADVERTISE,
|
||||
CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE,
|
||||
CONF_ON_BLE_SERVICE_DATA_ADVERTISE,
|
||||
CONF_SERVICE_UUID,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CONF_LN882H_BLE_ID = "ln882h_ble_id"
|
||||
CONF_ON_SCAN_END = "on_scan_end"
|
||||
|
||||
DEPENDENCIES = ["ln882x"]
|
||||
AUTO_LOAD = ["ble_device_base", "ln882h_ble"]
|
||||
@@ -39,27 +37,10 @@ LN882HBLETracker = ln882h_ble_tracker_ns.class_(
|
||||
StartScanAction = ln882h_ble_tracker_ns.class_("StartScanAction", automation.Action)
|
||||
StopScanAction = ln882h_ble_tracker_ns.class_("StopScanAction", automation.Action)
|
||||
|
||||
ESPBTDeviceConstRef = (
|
||||
cg.esphome_ns.namespace("ble_device_base")
|
||||
.class_("ESPBTDevice")
|
||||
.operator("ref")
|
||||
.operator("const")
|
||||
)
|
||||
ESPBTAdvertiseTrigger = ln882h_ble_tracker_ns.class_(
|
||||
"ESPBTAdvertiseTrigger", automation.Trigger.template(ESPBTDeviceConstRef)
|
||||
)
|
||||
adv_data_t = cg.std_vector.template(cg.uint8)
|
||||
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
|
||||
BLEServiceDataAdvertiseTrigger = ln882h_ble_tracker_ns.class_(
|
||||
"BLEServiceDataAdvertiseTrigger", automation.Trigger.template(adv_data_t_const_ref)
|
||||
)
|
||||
BLEManufacturerDataAdvertiseTrigger = ln882h_ble_tracker_ns.class_(
|
||||
"BLEManufacturerDataAdvertiseTrigger",
|
||||
automation.Trigger.template(adv_data_t_const_ref),
|
||||
)
|
||||
BLEEndOfScanTrigger = ln882h_ble_tracker_ns.class_(
|
||||
"BLEEndOfScanTrigger", automation.Trigger.template()
|
||||
)
|
||||
ESPBTAdvertiseTrigger = ble_automation.ESPBTAdvertiseTrigger
|
||||
BLEServiceDataAdvertiseTrigger = ble_automation.BLEServiceDataAdvertiseTrigger
|
||||
BLEManufacturerDataAdvertiseTrigger = ble_automation.BLEManufacturerDataAdvertiseTrigger
|
||||
BLEEndOfScanTrigger = ble_automation.BLEEndOfScanTrigger
|
||||
|
||||
|
||||
# LN882H SDK reference scan rate: 100 ms interval / 50 ms window (50 % duty).
|
||||
@@ -68,60 +49,33 @@ SCAN_PARAMETERS_SCHEMA = ble_device_base.scan_parameters_schema(
|
||||
)
|
||||
|
||||
|
||||
# UUID string length -> setter width. 16/32-bit go out as plain hex literals,
|
||||
# 128-bit as a reversed byte array (BLE wire order). Keyed exhaustively so an
|
||||
# impossible length fails as a KeyError instead of silently picking a width
|
||||
# (bt_uuid validation upstream only ever produces these three).
|
||||
_UUID_WIDTHS = {
|
||||
len(ble_device_base.BT_UUID16_FORMAT): "16",
|
||||
len(ble_device_base.BT_UUID32_FORMAT): "32",
|
||||
len(ble_device_base.BT_UUID128_FORMAT): "128",
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LN882HBLETracker),
|
||||
cv.GenerateID(CONF_LN882H_BLE_ID): cv.use_id(ln882h_ble.LN882HBLE),
|
||||
cv.Optional(CONF_SCAN_PARAMETERS, default={}): SCAN_PARAMETERS_SCHEMA,
|
||||
cv.Optional(CONF_ON_BLE_ADVERTISE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPBTAdvertiseTrigger),
|
||||
cv.Optional(CONF_MAC_ADDRESS): cv.ensure_list(cv.mac_address),
|
||||
}
|
||||
cv.Optional(CONF_ON_BLE_ADVERTISE): ble_automation.advertise_trigger_schema(
|
||||
ESPBTAdvertiseTrigger
|
||||
),
|
||||
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): ble_device_base.bt_uuid,
|
||||
}
|
||||
cv.Optional(
|
||||
CONF_ON_BLE_SERVICE_DATA_ADVERTISE
|
||||
): ble_automation.uuid_trigger_schema(
|
||||
BLEServiceDataAdvertiseTrigger,
|
||||
{cv.Required(CONF_SERVICE_UUID): ble_device_base.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): ble_device_base.bt_uuid,
|
||||
}
|
||||
): ble_automation.uuid_trigger_schema(
|
||||
BLEManufacturerDataAdvertiseTrigger,
|
||||
{cv.Required(CONF_MANUFACTURER_ID): ble_device_base.bt_uuid},
|
||||
),
|
||||
cv.Optional(CONF_ON_SCAN_END): automation.validate_automation(
|
||||
{cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BLEEndOfScanTrigger)}
|
||||
cv.Optional(CONF_ON_SCAN_END): ble_automation.scan_end_trigger_schema(
|
||||
BLEEndOfScanTrigger
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
# Triggers register as ble_device_base listeners in their constructors; count
|
||||
# them where they are created so the StaticVector cannot be undersized. Shares
|
||||
# the define with register_ble_device() via the core slot-counter factory.
|
||||
_count_listener = cg.slot_counter(ble_device_base.LISTENER_COUNT_DEFINE)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ln882h_ble_tracker.start_scan",
|
||||
StartScanAction,
|
||||
@@ -191,11 +145,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_configured_continuous(scan[CONF_CONTINUOUS]))
|
||||
|
||||
for conf in config.get(CONF_ON_BLE_ADVERTISE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
if (macs := conf.get(CONF_MAC_ADDRESS)) is not None:
|
||||
cg.add(trigger.set_addresses([it.as_hex for it in macs]))
|
||||
await automation.build_automation(trigger, [(ESPBTDeviceConstRef, "x")], conf)
|
||||
_count_listener()
|
||||
await ble_automation.advertise_trigger_to_code(conf, var)
|
||||
|
||||
for trigger_key, uuid_key, setter_prefix in (
|
||||
(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, CONF_SERVICE_UUID, "set_service_uuid"),
|
||||
@@ -206,23 +156,9 @@ async def to_code(config: ConfigType) -> None:
|
||||
),
|
||||
):
|
||||
for conf in config.get(trigger_key, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
uuid = conf[uuid_key]
|
||||
width = _UUID_WIDTHS[len(uuid)]
|
||||
value = (
|
||||
ble_device_base.as_hex(uuid)
|
||||
if width != "128"
|
||||
else ble_device_base.as_reversed_hex_array(uuid)
|
||||
await ble_automation.uuid_trigger_to_code(
|
||||
conf, var, uuid_key, setter_prefix
|
||||
)
|
||||
cg.add(getattr(trigger, f"{setter_prefix}{width}")(value))
|
||||
if (mac := conf.get(CONF_MAC_ADDRESS)) is not None:
|
||||
cg.add(trigger.set_address(mac.as_hex))
|
||||
await automation.build_automation(
|
||||
trigger, [(adv_data_t_const_ref, "x")], conf
|
||||
)
|
||||
_count_listener()
|
||||
|
||||
for conf in config.get(CONF_ON_SCAN_END, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
_count_listener()
|
||||
await ble_automation.scan_end_trigger_to_code(conf, var)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Automation triggers and actions for ln882h_ble_tracker: triggers follow the
|
||||
// esp32_ble_tracker design; only the scan-control actions are
|
||||
// platform-specific.
|
||||
// Scan-control actions for ln882h_ble_tracker. The automation triggers are the
|
||||
// neutral ble_device_base classes (ble_device_base/automation.h).
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -11,9 +10,6 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace esphome::ln882h_ble_tracker {
|
||||
|
||||
template<typename... Ts> class StartScanAction final : public Action<Ts...>, public Parented<LN882HBLETracker> {
|
||||
@@ -46,124 +42,6 @@ template<typename... Ts> class StopScanAction final : public Action<Ts...>, publ
|
||||
void play(const Ts &...x) override { this->parent_->stop_scan(); }
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Automation triggers.
|
||||
//
|
||||
// Each trigger is a ble_device_base::ESPBTDeviceListener registered on the hub —
|
||||
// the same design as esp32_ble_tracker, where the triggers sit in the listener
|
||||
// list and their parse_device() return feeds the "Found device" suppression.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// on_ble_advertise: fires on every BLE advertisement, optionally filtered to one or more MACs.
|
||||
class ESPBTAdvertiseTrigger final : public Trigger<const ble_device_base::ESPBTDevice &>,
|
||||
public ble_device_base::ESPBTDeviceListener {
|
||||
public:
|
||||
explicit ESPBTAdvertiseTrigger(LN882HBLETracker *parent) { parent->register_listener(this); }
|
||||
|
||||
void set_addresses(std::initializer_list<uint64_t> addresses) { this->addresses_ = addresses; }
|
||||
|
||||
bool parse_device(const ble_device_base::ESPBTDevice &device) override {
|
||||
if (!this->addresses_.empty() && std::find(this->addresses_.begin(), this->addresses_.end(),
|
||||
device.address_uint64()) == this->addresses_.end()) {
|
||||
return false;
|
||||
}
|
||||
this->trigger(device);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
FixedVector<uint64_t> addresses_;
|
||||
};
|
||||
|
||||
// on_ble_service_data_advertise: fires when an advertisement contains service
|
||||
// data for the given UUID. Optional single-MAC filter.
|
||||
class BLEServiceDataAdvertiseTrigger final : public Trigger<const ble_device_base::adv_data_t &>,
|
||||
public ble_device_base::ESPBTDeviceListener {
|
||||
public:
|
||||
explicit BLEServiceDataAdvertiseTrigger(LN882HBLETracker *parent) { parent->register_listener(this); }
|
||||
|
||||
void set_service_uuid16(uint64_t uuid) {
|
||||
this->uuid_ = ble_device_base::ESPBTUUID::from_uint16(static_cast<uint16_t>(uuid));
|
||||
}
|
||||
void set_service_uuid32(uint64_t uuid) {
|
||||
this->uuid_ = ble_device_base::ESPBTUUID::from_uint32(static_cast<uint32_t>(uuid));
|
||||
}
|
||||
void set_service_uuid128(const uint8_t *uuid) { this->uuid_ = ble_device_base::ESPBTUUID::from_raw(uuid); }
|
||||
|
||||
void set_address(uint64_t address) {
|
||||
this->address_ = address;
|
||||
this->has_address_ = true;
|
||||
}
|
||||
|
||||
bool parse_device(const ble_device_base::ESPBTDevice &device) override {
|
||||
if (this->has_address_ && device.address_uint64() != this->address_) {
|
||||
return false;
|
||||
}
|
||||
for (const auto &sd : device.get_service_datas()) {
|
||||
if (sd.uuid == this->uuid_) {
|
||||
this->trigger(sd.data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
ble_device_base::ESPBTUUID uuid_{};
|
||||
uint64_t address_{0};
|
||||
bool has_address_{false};
|
||||
};
|
||||
|
||||
// on_ble_manufacturer_data_advertise: fires when an advertisement contains
|
||||
// manufacturer data for the given ID. Optional single-MAC filter.
|
||||
class BLEManufacturerDataAdvertiseTrigger final : public Trigger<const ble_device_base::adv_data_t &>,
|
||||
public ble_device_base::ESPBTDeviceListener {
|
||||
public:
|
||||
explicit BLEManufacturerDataAdvertiseTrigger(LN882HBLETracker *parent) { parent->register_listener(this); }
|
||||
|
||||
void set_manufacturer_uuid16(uint64_t uuid) {
|
||||
this->uuid_ = ble_device_base::ESPBTUUID::from_uint16(static_cast<uint16_t>(uuid));
|
||||
}
|
||||
void set_manufacturer_uuid32(uint64_t uuid) {
|
||||
this->uuid_ = ble_device_base::ESPBTUUID::from_uint32(static_cast<uint32_t>(uuid));
|
||||
}
|
||||
void set_manufacturer_uuid128(const uint8_t *uuid) { this->uuid_ = ble_device_base::ESPBTUUID::from_raw(uuid); }
|
||||
|
||||
void set_address(uint64_t address) {
|
||||
this->address_ = address;
|
||||
this->has_address_ = true;
|
||||
}
|
||||
|
||||
bool parse_device(const ble_device_base::ESPBTDevice &device) override {
|
||||
if (this->has_address_ && device.address_uint64() != this->address_) {
|
||||
return false;
|
||||
}
|
||||
for (const auto &md : device.get_manufacturer_datas()) {
|
||||
if (md.uuid == this->uuid_) {
|
||||
this->trigger(md.data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
ble_device_base::ESPBTUUID uuid_{};
|
||||
uint64_t address_{0};
|
||||
bool has_address_{false};
|
||||
};
|
||||
|
||||
// on_scan_end: fires whenever a scan period ends (duration elapsed or stop_scan
|
||||
// called). A listener whose on_scan_end() hook fires the trigger — never claims
|
||||
// devices (parse_device always returns false).
|
||||
class BLEEndOfScanTrigger final : public Trigger<>, public ble_device_base::ESPBTDeviceListener {
|
||||
public:
|
||||
explicit BLEEndOfScanTrigger(LN882HBLETracker *parent) { parent->register_listener(this); }
|
||||
|
||||
bool parse_device(const ble_device_base::ESPBTDevice &device) override { return false; }
|
||||
void on_scan_end() override { this->trigger(); }
|
||||
};
|
||||
|
||||
} // namespace esphome::ln882h_ble_tracker
|
||||
|
||||
#endif // USE_LIBRETINY
|
||||
|
||||
@@ -4,12 +4,17 @@ esphome:
|
||||
then:
|
||||
- bk72xx_ble_tracker.start_scan:
|
||||
continuous: true
|
||||
# Bare form: restores the configured scan_parameters mode — no
|
||||
# set_continuous emitted (asserted in the codegen test).
|
||||
- bk72xx_ble_tracker.start_scan:
|
||||
- bk72xx_ble_tracker.stop_scan
|
||||
|
||||
bk72xx:
|
||||
board: cb2s
|
||||
|
||||
bk72xx_ble_tracker:
|
||||
scan_parameters:
|
||||
continuous: false
|
||||
on_ble_advertise:
|
||||
- mac_address:
|
||||
- AC:37:43:77:5F:4C
|
||||
|
||||
@@ -39,9 +39,15 @@ def test_trigger_codegen(
|
||||
"set_manufacturer_uuid128((uint8_t*)(const uint8_t[16]){0xCD,0xAB,0xCD,0xAB,"
|
||||
"0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB,0xCD,0xAB})" in main_cpp
|
||||
)
|
||||
# scan-control actions: templatable continuous lambda + parented actions
|
||||
# scan-control actions: templatable continuous lambda + parented actions.
|
||||
# Exactly one set_continuous: the bare start_scan emits none, pinning the
|
||||
# restore-configured-mode divergence from esp32 against a future default=.
|
||||
assert main_cpp.count("->set_continuous(") == 1
|
||||
assert "startscanaction_id->set_continuous(" in main_cpp
|
||||
assert "stopscanaction_id->set_parent(" in main_cpp
|
||||
# scan_parameters continuous: false reaches the YAML-mode setter, not the
|
||||
# runtime override.
|
||||
assert "->set_configured_continuous(false)" in main_cpp
|
||||
# Constructor call, not just the declaration: the parent argument is what
|
||||
# registers the trigger as a listener.
|
||||
assert re.search(
|
||||
|
||||
@@ -13,6 +13,8 @@ ln882x:
|
||||
board: generic-ln882h
|
||||
|
||||
ln882h_ble_tracker:
|
||||
scan_parameters:
|
||||
continuous: false
|
||||
on_ble_advertise:
|
||||
- mac_address:
|
||||
- AC:37:43:77:5F:4C
|
||||
|
||||
@@ -40,10 +40,13 @@ def test_trigger_codegen(
|
||||
assert main_cpp.count("->set_continuous(") == 1
|
||||
assert "startscanaction_id->set_continuous(" in main_cpp
|
||||
assert "stopscanaction_id->set_parent(" in main_cpp
|
||||
# scan_parameters continuous: false reaches the YAML-mode setter, not the
|
||||
# runtime override.
|
||||
assert "->set_configured_continuous(false)" in main_cpp
|
||||
# Constructor call, not just the declaration: the parent argument is what
|
||||
# registers the trigger as a listener.
|
||||
assert re.search(
|
||||
r"new\(\w+\) ln882h_ble_tracker::BLEEndOfScanTrigger\(\w+\)", main_cpp
|
||||
r"new\(\w+\) ble_device_base::BLEEndOfScanTrigger\(\w+\)", main_cpp
|
||||
)
|
||||
|
||||
# Seven triggers register as listeners; an undercount silently drops the
|
||||
|
||||
Reference in New Issue
Block a user