Files
esphome-xtool-ap2/components/ap2_hub/ap2_fan.cpp
T
mrambossekandClaude Opus 4.8 878f7c9c27 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>
2026-06-22 14:45:11 +02:00

35 lines
1.1 KiB
C++

#include "ap2_fan.h"
#include "esphome/core/log.h"
#include <algorithm>
namespace esphome {
namespace ap2_hub {
static const char *const TAG = "ap2_hub.fan";
void AP2Fan::control(const fan::FanCall &call) {
if (this->hub_ == nullptr || !this->hub_->slot_ready(this->slot_)) {
ESP_LOGD(TAG, "slot %u not ready; command ignored", (unsigned) this->slot_);
return; // leave published state unchanged
}
bool had_state = call.get_state().has_value();
bool had_speed = call.get_speed().has_value();
if (had_state)
this->state = *call.get_state();
if (had_speed)
this->speed = *call.get_speed();
// A speed-only change (e.g. the web_server slider) of 0 means "off", any
// other speed means "on" — keeps the slider's 0 position consistent with the
// on/off toggle. An explicit state in the call always wins.
if (had_speed && !had_state)
this->state = this->speed > 0;
uint8_t gear = this->state ? (uint8_t) std::max(1, this->speed) : 0;
this->hub_->set_gear(this->slot_, gear);
this->publish_state();
}
} // namespace ap2_hub
} // namespace esphome