[ln882h_ble_tracker] Automation triggers and actions (#17778)

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Edvard Filistovič
2026-08-05 09:30:46 -05:00
committed by GitHub
co-authored by pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> J. Nick Koston
parent 64f0e38a51
commit d548454dbe
7 changed files with 517 additions and 3 deletions
@@ -2,6 +2,7 @@
top of the ln882h_ble controller. With continuous: false nothing scans until
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
@@ -12,10 +13,19 @@ 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"]
@@ -26,6 +36,31 @@ LN882HBLETracker = ln882h_ble_tracker_ns.class_(
"LN882HBLETracker", ble_device_base.BLEHub, cg.Component
)
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()
)
# LN882H SDK reference scan rate: 100 ms interval / 50 ms window (50 % duty).
SCAN_PARAMETERS_SCHEMA = ble_device_base.scan_parameters_schema(
@@ -33,15 +68,108 @@ 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_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_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,
}
),
cv.Optional(CONF_ON_SCAN_END): automation.validate_automation(
{cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(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,
cv.Schema(
{
cv.GenerateID(): cv.use_id(LN882HBLETracker),
cv.Optional(CONF_CONTINUOUS): cv.templatable(cv.boolean),
}
),
synchronous=True,
)
async def start_scan_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: list,
) -> cg.MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
if (continuous := config.get(CONF_CONTINUOUS)) is not None:
template_ = await cg.templatable(continuous, args, cg.bool_)
cg.add(var.set_continuous(template_))
return var
@automation.register_action(
"ln882h_ble_tracker.stop_scan",
StopScanAction,
automation.maybe_simple_id(
cv.Schema(
{
cv.GenerateID(): cv.use_id(LN882HBLETracker),
}
)
),
synchronous=True,
)
async def stop_scan_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: list,
) -> cg.MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
@@ -60,4 +188,41 @@ async def to_code(config: ConfigType) -> None:
cg.add(var.set_scan_window(ble_device_base.to_ble_units(scan[CONF_WINDOW])))
cg.add(var.set_scan_duration(scan[CONF_DURATION].total_milliseconds))
cg.add(var.set_scan_active(scan[CONF_ACTIVE]))
cg.add(var.set_scan_continuous(scan[CONF_CONTINUOUS]))
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()
for trigger_key, uuid_key, setter_prefix in (
(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, CONF_SERVICE_UUID, "set_service_uuid"),
(
CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE,
CONF_MANUFACTURER_ID,
"set_manufacturer_uuid",
),
):
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)
)
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()
@@ -0,0 +1,169 @@
// Automation triggers and actions for ln882h_ble_tracker: triggers follow the
// esp32_ble_tracker design; only the scan-control actions are
// platform-specific.
#pragma once
#ifdef USE_LIBRETINY
#include "ln882h_ble_tracker.h"
#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> {
public:
TEMPLATABLE_VALUE(bool, continuous)
void play(const Ts &...x) override {
// With continuous: set, the action wins. Without it, the configured value
// is used - stop_scan() clears the runtime flag permanently, so a bare
// stop_scan/start_scan pair would otherwise never resume continuous mode.
const bool want =
this->continuous_.has_value() ? this->continuous_.value(x...) : this->parent_->configured_continuous();
if (this->parent_->scan_running()) {
// Same mode on a running scan is a no-op (esp32 parity): re-anchoring
// the duration window here would let a repeated action keep a one-shot
// scan alive forever. A real mode switch re-anchors so a change to
// one-shot runs a full duration from now.
if (want != this->parent_->scan_continuous()) {
this->parent_->set_scan_continuous(want);
this->parent_->restart_scan_duration();
}
return;
}
this->parent_->set_scan_continuous(want);
this->parent_->start_scan();
}
};
template<typename... Ts> class StopScanAction final : public Action<Ts...>, public Parented<LN882HBLETracker> {
public:
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
@@ -22,7 +22,10 @@ void LN882HBLETracker::setup() {
// Receive the controller's scan reports; the controller queues them from the
// rw task and delivers here on the main task.
this->parent_->register_scan_listener(this);
if (!this->scan_continuous_) {
// scan_running_ check: an on_boot start_scan action (priority 600) runs
// before this setup() (200) and enable_loop() is a no-op pre-setup — parking
// the loop here would strand that already-running scan.
if (!this->scan_continuous_ && !this->scan_running_ && !this->pending_start_) {
// Say so once: with continuous: false nothing scans until an explicit
// start_scan() — silence here reads as a broken scanner.
ESP_LOGD(TAG, "Scanning not started (continuous: false) - waiting for an explicit start_scan()");
@@ -61,6 +64,14 @@ void LN882HBLETracker::on_ota_global_state(ota::OTAState state, float progress,
#endif // USE_OTA_STATE_LISTENER
void LN882HBLETracker::loop() {
if (this->pending_start_) {
// A start_scan latched before the controller's setup(); safe now — loop()
// only runs after every component set up.
this->pending_start_ = false;
if (!this->scan_running_) {
this->start_scan_();
}
}
// Flush pending scannable advertisements whose scan response never arrived
// (device didn't answer / frame lost) — delivered unmerged after the timeout.
// Main-task only, like every consumer of pending_adv_.
@@ -263,12 +274,34 @@ void LN882HBLETracker::process_adv_(const uint8_t *mac, int8_t rssi, uint8_t add
void LN882HBLETracker::start_scan() {
// Mirrors esp32_ble_tracker::start_scan(): caller sets scan_continuous_ via
// set_scan_continuous() first, then calls start_scan() to begin scanning.
if (!this->parent_->is_ready()) {
// An on_boot automation (priority 600) runs before the controller's
// setup() has resolved the BLE MAC; scan_start() now would rw_init() the
// all-zero address and bring BLE up before WiFi. Latch; loop() applies
// the start once every setup() has run.
this->pending_start_ = true;
return;
}
if (!this->scan_running_) {
this->start_scan_();
}
}
void LN882HBLETracker::restart_scan_duration() {
if (!this->scan_running_)
return;
// Re-anchor only the one-shot duration clock. scan_period_start_ (the
// continuous-mode on_scan_end period) is deliberately left alone: a
// start_scan action fired more often than scan_duration_ would otherwise
// suppress on_scan_end indefinitely — and absence detection (ble_rssi's NAN
// publish) rides on that period.
this->scan_start_time_ = millis();
}
void LN882HBLETracker::stop_scan() {
// Cancel a start latched before the controller's setup(); without this an
// on_boot start_scan/stop_scan pair would still start at the first loop().
this->pending_start_ = false;
this->scan_continuous_ = false;
this->stop_scan_();
}
@@ -308,7 +341,10 @@ void LN882HBLETracker::stop_scan_() {
// scanner failing to come back up.
ESP_LOGD(TAG, "BLE scan stopped");
this->end_scan_period_(millis()); // also resets the period clock so on_scan_end does not double-fire
if (!this->scan_continuous_) {
// scan_running_ re-check: an on_scan_end automation runs synchronously inside
// end_scan_period_() and may have called start_scan() — parking the loop then
// would leave the radio scanning with no period timing or pending-adv sweep.
if (!this->scan_continuous_ && !this->scan_running_) {
// Nothing left to time; start_scan_() re-enables the loop.
this->disable_loop();
}
@@ -52,7 +52,22 @@ class LN882HBLETracker : public Component,
void set_scan_interval(uint16_t scan_interval) { this->scan_interval_ = scan_interval; }
void set_scan_window(uint16_t scan_window) { this->scan_window_ = scan_window; }
void set_scan_duration(uint32_t scan_duration) { this->scan_duration_ = scan_duration; }
/// Set from YAML (scan_parameters.continuous); also the value
/// configured_continuous() reports and a bare start_scan action restores.
void set_configured_continuous(bool scan_continuous) {
this->scan_continuous_ = scan_continuous;
this->scan_continuous_configured_ = scan_continuous;
}
/// Runtime control (esp32_ble_tracker lambda parity): does not change the
/// configured value, so configured_continuous() still reports what YAML
/// asked for.
void set_scan_continuous(bool scan_continuous) { this->scan_continuous_ = scan_continuous; }
bool scan_continuous() const { return this->scan_continuous_; }
bool configured_continuous() const { return this->scan_continuous_configured_; }
/// Re-anchor the one-shot duration clock of a running scan to now — used
/// when an action changes the scan mode without stopping the radio. The
/// continuous-mode on_scan_end period is deliberately not touched.
void restart_scan_duration();
// ---- Public scan control ----
// Mirrors esp32_ble_tracker: set_scan_continuous() + start_scan() / stop_scan().
@@ -125,6 +140,8 @@ class LN882HBLETracker : public Component,
uint16_t scan_window_{80}; // 80 × 0.625 ms = 50 ms (SDK SCAN_WINDOW_DEF; 50/100 = 50 %)
uint32_t scan_duration_{300000};
bool scan_continuous_{true};
bool pending_start_{false}; // start_scan() latched before the controller's setup()
bool scan_continuous_configured_{true}; // YAML value; stop_scan() must not lose it
#ifdef USE_OTA_STATE_LISTENER
bool scan_continuous_before_ota_{false}; // continuous mode saved at OTA start, restored on OTA failure
bool scan_running_before_ota_{false}; // one-shot scan running at OTA start, restarted on OTA failure
@@ -0,0 +1,42 @@
esphome:
name: ln-trigger-codegen
on_boot:
then:
- ln882h_ble_tracker.start_scan:
continuous: true
# Bare form: restores the configured scan_parameters mode — no
# set_continuous emitted (asserted in the codegen test).
- ln882h_ble_tracker.start_scan:
- ln882h_ble_tracker.stop_scan:
ln882x:
board: generic-ln882h
ln882h_ble_tracker:
on_ble_advertise:
- mac_address:
- AC:37:43:77:5F:4C
- 11:22:33:44:55:66
then:
- lambda: 'ESP_LOGD("t", "%s", x.address_str().c_str());'
on_ble_service_data_advertise:
- service_uuid: ABCDABCD-ABCD-ABCD-ABCD-ABCDABCDABCD
mac_address: AC:37:43:77:5F:4C
then:
- lambda: 'ESP_LOGD("t", "%zu", x.size());'
- service_uuid: ABCDABCD
then:
- lambda: 'ESP_LOGD("t", "%zu", x.size());'
on_ble_manufacturer_data_advertise:
- manufacturer_id: ABCD
then:
- lambda: 'ESP_LOGD("t", "%zu", x.size());'
- manufacturer_id: ABCDABCD
then:
- lambda: 'ESP_LOGD("t", "%zu", x.size());'
- manufacturer_id: ABCDABCD-ABCD-ABCD-ABCD-ABCDABCDABCD
then:
- lambda: 'ESP_LOGD("t", "%zu", x.size());'
on_scan_end:
- then:
- lambda: 'ESP_LOGD("t", "end");'
@@ -0,0 +1,52 @@
"""Codegen tests for the tracker automations: the generated main is the
automated check on the setter calls and the listener accounting (the
test.ln882x-ard.yaml compile fixture proves linkage, not codegen shape)."""
from collections.abc import Callable
from pathlib import Path
import re
from esphome.components import ble_device_base
from tests.component_tests.helpers import get_define_value
def test_trigger_codegen(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
main_cpp = generate_main(component_config_path("test_automations.yaml"))
# on_ble_advertise: multi-mac filter (two addresses in one initializer list)
assert "set_addresses({0xAC3743775F4CULL, 0x112233445566ULL})" in main_cpp
# 128-bit service uuid goes out reversed (BLE wire order); single-mac filter
assert (
"set_service_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
)
assert "set_address(0xAC3743775F4CULL)" in main_cpp
# 32-bit middle branch of the width dispatch
assert "set_service_uuid32(0xABCDABCDULL)" in main_cpp
# All three manufacturer widths: getattr() builds these names as strings,
# so a misspelling only ever fails here.
assert "set_manufacturer_uuid16(0xABCDULL)" in main_cpp
assert "set_manufacturer_uuid32(0xABCDABCDULL)" in main_cpp
assert (
"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.
# 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
# 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
)
# Seven triggers register as listeners; an undercount silently drops the
# last trigger at runtime (StaticVector::push_back past capacity), so the
# define is the assertion that matters most.
assert get_define_value(ble_device_base.LISTENER_COUNT_DEFINE) == "7"
@@ -0,0 +1,33 @@
packages:
ln882h_ble_tracker: !include common.yaml
esphome:
on_boot:
then:
- ln882h_ble_tracker.start_scan
- ln882h_ble_tracker.start_scan:
continuous: true
- ln882h_ble_tracker.start_scan:
continuous: !lambda return false;
- ln882h_ble_tracker.stop_scan
ln882h_ble_tracker:
on_ble_advertise:
- mac_address: AC:37:43:77:5F:4C
then:
- lambda: |-
ESP_LOGD("main", "The device address is %s", x.address_str().c_str());
on_ble_service_data_advertise:
- service_uuid: ABCD
then:
- lambda: |-
ESP_LOGD("main", "Length of service data is %zu", x.size());
on_ble_manufacturer_data_advertise:
- manufacturer_id: ABCD
then:
- lambda: |-
ESP_LOGD("main", "Length of manufacturer data is %zu", x.size());
on_scan_end:
- then:
- lambda: |-
ESP_LOGD("main", "Scan ended");