This commit is contained in:
Alexander Pohl 2020-10-14 07:37:19 +02:00
commit 4774061405
3 changed files with 103 additions and 70 deletions

View file

@ -12,6 +12,75 @@ namespace 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) {
result.has_encryption = (message[0] & 0x08) ? true : false; // update encryption status
if (result.has_encryption) {
@ -25,81 +94,39 @@ bool parse_xiaomi_message(const std::vector<uint8_t> &message, XiaomiParseResult
// Byte 2: length
// Byte 3..3+len-1: data point value
const uint8_t *raw = message.data() + result.raw_offset;
const uint8_t *data = raw + 3;
const uint8_t data_length = raw[2];
const uint8_t *payload = message.data() + result.raw_offset;
uint8_t payload_length = message.size() - result.raw_offset;
uint8_t payload_offset = 0;
bool success = false;
if ((data_length < 1) || (data_length > 4)) {
ESP_LOGVV(TAG, "parse_xiaomi_message(): payload has wrong size (%d)!", data_length);
if (payload_length < 4) {
ESP_LOGVV(TAG, "parse_xiaomi_message(): payload has wrong size (%d)!", payload_length);
return false;
}
// motion detection, 1 byte, 8-bit unsigned integer
if ((raw[0] == 0x03) && (data_length == 1)) {
result.has_motion = (data[0]) ? true : false;
}
// 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;
while (payload_length > 0) {
if (payload[payload_offset + 1] != 0x10) {
ESP_LOGVV(TAG, "parse_xiaomi_message(): fixed byte not found, stop parsing residual data.");
break;
}
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 true;
return success;
}
optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data) {

View file

@ -57,6 +57,7 @@ struct XiaomiAESVector {
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);
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);

View file

@ -205,7 +205,7 @@ def process_stacktrace(config, line, backtrace_state):
# ESP8266 Exception type
match = re.match(STACKTRACE_ESP8266_EXCEPTION_TYPE_RE, line)
if match is not None:
code = match.group(1)
code = int(match.group(1))
_LOGGER.warning("Exception type: %s", ESP8266_EXCEPTION_CODES.get(code, 'unknown'))
# ESP8266 PC/EXCVADDR
@ -273,4 +273,9 @@ class IDEData:
if cc_path is None:
return None
# replace gcc at end with addr2line
# Windows
if cc_path.endswith('.exe'):
return cc_path[:-7] + 'addr2line.exe'
return cc_path[:-3] + 'addr2line'