diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index a7e6af427f..55dc3984b0 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -61,6 +61,7 @@ service APIConnection { rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {} rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {} rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {} + rpc bluetooth_scanner_set_mode(BluetoothScannerSetModeRequest) returns (void) {} rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {} rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {} @@ -1472,6 +1473,37 @@ message BluetoothDeviceClearCacheResponse { int32 error = 3; } +enum BluetoothScannerState { + BLUETOOTH_SCANNER_STATE_IDLE = 0; + BLUETOOTH_SCANNER_STATE_STARTING = 1; + BLUETOOTH_SCANNER_STATE_RUNNING = 2; + BLUETOOTH_SCANNER_STATE_FAILED = 3; + BLUETOOTH_SCANNER_STATE_STOPPING = 4; + BLUETOOTH_SCANNER_STATE_STOPPED = 5; +} + +enum BluetoothScannerMode { + BLUETOOTH_SCANNER_MODE_PASSIVE = 0; + BLUETOOTH_SCANNER_MODE_ACTIVE = 1; +} + +message BluetoothScannerStateResponse { + option(id) = 126; + option(source) = SOURCE_SERVER; + option(ifdef) = "USE_BLUETOOTH_PROXY"; + + BluetoothScannerState state = 1; + BluetoothScannerMode mode = 2; +} + +message BluetoothScannerSetModeRequest { + option(id) = 127; + option(source) = SOURCE_CLIENT; + option(ifdef) = "USE_BLUETOOTH_PROXY"; + + BluetoothScannerMode mode = 1; +} + // ==================== PUSH TO TALK ==================== enum VoiceAssistantSubscribeFlag { VOICE_ASSISTANT_SUBSCRIBE_NONE = 0; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 27db953329..4670aeca63 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1475,6 +1475,11 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_ resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit(); return resp; } + +void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) { + bluetooth_proxy::global_bluetooth_proxy->bluetooth_scanner_set_mode( + msg.mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE); +} #endif #ifdef USE_VOICE_ASSISTANT diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 09534af8dc..3fefe71cbb 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -221,6 +221,7 @@ class APIConnection : public APIServerConnection { void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override; BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free( const SubscribeBluetoothConnectionsFreeRequest &msg) override; + void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override; #endif #ifdef USE_HOMEASSISTANT_TIME diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 45d620715a..90e5bcb548 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -422,6 +422,38 @@ const char *proto_enum_to_string(enums::Bluet } #endif #ifdef HAS_PROTO_MESSAGE_DUMP +template<> const char *proto_enum_to_string(enums::BluetoothScannerState value) { + switch (value) { + case enums::BLUETOOTH_SCANNER_STATE_IDLE: + return "BLUETOOTH_SCANNER_STATE_IDLE"; + case enums::BLUETOOTH_SCANNER_STATE_STARTING: + return "BLUETOOTH_SCANNER_STATE_STARTING"; + case enums::BLUETOOTH_SCANNER_STATE_RUNNING: + return "BLUETOOTH_SCANNER_STATE_RUNNING"; + case enums::BLUETOOTH_SCANNER_STATE_FAILED: + return "BLUETOOTH_SCANNER_STATE_FAILED"; + case enums::BLUETOOTH_SCANNER_STATE_STOPPING: + return "BLUETOOTH_SCANNER_STATE_STOPPING"; + case enums::BLUETOOTH_SCANNER_STATE_STOPPED: + return "BLUETOOTH_SCANNER_STATE_STOPPED"; + default: + return "UNKNOWN"; + } +} +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP +template<> const char *proto_enum_to_string(enums::BluetoothScannerMode value) { + switch (value) { + case enums::BLUETOOTH_SCANNER_MODE_PASSIVE: + return "BLUETOOTH_SCANNER_MODE_PASSIVE"; + case enums::BLUETOOTH_SCANNER_MODE_ACTIVE: + return "BLUETOOTH_SCANNER_MODE_ACTIVE"; + default: + return "UNKNOWN"; + } +} +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::VoiceAssistantSubscribeFlag value) { switch (value) { @@ -6775,6 +6807,61 @@ void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const { out.append("}"); } #endif +bool BluetoothScannerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->state = value.as_enum(); + return true; + } + case 2: { + this->mode = value.as_enum(); + return true; + } + default: + return false; + } +} +void BluetoothScannerStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_enum(1, this->state); + buffer.encode_enum(2, this->mode); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothScannerStateResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothScannerStateResponse {\n"); + out.append(" state: "); + out.append(proto_enum_to_string(this->state)); + out.append("\n"); + + out.append(" mode: "); + out.append(proto_enum_to_string(this->mode)); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothScannerSetModeRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->mode = value.as_enum(); + return true; + } + default: + return false; + } +} +void BluetoothScannerSetModeRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_enum(1, this->mode); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothScannerSetModeRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothScannerSetModeRequest {\n"); + out.append(" mode: "); + out.append(proto_enum_to_string(this->mode)); + out.append("\n"); + out.append("}"); +} +#endif bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 383d566a16..18e4002107 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -169,6 +169,18 @@ enum BluetoothDeviceRequestType : uint32_t { BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5, BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6, }; +enum BluetoothScannerState : uint32_t { + BLUETOOTH_SCANNER_STATE_IDLE = 0, + BLUETOOTH_SCANNER_STATE_STARTING = 1, + BLUETOOTH_SCANNER_STATE_RUNNING = 2, + BLUETOOTH_SCANNER_STATE_FAILED = 3, + BLUETOOTH_SCANNER_STATE_STOPPING = 4, + BLUETOOTH_SCANNER_STATE_STOPPED = 5, +}; +enum BluetoothScannerMode : uint32_t { + BLUETOOTH_SCANNER_MODE_PASSIVE = 0, + BLUETOOTH_SCANNER_MODE_ACTIVE = 1, +}; enum VoiceAssistantSubscribeFlag : uint32_t { VOICE_ASSISTANT_SUBSCRIBE_NONE = 0, VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1, @@ -1742,6 +1754,29 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class BluetoothScannerStateResponse : public ProtoMessage { + public: + enums::BluetoothScannerState state{}; + enums::BluetoothScannerMode mode{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothScannerSetModeRequest : public ProtoMessage { + public: + enums::BluetoothScannerMode mode{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; class SubscribeVoiceAssistantRequest : public ProtoMessage { public: bool subscribe{false}; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 8238bcf96d..dd86c9538a 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -472,6 +472,16 @@ bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const B return this->send_message_(msg, 88); } #endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_scanner_state_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 126); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +#endif #ifdef USE_VOICE_ASSISTANT #endif #ifdef USE_VOICE_ASSISTANT @@ -1212,6 +1222,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, ESP_LOGVV(TAG, "on_noise_encryption_set_key_request: %s", msg.dump().c_str()); #endif this->on_noise_encryption_set_key_request(msg); +#endif + break; + } + case 127: { +#ifdef USE_BLUETOOTH_PROXY + BluetoothScannerSetModeRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_bluetooth_scanner_set_mode_request: %s", msg.dump().c_str()); +#endif + this->on_bluetooth_scanner_set_mode_request(msg); #endif break; } @@ -1705,6 +1726,19 @@ void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request( this->unsubscribe_bluetooth_le_advertisements(msg); } #endif +#ifdef USE_BLUETOOTH_PROXY +void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->bluetooth_scanner_set_mode(msg); +} +#endif #ifdef USE_VOICE_ASSISTANT void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) { if (!this->is_connection_setup()) { diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 4a3a1da8f0..1012d8a65b 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -234,6 +234,12 @@ class APIServerConnectionBase : public ProtoService { #ifdef USE_BLUETOOTH_PROXY bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg); #endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){}; +#endif #ifdef USE_VOICE_ASSISTANT virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){}; #endif @@ -440,6 +446,9 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_BLUETOOTH_PROXY virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0; #endif +#ifdef USE_BLUETOOTH_PROXY + virtual void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) = 0; +#endif #ifdef USE_VOICE_ASSISTANT virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0; #endif @@ -551,6 +560,9 @@ class APIServerConnection : public APIServerConnectionBase { void on_unsubscribe_bluetooth_le_advertisements_request( const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override; #endif +#ifdef USE_BLUETOOTH_PROXY + void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override; +#endif #ifdef USE_VOICE_ASSISTANT void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override; #endif diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 03213432cd..e40f4e5dcc 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -25,6 +25,22 @@ std::vector get_128bit_uuid_vec(esp_bt_uuid_t uuid_source) { BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; } +void BluetoothProxy::setup() { + this->parent_->add_scanner_state_callback([this](esp32_ble_tracker::ScannerState state) { + if (this->api_connection_ != nullptr) { + this->send_bluetooth_scanner_state_(state); + } + }); +} + +void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state) { + api::BluetoothScannerStateResponse resp; + resp.state = static_cast(state); + resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE + : api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE; + this->api_connection_->send_bluetooth_scanner_state_response(resp); +} + bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || this->raw_advertisements_) return false; @@ -453,6 +469,8 @@ void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection this->api_connection_ = api_connection; this->raw_advertisements_ = flags & BluetoothProxySubscriptionFlag::SUBSCRIPTION_RAW_ADVERTISEMENTS; this->parent_->recalculate_advertisement_parser_types(); + + this->send_bluetooth_scanner_state_(this->parent_->get_scanner_state()); } void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connection) { @@ -525,6 +543,17 @@ void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_e this->api_connection_->send_bluetooth_device_unpairing_response(call); } +void BluetoothProxy::bluetooth_scanner_set_mode(bool active) { + if (this->parent_->get_scan_active() == active) { + return; + } + ESP_LOGD(TAG, "Setting scanner mode to %s", active ? "active" : "passive"); + this->parent_->set_scan_active(active); + this->parent_->stop_scan(); + this->parent_->set_scan_continuous( + true); // Set this to true to automatically start scanning again when it has cleaned up. +} + BluetoothProxy *global_bluetooth_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace bluetooth_proxy diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index e0345ff248..de24165fe8 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -41,6 +41,7 @@ enum BluetoothProxyFeature : uint32_t { FEATURE_PAIRING = 1 << 3, FEATURE_CACHE_CLEARING = 1 << 4, FEATURE_RAW_ADVERTISEMENTS = 1 << 5, + FEATURE_STATE_AND_MODE = 1 << 6, }; enum BluetoothProxySubscriptionFlag : uint32_t { @@ -53,6 +54,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override; void dump_config() override; + void setup() override; void loop() override; esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override; @@ -84,6 +86,8 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com void send_device_unpairing(uint64_t address, bool success, esp_err_t error = ESP_OK); void send_device_clear_cache(uint64_t address, bool success, esp_err_t error = ESP_OK); + void bluetooth_scanner_set_mode(bool active); + static void uint64_to_bd_addr(uint64_t address, esp_bd_addr_t bd_addr) { bd_addr[0] = (address >> 40) & 0xff; bd_addr[1] = (address >> 32) & 0xff; @@ -107,6 +111,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com uint32_t flags = 0; flags |= BluetoothProxyFeature::FEATURE_PASSIVE_SCAN; flags |= BluetoothProxyFeature::FEATURE_RAW_ADVERTISEMENTS; + flags |= BluetoothProxyFeature::FEATURE_STATE_AND_MODE; if (this->active_) { flags |= BluetoothProxyFeature::FEATURE_ACTIVE_CONNECTIONS; flags |= BluetoothProxyFeature::FEATURE_REMOTE_CACHING; @@ -124,6 +129,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com protected: void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device); + void send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state); BluetoothConnection *get_connection_(uint64_t address, bool reserve); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 34d4e6727a..0dc0f58fa2 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -245,7 +245,7 @@ void ESP32BLETracker::stop_scan_() { return; } this->cancel_timeout("scan"); - this->scanner_state_ = ScannerState::STOPPING; + this->set_scanner_state_(ScannerState::STOPPING); esp_err_t err = esp_ble_gap_stop_scanning(); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gap_stop_scanning failed: %d", err); @@ -272,7 +272,7 @@ void ESP32BLETracker::start_scan_(bool first) { } return; } - this->scanner_state_ = ScannerState::STARTING; + this->set_scanner_state_(ScannerState::STARTING); ESP_LOGD(TAG, "Starting scan, set scanner state to STARTING."); if (!first) { for (auto *listener : this->listeners_) @@ -315,7 +315,7 @@ void ESP32BLETracker::end_of_scan_() { for (auto *listener : this->listeners_) listener->on_scan_end(); - this->scanner_state_ = ScannerState::IDLE; + this->set_scanner_state_(ScannerState::IDLE); } void ESP32BLETracker::register_client(ESPBTClient *client) { @@ -398,9 +398,9 @@ void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble } if (param.status == ESP_BT_STATUS_SUCCESS) { this->scan_start_fail_count_ = 0; - this->scanner_state_ = ScannerState::RUNNING; + this->set_scanner_state_(ScannerState::RUNNING); } else { - this->scanner_state_ = ScannerState::FAILED; + this->set_scanner_state_(ScannerState::FAILED); if (this->scan_start_fail_count_ != std::numeric_limits::max()) { this->scan_start_fail_count_++; } @@ -422,7 +422,7 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_ ESP_LOGE(TAG, "Scan was stopped when stop complete."); } } - this->scanner_state_ = ScannerState::STOPPED; + this->set_scanner_state_(ScannerState::STOPPED); } void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { @@ -449,7 +449,7 @@ void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_re ESP_LOGE(TAG, "Scan was stopped when scan completed."); } } - this->scanner_state_ = ScannerState::STOPPED; + this->set_scanner_state_(ScannerState::STOPPED); } } @@ -460,6 +460,11 @@ void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i } } +void ESP32BLETracker::set_scanner_state_(ScannerState state) { + this->scanner_state_ = state; + this->scanner_state_callbacks_.call(state); +} + ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); } optional ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) { if (!data.uuid.contains(0x4C, 0x00)) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 6ca763db07..ca2e53c343 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -218,6 +218,7 @@ class ESP32BLETracker : public Component, void set_scan_interval(uint32_t scan_interval) { scan_interval_ = scan_interval; } void set_scan_window(uint32_t scan_window) { scan_window_ = scan_window; } void set_scan_active(bool scan_active) { scan_active_ = scan_active; } + bool get_scan_active() const { return scan_active_; } void set_scan_continuous(bool scan_continuous) { scan_continuous_ = scan_continuous; } /// Setup the FreeRTOS task and the Bluetooth stack. @@ -241,6 +242,11 @@ class ESP32BLETracker : public Component, void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; void ble_before_disabled_event_handler() override; + void add_scanner_state_callback(std::function &&callback) { + this->scanner_state_callbacks_.add(std::move(callback)); + } + ScannerState get_scanner_state() const { return this->scanner_state_; } + protected: void stop_scan_(); /// Start a single scan by setting up the parameters and doing some esp-idf calls. @@ -255,6 +261,8 @@ class ESP32BLETracker : public Component, void gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m); /// Called when a `ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT` event is received. void gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m); + /// Called to set the scanner state. Will also call callbacks to let listeners know when state is changed. + void set_scanner_state_(ScannerState state); int app_id_{0}; @@ -273,6 +281,7 @@ class ESP32BLETracker : public Component, bool scan_continuous_; bool scan_active_; ScannerState scanner_state_{ScannerState::IDLE}; + CallbackManager scanner_state_callbacks_; bool ble_was_disabled_{true}; bool raw_advertisements_{false}; bool parse_advertisements_{false};