#pragma once #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include #include #include namespace esphome { namespace ap2_hub { namespace espbt = esphome::esp32_ble_tracker; /* xTool AP2 Nordic-UART service + the F0F7 / M-code framing, shared by the * control slots and the scan prober. See SPEC.md for the full protocol. */ inline espbt::ESPBTUUID nus_svc() { return espbt::ESPBTUUID::from_raw("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); } inline espbt::ESPBTUUID nus_rx() { return espbt::ESPBTUUID::from_raw("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); } inline espbt::ESPBTUUID nus_tx() { return espbt::ESPBTUUID::from_raw("6e400003-b5a3-f393-e0a9-e50e24dcca9e"); } /* Build an F0F7 frame for an ASCII M-code into f (>= 64 bytes). Returns length. */ inline uint8_t ap2_build_frame(const char *cmd, uint8_t seq, uint8_t *f) { static const uint8_t AP2_ID[3] = {0x45, 0x73, 0x60}; // PURIFIER_V2 device id/prefix uint8_t i = 0, sum = 0, start; f[i++] = 0xF0; start = i; f[i++] = AP2_ID[0]; f[i++] = AP2_ID[1]; f[i++] = AP2_ID[2]; f[i++] = 0x01; f[i++] = seq; for (const char *p = cmd; *p != '\0'; ++p) f[i++] = (uint8_t) *p; f[i++] = 0x0A; for (uint8_t j = start; j < i; j++) sum += f[j]; f[i++] = sum & 0x7F; f[i++] = 0xF7; return i; } /* Printable-ASCII view of a notification payload. */ inline std::string ap2_ascii(const uint8_t *data, uint16_t len) { std::string s; s.reserve(len); for (uint16_t k = 0; k < len; k++) { uint8_t c = data[k]; if (c >= 32 && c < 127) s += (char) c; } return s; } /* If s is an M99 reply, return the firmware string (after 'V', up to a space), * else "". A non-empty result is the definitive "this is an AP2" fingerprint. */ inline std::string ap2_parse_m99_fw(const std::string &s) { auto p = s.find("M99"); if (p == std::string::npos) return ""; auto v = s.find('V', p); if (v == std::string::npos) return ""; auto e = s.find(' ', v); return s.substr(v + 1, e == std::string::npos ? std::string::npos : e - (v + 1)); } /* 0xAABBCCDDEEFF -> "AA:BB:CC:DD:EE:FF"; 0 -> "". */ inline std::string ap2_format_mac(uint64_t mac) { if (mac == 0) return ""; char b[18]; snprintf(b, sizeof(b), "%02X:%02X:%02X:%02X:%02X:%02X", (unsigned) ((mac >> 40) & 0xFF), (unsigned) ((mac >> 32) & 0xFF), (unsigned) ((mac >> 24) & 0xFF), (unsigned) ((mac >> 16) & 0xFF), (unsigned) ((mac >> 8) & 0xFF), (unsigned) (mac & 0xFF)); return b; } } // namespace ap2_hub } // namespace esphome