mirror of
https://github.com/esphome/esphome.git
synced 2024-11-26 08:55:22 +01:00
Introduce hex parsing & formatting helper functions (#2882)
This commit is contained in:
parent
b2f05faee0
commit
beeb0c7c5a
20 changed files with 186 additions and 109 deletions
|
@ -252,7 +252,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||
|
||||
// uncomment for even more debugging
|
||||
#ifdef HELPER_LOG_PACKETS
|
||||
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
|
||||
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
|
||||
#endif
|
||||
frame->msg = std::move(rx_buf_);
|
||||
// consume msg
|
||||
|
@ -546,7 +546,8 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
|||
size_t total_write_len = 0;
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
#ifdef HELPER_LOG_PACKETS
|
||||
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
||||
ESP_LOGVV(TAG, "Sending raw: %s",
|
||||
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
||||
#endif
|
||||
total_write_len += iov[i].iov_len;
|
||||
}
|
||||
|
@ -855,7 +856,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||
|
||||
// uncomment for even more debugging
|
||||
#ifdef HELPER_LOG_PACKETS
|
||||
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
|
||||
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
|
||||
#endif
|
||||
frame->msg = std::move(rx_buf_);
|
||||
// consume msg
|
||||
|
@ -934,7 +935,8 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
|
|||
size_t total_write_len = 0;
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
#ifdef HELPER_LOG_PACKETS
|
||||
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
||||
ESP_LOGVV(TAG, "Sending raw: %s",
|
||||
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
||||
#endif
|
||||
total_write_len += iov[i].iov_len;
|
||||
}
|
||||
|
|
|
@ -524,7 +524,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
|
|||
ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str());
|
||||
}
|
||||
for (auto &data : this->manufacturer_datas_) {
|
||||
ESP_LOGVV(TAG, " Manufacturer data: %s", hexencode(data.data).c_str());
|
||||
ESP_LOGVV(TAG, " Manufacturer data: %s", format_hex_pretty(data.data).c_str());
|
||||
if (this->get_ibeacon().has_value()) {
|
||||
auto ibeacon = this->get_ibeacon().value();
|
||||
ESP_LOGVV(TAG, " iBeacon data:");
|
||||
|
@ -537,10 +537,10 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
|
|||
for (auto &data : this->service_datas_) {
|
||||
ESP_LOGVV(TAG, " Service data:");
|
||||
ESP_LOGVV(TAG, " UUID: %s", data.uuid.to_string().c_str());
|
||||
ESP_LOGVV(TAG, " Data: %s", hexencode(data.data).c_str());
|
||||
ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str());
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "Adv data: %s", hexencode(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
|
||||
ESP_LOGVV(TAG, "Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
|
||||
#endif
|
||||
}
|
||||
void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
|
||||
|
|
|
@ -219,7 +219,7 @@ void ESP32ImprovComponent::dump_config() {
|
|||
void ESP32ImprovComponent::process_incoming_data_() {
|
||||
uint8_t length = this->incoming_data_[1];
|
||||
|
||||
ESP_LOGD(TAG, "Processing bytes - %s", hexencode(this->incoming_data_).c_str());
|
||||
ESP_LOGD(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str());
|
||||
if (this->incoming_data_.size() - 3 == length) {
|
||||
this->set_error_(improv::ERROR_NONE);
|
||||
improv::ImprovCommand command = improv::parse_improv_data(this->incoming_data_);
|
||||
|
|
|
@ -23,7 +23,7 @@ void MD5Digest::get_hex(char *output) {
|
|||
}
|
||||
}
|
||||
|
||||
bool MD5Digest::equals_bytes(const char *expected) {
|
||||
bool MD5Digest::equals_bytes(const uint8_t *expected) {
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
if (expected[i] != this->digest_[i]) {
|
||||
return false;
|
||||
|
@ -33,18 +33,10 @@ bool MD5Digest::equals_bytes(const char *expected) {
|
|||
}
|
||||
|
||||
bool MD5Digest::equals_hex(const char *expected) {
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
auto high = parse_hex(expected[i * 2]);
|
||||
auto low = parse_hex(expected[i * 2 + 1]);
|
||||
if (!high.has_value() || !low.has_value()) {
|
||||
return false;
|
||||
}
|
||||
auto value = (*high << 4) | *low;
|
||||
if (value != this->digest_[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
uint8_t parsed[16];
|
||||
if (!parse_hex(expected, parsed, 16))
|
||||
return false;
|
||||
return equals_bytes(parsed);
|
||||
}
|
||||
|
||||
} // namespace md5
|
||||
|
|
|
@ -44,7 +44,7 @@ class MD5Digest {
|
|||
void get_hex(char *output);
|
||||
|
||||
/// Compare the digest against a provided byte-encoded digest (16 bytes).
|
||||
bool equals_bytes(const char *expected);
|
||||
bool equals_bytes(const uint8_t *expected);
|
||||
|
||||
/// Compare the digest against a provided hex-encoded digest (32 bytes).
|
||||
bool equals_hex(const char *expected);
|
||||
|
|
|
@ -181,7 +181,7 @@ void Modbus::send(uint8_t address, uint8_t function_code, uint16_t start_address
|
|||
this->flow_control_pin_->digital_write(false);
|
||||
waiting_for_response = address;
|
||||
last_send_ = millis();
|
||||
ESP_LOGV(TAG, "Modbus write: %s", hexencode(data).c_str());
|
||||
ESP_LOGV(TAG, "Modbus write: %s", format_hex_pretty(data).c_str());
|
||||
}
|
||||
|
||||
// Helper function for lambdas
|
||||
|
@ -202,7 +202,7 @@ void Modbus::send_raw(const std::vector<uint8_t> &payload) {
|
|||
if (this->flow_control_pin_ != nullptr)
|
||||
this->flow_control_pin_->digital_write(false);
|
||||
waiting_for_response = payload[0];
|
||||
ESP_LOGV(TAG, "Modbus write raw: %s", hexencode(payload).c_str());
|
||||
ESP_LOGV(TAG, "Modbus write raw: %s", format_hex_pretty(payload).c_str());
|
||||
last_send_ = millis();
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ void ModbusSwitch::write_state(bool state) {
|
|||
}
|
||||
}
|
||||
if (!data.empty()) {
|
||||
ESP_LOGV(TAG, "Modbus Switch write raw: %s", hexencode(data).c_str());
|
||||
ESP_LOGV(TAG, "Modbus Switch write raw: %s", format_hex_pretty(data).c_str());
|
||||
cmd = ModbusCommandItem::create_custom_command(
|
||||
this->parent_, data,
|
||||
[this, cmd](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data) {
|
||||
|
|
|
@ -26,7 +26,7 @@ bool PN532Spi::write_data(const std::vector<uint8_t> &data) {
|
|||
delay(2);
|
||||
// First byte, communication mode: Write data
|
||||
this->write_byte(0x01);
|
||||
ESP_LOGV(TAG, "Writing data: %s", hexencode(data).c_str());
|
||||
ESP_LOGV(TAG, "Writing data: %s", format_hex_pretty(data).c_str());
|
||||
this->write_array(data.data(), data.size());
|
||||
this->disable();
|
||||
|
||||
|
@ -65,7 +65,7 @@ bool PN532Spi::read_data(std::vector<uint8_t> &data, uint8_t len) {
|
|||
this->read_array(data.data(), len);
|
||||
this->disable();
|
||||
data.insert(data.begin(), 0x01);
|
||||
ESP_LOGV(TAG, "Read data: %s", hexencode(data).c_str());
|
||||
ESP_LOGV(TAG, "Read data: %s", format_hex_pretty(data).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ bool PN532Spi::read_response(uint8_t command, std::vector<uint8_t> &data) {
|
|||
std::vector<uint8_t> header(7);
|
||||
this->read_array(header.data(), 7);
|
||||
|
||||
ESP_LOGV(TAG, "Header data: %s", hexencode(header).c_str());
|
||||
ESP_LOGV(TAG, "Header data: %s", format_hex_pretty(header).c_str());
|
||||
|
||||
if (header[0] != 0x00 && header[1] != 0x00 && header[2] != 0xFF) {
|
||||
// invalid packet
|
||||
|
@ -127,7 +127,7 @@ bool PN532Spi::read_response(uint8_t command, std::vector<uint8_t> &data) {
|
|||
this->read_array(data.data(), len + 1);
|
||||
this->disable();
|
||||
|
||||
ESP_LOGV(TAG, "Response data: %s", hexencode(data).c_str());
|
||||
ESP_LOGV(TAG, "Response data: %s", format_hex_pretty(data).c_str());
|
||||
|
||||
uint8_t checksum = header[5] + header[6]; // TFI + Command response code
|
||||
for (int i = 0; i < len - 1; i++) {
|
||||
|
|
|
@ -26,7 +26,7 @@ class MideaData {
|
|||
bool is_valid() const { return this->data_[OFFSET_CS] == this->calc_cs_(); }
|
||||
void finalize() { this->data_[OFFSET_CS] = this->calc_cs_(); }
|
||||
bool check_compliment(const MideaData &rhs) const;
|
||||
std::string to_string() const { return hexencode(*this); }
|
||||
std::string to_string() const { return format_hex_pretty(this->data_, sizeof(this->data_)); }
|
||||
// compare only 40-bits
|
||||
bool operator==(const MideaData &rhs) const { return !memcmp(this->data_, rhs.data_, OFFSET_CS); }
|
||||
enum MideaDataType : uint8_t {
|
||||
|
|
|
@ -37,9 +37,9 @@ void TuyaLight::setup() {
|
|||
}
|
||||
if (rgb_id_.has_value()) {
|
||||
this->parent_->register_listener(*this->rgb_id_, [this](const TuyaDatapoint &datapoint) {
|
||||
auto red = parse_hex(datapoint.value_string, 0, 2);
|
||||
auto green = parse_hex(datapoint.value_string, 2, 2);
|
||||
auto blue = parse_hex(datapoint.value_string, 4, 2);
|
||||
auto red = parse_hex<uint8_t>(datapoint.value_string.substr(0, 2));
|
||||
auto green = parse_hex<uint8_t>(datapoint.value_string.substr(2, 2));
|
||||
auto blue = parse_hex<uint8_t>(datapoint.value_string.substr(4, 2));
|
||||
if (red.has_value() && green.has_value() && blue.has_value()) {
|
||||
auto call = this->state_->make_call();
|
||||
call.set_rgb(float(*red) / 255, float(*green) / 255, float(*blue) / 255);
|
||||
|
@ -48,9 +48,9 @@ void TuyaLight::setup() {
|
|||
});
|
||||
} else if (hsv_id_.has_value()) {
|
||||
this->parent_->register_listener(*this->hsv_id_, [this](const TuyaDatapoint &datapoint) {
|
||||
auto hue = parse_hex(datapoint.value_string, 0, 4);
|
||||
auto saturation = parse_hex(datapoint.value_string, 4, 4);
|
||||
auto value = parse_hex(datapoint.value_string, 8, 4);
|
||||
auto hue = parse_hex<uint16_t>(datapoint.value_string.substr(0, 4));
|
||||
auto saturation = parse_hex<uint16_t>(datapoint.value_string.substr(4, 4));
|
||||
auto value = parse_hex<uint16_t>(datapoint.value_string.substr(8, 4));
|
||||
if (hue.has_value() && saturation.has_value() && value.has_value()) {
|
||||
float red, green, blue;
|
||||
hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, red, green, blue);
|
||||
|
|
|
@ -14,7 +14,7 @@ void TuyaTextSensor::setup() {
|
|||
this->publish_state(datapoint.value_string);
|
||||
break;
|
||||
case TuyaDatapointType::RAW: {
|
||||
std::string data = hexencode(datapoint.value_raw);
|
||||
std::string data = format_hex_pretty(datapoint.value_raw);
|
||||
ESP_LOGD(TAG, "MCU reported text sensor %u is: %s", datapoint.id, data.c_str());
|
||||
this->publish_state(data);
|
||||
break;
|
||||
|
|
|
@ -34,7 +34,7 @@ void Tuya::dump_config() {
|
|||
}
|
||||
for (auto &info : this->datapoints_) {
|
||||
if (info.type == TuyaDatapointType::RAW)
|
||||
ESP_LOGCONFIG(TAG, " Datapoint %u: raw (value: %s)", info.id, hexencode(info.value_raw).c_str());
|
||||
ESP_LOGCONFIG(TAG, " Datapoint %u: raw (value: %s)", info.id, format_hex_pretty(info.value_raw).c_str());
|
||||
else if (info.type == TuyaDatapointType::BOOLEAN)
|
||||
ESP_LOGCONFIG(TAG, " Datapoint %u: switch (value: %s)", info.id, ONOFF(info.value_bool));
|
||||
else if (info.type == TuyaDatapointType::INTEGER)
|
||||
|
@ -104,7 +104,7 @@ bool Tuya::validate_message_() {
|
|||
// valid message
|
||||
const uint8_t *message_data = data + 6;
|
||||
ESP_LOGV(TAG, "Received Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", command, version,
|
||||
hexencode(message_data, length).c_str(), static_cast<uint8_t>(this->init_state_));
|
||||
format_hex_pretty(message_data, length).c_str(), static_cast<uint8_t>(this->init_state_));
|
||||
this->handle_command_(command, version, message_data, length);
|
||||
|
||||
// return false to reset rx buffer
|
||||
|
@ -253,7 +253,7 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) {
|
|||
switch (datapoint.type) {
|
||||
case TuyaDatapointType::RAW:
|
||||
datapoint.value_raw = std::vector<uint8_t>(data, data + data_len);
|
||||
ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, hexencode(datapoint.value_raw).c_str());
|
||||
ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, format_hex_pretty(datapoint.value_raw).c_str());
|
||||
break;
|
||||
case TuyaDatapointType::BOOLEAN:
|
||||
if (data_len != 1) {
|
||||
|
@ -348,7 +348,7 @@ void Tuya::send_raw_command_(TuyaCommand command) {
|
|||
}
|
||||
|
||||
ESP_LOGV(TAG, "Sending Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", static_cast<uint8_t>(command.cmd),
|
||||
version, hexencode(command.payload).c_str(), static_cast<uint8_t>(this->init_state_));
|
||||
version, format_hex_pretty(command.payload).c_str(), static_cast<uint8_t>(this->init_state_));
|
||||
|
||||
this->write_array({0x55, 0xAA, version, (uint8_t) command.cmd, len_hi, len_lo});
|
||||
if (!command.payload.empty())
|
||||
|
@ -526,7 +526,7 @@ void Tuya::set_numeric_datapoint_value_(uint8_t datapoint_id, TuyaDatapointType
|
|||
}
|
||||
|
||||
void Tuya::set_raw_datapoint_value_(uint8_t datapoint_id, const std::vector<uint8_t> &value, bool forced) {
|
||||
ESP_LOGD(TAG, "Setting datapoint %u to %s", datapoint_id, hexencode(value).c_str());
|
||||
ESP_LOGD(TAG, "Setting datapoint %u to %s", datapoint_id, format_hex_pretty(value).c_str());
|
||||
optional<TuyaDatapoint> datapoint = this->get_datapoint_(datapoint_id);
|
||||
if (!datapoint.has_value()) {
|
||||
ESP_LOGW(TAG, "Setting unknown datapoint %u", datapoint_id);
|
||||
|
|
|
@ -217,7 +217,7 @@ optional<XiaomiParseResult> parse_xiaomi_header(const esp32_ble_tracker::Service
|
|||
bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, const uint64_t &address) {
|
||||
if (!((raw.size() == 19) || ((raw.size() >= 22) && (raw.size() <= 24)))) {
|
||||
ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): data packet has wrong size (%d)!", raw.size());
|
||||
ESP_LOGVV(TAG, " Packet : %s", hexencode(raw.data(), raw.size()).c_str());
|
||||
ESP_LOGVV(TAG, " Packet : %s", format_hex_pretty(raw.data(), raw.size()).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -274,12 +274,12 @@ bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, c
|
|||
memcpy(mac_address + 4, mac_reverse + 1, 1);
|
||||
memcpy(mac_address + 5, mac_reverse, 1);
|
||||
ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): authenticated decryption failed.");
|
||||
ESP_LOGVV(TAG, " MAC address : %s", hexencode(mac_address, 6).c_str());
|
||||
ESP_LOGVV(TAG, " Packet : %s", hexencode(raw.data(), raw.size()).c_str());
|
||||
ESP_LOGVV(TAG, " Key : %s", hexencode(vector.key, vector.keysize).c_str());
|
||||
ESP_LOGVV(TAG, " Iv : %s", hexencode(vector.iv, vector.ivsize).c_str());
|
||||
ESP_LOGVV(TAG, " Cipher : %s", hexencode(vector.ciphertext, vector.datasize).c_str());
|
||||
ESP_LOGVV(TAG, " Tag : %s", hexencode(vector.tag, vector.tagsize).c_str());
|
||||
ESP_LOGVV(TAG, " MAC address : %s", format_hex_pretty(mac_address, 6).c_str());
|
||||
ESP_LOGVV(TAG, " Packet : %s", format_hex_pretty(raw.data(), raw.size()).c_str());
|
||||
ESP_LOGVV(TAG, " Key : %s", format_hex_pretty(vector.key, vector.keysize).c_str());
|
||||
ESP_LOGVV(TAG, " Iv : %s", format_hex_pretty(vector.iv, vector.ivsize).c_str());
|
||||
ESP_LOGVV(TAG, " Cipher : %s", format_hex_pretty(vector.ciphertext, vector.datasize).c_str());
|
||||
ESP_LOGVV(TAG, " Tag : %s", format_hex_pretty(vector.tag, vector.tagsize).c_str());
|
||||
mbedtls_ccm_free(&ctx);
|
||||
return false;
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, c
|
|||
raw[0] &= ~0x08;
|
||||
|
||||
ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): authenticated decryption passed.");
|
||||
ESP_LOGVV(TAG, " Plaintext : %s, Packet : %d", hexencode(raw.data() + cipher_pos, vector.datasize).c_str(),
|
||||
ESP_LOGVV(TAG, " Plaintext : %s, Packet : %d", format_hex_pretty(raw.data() + cipher_pos, vector.datasize).c_str(),
|
||||
static_cast<int>(raw[4]));
|
||||
|
||||
mbedtls_ccm_free(&ctx);
|
||||
|
|
|
@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_cgd1";
|
|||
|
||||
void XiaomiCGD1::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Xiaomi CGD1");
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", hexencode(this->bindkey_, 16).c_str());
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str());
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||
|
|
|
@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_cgdk2";
|
|||
|
||||
void XiaomiCGDK2::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Xiaomi CGDK2");
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", hexencode(this->bindkey_, 16).c_str());
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str());
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||
|
|
|
@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_cgg1";
|
|||
|
||||
void XiaomiCGG1::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Xiaomi CGG1");
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", hexencode(this->bindkey_, 16).c_str());
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str());
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||
|
|
|
@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_lywsd03mmc";
|
|||
|
||||
void XiaomiLYWSD03MMC::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Xiaomi LYWSD03MMC");
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", hexencode(this->bindkey_, 16).c_str());
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str());
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||
|
|
|
@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_mhoc401";
|
|||
|
||||
void XiaomiMHOC401::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Xiaomi MHOC401");
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", hexencode(this->bindkey_, 16).c_str());
|
||||
ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str());
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||
|
|
|
@ -244,38 +244,6 @@ std::string to_string(long double val) {
|
|||
return buf;
|
||||
}
|
||||
|
||||
optional<int> parse_hex(const char chr) {
|
||||
int out = chr;
|
||||
if (out >= '0' && out <= '9')
|
||||
return (out - '0');
|
||||
if (out >= 'A' && out <= 'F')
|
||||
return (10 + (out - 'A'));
|
||||
if (out >= 'a' && out <= 'f')
|
||||
return (10 + (out - 'a'));
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<int> parse_hex(const std::string &str, size_t start, size_t length) {
|
||||
if (str.length() < start) {
|
||||
return {};
|
||||
}
|
||||
size_t end = start + length;
|
||||
if (str.length() < end) {
|
||||
return {};
|
||||
}
|
||||
int out = 0;
|
||||
for (size_t i = start; i < end; i++) {
|
||||
char chr = str[i];
|
||||
auto digit = parse_hex(chr);
|
||||
if (!digit.has_value()) {
|
||||
ESP_LOGW(TAG, "Can't convert '%s' to number, invalid character %c!", str.substr(start, length).c_str(), chr);
|
||||
return {};
|
||||
}
|
||||
out = (out << 4) | *digit;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
uint32_t fnv1_hash(const std::string &str) {
|
||||
uint32_t hash = 2166136261UL;
|
||||
for (char c : str) {
|
||||
|
@ -355,22 +323,6 @@ std::string str_sprintf(const char *fmt, ...) {
|
|||
return str;
|
||||
}
|
||||
|
||||
std::string hexencode(const uint8_t *data, uint32_t len) {
|
||||
char buf[20];
|
||||
std::string res;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (i + 1 != len) {
|
||||
sprintf(buf, "%02X.", data[i]);
|
||||
} else {
|
||||
sprintf(buf, "%02X ", data[i]);
|
||||
}
|
||||
res += buf;
|
||||
}
|
||||
sprintf(buf, "(%u)", len);
|
||||
res += buf;
|
||||
return res;
|
||||
}
|
||||
|
||||
void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value) {
|
||||
float max_color_value = std::max(std::max(red, green), blue);
|
||||
float min_color_value = std::min(std::min(red, green), blue);
|
||||
|
@ -445,6 +397,8 @@ IRAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); }
|
|||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Strings
|
||||
|
||||
std::string str_truncate(const std::string &str, size_t length) {
|
||||
return str.length() > length ? str.substr(0, length) : str;
|
||||
}
|
||||
|
@ -468,4 +422,53 @@ std::string str_sanitize(const std::string &str) {
|
|||
return out;
|
||||
}
|
||||
|
||||
// Parsing & formatting
|
||||
|
||||
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count) {
|
||||
uint8_t val;
|
||||
size_t chars = std::min(length, 2 * count);
|
||||
for (size_t i = 2 * count - chars; i < 2 * count; i++, str++) {
|
||||
if (*str >= '0' && *str <= '9')
|
||||
val = *str - '0';
|
||||
else if (*str >= 'A' && *str <= 'F')
|
||||
val = 10 + (*str - 'A');
|
||||
else if (*str >= 'a' && *str <= 'f')
|
||||
val = 10 + (*str - 'a');
|
||||
else
|
||||
return 0;
|
||||
data[i >> 1] = !(i & 1) ? val << 4 : data[i >> 1] | val;
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
|
||||
static char format_hex_char(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; }
|
||||
std::string format_hex(const uint8_t *data, size_t length) {
|
||||
std::string ret;
|
||||
ret.resize(length * 2);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
ret[2 * i] = format_hex_char((data[i] & 0xF0) >> 4);
|
||||
ret[2 * i + 1] = format_hex_char(data[i] & 0x0F);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
std::string format_hex(std::vector<uint8_t> data) { return format_hex(data.data(), data.size()); }
|
||||
|
||||
static char format_hex_pretty_char(uint8_t v) { return v >= 10 ? 'A' + (v - 10) : '0' + v; }
|
||||
std::string format_hex_pretty(const uint8_t *data, size_t length) {
|
||||
if (length == 0)
|
||||
return "";
|
||||
std::string ret;
|
||||
ret.resize(3 * length - 1);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
ret[3 * i] = format_hex_pretty_char((data[i] & 0xF0) >> 4);
|
||||
ret[3 * i + 1] = format_hex_pretty_char(data[i] & 0x0F);
|
||||
if (i != length - 1)
|
||||
ret[3 * i + 2] = '.';
|
||||
}
|
||||
if (length > 4)
|
||||
return ret + " (" + to_string(length) + ")";
|
||||
return ret;
|
||||
}
|
||||
std::string format_hex_pretty(std::vector<uint8_t> data) { return format_hex_pretty(data.data(), data.size()); }
|
||||
|
||||
} // namespace esphome
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
@ -45,8 +46,6 @@ std::string to_string(unsigned long long val); // NOLINT
|
|||
std::string to_string(float val);
|
||||
std::string to_string(double val);
|
||||
std::string to_string(long double val);
|
||||
optional<int> parse_hex(const std::string &str, size_t start, size_t length);
|
||||
optional<int> parse_hex(char chr);
|
||||
|
||||
/// Compare string a to string b (ignoring case) and return whether they are equal.
|
||||
bool str_equals_case_insensitive(const std::string &a, const std::string &b);
|
||||
|
@ -186,10 +185,6 @@ enum ParseOnOffState {
|
|||
|
||||
ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const char *off = nullptr);
|
||||
|
||||
// Encode raw data to a human-readable string (for debugging)
|
||||
std::string hexencode(const uint8_t *data, uint32_t len);
|
||||
template<typename T> std::string hexencode(const T &data) { return hexencode(data.data(), data.size()); }
|
||||
|
||||
// https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/7858971#7858971
|
||||
template<int...> struct seq {}; // NOLINT
|
||||
template<int N, int... S> struct gens : gens<N - 1, N - 1, S...> {}; // NOLINT
|
||||
|
@ -408,6 +403,77 @@ optional<T> parse_number(const std::string &str) {
|
|||
return parse_number<T>(str.c_str());
|
||||
}
|
||||
|
||||
/** Parse bytes from a hex-encoded string into a byte array.
|
||||
*
|
||||
* When \p len is less than \p 2*count, the result is written to the back of \p data (i.e. this function treats \p str
|
||||
* as if it were padded with zeros at the front).
|
||||
*
|
||||
* @param str String to read from.
|
||||
* @param len Length of \p str (excluding optional null-terminator), is a limit on the number of characters parsed.
|
||||
* @param data Byte array to write to.
|
||||
* @param count Length of \p data.
|
||||
* @return The number of characters parsed from \p str.
|
||||
*/
|
||||
size_t parse_hex(const char *str, size_t len, uint8_t *data, size_t count);
|
||||
/// Parse \p count bytes from the hex-encoded string \p str of at least \p 2*count characters into array \p data.
|
||||
inline bool parse_hex(const char *str, uint8_t *data, size_t count) {
|
||||
return parse_hex(str, strlen(str), data, count) == 2 * count;
|
||||
}
|
||||
/// Parse \p count bytes from the hex-encoded string \p str of at least \p 2*count characters into array \p data.
|
||||
inline bool parse_hex(const std::string &str, uint8_t *data, size_t count) {
|
||||
return parse_hex(str.c_str(), str.length(), data, count) == 2 * count;
|
||||
}
|
||||
/// Parse \p count bytes from the hex-encoded string \p str of at least \p 2*count characters into vector \p data.
|
||||
inline bool parse_hex(const char *str, std::vector<uint8_t> &data, size_t count) {
|
||||
data.resize(count);
|
||||
return parse_hex(str, strlen(str), data.data(), count) == 2 * count;
|
||||
}
|
||||
/// Parse \p count bytes from the hex-encoded string \p str of at least \p 2*count characters into vector \p data.
|
||||
inline bool parse_hex(const std::string &str, std::vector<uint8_t> &data, size_t count) {
|
||||
data.resize(count);
|
||||
return parse_hex(str.c_str(), str.length(), data.data(), count) == 2 * count;
|
||||
}
|
||||
/** Parse a hex-encoded string into an unsigned integer.
|
||||
*
|
||||
* @param str String to read from, starting with the most significant byte.
|
||||
* @param len Length of \p str (excluding optional null-terminator), is a limit on the number of characters parsed.
|
||||
*/
|
||||
template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
|
||||
optional<T> parse_hex(const char *str, size_t len) {
|
||||
T val = 0;
|
||||
if (len > 2 * sizeof(T) || parse_hex(str, len, reinterpret_cast<uint8_t *>(&val), sizeof(T)) == 0)
|
||||
return {};
|
||||
return convert_big_endian(val);
|
||||
}
|
||||
/// Parse a hex-encoded null-terminated string (starting with the most significant byte) into an unsigned integer.
|
||||
template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const char *str) {
|
||||
return parse_hex<T>(str, strlen(str));
|
||||
}
|
||||
/// Parse a hex-encoded null-terminated string (starting with the most significant byte) into an unsigned integer.
|
||||
template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const std::string &str) {
|
||||
return parse_hex<T>(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
/// Format the byte array \p data of length \p len in lowercased hex.
|
||||
std::string format_hex(const uint8_t *data, size_t length);
|
||||
/// Format the vector \p data in lowercased hex.
|
||||
std::string format_hex(std::vector<uint8_t> data);
|
||||
/// Format an unsigned integer in lowercased hex, starting with the most significant byte.
|
||||
template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_hex(T val) {
|
||||
val = convert_big_endian(val);
|
||||
return format_hex(reinterpret_cast<uint8_t *>(&val), sizeof(T));
|
||||
}
|
||||
|
||||
/// Format the byte array \p data of length \p len in pretty-printed, human-readable hex.
|
||||
std::string format_hex_pretty(const uint8_t *data, size_t length);
|
||||
/// Format the vector \p data in pretty-printed, human-readable hex.
|
||||
std::string format_hex_pretty(std::vector<uint8_t> data);
|
||||
/// Format an unsigned integer in pretty-printed, human-readable hex, starting with the most significant byte.
|
||||
template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_hex_pretty(T val) {
|
||||
val = convert_big_endian(val);
|
||||
return format_hex_pretty(reinterpret_cast<uint8_t *>(&val), sizeof(T));
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/// @name Number manipulation
|
||||
|
@ -420,4 +486,18 @@ template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max
|
|||
|
||||
///@}
|
||||
|
||||
/// @name Deprecated functions
|
||||
///@{
|
||||
|
||||
ESPDEPRECATED("hexencode() is deprecated, use format_hex_pretty() instead.", "2022.1")
|
||||
inline std::string hexencode(const uint8_t *data, uint32_t len) { return format_hex_pretty(data, len); }
|
||||
|
||||
template<typename T>
|
||||
ESPDEPRECATED("hexencode() is deprecated, use format_hex_pretty() instead.", "2022.1")
|
||||
std::string hexencode(const T &data) {
|
||||
return hexencode(data.data(), data.size());
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
} // namespace esphome
|
||||
|
|
Loading…
Reference in a new issue