Files
2026-10-07 07:09:35 -10:00

77 lines
3.8 KiB
C++

#pragma once
#include "esphome/components/xiaomi_mccgq02hl/xiaomi_mccgq02hl.h"
#include <array>
#include <vector>
namespace esphome::xiaomi_mccgq02hl::testing {
using Frame = std::array<uint8_t, 22>;
using PlainFrame = std::array<uint8_t, 15>;
static constexpr uint64_t ADDRESS = 0xE4AAEC010203ULL;
static constexpr uint64_t OTHER_ADDRESS = 0xE4AAEC010204ULL;
static constexpr const char *BINDKEY = "48403ebe2d385db8d0c187f81e62cb64";
// MiBeacon v5 frames (MAC included, AES-CCM encrypted) built with Python's cryptography AESCCM using ADDRESS and
// BINDKEY. Each frame has a different frame count.
// Door open (object 0x0019)
static constexpr Frame OPEN = {0x58, 0x59, 0x8b, 0x09, 0x01, 0x03, 0x02, 0x01, 0xec, 0xaa, 0xe4,
0xe4, 0xd9, 0x75, 0x9f, 0x01, 0x00, 0x00, 0x7e, 0x61, 0x36, 0x49};
// Door closed
static constexpr Frame CLOSED = {0x58, 0x59, 0x8b, 0x09, 0x02, 0x03, 0x02, 0x01, 0xec, 0xaa, 0xe4,
0x52, 0x9c, 0x58, 0x6e, 0x01, 0x00, 0x00, 0x69, 0x45, 0x8f, 0xec};
// Door left open past the device timeout (object 0x1019)
static constexpr Frame LEFT_OPEN = {0x58, 0x59, 0x8b, 0x09, 0x03, 0x03, 0x02, 0x01, 0xec, 0xaa, 0xe4,
0x38, 0xe3, 0xbd, 0x82, 0x01, 0x00, 0x00, 0x74, 0x97, 0x75, 0x9d};
// Device reset, must be ignored
static constexpr Frame DEVICE_RESET = {0x58, 0x59, 0x8b, 0x09, 0x04, 0x03, 0x02, 0x01, 0xec, 0xaa, 0xe4,
0x4b, 0x18, 0xdb, 0x8e, 0x01, 0x00, 0x00, 0x48, 0xa6, 0x52, 0x99};
// Light above threshold (object 0x1018)
static constexpr Frame BRIGHT = {0x58, 0x59, 0x8b, 0x09, 0x05, 0x03, 0x02, 0x01, 0xec, 0xaa, 0xe4,
0xe3, 0x96, 0x1c, 0xa4, 0x01, 0x00, 0x00, 0x4b, 0x43, 0xca, 0x14};
// Light below threshold (object 0x0018)
static constexpr Frame DARK = {0x58, 0x59, 0x8b, 0x09, 0x06, 0x03, 0x02, 0x01, 0xec, 0xaa, 0xe4,
0x78, 0x07, 0x43, 0xb5, 0x01, 0x00, 0x00, 0x69, 0x62, 0x88, 0x89};
// Battery 87 % (object 0x100A)
static constexpr Frame BATTERY = {0x58, 0x59, 0x8b, 0x09, 0x07, 0x03, 0x02, 0x01, 0xec, 0xaa, 0xe4,
0xdc, 0x88, 0xf2, 0xd0, 0x01, 0x00, 0x00, 0x39, 0xe8, 0x96, 0x08};
// OPEN with a corrupted MIC (same frame count as OPEN)
static constexpr Frame BAD_MIC = {0x58, 0x59, 0x8b, 0x09, 0x01, 0x03, 0x02, 0x01, 0xec, 0xaa, 0xe4,
0xe4, 0xd9, 0x75, 0x9f, 0x01, 0x00, 0x00, 0x7e, 0x61, 0x36, 0xb6};
// OPEN sent without encryption (MAC included), which must be rejected
static constexpr PlainFrame PLAINTEXT_OPEN = {0x50, 0x59, 0x8b, 0x09, 0x09, 0x03, 0x02, 0x01,
0xec, 0xaa, 0xe4, 0x19, 0x00, 0x01, 0x00};
template<size_t N> ble_device_base::ESPBTDevice advert(uint64_t address, const std::array<uint8_t, N> &frame) {
// Service data AD structure for UUID 0xFE95
std::vector<uint8_t> adv = {static_cast<uint8_t>(frame.size() + 3), 0x16, 0x95, 0xFE};
adv.insert(adv.end(), frame.begin(), frame.end());
uint8_t mac[6];
for (size_t i = 0; i < 6; i++)
mac[i] = static_cast<uint8_t>(address >> (i * 8));
ble_device_base::ESPBTDevice device;
device.from_scan_result(mac, -60, 0, adv.data(), static_cast<uint16_t>(adv.size()));
return device;
}
struct Harness {
Harness() {
this->sensor.set_address(ADDRESS);
this->sensor.set_bindkey(BINDKEY);
this->sensor.set_light(&this->light);
this->sensor.set_battery_level(&this->battery_level);
// The door sensor starts as "closed", so count changes to see whether anything was published
this->sensor.add_full_state_callback([this](optional<bool>, optional<bool>) { this->door_changes++; });
}
XiaomiMCCGQ02HL sensor;
binary_sensor::BinarySensor light;
sensor::Sensor battery_level;
int door_changes{0};
};
} // namespace esphome::xiaomi_mccgq02hl::testing