Initial commit: esphome-xtool-ap2 ESPHome component
Control one or two xTool SafetyPro AP2 air purifiers over BLE and expose them to Home Assistant via an ESP32 (the ap2_hub external component): per-slot fan control with live gear sync, runtime-settable flash-persisted MACs, an on-device scanner, M9033 status polling (filter life, serial, firmware), buzzer control, and BLE bonding. Includes the PC debug tool (tools/ap2_ble.py) and the reverse-engineered protocol spec (SPEC.md). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/esp32_ble_client/ble_client_base.h"
|
||||
#include "ap2_protocol.h"
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
#include <string>
|
||||
|
||||
namespace esphome {
|
||||
namespace ap2_hub {
|
||||
|
||||
class AP2Hub;
|
||||
|
||||
/* Transient BLE client the hub uses during a scan: connect to one candidate,
|
||||
* look for the Nordic-UART service, send M99, and confirm an AP2 by its reply.
|
||||
* Every probe ends at ClientState::IDLE, which is the single signal back to the
|
||||
* hub that the prober is free for the next candidate. */
|
||||
class AP2ProbeClient : public esp32_ble_client::BLEClientBase {
|
||||
public:
|
||||
void set_hub(AP2Hub *hub) { this->hub_ = hub; }
|
||||
|
||||
void start_probe(uint64_t mac, esp_ble_addr_type_t type);
|
||||
void abort_probe();
|
||||
|
||||
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void set_state(espbt::ClientState st) override;
|
||||
|
||||
protected:
|
||||
AP2Hub *hub_{nullptr};
|
||||
uint16_t rx_handle_{0};
|
||||
uint16_t tx_handle_{0};
|
||||
uint16_t ccc_handle_{0};
|
||||
bool active_{false};
|
||||
bool confirmed_{false};
|
||||
std::string fw_;
|
||||
};
|
||||
|
||||
} // namespace ap2_hub
|
||||
} // namespace esphome
|
||||
Reference in New Issue
Block a user