[components] Decode BLE fields with the shared helpers (#21082)

This commit is contained in:
J. Nick Koston
2026-10-18 19:59:19 -04:00
committed by GitHub
parent a70d75e469
commit 03bf2de7df
13 changed files with 51 additions and 53 deletions
@@ -1,4 +1,5 @@
#include "airthings_listener.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cinttypes>
@@ -12,10 +13,7 @@ bool AirthingsListener::parse_device(const ble_device_base::ESPBTDevice &device)
if (it.data.size() < 4)
continue;
uint32_t sn = it.data[0];
sn |= ((uint32_t) it.data[1] << 8);
sn |= ((uint32_t) it.data[2] << 16);
sn |= ((uint32_t) it.data[3] << 24);
uint32_t sn = encode_value_le<uint32_t>(it.data.data());
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
ESP_LOGD(TAG, "Found AirThings device Serial:%" PRIu32 " (MAC: %s)", sn, device.address_str_to(addr_buf));
@@ -1,4 +1,5 @@
#include "atc_mithermometer.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::atc_mithermometer {
@@ -89,7 +90,7 @@ bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, Parse
}
// temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
const int16_t temperature = uint16_t(data[7]) | (uint16_t(data[6]) << 8);
const int16_t temperature = encode_value<int16_t>(&data[6]);
result.temperature = temperature / 10.0f;
// humidity, 1 byte, 8-bit unsigned integer, 1.0 %
@@ -99,7 +100,7 @@ bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, Parse
result.battery_level = data[9];
// battery, 2 bytes, 16-bit unsigned integer, 0.001 V
const int16_t battery_voltage = uint16_t(data[11]) | (uint16_t(data[10]) << 8);
const int16_t battery_voltage = encode_value<int16_t>(&data[10]);
result.battery_voltage = battery_voltage / 1.0e3f;
return true;
+7 -6
View File
@@ -1,4 +1,5 @@
#include "b_parasite.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::b_parasite {
@@ -62,29 +63,29 @@ bool BParasite::parse_device(const ble_device_base::ESPBTDevice &device) {
}
// Battery voltage in millivolts.
uint16_t battery_millivolt = data[2] << 8 | data[3];
uint16_t battery_millivolt = encode_value<uint16_t>(&data[2]);
float battery_voltage = battery_millivolt / 1000.0f;
// Temperature in 1000 * Celsius (protocol v1) or 100 * Celsius (protocol v2).
float temp_celsius;
if (protocol_version == 1) {
uint16_t temp_millicelsius = data[4] << 8 | data[5];
uint16_t temp_millicelsius = encode_value<uint16_t>(&data[4]);
temp_celsius = temp_millicelsius / 1000.0f;
} else {
int16_t temp_centicelsius = data[4] << 8 | data[5];
int16_t temp_centicelsius = encode_value<int16_t>(&data[4]);
temp_celsius = temp_centicelsius / 100.0f;
}
// Relative air humidity in the range [0, 2^16).
uint16_t humidity = data[6] << 8 | data[7];
uint16_t humidity = encode_value<uint16_t>(&data[6]);
float humidity_percent = (100.0f * humidity) / (1 << 16);
// Relative soil moisture in [0 - 2^16).
uint16_t soil_moisture = data[8] << 8 | data[9];
uint16_t soil_moisture = encode_value<uint16_t>(&data[8]);
float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
// Ambient light in lux.
float illuminance = has_illuminance ? data[16] << 8 | data[17] : 0.0f;
float illuminance = has_illuminance ? encode_value<uint16_t>(&data[16]) : 0.0f;
if (battery_voltage_ != nullptr) {
battery_voltage_->publish_state(battery_voltage);
@@ -493,13 +493,13 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0x19: // Appearance
if (ad_data_len >= 2)
this->appearance_ = static_cast<uint16_t>(ad_data[0]) | (static_cast<uint16_t>(ad_data[1]) << 8);
this->appearance_ = encode_value_le<uint16_t>(ad_data);
break;
case 0x02: // Incomplete List of 16-bit Service UUIDs
case 0x03: // Complete List of 16-bit Service UUIDs
for (uint8_t i = 0; (i + 1) < ad_data_len; i += 2) {
uint16_t uuid = (static_cast<uint16_t>(ad_data[i + 1]) << 8) | ad_data[i];
uint16_t uuid = encode_value_le<uint16_t>(&ad_data[i]);
this->service_uuids_.push_back(ESPBTUUID::from_uint16(uuid));
}
break;
@@ -507,9 +507,7 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0x04: // Incomplete List of 32-bit Service UUIDs
case 0x05: // Complete List of 32-bit Service UUIDs
for (uint8_t i = 0; (i + 3) < ad_data_len; i += 4) {
uint32_t uuid = (static_cast<uint32_t>(ad_data[i + 3]) << 24) |
(static_cast<uint32_t>(ad_data[i + 2]) << 16) | (static_cast<uint32_t>(ad_data[i + 1]) << 8) |
ad_data[i];
uint32_t uuid = encode_value_le<uint32_t>(&ad_data[i]);
this->service_uuids_.push_back(ESPBTUUID::from_uint32(uuid));
}
break;
@@ -522,7 +520,7 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0xFF: // Manufacturer Specific Data
if (ad_data_len >= 2) {
uint16_t company_id = (static_cast<uint16_t>(ad_data[1]) << 8) | ad_data[0];
uint16_t company_id = encode_value_le<uint16_t>(ad_data);
ServiceData sd;
sd.uuid = ESPBTUUID::from_uint16(company_id);
sd.data.assign(ad_data + 2, ad_data + ad_data_len);
@@ -532,7 +530,7 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0x16: // Service Data — 16-bit UUID
if (ad_data_len >= 2) {
uint16_t uuid = (static_cast<uint16_t>(ad_data[1]) << 8) | ad_data[0];
uint16_t uuid = encode_value_le<uint16_t>(ad_data);
ServiceData sd;
sd.uuid = ESPBTUUID::from_uint16(uuid);
sd.data.assign(ad_data + 2, ad_data + ad_data_len);
@@ -542,8 +540,7 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
case 0x20: // Service Data — 32-bit UUID
if (ad_data_len >= 4) {
uint32_t uuid = (static_cast<uint32_t>(ad_data[3]) << 24) | (static_cast<uint32_t>(ad_data[2]) << 16) |
(static_cast<uint32_t>(ad_data[1]) << 8) | ad_data[0];
uint32_t uuid = encode_value_le<uint32_t>(ad_data);
ServiceData sd;
sd.uuid = ESPBTUUID::from_uint32(uuid);
sd.data.assign(ad_data + 4, ad_data + ad_data_len);
@@ -4,6 +4,7 @@
#pragma once
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "esphome/components/ble_device_base/ble_client_state.h"
#include "esphome/components/ble_device_base/ble_device.h"
@@ -124,10 +125,8 @@ inline void fill_128bit_uuid_array(std::array<uint64_t, 2> &out, const ble_devic
if (uuid.type() == ESPBTUUID::Type::UUID128) {
const uint8_t *u = uuid.uuid128();
// out[0] = bytes 8-15 (big-endian), out[1] = bytes 0-7 (big-endian)
out[0] = ((uint64_t) u[15] << 56) | ((uint64_t) u[14] << 48) | ((uint64_t) u[13] << 40) | ((uint64_t) u[12] << 32) |
((uint64_t) u[11] << 24) | ((uint64_t) u[10] << 16) | ((uint64_t) u[9] << 8) | ((uint64_t) u[8]);
out[1] = ((uint64_t) u[7] << 56) | ((uint64_t) u[6] << 48) | ((uint64_t) u[5] << 40) | ((uint64_t) u[4] << 32) |
((uint64_t) u[3] << 24) | ((uint64_t) u[2] << 16) | ((uint64_t) u[1] << 8) | ((uint64_t) u[0]);
out[0] = encode_value_le<uint64_t>(&u[8]);
out[1] = encode_value_le<uint64_t>(u);
return;
}
// 16/32-bit UUID inserted into the Bluetooth base UUID:
@@ -408,7 +408,7 @@ bool BTHomeMiThermometer::handle_service_data_(const ble_device_base::ServiceDat
}
case 0x02: { // temperature
if (this->temperature_ != nullptr) {
const int16_t raw = encode_uint16(value[1], value[0]);
const int16_t raw = encode_value_le<int16_t>(value);
this->temperature_->publish_state(raw * 0.01f);
reported = true;
}
@@ -424,7 +424,7 @@ bool BTHomeMiThermometer::handle_service_data_(const ble_device_base::ServiceDat
}
case 0x45: { // temperature (0.1C), e.g. Shelly BLU H&T
if (this->temperature_ != nullptr) {
const int16_t raw = encode_uint16(value[1], value[0]);
const int16_t raw = encode_value_le<int16_t>(value);
this->temperature_->publish_state(raw * 0.1f);
reported = true;
}
@@ -680,18 +680,17 @@ float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) {
case 0xD: // int12.
case 0xE: // int16.
if (length > 2) {
return (float) ((int16_t) (value[1] << 8) + (int16_t) value[2]);
return (float) encode_value<int16_t>(&value[1]);
}
[[fallthrough]];
case 0xF: // int24.
if (length > 3) {
return (float) ((int32_t) (value[1] << 16) + (int32_t) (value[2] << 8) + (int32_t) (value[3]));
return (float) encode_uint24(value[1], value[2], value[3]);
}
[[fallthrough]];
case 0x10: // int32.
if (length > 4) {
return (float) ((int32_t) (value[1] << 24) + (int32_t) (value[2] << 16) + (int32_t) (value[3] << 8) +
(int32_t) (value[4]));
return (float) encode_value<int32_t>(&value[1]);
}
}
ESP_LOGW(TAG, "[%d] [%s] Cannot parse characteristic value of type 0x%x length %d", this->connection_index_,
@@ -1,4 +1,5 @@
#include "pvvx_mithermometer.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::pvvx_mithermometer {
@@ -96,15 +97,15 @@ bool PVVXMiThermometer::parse_message_(const std::vector<uint8_t> &message, Pars
}
// int16_t temperature; // x 0.01 degree [6,7]
const int16_t temperature = int16_t(data[6]) | (int16_t(data[7]) << 8);
const int16_t temperature = encode_value_le<int16_t>(&data[6]);
result.temperature = temperature / 1.0e2f;
// uint16_t humidity; // x 0.01 % [8,9]
const int16_t humidity = uint16_t(data[8]) | (uint16_t(data[9]) << 8);
const int16_t humidity = encode_value_le<int16_t>(&data[8]);
result.humidity = humidity / 1.0e2f;
// uint16_t battery_mv; // mV [10,11]
const int16_t battery_voltage = uint16_t(data[10]) | (uint16_t(data[11]) << 8);
const int16_t battery_voltage = encode_value_le<int16_t>(&data[10]);
result.battery_voltage = battery_voltage / 1.0e3f;
// uint8_t battery_level; // 0..100 % [12]
+8 -7
View File
@@ -1,4 +1,5 @@
#include "ruuvi_ble.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::ruuvi_ble {
@@ -19,9 +20,9 @@ bool parse_ruuvi_data_byte(const ble_device_base::adv_data_t &adv_data, RuuviPar
const float humidity = data[0] * 0.5f;
const float pressure = (encode_uint16(data[3], data[4]) + 50000.0f) / 100.0f;
const float acceleration_x = static_cast<int16_t>(encode_uint16(data[5], data[6])) / 1000.0f;
const float acceleration_y = static_cast<int16_t>(encode_uint16(data[7], data[8])) / 1000.0f;
const float acceleration_z = static_cast<int16_t>(encode_uint16(data[9], data[10])) / 1000.0f;
const float acceleration_x = encode_value<int16_t>(&data[5]) / 1000.0f;
const float acceleration_y = encode_value<int16_t>(&data[7]) / 1000.0f;
const float acceleration_z = encode_value<int16_t>(&data[9]) / 1000.0f;
const float battery_voltage = encode_uint16(data[11], data[12]) / 1000.0f;
result.humidity = humidity;
@@ -40,12 +41,12 @@ bool parse_ruuvi_data_byte(const ble_device_base::adv_data_t &adv_data, RuuviPar
if (adv_data.size() != 24)
return false;
const float temperature = static_cast<int16_t>(encode_uint16(data[0], data[1])) * 0.005f;
const float temperature = encode_value<int16_t>(data) * 0.005f;
const float humidity = encode_uint16(data[2], data[3]) / 400.0f;
const float pressure = (encode_uint16(data[4], data[5]) + 50000.0f) / 100.0f;
const float acceleration_x = static_cast<int16_t>(encode_uint16(data[6], data[7])) / 1000.0f;
const float acceleration_y = static_cast<int16_t>(encode_uint16(data[8], data[9])) / 1000.0f;
const float acceleration_z = static_cast<int16_t>(encode_uint16(data[10], data[11])) / 1000.0f;
const float acceleration_x = encode_value<int16_t>(&data[6]) / 1000.0f;
const float acceleration_y = encode_value<int16_t>(&data[8]) / 1000.0f;
const float acceleration_z = encode_value<int16_t>(&data[10]) / 1000.0f;
const uint16_t power_info = encode_uint16(data[12], data[13]);
const float battery_voltage = ((power_info >> 5) + 1600.0f) / 1000.0f;
@@ -1,5 +1,6 @@
#include "thermopro_ble.h"
#include <cmath>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::thermopro_ble {
@@ -113,16 +114,15 @@ void ThermoProBLE::update_device_type_(StringRef device_name) {
}
static inline uint16_t read_uint16(const uint8_t *data, std::size_t offset) {
return static_cast<uint16_t>(data[offset + 0]) | (static_cast<uint16_t>(data[offset + 1]) << 8);
return encode_value_le<uint16_t>(&data[offset]);
}
static inline int16_t read_int16(const uint8_t *data, std::size_t offset) {
return static_cast<int16_t>(read_uint16(data, offset));
return encode_value_le<int16_t>(&data[offset]);
}
static inline uint32_t read_uint32(const uint8_t *data, std::size_t offset) {
return static_cast<uint32_t>(data[offset + 0]) | (static_cast<uint32_t>(data[offset + 1]) << 8) |
(static_cast<uint32_t>(data[offset + 2]) << 16) | (static_cast<uint32_t>(data[offset + 3]) << 24);
return encode_value_le<uint32_t>(&data[offset]);
}
// Battery calculation used with permission from:
+4 -4
View File
@@ -38,12 +38,12 @@ bool parse_xiaomi_value(uint16_t value_type, const uint8_t *data, uint8_t value_
}
// temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
else if ((value_type == 0x1004) && (value_length == 2)) {
const int16_t temperature = encode_uint16(data[1], data[0]);
const int16_t temperature = encode_value_le<int16_t>(data);
result.temperature = temperature / 10.0f;
}
// humidity, 2 bytes, 16-bit signed integer (LE), 0.1 %
else if ((value_type == 0x1006) && (value_length == 2)) {
const int16_t humidity = encode_uint16(data[1], data[0]);
const int16_t humidity = encode_value_le<int16_t>(data);
result.humidity = humidity / 10.0f;
}
// illuminance (+ motion), 3 bytes, 24-bit unsigned integer (LE), 1 lx
@@ -69,8 +69,8 @@ bool parse_xiaomi_value(uint16_t value_type, const uint8_t *data, uint8_t value_
}
// temperature + humidity, 4 bytes, 16-bit signed integer (LE) each, 0.1 °C, 0.1 %
else if ((value_type == 0x100D) && (value_length == 4)) {
const int16_t temperature = encode_uint16(data[1], data[0]);
const int16_t humidity = encode_uint16(data[3], data[2]);
const int16_t temperature = encode_value_le<int16_t>(data);
const int16_t humidity = encode_value_le<int16_t>(&data[2]);
result.temperature = temperature / 10.0f;
result.humidity = humidity / 10.0f;
}
@@ -35,7 +35,7 @@ bool XiaomiHHCCJCY10::parse_device(const ble_device_base::ESPBTDevice &device) {
const uint8_t *data = service_data.data.data();
if (this->temperature_ != nullptr) {
const int16_t temperature = encode_uint16(data[1], data[2]);
const int16_t temperature = encode_value<int16_t>(&data[1]);
this->temperature_->publish_state((float) temperature / 10.0f);
}
@@ -1,5 +1,6 @@
#include "xiaomi_miscale.h"
#include "esphome/components/ble_device_base/ble_device.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::xiaomi_miscale {
@@ -92,7 +93,7 @@ bool XiaomiMiscale::parse_message_v1_(const std::vector<uint8_t> &message, Parse
const uint8_t *data = message.data();
// weight, 2 bytes, 16-bit unsigned integer, 1 kg
const int16_t weight = uint16_t(data[1]) | (uint16_t(data[2]) << 8);
const int16_t weight = encode_value_le<int16_t>(&data[1]);
if (data[0] == 0x22 || data[0] == 0xa2) {
result.weight = weight * 0.01f / 2.0f; // unit 'kg'
} else if (data[0] == 0x12 || data[0] == 0xb2) {
@@ -126,7 +127,7 @@ bool XiaomiMiscale::parse_message_v2_(const std::vector<uint8_t> &message, Parse
}
// weight, 2 bytes, 16-bit unsigned integer, 1 kg
const int16_t weight = uint16_t(data[11]) | (uint16_t(data[12]) << 8);
const int16_t weight = encode_value_le<int16_t>(&data[11]);
if (data[0] == 0x02) {
result.weight = weight * 0.01f / 2.0f; // unit 'kg'
} else if (data[0] == 0x03) {
@@ -135,7 +136,7 @@ bool XiaomiMiscale::parse_message_v2_(const std::vector<uint8_t> &message, Parse
if (has_impedance) {
// impedance, 2 bytes, 16-bit
const int16_t impedance = uint16_t(data[9]) | (uint16_t(data[10]) << 8);
const int16_t impedance = encode_value_le<int16_t>(&data[9]);
result.impedance = impedance;
if (impedance == 0 || impedance >= 3000) {