mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Introduce hex parsing & formatting helper functions (#2882)
This commit is contained in:
		| @@ -252,7 +252,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) { | |||||||
|  |  | ||||||
|   // uncomment for even more debugging |   // uncomment for even more debugging | ||||||
| #ifdef HELPER_LOG_PACKETS | #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 | #endif | ||||||
|   frame->msg = std::move(rx_buf_); |   frame->msg = std::move(rx_buf_); | ||||||
|   // consume msg |   // consume msg | ||||||
| @@ -546,7 +546,8 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) { | |||||||
|   size_t total_write_len = 0; |   size_t total_write_len = 0; | ||||||
|   for (int i = 0; i < iovcnt; i++) { |   for (int i = 0; i < iovcnt; i++) { | ||||||
| #ifdef HELPER_LOG_PACKETS | #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 | #endif | ||||||
|     total_write_len += iov[i].iov_len; |     total_write_len += iov[i].iov_len; | ||||||
|   } |   } | ||||||
| @@ -855,7 +856,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { | |||||||
|  |  | ||||||
|   // uncomment for even more debugging |   // uncomment for even more debugging | ||||||
| #ifdef HELPER_LOG_PACKETS | #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 | #endif | ||||||
|   frame->msg = std::move(rx_buf_); |   frame->msg = std::move(rx_buf_); | ||||||
|   // consume msg |   // consume msg | ||||||
| @@ -934,7 +935,8 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt | |||||||
|   size_t total_write_len = 0; |   size_t total_write_len = 0; | ||||||
|   for (int i = 0; i < iovcnt; i++) { |   for (int i = 0; i < iovcnt; i++) { | ||||||
| #ifdef HELPER_LOG_PACKETS | #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 | #endif | ||||||
|     total_write_len += iov[i].iov_len; |     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()); |     ESP_LOGVV(TAG, "  Service UUID: %s", uuid.to_string().c_str()); | ||||||
|   } |   } | ||||||
|   for (auto &data : this->manufacturer_datas_) { |   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()) { |     if (this->get_ibeacon().has_value()) { | ||||||
|       auto ibeacon = this->get_ibeacon().value(); |       auto ibeacon = this->get_ibeacon().value(); | ||||||
|       ESP_LOGVV(TAG, "    iBeacon data:"); |       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_) { |   for (auto &data : this->service_datas_) { | ||||||
|     ESP_LOGVV(TAG, "  Service data:"); |     ESP_LOGVV(TAG, "  Service data:"); | ||||||
|     ESP_LOGVV(TAG, "    UUID: %s", data.uuid.to_string().c_str()); |     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 | #endif | ||||||
| } | } | ||||||
| void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { | 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_() { | void ESP32ImprovComponent::process_incoming_data_() { | ||||||
|   uint8_t length = this->incoming_data_[1]; |   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) { |   if (this->incoming_data_.size() - 3 == length) { | ||||||
|     this->set_error_(improv::ERROR_NONE); |     this->set_error_(improv::ERROR_NONE); | ||||||
|     improv::ImprovCommand command = improv::parse_improv_data(this->incoming_data_); |     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++) { |   for (size_t i = 0; i < 16; i++) { | ||||||
|     if (expected[i] != this->digest_[i]) { |     if (expected[i] != this->digest_[i]) { | ||||||
|       return false; |       return false; | ||||||
| @@ -33,18 +33,10 @@ bool MD5Digest::equals_bytes(const char *expected) { | |||||||
| } | } | ||||||
|  |  | ||||||
| bool MD5Digest::equals_hex(const char *expected) { | bool MD5Digest::equals_hex(const char *expected) { | ||||||
|   for (size_t i = 0; i < 16; i++) { |   uint8_t parsed[16]; | ||||||
|     auto high = parse_hex(expected[i * 2]); |   if (!parse_hex(expected, parsed, 16)) | ||||||
|     auto low = parse_hex(expected[i * 2 + 1]); |  | ||||||
|     if (!high.has_value() || !low.has_value()) { |  | ||||||
|     return false; |     return false; | ||||||
|     } |   return equals_bytes(parsed); | ||||||
|     auto value = (*high << 4) | *low; |  | ||||||
|     if (value != this->digest_[i]) { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return true; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace md5 | }  // namespace md5 | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ class MD5Digest { | |||||||
|   void get_hex(char *output); |   void get_hex(char *output); | ||||||
|  |  | ||||||
|   /// Compare the digest against a provided byte-encoded digest (16 bytes). |   /// 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). |   /// Compare the digest against a provided hex-encoded digest (32 bytes). | ||||||
|   bool equals_hex(const char *expected); |   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); |     this->flow_control_pin_->digital_write(false); | ||||||
|   waiting_for_response = address; |   waiting_for_response = address; | ||||||
|   last_send_ = millis(); |   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 | // Helper function for lambdas | ||||||
| @@ -202,7 +202,7 @@ void Modbus::send_raw(const std::vector<uint8_t> &payload) { | |||||||
|   if (this->flow_control_pin_ != nullptr) |   if (this->flow_control_pin_ != nullptr) | ||||||
|     this->flow_control_pin_->digital_write(false); |     this->flow_control_pin_->digital_write(false); | ||||||
|   waiting_for_response = payload[0]; |   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(); |   last_send_ = millis(); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -61,7 +61,7 @@ void ModbusSwitch::write_state(bool state) { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   if (!data.empty()) { |   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( |     cmd = ModbusCommandItem::create_custom_command( | ||||||
|         this->parent_, data, |         this->parent_, data, | ||||||
|         [this, cmd](ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &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); |   delay(2); | ||||||
|   // First byte, communication mode: Write data |   // First byte, communication mode: Write data | ||||||
|   this->write_byte(0x01); |   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->write_array(data.data(), data.size()); | ||||||
|   this->disable(); |   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->read_array(data.data(), len); | ||||||
|   this->disable(); |   this->disable(); | ||||||
|   data.insert(data.begin(), 0x01); |   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; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -97,7 +97,7 @@ bool PN532Spi::read_response(uint8_t command, std::vector<uint8_t> &data) { | |||||||
|   std::vector<uint8_t> header(7); |   std::vector<uint8_t> header(7); | ||||||
|   this->read_array(header.data(), 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) { |   if (header[0] != 0x00 && header[1] != 0x00 && header[2] != 0xFF) { | ||||||
|     // invalid packet |     // 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->read_array(data.data(), len + 1); | ||||||
|   this->disable(); |   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 |   uint8_t checksum = header[5] + header[6];  // TFI + Command response code | ||||||
|   for (int i = 0; i < len - 1; i++) { |   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_(); } |   bool is_valid() const { return this->data_[OFFSET_CS] == this->calc_cs_(); } | ||||||
|   void finalize() { this->data_[OFFSET_CS] = this->calc_cs_(); } |   void finalize() { this->data_[OFFSET_CS] = this->calc_cs_(); } | ||||||
|   bool check_compliment(const MideaData &rhs) const; |   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 |   // compare only 40-bits | ||||||
|   bool operator==(const MideaData &rhs) const { return !memcmp(this->data_, rhs.data_, OFFSET_CS); } |   bool operator==(const MideaData &rhs) const { return !memcmp(this->data_, rhs.data_, OFFSET_CS); } | ||||||
|   enum MideaDataType : uint8_t { |   enum MideaDataType : uint8_t { | ||||||
|   | |||||||
| @@ -37,9 +37,9 @@ void TuyaLight::setup() { | |||||||
|   } |   } | ||||||
|   if (rgb_id_.has_value()) { |   if (rgb_id_.has_value()) { | ||||||
|     this->parent_->register_listener(*this->rgb_id_, [this](const TuyaDatapoint &datapoint) { |     this->parent_->register_listener(*this->rgb_id_, [this](const TuyaDatapoint &datapoint) { | ||||||
|       auto red = parse_hex(datapoint.value_string, 0, 2); |       auto red = parse_hex<uint8_t>(datapoint.value_string.substr(0, 2)); | ||||||
|       auto green = parse_hex(datapoint.value_string, 2, 2); |       auto green = parse_hex<uint8_t>(datapoint.value_string.substr(2, 2)); | ||||||
|       auto blue = parse_hex(datapoint.value_string, 4, 2); |       auto blue = parse_hex<uint8_t>(datapoint.value_string.substr(4, 2)); | ||||||
|       if (red.has_value() && green.has_value() && blue.has_value()) { |       if (red.has_value() && green.has_value() && blue.has_value()) { | ||||||
|         auto call = this->state_->make_call(); |         auto call = this->state_->make_call(); | ||||||
|         call.set_rgb(float(*red) / 255, float(*green) / 255, float(*blue) / 255); |         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()) { |   } else if (hsv_id_.has_value()) { | ||||||
|     this->parent_->register_listener(*this->hsv_id_, [this](const TuyaDatapoint &datapoint) { |     this->parent_->register_listener(*this->hsv_id_, [this](const TuyaDatapoint &datapoint) { | ||||||
|       auto hue = parse_hex(datapoint.value_string, 0, 4); |       auto hue = parse_hex<uint16_t>(datapoint.value_string.substr(0, 4)); | ||||||
|       auto saturation = parse_hex(datapoint.value_string, 4, 4); |       auto saturation = parse_hex<uint16_t>(datapoint.value_string.substr(4, 4)); | ||||||
|       auto value = parse_hex(datapoint.value_string, 8, 4); |       auto value = parse_hex<uint16_t>(datapoint.value_string.substr(8, 4)); | ||||||
|       if (hue.has_value() && saturation.has_value() && value.has_value()) { |       if (hue.has_value() && saturation.has_value() && value.has_value()) { | ||||||
|         float red, green, blue; |         float red, green, blue; | ||||||
|         hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, 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); |         this->publish_state(datapoint.value_string); | ||||||
|         break; |         break; | ||||||
|       case TuyaDatapointType::RAW: { |       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()); |         ESP_LOGD(TAG, "MCU reported text sensor %u is: %s", datapoint.id, data.c_str()); | ||||||
|         this->publish_state(data); |         this->publish_state(data); | ||||||
|         break; |         break; | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ void Tuya::dump_config() { | |||||||
|   } |   } | ||||||
|   for (auto &info : this->datapoints_) { |   for (auto &info : this->datapoints_) { | ||||||
|     if (info.type == TuyaDatapointType::RAW) |     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) |     else if (info.type == TuyaDatapointType::BOOLEAN) | ||||||
|       ESP_LOGCONFIG(TAG, "  Datapoint %u: switch (value: %s)", info.id, ONOFF(info.value_bool)); |       ESP_LOGCONFIG(TAG, "  Datapoint %u: switch (value: %s)", info.id, ONOFF(info.value_bool)); | ||||||
|     else if (info.type == TuyaDatapointType::INTEGER) |     else if (info.type == TuyaDatapointType::INTEGER) | ||||||
| @@ -104,7 +104,7 @@ bool Tuya::validate_message_() { | |||||||
|   // valid message |   // valid message | ||||||
|   const uint8_t *message_data = data + 6; |   const uint8_t *message_data = data + 6; | ||||||
|   ESP_LOGV(TAG, "Received Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", command, version, |   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); |   this->handle_command_(command, version, message_data, length); | ||||||
|  |  | ||||||
|   // return false to reset rx buffer |   // return false to reset rx buffer | ||||||
| @@ -253,7 +253,7 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) { | |||||||
|   switch (datapoint.type) { |   switch (datapoint.type) { | ||||||
|     case TuyaDatapointType::RAW: |     case TuyaDatapointType::RAW: | ||||||
|       datapoint.value_raw = std::vector<uint8_t>(data, data + data_len); |       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; |       break; | ||||||
|     case TuyaDatapointType::BOOLEAN: |     case TuyaDatapointType::BOOLEAN: | ||||||
|       if (data_len != 1) { |       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), |   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}); |   this->write_array({0x55, 0xAA, version, (uint8_t) command.cmd, len_hi, len_lo}); | ||||||
|   if (!command.payload.empty()) |   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) { | 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); |   optional<TuyaDatapoint> datapoint = this->get_datapoint_(datapoint_id); | ||||||
|   if (!datapoint.has_value()) { |   if (!datapoint.has_value()) { | ||||||
|     ESP_LOGW(TAG, "Setting unknown datapoint %u", datapoint_id); |     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) { | 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)))) { |   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, "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; |     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 + 4, mac_reverse + 1, 1); | ||||||
|     memcpy(mac_address + 5, mac_reverse, 1); |     memcpy(mac_address + 5, mac_reverse, 1); | ||||||
|     ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): authenticated decryption failed."); |     ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): authenticated decryption failed."); | ||||||
|     ESP_LOGVV(TAG, "  MAC address : %s", hexencode(mac_address, 6).c_str()); |     ESP_LOGVV(TAG, "  MAC address : %s", format_hex_pretty(mac_address, 6).c_str()); | ||||||
|     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()); | ||||||
|     ESP_LOGVV(TAG, "          Key : %s", hexencode(vector.key, vector.keysize).c_str()); |     ESP_LOGVV(TAG, "          Key : %s", format_hex_pretty(vector.key, vector.keysize).c_str()); | ||||||
|     ESP_LOGVV(TAG, "           Iv : %s", hexencode(vector.iv, vector.ivsize).c_str()); |     ESP_LOGVV(TAG, "           Iv : %s", format_hex_pretty(vector.iv, vector.ivsize).c_str()); | ||||||
|     ESP_LOGVV(TAG, "       Cipher : %s", hexencode(vector.ciphertext, vector.datasize).c_str()); |     ESP_LOGVV(TAG, "       Cipher : %s", format_hex_pretty(vector.ciphertext, vector.datasize).c_str()); | ||||||
|     ESP_LOGVV(TAG, "          Tag : %s", hexencode(vector.tag, vector.tagsize).c_str()); |     ESP_LOGVV(TAG, "          Tag : %s", format_hex_pretty(vector.tag, vector.tagsize).c_str()); | ||||||
|     mbedtls_ccm_free(&ctx); |     mbedtls_ccm_free(&ctx); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| @@ -295,7 +295,7 @@ bool decrypt_xiaomi_payload(std::vector<uint8_t> &raw, const uint8_t *bindkey, c | |||||||
|   raw[0] &= ~0x08; |   raw[0] &= ~0x08; | ||||||
|  |  | ||||||
|   ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): authenticated decryption passed."); |   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])); |             static_cast<int>(raw[4])); | ||||||
|  |  | ||||||
|   mbedtls_ccm_free(&ctx); |   mbedtls_ccm_free(&ctx); | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_cgd1"; | |||||||
|  |  | ||||||
| void XiaomiCGD1::dump_config() { | void XiaomiCGD1::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "Xiaomi CGD1"); |   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("  ", "Temperature", this->temperature_); | ||||||
|   LOG_SENSOR("  ", "Humidity", this->humidity_); |   LOG_SENSOR("  ", "Humidity", this->humidity_); | ||||||
|   LOG_SENSOR("  ", "Battery Level", this->battery_level_); |   LOG_SENSOR("  ", "Battery Level", this->battery_level_); | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_cgdk2"; | |||||||
|  |  | ||||||
| void XiaomiCGDK2::dump_config() { | void XiaomiCGDK2::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "Xiaomi CGDK2"); |   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("  ", "Temperature", this->temperature_); | ||||||
|   LOG_SENSOR("  ", "Humidity", this->humidity_); |   LOG_SENSOR("  ", "Humidity", this->humidity_); | ||||||
|   LOG_SENSOR("  ", "Battery Level", this->battery_level_); |   LOG_SENSOR("  ", "Battery Level", this->battery_level_); | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_cgg1"; | |||||||
|  |  | ||||||
| void XiaomiCGG1::dump_config() { | void XiaomiCGG1::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "Xiaomi CGG1"); |   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("  ", "Temperature", this->temperature_); | ||||||
|   LOG_SENSOR("  ", "Humidity", this->humidity_); |   LOG_SENSOR("  ", "Humidity", this->humidity_); | ||||||
|   LOG_SENSOR("  ", "Battery Level", this->battery_level_); |   LOG_SENSOR("  ", "Battery Level", this->battery_level_); | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_lywsd03mmc"; | |||||||
|  |  | ||||||
| void XiaomiLYWSD03MMC::dump_config() { | void XiaomiLYWSD03MMC::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "Xiaomi LYWSD03MMC"); |   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("  ", "Temperature", this->temperature_); | ||||||
|   LOG_SENSOR("  ", "Humidity", this->humidity_); |   LOG_SENSOR("  ", "Humidity", this->humidity_); | ||||||
|   LOG_SENSOR("  ", "Battery Level", this->battery_level_); |   LOG_SENSOR("  ", "Battery Level", this->battery_level_); | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ static const char *const TAG = "xiaomi_mhoc401"; | |||||||
|  |  | ||||||
| void XiaomiMHOC401::dump_config() { | void XiaomiMHOC401::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "Xiaomi MHOC401"); |   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("  ", "Temperature", this->temperature_); | ||||||
|   LOG_SENSOR("  ", "Humidity", this->humidity_); |   LOG_SENSOR("  ", "Humidity", this->humidity_); | ||||||
|   LOG_SENSOR("  ", "Battery Level", this->battery_level_); |   LOG_SENSOR("  ", "Battery Level", this->battery_level_); | ||||||
|   | |||||||
| @@ -244,38 +244,6 @@ std::string to_string(long double val) { | |||||||
|   return buf; |   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 fnv1_hash(const std::string &str) { | ||||||
|   uint32_t hash = 2166136261UL; |   uint32_t hash = 2166136261UL; | ||||||
|   for (char c : str) { |   for (char c : str) { | ||||||
| @@ -355,22 +323,6 @@ std::string str_sprintf(const char *fmt, ...) { | |||||||
|   return str; |   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) { | 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 max_color_value = std::max(std::max(red, green), blue); | ||||||
|   float min_color_value = std::min(std::min(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) { | std::string str_truncate(const std::string &str, size_t length) { | ||||||
|   return str.length() > length ? str.substr(0, length) : str; |   return str.length() > length ? str.substr(0, length) : str; | ||||||
| } | } | ||||||
| @@ -468,4 +422,53 @@ std::string str_sanitize(const std::string &str) { | |||||||
|   return out; |   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 | }  // namespace esphome | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <cmath> | #include <cmath> | ||||||
|  | #include <cstring> | ||||||
|  |  | ||||||
| #include <string> | #include <string> | ||||||
| #include <functional> | #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(float val); | ||||||
| std::string to_string(double val); | std::string to_string(double val); | ||||||
| std::string to_string(long 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. | /// 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); | 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); | 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 | // https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/7858971#7858971 | ||||||
| template<int...> struct seq {};                                       // NOLINT | template<int...> struct seq {};                                       // NOLINT | ||||||
| template<int N, int... S> struct gens : gens<N - 1, N - 1, S...> {};  // 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()); |   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 | /// @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 | }  // namespace esphome | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user