mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 09:17:46 +01:00
Fix Xiaomi merged packet parsing (#1293)
* Fix Xiaomi merged packet parsing solves #1500 * renamed variables and updated payload and value checking * renamed function and parameter * add function to header * changed log message
This commit is contained in:
parent
221ef07c8b
commit
540c62061d
2 changed files with 97 additions and 69 deletions
|
@ -12,6 +12,75 @@ namespace xiaomi_ble {
|
||||||
|
|
||||||
static const char *TAG = "xiaomi_ble";
|
static const char *TAG = "xiaomi_ble";
|
||||||
|
|
||||||
|
bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_length, XiaomiParseResult &result) {
|
||||||
|
// motion detection, 1 byte, 8-bit unsigned integer
|
||||||
|
if ((value_type == 0x03) && (value_length == 1)) {
|
||||||
|
result.has_motion = (data[0]) ? true : false;
|
||||||
|
}
|
||||||
|
// temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
|
||||||
|
else if ((value_type == 0x04) && (value_length == 2)) {
|
||||||
|
const int16_t temperature = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
result.temperature = temperature / 10.0f;
|
||||||
|
}
|
||||||
|
// humidity, 2 bytes, 16-bit signed integer (LE), 0.1 %
|
||||||
|
else if ((value_type == 0x06) && (value_length == 2)) {
|
||||||
|
const int16_t humidity = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
result.humidity = humidity / 10.0f;
|
||||||
|
}
|
||||||
|
// illuminance (+ motion), 3 bytes, 24-bit unsigned integer (LE), 1 lx
|
||||||
|
else if (((value_type == 0x07) || (value_type == 0x0F)) && (value_length == 3)) {
|
||||||
|
const uint32_t illuminance = uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16);
|
||||||
|
result.illuminance = illuminance;
|
||||||
|
result.is_light = (illuminance == 100) ? true : false;
|
||||||
|
if (value_type == 0x0F)
|
||||||
|
result.has_motion = true;
|
||||||
|
}
|
||||||
|
// soil moisture, 1 byte, 8-bit unsigned integer, 1 %
|
||||||
|
else if ((value_type == 0x08) && (value_length == 1)) {
|
||||||
|
result.moisture = data[0];
|
||||||
|
}
|
||||||
|
// conductivity, 2 bytes, 16-bit unsigned integer (LE), 1 µS/cm
|
||||||
|
else if ((value_type == 0x09) && (value_length == 2)) {
|
||||||
|
const uint16_t conductivity = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
result.conductivity = conductivity;
|
||||||
|
}
|
||||||
|
// battery, 1 byte, 8-bit unsigned integer, 1 %
|
||||||
|
else if ((value_type == 0x0A) && (value_length == 1)) {
|
||||||
|
result.battery_level = data[0];
|
||||||
|
}
|
||||||
|
// temperature + humidity, 4 bytes, 16-bit signed integer (LE) each, 0.1 °C, 0.1 %
|
||||||
|
else if ((value_type == 0x0D) && (value_length == 4)) {
|
||||||
|
const int16_t temperature = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
const int16_t humidity = uint16_t(data[2]) | (uint16_t(data[3]) << 8);
|
||||||
|
result.temperature = temperature / 10.0f;
|
||||||
|
result.humidity = humidity / 10.0f;
|
||||||
|
}
|
||||||
|
// formaldehyde, 2 bytes, 16-bit unsigned integer (LE), 0.01 mg / m3
|
||||||
|
else if ((value_type == 0x10) && (value_length == 2)) {
|
||||||
|
const uint16_t formaldehyde = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
||||||
|
result.formaldehyde = formaldehyde / 100.0f;
|
||||||
|
}
|
||||||
|
// on/off state, 1 byte, 8-bit unsigned integer
|
||||||
|
else if ((value_type == 0x12) && (value_length == 1)) {
|
||||||
|
result.is_active = (data[0]) ? true : false;
|
||||||
|
}
|
||||||
|
// mosquito tablet, 1 byte, 8-bit unsigned integer, 1 %
|
||||||
|
else if ((value_type == 0x13) && (value_length == 1)) {
|
||||||
|
result.tablet = data[0];
|
||||||
|
}
|
||||||
|
// idle time since last motion, 4 byte, 32-bit unsigned integer, 1 min
|
||||||
|
else if ((value_type == 0x17) && (value_length == 4)) {
|
||||||
|
const uint32_t idle_time =
|
||||||
|
uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16) | (uint32_t(data[2]) << 24);
|
||||||
|
result.idle_time = idle_time / 60.0f;
|
||||||
|
result.has_motion = (idle_time) ? false : true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult &result) {
|
bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult &result) {
|
||||||
result.has_encryption = (message[0] & 0x08) ? true : false; // update encryption status
|
result.has_encryption = (message[0] & 0x08) ? true : false; // update encryption status
|
||||||
if (result.has_encryption) {
|
if (result.has_encryption) {
|
||||||
|
@ -25,81 +94,39 @@ bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult
|
||||||
// Byte 2: length
|
// Byte 2: length
|
||||||
// Byte 3..3+len-1: data point value
|
// Byte 3..3+len-1: data point value
|
||||||
|
|
||||||
const uint8_t *raw = message.data() + result.raw_offset;
|
const uint8_t *payload = message.data() + result.raw_offset;
|
||||||
const uint8_t *data = raw + 3;
|
uint8_t payload_length = message.size() - result.raw_offset;
|
||||||
const uint8_t data_length = raw[2];
|
uint8_t payload_offset = 0;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
if ((data_length < 1) || (data_length > 4)) {
|
if (payload_length < 4) {
|
||||||
ESP_LOGVV(TAG, "parse_xiaomi_message(): payload has wrong size (%d)!", data_length);
|
ESP_LOGVV(TAG, "parse_xiaomi_message(): payload has wrong size (%d)!", payload_length);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// motion detection, 1 byte, 8-bit unsigned integer
|
while (payload_length > 0) {
|
||||||
if ((raw[0] == 0x03) && (data_length == 1)) {
|
if (payload[payload_offset + 1] != 0x10) {
|
||||||
result.has_motion = (data[0]) ? true : false;
|
ESP_LOGVV(TAG, "parse_xiaomi_message(): fixed byte not found, stop parsing residual data.");
|
||||||
}
|
break;
|
||||||
// temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
|
|
||||||
else if ((raw[0] == 0x04) && (data_length == 2)) {
|
|
||||||
const int16_t temperature = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
|
||||||
result.temperature = temperature / 10.0f;
|
|
||||||
}
|
|
||||||
// humidity, 2 bytes, 16-bit signed integer (LE), 0.1 %
|
|
||||||
else if ((raw[0] == 0x06) && (data_length == 2)) {
|
|
||||||
const int16_t humidity = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
|
||||||
result.humidity = humidity / 10.0f;
|
|
||||||
}
|
|
||||||
// illuminance (+ motion), 3 bytes, 24-bit unsigned integer (LE), 1 lx
|
|
||||||
else if (((raw[0] == 0x07) || (raw[0] == 0x0F)) && (data_length == 3)) {
|
|
||||||
const uint32_t illuminance = uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16);
|
|
||||||
result.illuminance = illuminance;
|
|
||||||
result.is_light = (illuminance == 100) ? true : false;
|
|
||||||
if (raw[0] == 0x0F)
|
|
||||||
result.has_motion = true;
|
|
||||||
}
|
|
||||||
// soil moisture, 1 byte, 8-bit unsigned integer, 1 %
|
|
||||||
else if ((raw[0] == 0x08) && (data_length == 1)) {
|
|
||||||
result.moisture = data[0];
|
|
||||||
}
|
|
||||||
// conductivity, 2 bytes, 16-bit unsigned integer (LE), 1 µS/cm
|
|
||||||
else if ((raw[0] == 0x09) && (data_length == 2)) {
|
|
||||||
const uint16_t conductivity = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
|
||||||
result.conductivity = conductivity;
|
|
||||||
}
|
|
||||||
// battery, 1 byte, 8-bit unsigned integer, 1 %
|
|
||||||
else if ((raw[0] == 0x0A) && (data_length == 1)) {
|
|
||||||
result.battery_level = data[0];
|
|
||||||
}
|
|
||||||
// temperature + humidity, 4 bytes, 16-bit signed integer (LE) each, 0.1 °C, 0.1 %
|
|
||||||
else if ((raw[0] == 0x0D) && (data_length == 4)) {
|
|
||||||
const int16_t temperature = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
|
||||||
const int16_t humidity = uint16_t(data[2]) | (uint16_t(data[3]) << 8);
|
|
||||||
result.temperature = temperature / 10.0f;
|
|
||||||
result.humidity = humidity / 10.0f;
|
|
||||||
}
|
|
||||||
// formaldehyde, 2 bytes, 16-bit unsigned integer (LE), 0.01 mg / m3
|
|
||||||
else if ((raw[0] == 0x10) && (data_length == 2)) {
|
|
||||||
const uint16_t formaldehyde = uint16_t(data[0]) | (uint16_t(data[1]) << 8);
|
|
||||||
result.formaldehyde = formaldehyde / 100.0f;
|
|
||||||
}
|
|
||||||
// on/off state, 1 byte, 8-bit unsigned integer
|
|
||||||
else if ((raw[0] == 0x12) && (data_length == 1)) {
|
|
||||||
result.is_active = (data[0]) ? true : false;
|
|
||||||
}
|
|
||||||
// mosquito tablet, 1 byte, 8-bit unsigned integer, 1 %
|
|
||||||
else if ((raw[0] == 0x13) && (data_length == 1)) {
|
|
||||||
result.tablet = data[0];
|
|
||||||
}
|
|
||||||
// idle time since last motion, 4 byte, 32-bit unsigned integer, 1 min
|
|
||||||
else if ((raw[0] == 0x17) && (data_length == 4)) {
|
|
||||||
const uint32_t idle_time =
|
|
||||||
uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16) | (uint32_t(data[2]) << 24);
|
|
||||||
result.idle_time = idle_time / 60.0f;
|
|
||||||
result.has_motion = (idle_time) ? false : true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
const uint8_t value_length = payload[payload_offset + 2];
|
||||||
|
if ((value_length < 1) || (value_length > 4) || (payload_length < (3 + value_length))) {
|
||||||
|
ESP_LOGVV(TAG, "parse_xiaomi_message(): value has wrong size (%d)!", value_length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t value_type = payload[payload_offset + 0];
|
||||||
|
const uint8_t *data = &payload[payload_offset + 3];
|
||||||
|
|
||||||
|
if (parse_xiaomi_value(value_type, data, value_length, result))
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
payload_length -= 3 + value_length;
|
||||||
|
payload_offset += 3 + value_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data) {
|
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data) {
|
||||||
|
|
|
@ -57,6 +57,7 @@ struct XiaomiAESVector {
|
||||||
size_t ivsize;
|
size_t ivsize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_length, XiaomiParseResult &result);
|
||||||
bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult &result);
|
bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult &result);
|
||||||
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data);
|
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data);
|
||||||
bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, const uint64_t &address);
|
bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, const uint64_t &address);
|
||||||
|
|
Loading…
Reference in a new issue